PISM, A Parallel Ice Sheet Model stable 0.4.1779

src/base/energy/bedrockThermalUnit.cc

Go to the documentation of this file.
00001 // Copyright (C) 2011 Ed Bueler and Constantine Khroulev
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 "bedrockThermalUnit.hh"
00020 #include "PISMIO.hh"
00021 
00022 bool IceModelVec3BTU::good_init() {
00023   return ((n_levels >= 2) && (Lbz > 0.0) && (v != PETSC_NULL));
00024 }
00025 
00026 
00027 PetscErrorCode IceModelVec3BTU::create(IceGrid &mygrid, const char my_short_name[], bool local,
00028                                       int myMbz, PetscReal myLbz, int stencil_width) {
00029   PetscErrorCode ierr;
00030   if (!utIsInit()) {
00031     SETERRQ(1, "PISM ERROR: UDUNITS *was not* initialized.\n");
00032   }
00033 
00034   if (v != PETSC_NULL) {
00035     SETERRQ1(2,"IceModelVec3BTU with name='%s' already allocated\n",name.c_str());
00036   }
00037 
00038   grid = &mygrid;
00039   name = my_short_name;
00040 
00041   n_levels = myMbz;
00042   Lbz = myLbz;
00043   zlevels.resize(n_levels);
00044   double dz = Lbz / (myMbz - 1);
00045   for (int i = 0; i < n_levels; ++i)
00046     zlevels[i] = -Lbz + i * dz;
00047   zlevels.back() = 0;
00048 
00049   da_stencil_width = stencil_width;
00050   ierr = create_2d_da(da, n_levels, da_stencil_width); CHKERRQ(ierr);
00051 
00052   localp = local;
00053   if (local) {
00054     ierr = DACreateLocalVector(da, &v); CHKERRQ(ierr);
00055   } else {
00056     ierr = DACreateGlobalVector(da, &v); CHKERRQ(ierr);
00057   }
00058 
00059   vars[0].init_3d(name, mygrid, zlevels);
00060   vars[0].dimensions["z"] = "zb";
00061 
00062   map<string,string> &attrs = vars[0].z_attrs;
00063   attrs["axis"]          = "Z";
00064   attrs["long_name"]     = "Z-coordinate in bedrock";
00065   // PROPOSED: attrs["standard_name"] = "projection_z_coordinate_in_lithosphere";
00066   attrs["units"]         = "m";
00067   attrs["positive"]      = "up";
00068 
00069   if (!good_init()) {
00070     SETERRQ1(1,"create() says IceModelVec3BTU with name %s was not properly created\n",
00071              name.c_str());  }
00072   return 0;
00073 }
00074 
00075 PetscErrorCode IceModelVec3BTU::get_layer_depth(PetscReal &depth) {
00076   if (!good_init()) {
00077     SETERRQ1(1,"get_layer_depth() says IceModelVec3BTU with name %s was not properly created\n",
00078              name.c_str());
00079   }
00080   depth = Lbz;
00081   return 0;
00082 }
00083 
00084 PetscErrorCode IceModelVec3BTU::get_spacing(PetscReal &dzb) {
00085   if (!good_init()) {
00086     SETERRQ1(1,"get_spacing() says IceModelVec3BTU with name %s was not properly created\n",
00087              name.c_str());
00088   }
00089   dzb = Lbz / (n_levels - 1);
00090   return 0;
00091 }
00092 
00093 PISMBedThermalUnit::PISMBedThermalUnit(IceGrid &g, const NCConfigVariable &conf)
00094     : PISMComponent_TS(g, conf) {
00095   bedtoptemp = NULL;
00096   ghf        = NULL;
00097 
00098   Mbz = 1;
00099   Lbz = 0;
00100 }
00101 
00104 PetscErrorCode PISMBedThermalUnit::allocate(int my_Mbz, double my_Lbz) {
00105   PetscErrorCode ierr;
00106 
00107   // to allow multiple calls to init() during the initialization sequence
00108   if (temp.was_created()) return 0;
00109 
00110   Mbz = my_Mbz;
00111   Lbz = my_Lbz;
00112 
00113   if ((Lbz <= 0.0) && (Mbz > 1)) {
00114      SETERRQ(1,"PISMBedThermalUnit can not be created with negative or zero Lbz value\n"
00115                " and more than one layers\n"); }
00116 
00117   if (Mbz > 1) {
00118     ierr = temp.create(grid, "litho_temp", false, Mbz, Lbz); CHKERRQ(ierr);
00119     ierr = temp.set_attrs("model_state",
00120                           "lithosphere (bedrock) temperature, in PISMBedThermalUnit",
00121                           "K", ""); CHKERRQ(ierr);
00122     ierr = temp.set_attr("valid_min", 0.0); CHKERRQ(ierr);
00123   }
00124 
00125   return 0;
00126 }
00127 
00128 
00130 PetscErrorCode PISMBedThermalUnit::init(PISMVars &vars) {
00131   PetscErrorCode ierr;
00132   bool i_set, Mbz_set, Lbz_set;
00133   string input_file;
00134   grid_info g;
00135 
00136   t = dt = GSL_NAN;  // every re-init restarts the clock
00137 
00138   ierr = verbPrintf(2,grid.com,
00139       "* Initializing the bedrock thermal unit ... setting constants ...\n"); CHKERRQ(ierr);
00140 
00141   // Get pointers to fields owned by IceModel.
00142   bedtoptemp = dynamic_cast<IceModelVec2S*>(vars.get("bedtoptemp"));
00143   if (bedtoptemp == NULL) SETERRQ(1, "bedtoptemp is not available");
00144 
00145   ghf = dynamic_cast<IceModelVec2S*>(vars.get("bheatflx"));
00146   if (ghf == NULL) SETERRQ(2, "bheatflx is not available");
00147 
00148   Mbz = (PetscInt)config.get("grid_Mbz");
00149   Lbz = (PetscInt)config.get("grid_Lbz");
00150 
00151   // build constant diffusivity for heat equation
00152   bed_rho = config.get("bedrock_thermal_density");
00153   bed_c   = config.get("bedrock_thermal_specific_heat_capacity");
00154   bed_k   = config.get("bedrock_thermal_conductivity");
00155   bed_D   = bed_k / (bed_rho * bed_c);
00156 
00157   ierr = PetscOptionsBegin(grid.com, "", "PISMBedThermalUnit options", ""); CHKERRQ(ierr);
00158   {
00159     ierr = PISMOptionsString("-i", "PISM input file name",
00160                              input_file, i_set); CHKERRQ(ierr);
00161     ierr = PISMOptionsInt("-Mbz", "number of levels in bedrock thermal layer", Mbz, Mbz_set); CHKERRQ(ierr);
00162     ierr = PISMOptionsReal("-Lbz", "depth (thickness) of bedrock thermal layer", Lbz, Lbz_set); CHKERRQ(ierr);
00163   }
00164   ierr = PetscOptionsEnd(); CHKERRQ(ierr);
00165 
00166   if (i_set) {
00167     // If we're initializing from a file we need to get the number of bedrock
00168     // levels and the depth of the bed thermal layer from it:
00169     PISMIO nc(&grid);
00170 
00171     ierr = nc.open_for_reading(input_file); CHKERRQ(ierr);
00172     
00173     bool exists;
00174     ierr = nc.find_variable("litho_temp", NULL, exists); CHKERRQ(ierr);
00175 
00176     if (exists) {
00177       ierr = nc.get_grid_info("litho_temp", g); CHKERRQ(ierr);
00178 
00179       Mbz = g.z_len;
00180       Lbz = - g.z_min;
00181 
00182       ierr = allocate(Mbz, Lbz); CHKERRQ(ierr);
00183 
00184       int last_record = g.t_len - 1;
00185       ierr = temp.read(input_file.c_str(), last_record); CHKERRQ(ierr);
00186     } else {
00187       Mbz = 1;
00188       Lbz = 0;
00189     }
00190 
00191     ierr = nc.close(); CHKERRQ(ierr);
00192 
00193     ierr = ignore_option(grid.com, "-Mbz"); CHKERRQ(ierr);
00194     ierr = ignore_option(grid.com, "-Lbz"); CHKERRQ(ierr);
00195   } else {
00196     // Bootstrapping
00197 
00198     if (Mbz_set && Mbz == 1) {
00199       ierr = ignore_option(grid.com, "-Lbz"); CHKERRQ(ierr);
00200       Lbz = 0;
00201     } else if (Mbz_set ^ Lbz_set) {
00202       PetscPrintf(grid.com, "PISMBedThermalUnit ERROR: please specify both -Mbz and -Lbz.\n");
00203       PISMEnd();
00204     }
00205 
00206     ierr = allocate(Mbz, Lbz); CHKERRQ(ierr);
00207 
00208     ierr = bootstrap(); CHKERRQ(ierr);
00209   }
00210 
00211   // If we're using a minimal model, then we're done:
00212   if (!temp.was_created()) {
00213     ierr = verbPrintf(2,grid.com,
00214       "  minimal model for lithosphere: stored geothermal flux applied to ice base ...\n");
00215       CHKERRQ(ierr);
00216       return 0;
00217   }
00218 
00219   ierr = regrid(); CHKERRQ(ierr);
00220 
00221   return 0;
00222 }
00223 
00224 
00225 void PISMBedThermalUnit::add_vars_to_output(string /*keyword*/, set<string> &result) {
00226   if (temp.was_created()) {
00227     result.insert(temp.string_attr("short_name"));
00228   }
00229 }
00230 
00231 PetscErrorCode PISMBedThermalUnit::define_variables(
00232                          set<string> vars, const NCTool &nc, nc_type nctype) {
00233   if (temp.was_created()) {
00234     PetscErrorCode ierr;
00235     if (set_contains(vars, temp.string_attr("short_name"))) {
00236       ierr = temp.define(nc, nctype); CHKERRQ(ierr);
00237     }
00238   }
00239   return 0;
00240 }
00241 
00242 PetscErrorCode PISMBedThermalUnit::write_variables(set<string> vars, string filename) {
00243   if (temp.was_created()) {
00244     PetscErrorCode ierr;
00245     if (set_contains(vars, temp.string_attr("short_name"))) {
00246       ierr = temp.write(filename.c_str()); CHKERRQ(ierr); 
00247     }
00248   }
00249   return 0;
00250 }
00251 
00252 
00269 PetscErrorCode PISMBedThermalUnit::max_timestep(PetscReal /*t_years*/, PetscReal &dt_years) {
00270 
00271   if (temp.was_created()) {
00272     PetscReal dzb;
00273     temp.get_spacing(dzb);
00274     dt_years = dzb * dzb / (2.0 * bed_D);  // max dt from stability; in seconds
00275     dt_years /= secpera;
00276   } else {
00277     dt_years = 5.0e9; // a long time for ice sheet modeling
00278   }
00279   return 0;
00280 }
00281 
00282 
00283 /* FIXME:  the old scheme had better stability properties, as follows:
00284 
00285 Because there is no advection, the simplest centered implicit (backward Euler) scheme is easily "bombproof" without choosing \f$\lambda\f$, or other complications.  It has this scaled form,
00286 \anchor bedrockeqn
00287 \f[ -R_b T_{k-1}^{n+1} + \left(1 + 2 R_b\right) T_k^{n+1} - R_b T_{k+1}^{n+1}
00288          = T_k^n, \tag{bedrockeqn} \f]
00289 where 
00290   \f[ R_b = \frac{k_b \Delta t}{\rho_b c_b \Delta z^2}. \f]
00291 This is unconditionally stable for a pure bedrock problem, and has a maximum principle, without any further qualification [\ref MortonMayers].
00292 
00293 FIXME:  now a trapezoid rule could be used
00294 */
00295 PetscErrorCode PISMBedThermalUnit::update(PetscReal t_years, PetscReal dt_years) {
00296   PetscErrorCode ierr;
00297 
00298   if (!temp.was_created())  return 0;  // in this case we are up to date
00299 
00300   // as a derived class of PISMComponent_TS, has t,dt members which keep track
00301   // of last update time-interval; so we do some checks ...
00302   // CHECK: has the desired time-interval already been dealt with?
00303   if ((fabs(t_years - t) < 1e-12) && (fabs(dt_years - dt) < 1e-12))  return 0;
00304 
00305   // CHECK: is the desired time interval a forward step?; backward heat equation not good!
00306   if (dt_years < 0) {
00307      SETERRQ(1,"PISMBedThermalUnit::update() does not allow negative timesteps\n"); }
00308   // CHECK: is desired time-interval equal to [t_years,t_years+dt_years] where t_years = t + dt?
00309   if ((!gsl_isnan(t)) && (!gsl_isnan(dt))) { // this check should not fire on first use
00310     bool contiguous = true;
00311 
00312     if (fabs(t + dt) < 1) {
00313       if ( fabs(t_years - (t + dt)) >= 1e-12 ) // check if the absolute difference is small
00314         contiguous = false;
00315     } else {
00316       if ( fabs(t_years - (t + dt)) / (t + dt) >= 1e-12 ) // check if the relative difference is small
00317         contiguous = false;
00318     }
00319 
00320     if (contiguous == false) {
00321      SETERRQ4(2,"PISMBedThermalUnit::update() requires next update to be contiguous with last;\n"
00322                 "  stored:        t = %f a,       dt = %f a\n"
00323                 "  desired: t_years = %f a, dt_years = %f a\n",
00324               t,dt,t_years,dt_years); }
00325   }
00326   // CHECK: is desired time-step too long?
00327   PetscScalar mydtyears;
00328   ierr = max_timestep(t_years,mydtyears); CHKERRQ(ierr);
00329   if (mydtyears < dt_years) {
00330      SETERRQ(3,"PISMBedThermalUnit::update() thinks you asked for too big a timestep\n"); }
00331 
00332   // o.k., we have checked; we are going to do the desired timestep!
00333   t  = t_years;
00334   dt = dt_years;
00335 
00336   if (bedtoptemp == NULL)      SETERRQ(5, "bedtoptemp was never initialized");
00337   if (ghf == NULL)      SETERRQ(6, "bheatflx was never initialized");
00338 
00339   PetscReal dzb;
00340   temp.get_spacing(dzb);
00341   const PetscInt  k0  = Mbz - 1;          // Tb[k0] = ice/bed interface temp, at z=0
00342 
00343 #ifdef PISM_DEBUG
00344   for (PetscInt k = 0; k < Mbz; k++) { // working upward from base
00345     const PetscReal  z = - Lbz + (double)k * dzb;
00346     ierr = temp.isLegalLevel(z); CHKERRQ(ierr);
00347   }
00348 #endif
00349 
00350   const PetscReal bed_R  = bed_D * (dt_years * secpera) / (dzb * dzb);
00351 
00352   PetscScalar *Tbold;
00353   vector<PetscScalar> Tbnew(Mbz);
00354 
00355   ierr = temp.begin_access(); CHKERRQ(ierr);
00356   ierr = ghf->begin_access(); CHKERRQ(ierr);
00357   ierr = bedtoptemp->begin_access(); CHKERRQ(ierr);
00358   for (PetscInt   i = grid.xs; i < grid.xs+grid.xm; ++i) {
00359     for (PetscInt j = grid.ys; j < grid.ys+grid.ym; ++j) {
00360 
00361       ierr = temp.getInternalColumn(i,j,&Tbold); CHKERRQ(ierr); // Tbold actually points into temp memory
00362       Tbold[k0] = (*bedtoptemp)(i,j);  // sets Dirichlet explicit-in-time b.c. at top of bedrock column
00363 
00364       const PetscReal Tbold_negone = Tbold[1] + 2 * (*ghf)(i,j) * dzb / bed_k;
00365       Tbnew[0] = Tbold[0] + bed_R * (Tbold_negone - 2 * Tbold[0] + Tbold[1]);
00366       for (PetscInt k = 1; k < k0; k++) { // working upward from base
00367         Tbnew[k] = Tbold[k] + bed_R * (Tbold[k-1] - 2 * Tbold[k] + Tbold[k+1]);
00368       }
00369       Tbnew[k0] = (*bedtoptemp)(i,j);
00370 
00371       ierr = temp.setInternalColumn(i,j,Tbnew.data()); CHKERRQ(ierr); // copy from Tbnew into temp memory
00372     }
00373   }
00374   ierr = bedtoptemp->end_access(); CHKERRQ(ierr);
00375   ierr = ghf->end_access(); CHKERRQ(ierr);
00376   ierr = temp.end_access(); CHKERRQ(ierr);
00377 
00378   return 0;
00379 }
00380 
00381 
00393 PetscErrorCode PISMBedThermalUnit::get_upward_geothermal_flux(IceModelVec2S &result) {
00394   PetscErrorCode ierr;
00395 
00396   if (!temp.was_created()) {
00397     result.copy_from(*ghf);
00398     return 0;
00399   }
00400 
00401   PetscReal dzb;
00402   temp.get_spacing(dzb);
00403   const PetscInt  k0  = Mbz - 1;  // Tb[k0] = ice/bed interface temp, at z=0
00404 
00405   PetscScalar *Tb;
00406   ierr = temp.begin_access(); CHKERRQ(ierr);
00407   ierr = result.begin_access(); CHKERRQ(ierr);
00408   for (PetscInt   i = grid.xs; i < grid.xs+grid.xm; ++i) {
00409     for (PetscInt j = grid.ys; j < grid.ys+grid.ym; ++j) {
00410       ierr = temp.getInternalColumn(i,j,&Tb); CHKERRQ(ierr);
00411       if (Mbz >= 3) {
00412         result(i,j) = - bed_k * (3 * Tb[k0] - 4 * Tb[k0-1] + Tb[k0-2]) / (2 * dzb);
00413       } else {
00414         result(i,j) = - bed_k * (Tb[k0] - Tb[k0-1]) / dzb;
00415       }
00416     }
00417   }
00418   ierr = temp.end_access(); CHKERRQ(ierr);
00419   ierr = result.end_access(); CHKERRQ(ierr);
00420 
00421   return 0;
00422 }
00423 
00424 PetscErrorCode PISMBedThermalUnit::regrid() {
00425   PetscErrorCode ierr;
00426   bool regrid_file_set, regrid_vars_set;
00427   string regrid_file;
00428   vector<string> regrid_vars;
00429 
00430   ierr = PetscOptionsBegin(grid.com, "", "PISMBedThermalUnit regridding options", ""); CHKERRQ(ierr);
00431   {
00432     ierr = PISMOptionsString("-regrid_file", "regridding file name",
00433                              regrid_file, regrid_file_set); CHKERRQ(ierr);
00434     ierr = PISMOptionsStringArray("-regrid_vars", "comma-separated list of regridding variables",
00435                                   "", regrid_vars, regrid_vars_set); CHKERRQ(ierr);
00436   }
00437   ierr = PetscOptionsEnd(); CHKERRQ(ierr);
00438 
00439   // stop unless both options are set
00440   if (! regrid_file_set) return 0;
00441 
00442   // stop if temp was not allocated
00443   if (! temp.was_created()) return 0;
00444 
00445   set<string> vars;
00446   for (unsigned int i = 0; i < regrid_vars.size(); ++i)
00447     vars.insert(regrid_vars[i]);
00448 
00449   // stop if the user did not ask to regrid BTU temperature
00450   if (regrid_vars_set && ! set_contains(vars, temp.string_attr("short_name")))
00451     return 0;
00452 
00453   ierr = temp.regrid(regrid_file.c_str(), true); CHKERRQ(ierr);
00454 
00455   return 0;
00456 }
00457 
00458 PetscErrorCode PISMBedThermalUnit::bootstrap() {
00459   PetscErrorCode ierr;
00460 
00461   if (Mbz < 2) return 0;
00462 
00463   ierr = verbPrintf(2,grid.com,
00464                     "  bootstrapping to fill lithosphere temperatures in bedrock thermal layers,\n"
00465                     "    using provided bedtoptemp and a linear function from provided geothermal flux ...\n");
00466   CHKERRQ(ierr);
00467 
00468   PetscScalar* Tb;
00469   PetscReal dzb;
00470   temp.get_spacing(dzb);
00471   const PetscInt k0 = Mbz-1; // Tb[k0] = ice/bedrock interface temp
00472 
00473   ierr = bedtoptemp->begin_access(); CHKERRQ(ierr);
00474   ierr = ghf->begin_access(); CHKERRQ(ierr);
00475   ierr = temp.begin_access(); CHKERRQ(ierr);
00476   for (PetscInt   i = grid.xs; i < grid.xs+grid.xm; ++i) {
00477     for (PetscInt j = grid.ys; j < grid.ys+grid.ym; ++j) {
00478       ierr = temp.getInternalColumn(i,j,&Tb); CHKERRQ(ierr); // Tb points into temp memory
00479       Tb[k0] = (*bedtoptemp)(i,j);
00480       for (PetscInt k = k0-1; k >= 0; k--) {
00481         Tb[k] = Tb[k+1] + dzb * (*ghf)(i,j) / bed_k;
00482       }
00483     }
00484   }
00485   ierr = bedtoptemp->end_access(); CHKERRQ(ierr);
00486   ierr = ghf->end_access(); CHKERRQ(ierr);
00487   ierr = temp.end_access(); CHKERRQ(ierr);
00488 
00489   return 0;
00490 }
00491 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines