PISM, A Parallel Ice Sheet Model 2.3.0-79cae578d committed by Constantine Khrulev on 2026-03-22
Loading...
Searching...
No Matches
EnergyModel.cc
Go to the documentation of this file.
1/* Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025 PISM Authors
2 *
3 * This file is part of PISM.
4 *
5 * PISM is free software; you can redistribute it and/or modify it under the
6 * terms of the GNU General Public License as published by the Free Software
7 * Foundation; either version 3 of the License, or (at your option) any later
8 * version.
9 *
10 * PISM is distributed in the hope that it will be useful, but WITHOUT ANY
11 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13 * details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with PISM; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include "pism/energy/EnergyModel.hh"
21#include "pism/energy/utilities.hh"
22#include "pism/stressbalance/StressBalance.hh"
23#include "pism/util/MaxTimestep.hh"
24#include "pism/util/Profiling.hh"
25#include "pism/util/Vars.hh"
26#include "pism/util/array/CellType.hh"
27#include "pism/util/error_handling.hh"
28#include "pism/util/io/File.hh"
29#include "pism/util/pism_utilities.hh"
30#include "pism/util/Logger.hh"
31#include "pism/util/io/IO_Flags.hh"
32
33namespace pism {
34namespace energy {
35
36static void check_input(const array::Array *ptr, const char *name) {
37 if (ptr == NULL) {
38 throw RuntimeError::formatted(PISM_ERROR_LOCATION, "energy balance model input %s was not provided", name);
39 }
40}
41
44 basal_heat_flux = NULL;
45 cell_type = NULL;
46 ice_thickness = NULL;
48 shelf_base_temp = NULL;
49 surface_temp = NULL;
51
53 u3 = NULL;
54 v3 = NULL;
55 w3 = NULL;
56
57 no_model_mask = NULL;
58}
59
60void Inputs::check() const {
61 check_input(cell_type, "cell_type");
62 check_input(basal_frictional_heating, "basal_frictional_heating");
63 check_input(basal_heat_flux, "basal_heat_flux");
64 check_input(ice_thickness, "ice_thickness");
65 check_input(surface_liquid_fraction, "surface_liquid_fraction");
66 check_input(shelf_base_temp, "shelf_base_temp");
67 check_input(surface_temp, "surface_temp");
68 check_input(till_water_thickness, "till_water_thickness");
69
70 check_input(volumetric_heating_rate, "volumetric_heating_rate");
71 check_input(u3, "u3");
72 check_input(v3, "v3");
73 check_input(w3, "w3");
74}
75
82
90
91
92bool marginal(const array::Scalar1 &thickness, int i, int j, double threshold) {
93 auto H = thickness.box(i, j);
94
95 return ((H.e < threshold) or (H.ne < threshold) or (H.n < threshold) or (H.nw < threshold) or
96 (H.w < threshold) or (H.sw < threshold) or (H.s < threshold) or (H.se < threshold));
97}
98
99
106
107
108EnergyModel::EnergyModel(std::shared_ptr<const Grid> grid,
109 std::shared_ptr<const stressbalance::StressBalance> stress_balance)
110 : Component(grid),
111 m_ice_enthalpy(m_grid, "enthalpy", array::WITH_GHOSTS, m_grid->z(),
112 m_config->get_number("grid.max_stencil_width")),
113 m_work(m_grid, "work_vector", array::WITHOUT_GHOSTS, m_grid->z()),
114 m_basal_melt_rate(m_grid, "basal_melt_rate_grounded"),
115 m_stress_balance(stress_balance) {
116
117 // POSSIBLE standard name = land_ice_enthalpy
119 .long_name("ice enthalpy (includes sensible heat, latent heat, pressure)")
120 .units("J kg^-1");
121
122 {
123 // ghosted to allow the "redundant" computation of tauc
125 .long_name(
126 "ice basal melt rate from energy conservation, in ice thickness per time (valid in grounded areas)")
127 .units("m s^-1");
128 // We could use land_ice_basal_melt_rate, but that way both basal_melt_rate_grounded and bmelt
129 // have this standard name.
130 m_basal_melt_rate.metadata()["comment"] = "positive basal melt rate corresponds to ice loss";
131 }
132
133 // a 3d work vector
134 m_work.metadata(0).long_name("usually new values of temperature or enthalpy during time step");
135}
136
137void EnergyModel::init_enthalpy(const File &input_file, bool do_regrid, int record) {
138
139 if (input_file.variable_exists("enthalpy")) {
140 if (do_regrid) {
142 } else {
143 m_ice_enthalpy.read(input_file, record);
144 }
145 } else if (input_file.variable_exists("temp")) {
146 array::Array3D &temp = m_work, &liqfrac = m_ice_enthalpy;
147
148 {
149 temp.set_name("temp");
150 temp.metadata(0).set_name("temp");
151 temp.metadata(0)
152 .long_name("ice temperature")
153 .units("kelvin")
154 .standard_name("land_ice_temperature");
155
156 if (do_regrid) {
157 temp.regrid(input_file, io::Default::Nil());
158 } else {
159 temp.read(input_file, record);
160 }
161 }
162
163 const array::Scalar &ice_thickness = *m_grid->variables().get_2d_scalar("land_ice_thickness");
164
165 if (input_file.variable_exists("liqfrac")) {
166 auto enthalpy_metadata = m_ice_enthalpy.metadata();
167
168 liqfrac.set_name("liqfrac");
169 liqfrac.metadata(0).set_name("liqfrac");
170 liqfrac.metadata(0).long_name("ice liquid water fraction").units("1");
171
172 if (do_regrid) {
173 liqfrac.regrid(input_file, io::Default::Nil());
174 } else {
175 liqfrac.read(input_file, record);
176 }
177
178 m_ice_enthalpy.set_name(enthalpy_metadata.get_name());
179 m_ice_enthalpy.metadata() = enthalpy_metadata;
180
181 m_log->message(2,
182 " - Computing enthalpy using ice temperature,"
183 " liquid water fraction and thickness...\n");
184 compute_enthalpy(temp, liqfrac, ice_thickness, m_ice_enthalpy);
185 } else {
186 m_log->message(2,
187 " - Computing enthalpy using ice temperature and thickness "
188 "and assuming zero liquid water fraction...\n");
189 compute_enthalpy_cold(temp, ice_thickness, m_ice_enthalpy);
190 }
191 } else {
193 "neither enthalpy nor temperature was found in '%s'.\n",
194 input_file.name().c_str());
195 }
196}
197
198/*!
199 * The `-regrid_file` may contain enthalpy, temperature, or *both* temperature and liquid water
200 * fraction.
201 */
203
204 auto regrid_filename = m_config->get_string("input.regrid.file");
205 auto regrid_vars = set_split(m_config->get_string("input.regrid.vars"), ',');
206
207
208 if (regrid_filename.empty()) {
209 return;
210 }
211
212 std::string enthalpy_name = m_ice_enthalpy.metadata().get_name();
213
214 if (regrid_vars.empty() or set_member(enthalpy_name, regrid_vars)) {
215 File regrid_file(m_grid->com, regrid_filename, io::PISM_GUESS, io::PISM_READONLY);
216 init_enthalpy(regrid_file, true, 0);
217 }
218}
219
220
221void EnergyModel::restart(const File &input_file, int record) {
222 this->restart_impl(input_file, record);
223}
224
225void EnergyModel::bootstrap(const File &input_file,
226 const array::Scalar &ice_thickness,
227 const array::Scalar &surface_temperature,
228 const array::Scalar &climatic_mass_balance,
229 const array::Scalar &basal_heat_flux) {
230 this->bootstrap_impl(input_file,
231 ice_thickness, surface_temperature,
232 climatic_mass_balance, basal_heat_flux);
233}
234
235void EnergyModel::initialize(const array::Scalar &basal_melt_rate,
236 const array::Scalar &ice_thickness,
237 const array::Scalar &surface_temperature,
238 const array::Scalar &climatic_mass_balance,
239 const array::Scalar &basal_heat_flux) {
240 this->initialize_impl(basal_melt_rate,
241 ice_thickness,
242 surface_temperature,
243 climatic_mass_balance,
244 basal_heat_flux);
245}
246
247void EnergyModel::update(double t, double dt, const Inputs &inputs) {
248 // reset standard out flags at the beginning of every time step
249 m_stdout_flags = "";
251
252 profiling().begin("ice_energy");
253 {
254 // this call should fill m_work with new values of enthalpy
255 this->update_impl(t, dt, inputs);
256
258 }
259 profiling().end("ice_energy");
260
261 // globalize m_stats and update m_stdout_flags
262 {
263 m_stats.sum(m_grid->com);
264
265 if (m_stats.reduced_accuracy_counter > 0.0) { // count of when BOMBPROOF switches to lower accuracy
266 const double reduced_accuracy_percentage = 100.0 * (m_stats.reduced_accuracy_counter / (m_grid->Mx() * m_grid->My()));
267 const double reporting_threshold = 5.0; // only report if above 5%
268
269 if (reduced_accuracy_percentage > reporting_threshold and m_log->get_threshold() > 2) {
270 m_stdout_flags = (pism::printf(" [BPsacr=%.4f%%] ", reduced_accuracy_percentage) +
272 }
273 }
274
275 if (m_stats.bulge_counter > 0) {
276 // count of when advection bulges are limited; frequently it is identically zero
279 }
280 }
281}
282
284 // silence a compiler warning
285 (void) t;
286
287 if (m_stress_balance == NULL) {
289 "EnergyModel: no stress balance provided."
290 " Cannot compute max. time step.");
291 }
292
293 return MaxTimestep(m_stress_balance->max_timestep_cfl_3d().dt_max.value(), "energy");
294}
295
296const std::string& EnergyModel::stdout_flags() const {
297 return m_stdout_flags;
298}
299
301 return m_stats;
302}
303
305 return m_ice_enthalpy;
306}
307
308/*! @brief Basal melt rate in grounded areas. (It is set to zero elsewhere.) */
312
313/*! @brief Ice loss "flux" due to ice liquefaction. */
314class LiquifiedIceFlux : public TSDiag<TSFluxDiagnostic,EnergyModel> {
315public:
317 : TSDiag<TSFluxDiagnostic, EnergyModel>(m, "liquified_ice_flux") {
318
319 set_units("m^3 / second", "m^3 / year");
320 m_variable["long_name"] =
321 "rate of ice loss due to liquefaction, averaged over the reporting interval";
322 m_variable["comment"] = "positive means ice loss";
323 m_variable["cell_methods"] = "time: mean";
324 }
325protected:
326 double compute() {
327 // liquified ice volume during the last time step
328 return model->stats().liquified_ice_volume;
329 }
330};
331
333 DiagnosticList result;
334 result = {
335 {"enthalpy", Diagnostic::wrap(m_ice_enthalpy)},
336 {"basal_melt_rate_grounded", Diagnostic::wrap(m_basal_melt_rate)}
337 };
338 return result;
339}
340
342 return {
343 {"liquified_ice_flux", TSDiagnostic::Ptr(new LiquifiedIceFlux(this))}
344 };
345}
346
347} // end of namespace energy
348
349} // end of namespace pism
std::shared_ptr< const Config > m_config
configuration database used by this component
Definition Component.hh:160
const std::shared_ptr< const Grid > m_grid
grid used by this component
Definition Component.hh:158
const Profiling & profiling() const
Definition Component.cc:115
std::shared_ptr< const Logger > m_log
logger (for easy access)
Definition Component.hh:164
A class defining a common interface for most PISM sub-models.
Definition Component.hh:118
static Ptr wrap(const T &input)
bool variable_exists(const std::string &short_name) const
Checks if a variable exists.
Definition File.cc:378
std::string name() const
Definition File.cc:274
High-level PISM I/O class.
Definition File.hh:57
Combines the max. time step with the flag indicating if a restriction is active. Makes is possible to...
void begin(const char *name) const
Definition Profiling.cc:75
void end(const char *name) const
Definition Profiling.cc:91
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
void set_units(const std::string &units, const std::string &output_units)
VariableMetadata m_variable
std::shared_ptr< TSDiagnostic > Ptr
Scalar diagnostic reporting a "flux".
VariableMetadata & long_name(const std::string &input)
VariableMetadata & units(const std::string &input)
VariableMetadata & set_name(const std::string &name)
VariableMetadata & standard_name(const std::string &input)
std::string get_name() const
stencils::Box< T > box(int i, int j) const
Definition Array2D.hh:121
void copy_from(const Array3D &input)
Definition Array3D.cc:215
A virtual class collecting methods common to ice and bedrock 3D fields.
Definition Array3D.hh:33
void read(const std::string &filename, unsigned int time)
Definition Array.cc:753
void set_name(const std::string &name)
Sets the variable name to name.
Definition Array.cc:345
void regrid(const std::string &filename, io::Default default_value)
Definition Array.cc:758
VariableMetadata & metadata(unsigned int N=0)
Returns a reference to the VariableMetadata object containing metadata for the compoment N.
Definition Array.cc:467
Abstract class for reading, writing, allocating, and accessing a DA-based PETSc Vec (2D and 3D fields...
Definition Array.hh:209
EnergyModelStats & operator+=(const EnergyModelStats &other)
void init_enthalpy(const File &input_file, bool regrid, int record)
Initialize enthalpy by reading it from a file, or by reading temperature and liquid water fraction,...
const EnergyModelStats & stats() const
void update(double t, double dt, const Inputs &inputs)
const array::Array3D & enthalpy() const
void bootstrap(const File &input_file, const array::Scalar &ice_thickness, const array::Scalar &surface_temperature, const array::Scalar &climatic_mass_balance, const array::Scalar &basal_heat_flux)
Bootstrapping using heuristics.
virtual TSDiagnosticList scalar_diagnostics_impl() const
void initialize(const array::Scalar &basal_melt_rate, const array::Scalar &ice_thickness, const array::Scalar &surface_temperature, const array::Scalar &climatic_mass_balance, const array::Scalar &basal_heat_flux)
Initialize using formulas (for runs using synthetic data).
virtual MaxTimestep max_timestep_impl(double t) const
EnergyModelStats m_stats
std::shared_ptr< const stressbalance::StressBalance > m_stress_balance
const std::string & stdout_flags() const
virtual void initialize_impl(const array::Scalar &basal_melt_rate, const array::Scalar &ice_thickness, const array::Scalar &surface_temperature, const array::Scalar &climatic_mass_balance, const array::Scalar &basal_heat_flux)=0
void restart(const File &input_file, int record)
virtual void restart_impl(const File &input_file, int record)=0
const array::Scalar & basal_melt_rate() const
Basal melt rate in grounded areas. (It is set to zero elsewhere.)
array::Array3D m_ice_enthalpy
virtual void bootstrap_impl(const File &input_file, const array::Scalar &ice_thickness, const array::Scalar &surface_temperature, const array::Scalar &climatic_mass_balance, const array::Scalar &basal_heat_flux)=0
EnergyModel(std::shared_ptr< const Grid > grid, std::shared_ptr< const stressbalance::StressBalance > stress_balance)
virtual void update_impl(double t, double dt, const Inputs &inputs)=0
array::Scalar m_basal_melt_rate
virtual DiagnosticList spatial_diagnostics_impl() const
void regrid_enthalpy()
Regrid enthalpy from the -regrid_file.
const array::Scalar * surface_liquid_fraction
const array::Scalar1 * ice_thickness
const array::Array3D * w3
const array::Array3D * v3
const array::Scalar * shelf_base_temp
const array::Scalar * basal_heat_flux
const array::Scalar * basal_frictional_heating
const array::Scalar * till_water_thickness
const array::Scalar * no_model_mask
const array::Array3D * u3
const array::Scalar * surface_temp
const array::CellType * cell_type
const array::Array3D * volumetric_heating_rate
LiquifiedIceFlux(const EnergyModel *m)
Ice loss "flux" due to ice liquefaction.
static Default Nil()
Definition IO_Flags.hh:94
#define PISM_ERROR_LOCATION
static void check_input(const array::Array *ptr, const char *name)
bool marginal(const array::Scalar1 &thickness, int i, int j, double threshold)
void compute_enthalpy_cold(const array::Array3D &temperature, const array::Scalar &ice_thickness, array::Array3D &result)
Compute ice enthalpy from temperature temperature by assuming the ice has zero liquid fraction.
Definition utilities.cc:48
void compute_enthalpy(const array::Array3D &temperature, const array::Array3D &liquid_water_fraction, const array::Scalar &ice_thickness, array::Array3D &result)
Compute result (enthalpy) from temperature and liquid fraction.
Definition utilities.cc:105
@ PISM_GUESS
Definition IO_Flags.hh:57
@ PISM_READONLY
open an existing file for reading only
Definition IO_Flags.hh:69
std::map< std::string, TSDiagnostic::Ptr > TSDiagnosticList
std::string printf(const char *format,...)
std::map< std::string, Diagnostic::Ptr > DiagnosticList
std::set< std::string > set_split(const std::string &input, char separator)
Transform a separator-separated list (a string) into a set of strings.
void GlobalSum(MPI_Comm comm, double *local, double *result, int count)
bool set_member(const std::string &string, const std::set< std::string > &set)