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