|
PISM, A Parallel Ice Sheet Model stable 0.4.1779
|
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
1.7.3