Loading [MathJax]/extensions/tex2jax.js
PISM, A Parallel Ice Sheet Model 2.2.2-d6b3a29ca committed by Constantine Khrulev on 2025-03-28
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
IBIceModel.cc
Go to the documentation of this file.
1#include <iostream>
2#include <limits>
3#include <memory>
4#include <string>
5
6#include "pism/energy/BedThermalUnit.hh"
7#include "pism/util/EnthalpyConverter.hh"
8#include "pism/util/io/File.hh"
9#include "pism/util/io/io_helpers.hh"
10
11#include "pism/icebin/IBIceModel.hh"
12#include "pism/icebin/IBSurfaceModel.hh"
13#include "pism/coupler/ocean/Factory.hh"
14#include "pism/energy/EnergyModel.hh"
15
16namespace pism {
17namespace icebin {
18
19static double const NaN = std::numeric_limits<double>::quiet_NaN();
20
21// ================================
22
23IBIceModel::IBIceModel(std::shared_ptr<pism::Grid> grid, const std::shared_ptr<Context> &context,
24 IBIceModel::Params const &_params)
25 : pism::IceModel(grid, context),
26 params(_params),
27 // FIXME: should `prefix` be empty below?
28 base(grid, ""),
29 cur(grid, ""),
30 rate(grid, ""),
31 ice_top_senth(grid, "ice_top_senth"),
32 elevmask_ice(grid, "elevmask_ice"),
33 elevmask_land(grid, "elevmask_land") {
34
35 std::cout << "IBIceModel Conservation Formulas:" << std::endl;
36 cur.print_formulas(std::cout);
37}
38
40 // empty
41}
42
43
46 // indicates it has already been allocated
47 return;
48 }
49
51}
52
53
56
57 m_log->message(2, "# Allocating the icebin surface model...\n");
58 m_surface = std::make_shared<IBSurfaceModel>(m_grid);
59 m_submodels["surface process model"] = m_surface.get();
60}
61
63#if 0 // Avoid warnings until we put something in this method
64
65 // Enthalpy and mass continuity are stepped with different timesteps.
66 // Fish out the timestep relevant to US.
67 const double my_t0 = m_grid.time->current();
68 const double my_dt = this->dt;
69#endif
70}
71
72
74#if 0 // Avoid warnings until we put something in this method
75
76 // Enthalpy and mass continuity are stepped with different timesteps.
77 // Fish out the timestep relevant to US.
78 const double my_t0 = m_grid.time->current();
79 const double my_dt = this->dt;
80#endif
81}
82
83
85
86 printf("BEGIN IBIceModel::energyStep(t=%f, dt=%f)\n", t_TempAge, dt_TempAge);
87
88 // Enthalpy and mass continuity are stepped with different timesteps.
89 // Fish out the timestep relevant to US.
90 // const double my_t0 = t_TempAge; // Time at beginning of timestep
91 const double my_dt = dt_TempAge;
92
93 // =========== BEFORE Energy Step
94
95 // =========== The Energy Step Itself
97
98 // =========== AFTER Energy Step
99
100 // We need to integrate over strain_heating and geothermal_flux, which
101 // are given in PISM as rates.
102
103
104 // --------- Upward Geothermal Flux
105 // Use actual geothermal flux, not the long-term average..
106 // See: file:///Users/rpfische/git/pism/build/doc/browser/html/classPISMBedThermalUnit.html#details
107 { cur.upward_geothermal_flux.add(my_dt, m_btu->flux_through_top_surface()); }
108
109 // ----------- Geothermal Flux
110 cur.geothermal_flux.add(my_dt, m_btu->flux_through_bottom_surface());
111
112 // ---------- Basal Frictional Heating (see iMenthalpy.cc l. 220)
113 array::Scalar const &Rb(m_stress_balance->basal_frictional_heating());
115
116 // NOTE: strain_heating is inf at the coastlines.
117 // See: https://github.com/pism/pism/issues/292
118 // ------------ Volumetric Strain Heating
119 // strain_heating_sum += my_dt * sum_columns(strainheating3p)
120 const auto &strain_heating3 = m_stress_balance->volumetric_strain_heating();
121
122 array::sum_columns(strain_heating3, 1.0, my_dt, cur.strain_heating);
123}
124
126
127 printf("BEGIN IBIceModel::MassContExplicitStep()\n");
128
129 m_ice_density = m_config->get_number("constants.ice.density");
131
132
133 // =========== The Mass Continuity Step Itself
134 // This will call through to accumulateFluxes_massContExplicitStep()
135 // in the inner loop. We must open access to variables we will use
136 // in that subroutine.
137 {
140
141 // FIXME: update to use PISM's current mass transport code
142 // super::massContExplicitStep();
143 }
144
145 // =========== AFTER the Mass Continuity Step
146
147 // ----------- SMB: Pass inputs through to outputs.
148 // They are needed to participate in mass/energy budget
149 // This allows them to be used in the MassEnergyBudget computation.
150 IBSurfaceModel *ib_surface = ib_surface_model();
151
152 {
153 array::AccessScope access{ &ib_surface->massxfer, &ib_surface->enthxfer, &ib_surface->deltah,
154 &cur.smb, &cur.deltah };
155
156 for (auto p = m_grid->points(); p; p.next()) {
157 const int i = p.i(), j = p.j();
158
159 cur.smb.mass(i, j) += m_dt * ib_surface->massxfer(i, j);
160 cur.smb.enth(i, j) += m_dt * ib_surface->enthxfer(i, j);
161 cur.deltah(i, j) += m_dt * ib_surface->deltah(i, j);
162 }
163 }
164}
165
166
167/** This is called IMMEDIATELY after ice is gained/lost in
168iMgeometry.cc (massContExplicitStep()). Here we can record the same
169values that PISM saw when moving ice around. */
171 int i, int j,
172 double surface_mass_balance, // [m s-1] ice equivalent
173 double basal_melt_rate, // [m s-1] ice equivalent
174 double divQ_SIA, // [m s-1] ice equivalent
175 double divQ_SSA, // [m s-1] ice equivalent
176 double Href_to_H_flux, // [m] ice equivalent
177 double nonneg_rule_flux) // [m] ice equivalent
178{
179 EnthalpyConverter::Ptr EC = ctx()->enthalpy_converter();
180
181 const auto &ice_thickness = m_geometry.ice_thickness;
182
183 const auto &ice_enthalpy = m_energy_model->enthalpy();
184
185 // -------------- Melting
186 double p_basal = EC->pressure(ice_thickness(i, j));
187 double T = EC->melting_temperature(p_basal);
188 double specific_enth_basal = EC->enthalpy_permissive(T, 1.0, p_basal);
189 double mass;
190
191 // ------- Melting at base of ice sheet
192 mass = -basal_melt_rate * m_meter_per_s_to_kg_per_m2;
193 // TODO: Change this to just cur.melt_rate
194 cur.melt_grounded.mass(i, j) += mass;
195 cur.melt_grounded.enth(i, j) += mass * specific_enth_basal;
196
197 // -------------- internal_advection
198 const int ks = m_grid->kBelowHeight(ice_thickness(i, j));
199 const double *Enth = ice_enthalpy.get_column(i, j);
200 double specific_enth_top = Enth[ks]; // Approximate, we will use the enthalpy of the top layer...
201
202 mass = -(divQ_SIA + divQ_SSA) * m_meter_per_s_to_kg_per_m2;
203
204 cur.internal_advection.mass(i, j) += mass;
205 cur.internal_advection.enth(i, j) += mass * specific_enth_top;
206
207
208 // -------------- Get the easy variables out of the way...
209 mass = surface_mass_balance * m_meter_per_s_to_kg_per_m2;
210 cur.pism_smb.mass(i, j) += mass;
211 cur.pism_smb.enth(i, j) += mass * specific_enth_top;
212 cur.nonneg_rule(i, j) -= nonneg_rule_flux * m_ice_density;
213 cur.href_to_h(i, j) += Href_to_H_flux * m_ice_density;
214}
215
216/** Differences and divides by accumulated time over coupling timestep.
217Eg, to convert [kg m-2] --> [kg m-2 s-1]
218@param t0 Time of last time we coupled. */
219void IBIceModel::set_rate(double dt) {
220
221 if (dt == 0)
222 throw RuntimeError(PISM_ERROR_LOCATION, "Coupling timestep has size dt=0");
223
224 double by_dt = 1.0 / dt;
225 printf("IBIceModel::set_rate(dt=%f)\n", dt);
226
229
230 // Compute differences, and set base = cur
231 auto base_ii(base.all_vecs.begin());
232 auto cur_ii(cur.all_vecs.begin());
233 auto rate_ii(rate.all_vecs.begin());
234 for (; base_ii != base.all_vecs.end(); ++base_ii, ++cur_ii, ++rate_ii) {
235 array::Scalar &vbase(base_ii->vec);
236 array::Scalar &vcur(cur_ii->vec);
237 array::Scalar &vrate(rate_ii->vec);
238
239 {
240 array::AccessScope access{ &vbase, &vcur, &vrate };
241 for (auto p = m_grid->points(); p; p.next()) {
242 const int i = p.i(), j = p.j();
243
244 // rate = cur - base: Just for DELTA and EPSILON flagged vectors
245 if (base_ii->flags & (MassEnergyBudget::DELTA | MassEnergyBudget::EPSILON)) {
246 vrate(i, j) = (vcur(i, j) - vbase(i, j)) * by_dt;
247 } else {
248 // Or else just copy the to "rate"
249 vrate(i, j) = vcur(i, j);
250 }
251 }
252 }
253 }
254}
255
257 // Compute differences, and set base = cur
258 auto base_ii(base.all_vecs.begin());
259 auto cur_ii(cur.all_vecs.begin());
260 for (; base_ii != base.all_vecs.end(); ++base_ii, ++cur_ii) {
261 array::Scalar &vbase(base_ii->vec);
262 array::Scalar &vcur(cur_ii->vec);
263
264 // This cannot go in the loop above with PETSc because
265 // vbase is needed on the RHS of the equations above.
266 array::AccessScope access{ &vbase, &vcur };
267 for (auto p = m_grid->points(); p; p.next()) {
268 const int i = p.i(), j = p.j();
269 // base = cur: For ALL vectors
270 vbase(i, j) = vcur(i, j);
271 }
272 }
273}
274
275void IBIceModel::prepare_outputs(double time_s) {
276 (void)time_s;
277
278 EnthalpyConverter::Ptr EC = ctx()->enthalpy_converter();
279
280 // --------- ice_surface_enth from m_ice_enthalpy
281 const auto &ice_surface_elevation = m_geometry.ice_surface_elevation;
282 const auto &cell_type = m_geometry.cell_type;
283 const auto &ice_enthalpy = m_energy_model->enthalpy();
284 const auto &ice_thickness = m_geometry.ice_thickness;
285
286 array::AccessScope access{ &ice_enthalpy, &ice_thickness, // INPUTS
287 &ice_surface_elevation, &cell_type, &ice_top_senth,
288 &elevmask_ice, &elevmask_land }; // OUTPUT
289 for (int i = m_grid->xs(); i < m_grid->xs() + m_grid->xm(); ++i) {
290 for (int j = m_grid->ys(); j < m_grid->ys() + m_grid->ym(); ++j) {
291 double const *Enth = ice_enthalpy.get_column(i, j);
292
293 // Top Layer
294 auto ks = m_grid->kBelowHeight(ice_thickness(i, j));
295 double senth = Enth[ks]; // [J kg-1]
296 ice_top_senth(i, j) = senth;
297
298 // elevmask_ice and elevmask_land: Used by IceBin for elevation and masking
299 switch ((int)cell_type(i, j)) {
300 case MASK_GROUNDED:
301 case MASK_FLOATING:
302 elevmask_ice(i, j) = ice_surface_elevation(i, j);
303 elevmask_land(i, j) = ice_surface_elevation(i, j);
304 break;
306 elevmask_ice(i, j) = NaN;
307 elevmask_land(i, j) = ice_surface_elevation(i, j);
308 break;
310 case MASK_UNKNOWN:
311 elevmask_ice(i, j) = NaN;
312 elevmask_land(i, j) = NaN;
313 break;
314 }
315 }
316 }
317
318 // ====================== Write to the post_energy.nc file (OPTIONAL)
319}
320
321void IBIceModel::dumpToFile(const std::string &filename) const {
322 File file(m_grid->com, filename,
323 string_to_backend(m_config->get_string("output.format")), io::PISM_READWRITE_MOVE);
324
326 write_run_stats(file, run_stats());
327
328 // assume that "dumpToFile" is expected to save the model state *only*.
329 save_variables(file, INCLUDE_MODEL_STATE, {}, m_time->current());
330}
331
333 // super::m_grid_setup() trashes m_time->start(). Now set it correctly.
334 m_time->set_start(params.time_start_s);
336
337 m_log->message(2, "* Run time: [%s, %s] (%s years, using the '%s' calendar)\n",
338 m_time->date(m_time->start()).c_str(), m_time->date(m_time->end()).c_str(),
339 m_time->run_length().c_str(), m_time->calendar().c_str());
340}
341
344
345
346 // ------ Initialize MassEnth structures: base, cur, rate
347 for (auto &ii : cur.all_vecs) {
348 ii.vec.set(0);
349 }
352
353 // base = cur
354 auto base_ii(base.all_vecs.begin());
355 auto cur_ii(cur.all_vecs.begin());
356 for (; base_ii != base.all_vecs.end(); ++base_ii, ++cur_ii) {
357 base_ii->vec.copy_from(cur_ii->vec);
358 }
359}
360
361/** Sums over columns to compute enthalpy on 2D m_grid.
362This is used to track conservation of energy.
363
364NOTE: Unfortunately so far PISM does not keep track of enthalpy in
365"partially-filled" cells, so Enth2(i,j) is not valid at locations like
366this one. We need to address this, but so far, it seems to me, the
367best thing we can do is estimate Enth2(i,j) at partially-filled cells
368by computing the average over icy neighbors. I think you can re-use
369the idea from IceModel::get_threshold_thickness(...) (iMpartm_grid->cc). */
370
372 // getInternalColumn() is allocated already
373 double ice_density = m_config->get_number("constants.ice.density", "kg m-3");
374
375 const auto &ice_enthalpy = m_energy_model->enthalpy();
376 const auto &ice_thickness = m_geometry.ice_thickness;
377
378
379 array::AccessScope access{ &ice_thickness, &ice_enthalpy, // Inputs
380 &enth2, &mass2 }; // Outputs
381 for (auto p = m_grid->points(); p; p.next()) {
382 const int i = p.i(), j = p.j();
383
384 enth2(i, j) = 0;
385 mass2(i, j) = 0;
386
387 // count all ice, including cells that have so little they
388 // are considered "ice-free"
389 if (ice_thickness(i, j) > 0) {
390 int ks = (int)m_grid->kBelowHeight(ice_thickness(i, j));
391 const auto *Enth = ice_enthalpy.get_column(i, j);
392
393 for (int k = 0; k < ks; ++k) {
394 double dz = (m_grid->z(k + 1) - m_grid->z(k));
395 enth2(i, j) += Enth[k] * dz; // [m J kg-1]
396 }
397
398 // Last layer is (potentially) partial
399 double dz = (ice_thickness(i, j) - m_grid->z(ks));
400 enth2(i, j) += Enth[ks] * dz; // [m J kg-1]
401
402 // Finish off after all layers processed
403 enth2(i, j) *= ice_density; // --> [J m-2]
404 mass2(i, j) = ice_thickness(i, j) * ice_density; // --> [kg m-2]
405 }
406 }
407}
408
409
410/** Merges surface temperature derived from m_ice_enthalpy into any NaN values
411in the vector provided.
412@param deltah IN: Input from Icebin (change in enthalpy of each m_grid
413 cell over the timestep) [W m-2].
414@param default_val: The value that deltah(i,j) will have if no value
415 is listed for that m_grid cell
416@param timestep_s: Length of the current coupling timestep [s]
417@param surface_temp OUT: Resulting surface temperature to use as the Dirichlet B.C.
418*/
420 pism::array::Scalar &deltah, // IN: Input from Icebin
421 double default_val,
422 double timestep_s, // Length of this coupling interval [s]
423 pism::array::Scalar &surface_temp) // OUT: Temperature @ top of ice sheet (to use for Dirichlet B.C.)
424
425{
426 printf("BEGIN IBIceModel::merge_surface_temp default_val=%g\n", default_val);
427 EnthalpyConverter::Ptr EC = ctx()->enthalpy_converter();
428
429 double ice_density = m_config->get_number("constants.ice.density");
430 const auto &ice_enthalpy = m_energy_model->enthalpy();
431 const auto &ice_thickness = m_geometry.ice_thickness;
432
433 {
434 array::AccessScope access{ &ice_enthalpy, &deltah, &ice_thickness, &surface_temp };
435
436 // First time around, set effective_surface_temp to top temperature
437 for (int i = m_grid->xs(); i < m_grid->xs() + m_grid->xm(); ++i) {
438 for (int j = m_grid->ys(); j < m_grid->ys() + m_grid->ym(); ++j) {
439 double &surface_temp_ij(surface_temp(i, j));
440 double const &deltah_ij(deltah(i, j));
441
442 const auto *Enth = ice_enthalpy.get_column(i, j);
443
444 // Enthalpy at top of ice sheet
445 const int ks = m_grid->kBelowHeight(ice_thickness(i, j));
446 double spec_enth3 = Enth[ks]; // Specific enthalpy [J kg-1]
447
448 if (deltah_ij != default_val) {
449 // Adjust enthalpy @top by deltah
450 double toplayer_dz = ice_thickness(i, j) - m_grid->z(ks); // [m]
451
452 // [J kg-1] = [J kg-1]
453 // + [J m-2 s-1] * [m^2 m-3] * [m^3 kg-1] * [s]
454 spec_enth3 = spec_enth3 + deltah_ij / (toplayer_dz * ice_density) * timestep_s;
455 }
456
457
458 // Convert specific enthalpy value to surface temperature
459 const double p = 0.0; // Pressure at top of ice sheet
460 surface_temp_ij = EC->temperature(spec_enth3, p); // [K]
461 }
462 }
463 }
464
465 printf("END IBIceModel::merge_surface_temp\n");
466}
467} // namespace icebin
468} // namespace pism
std::shared_ptr< EnthalpyConverter > Ptr
High-level PISM I/O class.
Definition File.hh:55
array::Scalar2 ice_surface_elevation
Definition Geometry.hh:57
array::CellType2 cell_type
Definition Geometry.hh:55
array::Scalar2 ice_thickness
Definition Geometry.hh:51
std::map< std::string, const Component * > m_submodels
the list of sub-models, for writing model states and obtaining diagnostics
Definition IceModel.hh:252
std::shared_ptr< stressbalance::StressBalance > m_stress_balance
Definition IceModel.hh:395
VariableMetadata run_stats() const
Definition utilities.cc:91
virtual void energy_step()
Manage the solution of the energy equation, and related parallel communication.
Definition energy.cc:60
std::shared_ptr< surface::SurfaceModel > m_surface
Definition IceModel.hh:279
const Time::Ptr m_time
Time manager.
Definition IceModel.hh:246
Geometry m_geometry
Definition IceModel.hh:288
virtual void misc_setup()
Miscellaneous initialization tasks plus tasks that need the fields that can come from regridding.
const Logger::Ptr m_log
Logger.
Definition IceModel.hh:244
double dt() const
Definition IceModel.cc:126
Config::Ptr m_config
Configuration flags and parameters.
Definition IceModel.hh:238
std::shared_ptr< energy::BedThermalUnit > m_btu
Definition IceModel.hh:259
virtual void save_variables(const File &file, OutputKind kind, const std::set< std::string > &variables, double time, io::Type default_diagnostics_type=io::PISM_FLOAT) const
Definition output.cc:150
double t_TempAge
time of last update for enthalpy/temperature
Definition IceModel.hh:313
std::shared_ptr< Context > ctx() const
Return the context this model is running in.
Definition utilities.cc:126
virtual void allocate_couplers()
virtual void write_metadata(const File &file, MappingTreatment mapping_flag, HistoryTreatment history_flag) const
Write time-independent metadata to a file.
Definition output.cc:72
std::unique_ptr< hydrology::Hydrology > m_subglacial_hydrology
Definition IceModel.hh:254
double dt_TempAge
enthalpy/temperature and age time-steps
Definition IceModel.hh:315
double m_dt
mass continuity time step, s
Definition IceModel.hh:311
std::shared_ptr< energy::EnergyModel > m_energy_model
Definition IceModel.hh:260
const std::shared_ptr< Grid > m_grid
Computational grid.
Definition IceModel.hh:236
Makes sure that we call begin_access() and end_access() for all accessed array::Arrays.
Definition Array.hh:64
void add(double alpha, const Array2D< T > &x)
Definition Array2D.hh:65
pism::array::Scalar elevmask_ice
Definition IBIceModel.hh:60
pism::array::Scalar ice_top_senth
Definition IBIceModel.hh:56
void construct_surface_temp(pism::array::Scalar &deltah, double default_val, double timestep_s, pism::array::Scalar &surface_temp)
virtual void misc_setup()
Miscellaneous initialization tasks plus tasks that need the fields that can come from regridding.
MassEnergyBudget base
Definition IBIceModel.hh:48
void dumpToFile(const std::string &filename) const
virtual void time_setup()
Initialize time from an input file or command-line options.
virtual void accumulateFluxes_massContExplicitStep(int i, int j, double surface_mass_balance, double basal_melt_rate, double divQ_SIA, double divQ_SSA, double Href_to_H_flux, double nonneg_rule_flux)
pism::icebin::IBSurfaceModel * ib_surface_model()
virtual void allocate_couplers()
Definition IBIceModel.cc:54
virtual void massContExplicitStep()
IBIceModel(std::shared_ptr< pism::Grid > grid, const std::shared_ptr< Context > &context, IBIceModel::Params const &_params)
Definition IBIceModel.cc:23
void compute_enth2(pism::array::Scalar &enth2, pism::array::Scalar &mass2)
void prepare_outputs(double time_s)
MassEnergyBudget rate
Definition IBIceModel.hh:50
void energy_step()
Manage the solution of the energy equation, and related parallel communication.
Definition IBIceModel.cc:84
virtual void allocate_subglacial_hydrology()
Decide which subglacial hydrology model to use.
Definition IBIceModel.cc:44
MassEnergyBudget cur
Definition IBIceModel.hh:49
void set_rate(double dt)
pism::array::Scalar elevmask_land
Definition IBIceModel.hh:62
pism::array::Scalar enthxfer
pism::array::Scalar massxfer
pism::array::Scalar href_to_h
SMB as seen by PISM in iMgeometry.cc massContExplicitSte(). Used to check icebin_smb....
std::ostream & print_formulas(std::ostream &out)
pism::array::Scalar geothermal_flux
Total amount of geothermal energy [J/m^2].
MassEnthVec2S smb
accumulation / ablation, as provided by Icebin
pism::array::Scalar basal_frictional_heating
Total amount of basal friction heating [J/m^2].
std::vector< VecWithFlags > all_vecs
pism::array::Scalar strain_heating
Total amount of strain heating [J/m^2].
MassEnthVec2S melt_grounded
basal melt (grounded) (from summing meltrate_grounded)
pism::array::Scalar upward_geothermal_flux
Total amount of geothermal energy [J/m^2].
MassEnthVec2S melt_floating
sub-shelf melt (from summing meltrate_floating)
pism::array::Scalar deltah
Change in enthalpy of top layer.
#define PISM_ERROR_LOCATION
void sum_columns(const Array3D &data, double A, double B, Scalar &output)
Definition Array3D.cc:191
static double const NaN
Definition IBIceModel.cc:19
@ PISM_READWRITE_MOVE
create a file for writing, move foo.nc to foo.nc~ if present
Definition IO_Flags.hh:74
io::Backend string_to_backend(const std::string &backend)
Definition File.cc:56
std::string printf(const char *format,...)
static const double k
Definition exactTestP.cc:42
@ MASK_FLOATING
Definition Mask.hh:34
@ MASK_ICE_FREE_OCEAN
Definition Mask.hh:35
@ MASK_ICE_FREE_BEDROCK
Definition Mask.hh:32
@ MASK_GROUNDED
Definition Mask.hh:33
@ MASK_UNKNOWN
Definition Mask.hh:31
void write_run_stats(const File &file, const pism::VariableMetadata &stats)
Definition output.cc:143