|
PISM, A Parallel Ice Sheet Model stable 0.4.1779
|
00001 // Copyright (C) 2010, 2011 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 "PSExternal.hh" 00020 #include "PISMIO.hh" 00021 #include <sys/file.h> 00022 #include <time.h> 00023 00024 PSExternal::~PSExternal() { 00025 int done = 1; 00026 00027 // tell the EBM driver to stop: 00028 if (grid.rank == 0) { 00029 MPI_Send(&done, 1, MPI_INT, 0, TAG_EBM_STOP, inter_comm); 00030 } 00031 } 00032 00034 PetscErrorCode PSExternal::init(PISMVars &vars) { 00035 PetscErrorCode ierr; 00036 00037 ierr = verbPrintf(2, grid.com, 00038 "* Initializing the PISM surface model running an external program\n" 00039 " to compute top-surface boundary conditions...\n"); CHKERRQ(ierr); 00040 00041 ierr = acab.create(grid, "acab", false); CHKERRQ(ierr); 00042 ierr = acab.set_attrs( 00043 "climate_from_PISMSurfaceModel", // FIXME: can we do better? 00044 "ice-equivalent surface mass balance (accumulation/ablation) rate", 00045 "m s-1", // m *ice-equivalent* per second 00046 "land_ice_surface_specific_mass_balance"); // CF standard_name 00047 CHKERRQ(ierr); 00048 ierr = acab.set_glaciological_units("m year-1"); CHKERRQ(ierr); 00049 acab.write_in_glaciological_units = true; 00050 acab.set_attr("comment", "positive values correspond to ice gain"); 00051 00052 // annual mean air temperature at "ice surface", at level below all firn 00053 // processes (e.g. "10 m" or ice temperatures) 00054 ierr = artm.create(grid, "artm", false); CHKERRQ(ierr); 00055 ierr = artm.set_attrs( 00056 "climate_from_PISMSurfaceModel", // FIXME: can we do better? 00057 "annual average ice surface temperature, below firn processes", 00058 "K", 00059 ""); // PROPOSED CF standard_name = land_ice_surface_temperature_below_firn 00060 CHKERRQ(ierr); 00061 00062 vector<string> ebm_var_names; 00063 bool ebm_input_set, ebm_output_set, ebm_command_set; 00064 ierr = PetscOptionsBegin(grid.com, "", "PSExternal model options", ""); CHKERRQ(ierr); 00065 { 00066 bool flag; 00067 ierr = PISMOptionsReal("-update_interval", "Energy balance model update interval, years", 00068 update_interval, flag); CHKERRQ(ierr); 00069 00070 ierr = PISMOptionsString("-ebm_input_file", "Name of the file an external boundary model will read data", 00071 ebm_input, ebm_input_set); CHKERRQ(ierr); 00072 00073 ierr = PISMOptionsString("-ebm_output_file", 00074 "Name of the file into which an external boundary model will write B.C.", 00075 ebm_output, ebm_output_set); CHKERRQ(ierr); 00076 00077 ierr = PISMOptionsString("-ebm_command", 00078 "Command (with options) running an external boundary model", 00079 ebm_command, ebm_command_set); CHKERRQ(ierr); 00080 00081 ierr = PISMOptionsStringArray("-ebm_vars", 00082 "Comma-separated list of variables an EBM needs to compute B.C.s", 00083 "usurf,topg", ebm_var_names, flag); CHKERRQ(ierr); 00084 } 00085 ierr = PetscOptionsEnd(); CHKERRQ(ierr); 00086 00087 for (unsigned int i = 0; i < ebm_var_names.size(); ++i) { 00088 IceModelVec *var = vars.get(ebm_var_names[i]); 00089 00090 if (var) { 00091 ebm_vars.push_back(var); 00092 } else { 00093 ierr = verbPrintf(2, grid.com, 00094 "WARNING: variable %s is not available\n", ebm_var_names[i].c_str()); 00095 } 00096 } 00097 00098 // The first time an EBM is run to pre-compute B.C. it is done after a half 00099 // of the interval, i.e. in the middle. Then this variable is reset to 00100 // update_interval. 00101 ebm_update_interval = 0.5 * update_interval; 00102 00103 // Initialize the EBM driver: 00104 if (grid.rank == 0) { 00105 char command[PETSC_MAX_PATH_LEN]; 00106 strncpy(command, ebm_command.c_str(), PETSC_MAX_PATH_LEN); 00107 MPI_Send(command, PETSC_MAX_PATH_LEN, MPI_CHAR, 0, TAG_EBM_COMMAND, inter_comm); 00108 } 00109 00110 if (! (ebm_input_set && ebm_output_set && ebm_command_set)) { 00111 PetscPrintf(grid.com, 00112 "PISM ERROR: you need to specify all three of -ebm_input_file, -ebm_output_file and -ebm_command.\n"); 00113 00114 // tell the EBM side to stop: 00115 if (grid.rank == 0) { 00116 int done = 1; 00117 MPI_Send(&done, 1, MPI_INT, 0, TAG_EBM_STOP, inter_comm); 00118 } 00119 00120 PISMEnd(); 00121 } 00122 00123 return 0; 00124 } 00125 00126 PetscErrorCode PSExternal::ice_surface_mass_flux(IceModelVec2S &result) { 00127 PetscErrorCode ierr; 00128 00129 ierr = acab.copy_to(result); CHKERRQ(ierr); 00130 00131 return 0; 00132 } 00133 00134 PetscErrorCode PSExternal::ice_surface_temperature(IceModelVec2S &result) { 00135 PetscErrorCode ierr; 00136 00137 ierr = artm.copy_to(result); CHKERRQ(ierr); 00138 00139 return 0; 00140 } 00141 00142 PetscErrorCode PSExternal::max_timestep(PetscReal t_years, PetscReal &dt_years) { 00143 double delta = 0.5 * update_interval; 00144 double next_update = ceil(t_years / delta) * delta; 00145 00146 if (PetscAbs(next_update - t_years) < 1e-6) 00147 next_update = t_years + delta; 00148 00149 dt_years = next_update - t_years; 00150 00151 return 0; 00152 } 00153 00156 PetscErrorCode PSExternal::update(PetscReal t_years, PetscReal dt_years) { 00157 PetscErrorCode ierr; 00158 00159 if ((fabs(t_years - t) < 1e-12) && 00160 (fabs(dt_years - dt) < 1e-12)) 00161 return 0; 00162 00163 t = t_years; 00164 dt = dt_years; 00165 00166 // This convoluted comparison is here to make it update the B.C. at the 00167 // beginning of a run, when last_bc_update_year is NAN, plus later on when 00168 // necessary (any comparison with a NAN evaluates to "false"). 00169 if (! (t_years + dt_years <= last_bc_update_year + update_interval) ) { 00170 00171 if (ebm_is_running) { 00172 // EBM is running (pre-computing B.C.) 00173 ierr = wait(); CHKERRQ(ierr); 00174 } else { 00175 // EBM is not running (probably at the beginning of a run) 00176 ierr = run(t_years); CHKERRQ(ierr); 00177 ierr = wait(); CHKERRQ(ierr); 00178 } 00179 00180 ierr = update_acab(); CHKERRQ(ierr); 00181 ierr = update_artm(); CHKERRQ(ierr); 00182 last_bc_update_year = t_years; 00183 } else if (t_years + dt_years > last_ebm_update_year + ebm_update_interval) { 00184 if (ebm_is_running) { 00185 ierr = wait(); CHKERRQ(ierr); 00186 } 00187 00188 // we're at the end of a run, so no pre-computing is necessary. 00189 if (PetscAbs(t_years + dt_years - grid.end_year) < 1e-12) 00190 return 0; 00191 00192 // time to run EBM to pre-compute B.C. 00193 ierr = run(t_years); CHKERRQ(ierr); 00194 00195 // EBM always runs at the beginning of a PISM run, then after 1/2 of an 00196 // update interval. After than it runs once per update interval, in the 00197 // middle of it. 00198 ebm_update_interval = update_interval; 00199 } 00200 00201 return 0; 00202 } 00203 00204 PetscErrorCode PSExternal::update_artm() { 00205 PetscErrorCode ierr; 00206 00207 ierr = verbPrintf(2, grid.com, "Reading the temperature at the top of the ice from %s for year = %1.1f...\n", 00208 ebm_output.c_str(), t); 00209 00210 ierr = artm.regrid(ebm_output.c_str(), true); CHKERRQ(ierr); 00211 00212 return 0; 00213 } 00214 00215 PetscErrorCode PSExternal::update_acab() { 00216 PetscErrorCode ierr; 00217 00218 ierr = verbPrintf(2, grid.com, "Reading the accumulation/ablation rate from %s for year = %1.1f...\n", 00219 ebm_output.c_str(), t); 00220 00221 ierr = acab.regrid(ebm_output.c_str(), true); CHKERRQ(ierr); 00222 00223 return 0; 00224 } 00225 00227 PetscErrorCode PSExternal::write_coupling_fields() { 00228 PetscErrorCode ierr; 00229 PISMIO nc(&grid); 00230 00231 ierr = nc.open_for_writing(ebm_input.c_str(), 00232 true, false); CHKERRQ(ierr); 00233 // "append" (i.e. do not move the file aside) and do not check dimensions. 00234 00235 // Determine if the file is empty; if it is, create dimenstions and 00236 // dimensional variables, otherwise overwrite the time stored in the time 00237 // variable. 00238 unsigned int t_len; 00239 ierr = nc.get_dim_length("t", &t_len); CHKERRQ(ierr); 00240 00241 if (t_len == 0) { 00242 ierr = nc.create_dimensions(); CHKERRQ(ierr); 00243 ierr = nc.append_time(grid.year); CHKERRQ(ierr); 00244 } else { 00245 int t_varid; 00246 bool t_exists; 00247 ierr = nc.find_variable("t", &t_varid, t_exists); CHKERRQ(ierr); 00248 00249 vector<double> time(1); 00250 time[0] = grid.year; 00251 ierr = nc.put_dimension(t_varid, time); CHKERRQ(ierr); 00252 } 00253 00254 // define 00255 for (unsigned int i = 0; i < ebm_vars.size(); ++i) { 00256 IceModelVec *var = ebm_vars[i]; 00257 ierr = var->define(nc, NC_DOUBLE); CHKERRQ(ierr); 00258 } 00259 00260 ierr = nc.close(); CHKERRQ(ierr); 00261 00262 // write 00263 for (unsigned int i = 0; i < ebm_vars.size(); ++i) { 00264 IceModelVec *var = ebm_vars[i]; 00265 ierr = var->write(ebm_input.c_str()); CHKERRQ(ierr); 00266 } 00267 00268 return 0; 00269 } 00270 00272 PetscErrorCode PSExternal::run(double t_years) { 00273 PetscErrorCode ierr; 00274 double year = (double)t_years; 00275 00276 ierr = write_coupling_fields(); CHKERRQ(ierr); 00277 00278 if (grid.rank == 0) { 00279 MPI_Send(&year, 1, MPI_DOUBLE, 0, TAG_EBM_RUN, inter_comm); 00280 } 00281 00282 last_ebm_update_year = t_years; 00283 ebm_is_running = true; 00284 00285 return 0; 00286 } 00287 00289 PetscErrorCode PSExternal::wait() { 00290 int ebm_status; 00291 00292 if (grid.rank == 0) { 00293 double sleep_interval = 0.01, // seconds 00294 threshold = 60, // wait at most 1 minute 00295 message_interval = 5; // print a message every 5 seconds 00296 struct timespec rq; 00297 rq.tv_sec = 0; 00298 rq.tv_nsec = (long)(sleep_interval*1e9); // convert to nanoseconds 00299 00300 int wait_counter = 0, 00301 wait_message_counter = 1; 00302 00303 MPI_Status status; 00304 while (wait_counter * sleep_interval < threshold) { 00305 int flag; 00306 MPI_Iprobe(0, TAG_EBM_STATUS, inter_comm, &flag, &status); 00307 00308 if (flag) // we got a status message 00309 break; 00310 00311 if (sleep_interval * wait_counter / message_interval > wait_message_counter) { 00312 fprintf(stderr, "PISM: Waiting for a message from the EBM driver...\n"); 00313 wait_message_counter++; 00314 } 00315 nanosleep(&rq, 0); 00316 wait_counter++; 00317 } 00318 00319 if (sleep_interval * wait_counter >= threshold) { 00320 // exited the loop above because of a timeout 00321 fprintf(stderr, "ERROR: spent %1.1f minutes waiting for the EBM driver... Giving up...\n", 00322 threshold / 60.0); 00323 PISMEnd(); 00324 } 00325 00326 // fprintf(stderr, "PISM: Got a status message from EBM\n"); 00327 00328 // receive the EBM status 00329 MPI_Recv(&ebm_status, 1, MPI_INT, 00330 0, TAG_EBM_STATUS, inter_comm, NULL); 00331 00332 MPI_Barrier(grid.com); 00333 } else { 00334 MPI_Barrier(grid.com); 00335 } 00336 00337 // Broadcast status: 00338 MPI_Bcast(&ebm_status, 1, MPI_INT, 0, grid.com); 00339 00340 if (ebm_status == EBM_STATUS_FAILED) { 00341 PetscPrintf(grid.com, "PISM ERROR: EBM run failed. Exiting...\n"); 00342 PISMEnd(); 00343 } 00344 00345 ebm_is_running = false; 00346 00347 return 0; 00348 } 00349 00350 00351 void PSExternal::add_vars_to_output(string keyword, set<string> &result) { 00352 if (keyword == "big") { 00353 result.insert("acab"); 00354 result.insert("artm"); 00355 } 00356 } 00357 00358 PetscErrorCode PSExternal::define_variables(set<string> vars, const NCTool &nc, 00359 nc_type nctype) { 00360 PetscErrorCode ierr; 00361 00362 ierr = PISMSurfaceModel::define_variables(vars, nc, nctype); CHKERRQ(ierr); 00363 00364 if (set_contains(vars, "artm")) { 00365 ierr = artm.define(nc, nctype); CHKERRQ(ierr); 00366 } 00367 00368 if (set_contains(vars, "acab")) { 00369 ierr = acab.define(nc, nctype); CHKERRQ(ierr); 00370 } 00371 00372 return 0; 00373 } 00374 00375 PetscErrorCode PSExternal::write_variables(set<string> vars, string filename) { 00376 PetscErrorCode ierr; 00377 00378 if (set_contains(vars, "artm")) { 00379 ierr = artm.write(filename.c_str()); CHKERRQ(ierr); 00380 } 00381 00382 if (set_contains(vars, "acab")) { 00383 ierr = acab.write(filename.c_str()); CHKERRQ(ierr); 00384 } 00385 00386 return 0; 00387 } 00388 00390 00391 PetscErrorCode PSExternal_ALR::init(PISMVars &vars) { 00392 PetscErrorCode ierr; 00393 string pism_input; 00394 bool regrid; 00395 int start; 00396 00397 ierr = PSExternal::init(vars); CHKERRQ(ierr); 00398 00399 ierr = verbPrintf(2, grid.com, 00400 " [ using an atmospheric lapse rate correction for the temperature at the top of the ice ]\n"); 00401 CHKERRQ(ierr); 00402 00403 usurf = dynamic_cast<IceModelVec2S*>(vars.get("surface_altitude")); 00404 if (usurf == NULL) SETERRQ(1, "surface_altitude is not available"); 00405 00406 // artm_0 is the initial condition; artm_0 = artm(t_0) + gamma*usurf(t_0) 00407 ierr = artm_0.create(grid, "usurf", false); CHKERRQ(ierr); 00408 ierr = artm_0.set_attrs("internal", "ice upper surface elevation", 00409 "m", "surface_altitude"); CHKERRQ(ierr); 00410 00411 ierr = find_pism_input(pism_input, regrid, start); CHKERRQ(ierr); 00412 00413 if (regrid) { 00414 ierr = artm_0.regrid(pism_input.c_str(), true); CHKERRQ(ierr); 00415 ierr = artm.regrid(pism_input.c_str(), true); CHKERRQ(ierr); 00416 } else { 00417 ierr = artm_0.read(pism_input.c_str(), start); CHKERRQ(ierr); 00418 ierr = artm.read(pism_input.c_str(), start); CHKERRQ(ierr); 00419 } 00420 00421 ierr = PetscOptionsBegin(grid.com, "", "PSExternal_ALR options", ""); CHKERRQ(ierr); 00422 { 00423 bool flag; 00424 ierr = PISMOptionsReal("-artm_lapse_rate", "Top of the ice temperature lapse rate, degrees K per kilometer", 00425 gamma, flag); CHKERRQ(ierr); 00426 } 00427 ierr = PetscOptionsEnd(); CHKERRQ(ierr); 00428 00429 gamma = gamma / 1000; // convert to K/meter 00430 00431 // Use gamma to compute the initial condition: 00432 ierr = artm_0.scale(gamma); CHKERRQ(ierr); 00433 ierr = artm_0.add(1.0, artm); CHKERRQ(ierr); 00434 00435 return 0; 00436 } 00437 00439 void PSExternal_ALR::add_vars_to_output(string keyword, set<string> &result) { 00440 result.insert("artm"); 00441 00442 if (keyword == "big") { 00443 result.insert("acab"); 00444 } 00445 } 00446 00448 PetscErrorCode PSExternal_ALR::update_artm() { 00449 PetscErrorCode ierr; 00450 00451 ierr = usurf->begin_access(); CHKERRQ(ierr); 00452 ierr = artm.begin_access(); CHKERRQ(ierr); 00453 ierr = artm_0.begin_access(); CHKERRQ(ierr); 00454 for (PetscInt i=grid.xs; i<grid.xs+grid.xm; ++i) { 00455 for (PetscInt j=grid.ys; j<grid.ys+grid.ym; ++j) { 00456 artm(i,j) = artm_0(i,j) - gamma * (*usurf)(i,j); 00457 } 00458 } 00459 ierr = usurf->end_access(); CHKERRQ(ierr); 00460 ierr = artm.end_access(); CHKERRQ(ierr); 00461 ierr = artm_0.end_access(); CHKERRQ(ierr); 00462 00463 return 0; 00464 }
1.7.3