PISM, A Parallel Ice Sheet Model stable 0.4.1779

src/coupler/PSExternal.cc

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