PISM, A Parallel Ice Sheet Model  stable v2.1-1-g6902d5502 committed by Ed Bueler on 2023-12-20 08:38:27 -0800
enthSystem.cc
Go to the documentation of this file.
1 // Copyright (C) 2009-2018, 2020, 2021, 2022, 2023 Andreas Aschwanden and Ed Bueler and Constantine Khroulev
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 #include <cmath> // NAN, std::isnan, std::pow
20 #include <algorithm> // std::min
21 
22 #include "pism/energy/enthSystem.hh"
23 #include "pism/util/ConfigInterface.hh"
24 #include "pism/util/EnthalpyConverter.hh"
25 
26 #include "pism/util/error_handling.hh"
27 
28 namespace pism {
29 namespace energy {
30 
31 enthSystemCtx::enthSystemCtx(const std::vector<double>& storage_grid,
32  const std::string &prefix,
33  double dx, double dy, double dt,
34  const Config &config,
35  const array::Array3D &Enth3,
36  const array::Array3D &u3,
37  const array::Array3D &v3,
38  const array::Array3D &w3,
39  const array::Array3D &strain_heating3,
41 : columnSystemCtx(storage_grid, prefix, dx, dy, dt, u3, v3, w3),
42  m_Enth3(Enth3),
43  m_strain_heating3(strain_heating3),
44  m_EC(EC) {
45 
46  using std::pow;
47 
48  // set some values so we can check if init was called
49  m_R_cold = -1.0;
50  m_R_temp = -1.0;
51  m_lambda = -1.0;
52  m_D0 = NAN;
53  m_U0 = NAN;
54  m_B0 = NAN;
55  m_L_ks = NAN;
56  m_D_ks = NAN;
57  m_U_ks = NAN;
58  m_B_ks = NAN;
59 
60  m_ice_density = config.get_number("constants.ice.density");
61  m_ice_c = config.get_number("constants.ice.specific_heat_capacity");
62  m_ice_k = config.get_number("constants.ice.thermal_conductivity");
63  m_p_air = config.get_number("surface.pressure");
64 
65  m_margin_exclude_horizontal_advection = config.get_flag("energy.margin_exclude_horizontal_advection");
66  m_margin_exclude_vertical_advection = config.get_flag("energy.margin_exclude_vertical_advection");
67  m_margin_exclude_strain_heat = config.get_flag("energy.margin_exclude_strain_heating");
68 
69  size_t Mz = m_z.size();
70  m_Enth.resize(Mz);
71  m_Enth_s.resize(Mz);
72  m_strain_heating.resize(Mz);
73  m_R.resize(Mz);
74 
75  m_E_n.resize(Mz);
76  m_E_e.resize(Mz);
77  m_E_s.resize(Mz);
78  m_E_w.resize(Mz);
79 
80  m_nu = m_dt / m_dz;
81 
82  double
83  ratio = config.get_number(prefix + ".temperate_ice_thermal_conductivity_ratio"),
84  K = m_ice_k / m_ice_c,
85  K0 = (ratio * m_ice_k) / m_ice_c;
86 
87  m_R_factor = m_dt / (pow(m_dz, 2) * m_ice_density);
88  m_R_cold = K * m_R_factor;
89  m_R_temp = K0 * m_R_factor;
90 
91  if (config.get_flag("energy.temperature_dependent_thermal_conductivity")) {
92  m_k_depends_on_T = true;
93  } else {
94  m_k_depends_on_T = false;
95  }
96 }
97 
98 /*!
99  In this implementation \f$k\f$ does not depend on temperature.
100  */
101 double enthSystemCtx::k_from_T(double T) const {
102 
103  if (m_k_depends_on_T) {
104  return 9.828 * exp(-0.0057 * T);
105  }
106 
107  return m_ice_k;
108 }
109 
110 void enthSystemCtx::init(int i, int j, bool marginal, double ice_thickness) {
111  m_ice_thickness = ice_thickness;
112 
114 
116 
117  if (m_ks == 0) {
118  return;
119  }
120 
121  coarse_to_fine(m_u3, m_i, m_j, m_u.data());
122  coarse_to_fine(m_v3, m_i, m_j, m_v.data());
123 
125  for (unsigned int k = 0; k < m_w.size(); ++k) {
126  m_w[k] = 0.0;
127  }
128  } else {
129  coarse_to_fine(m_w3, m_i, m_j, m_w.data());
130  }
131 
133  coarse_to_fine(m_Enth3, m_i, m_j, m_Enth.data());
134 
135  coarse_to_fine(m_Enth3, m_i, m_j+1, m_E_n.data());
136  coarse_to_fine(m_Enth3, m_i+1, m_j, m_E_e.data());
137  coarse_to_fine(m_Enth3, m_i, m_j-1, m_E_s.data());
138  coarse_to_fine(m_Enth3, m_i-1, m_j, m_E_w.data());
139 
141 
143 
144  assemble_R();
145 }
146 
147 //! Compute the CTS value of enthalpy in an ice column.
148 /*! m_Enth_s is set to the enthalpy value for the pressure-melting
149  temperature with zero water fraction at the corresponding z level.
150  */
152 
153  for (unsigned int k = 0; k <= m_ks; k++) {
154  const double
155  depth = m_ice_thickness - k * m_dz,
156  p = m_EC->pressure(depth); // FIXME issue #15
157  m_Enth_s[k] = m_EC->enthalpy_cts(p);
158  }
159 
160  const double Es_air = m_EC->enthalpy_cts(m_p_air);
161  for (unsigned int k = m_ks+1; k < m_Enth_s.size(); k++) {
162  m_Enth_s[k] = Es_air;
163  }
164 }
165 
166 //! Compute the lambda for BOMBPROOF.
167 /*!
168 See page \ref bombproofenth.
169  */
171  double result = 1.0; // start with centered implicit for more accuracy
172  const double epsilon = 1e-6 / 3.15569259747e7;
173 
174  for (unsigned int k = 0; k <= m_ks; k++) {
175  if (m_Enth[k] > m_Enth_s[k]) { // lambda = 0 if temperate ice present in column
176  result = 0.0;
177  } else {
178  const double denom = (fabs(m_w[k]) + epsilon) * m_ice_density * m_ice_c * m_dz;
179  result = std::min(result, 2.0 * m_ice_k / denom);
180  }
181  }
182  return result;
183 }
184 
185 
187 #if (Pism_DEBUG==1)
188  if ((m_nu < 0.0) || (m_R_cold < 0.0) || (m_R_temp < 0.0)) {
189  throw RuntimeError(PISM_ERROR_LOCATION, "setDirichletSurface() should only be called after\n"
190  "initAllColumns() in enthSystemCtx");
191  }
192 #endif
193  m_L_ks = 0.0;
194  m_D_ks = 1.0;
195  m_U_ks = 0.0;
196  m_B_ks = E_surface;
197 }
198 
199 static inline double upwind(double u, double E_m, double E, double E_p, double delta_inverse) {
200  return u * delta_inverse * (u < 0 ? (E_p - E) : (E - E_m));
201 }
202 
203 
204 //! Set the top surface heat flux *into* the ice.
205 /** @param[in] heat_flux prescribed heat flux (positive means flux into the ice)
206  */
207 void enthSystemCtx::set_surface_heat_flux(double heat_flux) {
208  using std::pow;
209  // extract K from R[ks], so this code works even if K=K(T)
210  // recall: R = (ice_K / ice_density) * dt / PetscSqr(dz)
211  const double
212  K = (m_ice_density * pow(m_dz, 2) * m_R[m_ks]) / m_dt,
213  G = heat_flux / K;
214 
215  this->set_surface_neumann_bc(G);
216 }
217 
218 //! Set enthalpy flux at the surface.
219 /*! This method should probably be used for debugging only. Its purpose is to allow setting the
220  enthalpy flux even if K == 0, i.e. in a "pure advection" setup.
221  */
223  const bool include_horizontal_advection =
225  const bool include_strain_heating = not(m_marginal and m_margin_exclude_strain_heat);
226 
227  const double Rminus = 0.5 * (m_R[m_ks - 1] + m_R[m_ks]), // R_{ks-1/2}
228  Rplus = m_R[m_ks], // R_{ks+1/2}
229  mu_w = 0.5 * m_nu * m_w[m_ks];
230 
231  const double A_l = m_w[m_ks] < 0.0 ? 1.0 - m_lambda : m_lambda - 1.0;
232  const double A_d = m_w[m_ks] < 0.0 ? m_lambda - 1.0 : 1.0 - m_lambda;
233  const double A_b = m_w[m_ks] < 0.0 ? m_lambda - 2.0 : -m_lambda;
234 
235  // modified lower-diagonal entry:
236  m_L_ks = -Rminus - Rplus + 2.0 * mu_w * A_l;
237  // diagonal entry
238  m_D_ks = 1.0 + Rminus + Rplus + 2.0 * mu_w * A_d;
239  // upper-diagonal entry (not used)
240  m_U_ks = 0.0;
241  // m_Enth[m_ks] (below) is there due to the fully-implicit discretization in time, the second term is
242  // the modification of the right-hand side implementing the Neumann B.C. (similar to
243  // set_basal_heat_flux(); see that method for details)
244  m_B_ks = m_Enth[m_ks] + 2.0 * dE * m_dz * (Rplus + mu_w * A_b);
245 
246  // treat horizontal velocity using first-order upwinding:
247  double upwind_u = 0.0;
248  double upwind_v = 0.0;
249  if (include_horizontal_advection) {
250  upwind_u = upwind(m_u[m_ks], m_E_w[m_ks], m_Enth[m_ks], m_E_e[m_ks], 1.0 / m_dx);
251  upwind_v = upwind(m_v[m_ks], m_E_s[m_ks], m_Enth[m_ks], m_E_n[m_ks], 1.0 / m_dy);
252  }
253  double Sigma = 0.0;
254  if (include_strain_heating) {
255  Sigma = m_strain_heating[m_ks];
256  }
257 
258  m_B_ks += m_dt * ((Sigma / m_ice_density) - upwind_u - upwind_v); // = rhs[m_ks]
259 }
260 
262  if (m_nu < 0.0 || m_R_cold < 0.0 || m_R_temp < 0.0) {
264  "not ready to solve: need initAllColumns() in enthSystemCtx");
265  }
266  if (m_lambda < 0.0) {
268  "not ready to solve: need setSchemeParamsThisColumn() in enthSystemCtx");
269  }
270 }
271 
272 
273 //! Set coefficients in discrete equation for \f$E = Y\f$ at base of ice.
274 /*!
275 This method should only be called if everything but the basal boundary condition
276 is already set.
277  */
279 #if (Pism_DEBUG == 1)
281  if (not std::isnan(m_D0) || not std::isnan(m_U0) || not std::isnan(m_B0)) {
283  "setting basal boundary conditions twice in enthSystemCtx");
284  }
285 #endif
286  m_D0 = 1.0;
287  m_U0 = 0.0;
288  m_B0 = E_basal;
289 }
290 
291 //! Set coefficients in discrete equation for Neumann condition at base of ice.
292 /*!
293 This method generates the Neumann boundary condition for the linear system.
294 
295 The Neumann boundary condition is
296  @f[ \frac{\partial E}{\partial z} = - \frac{\phi}{K} @f]
297 where \f$\phi\f$ is the heat flux. Here \f$K\f$ is allowed to vary, and takes
298 its value from the value computed in assemble_R().
299 
300 The boundary condition is combined with the partial differential equation by the
301 technique of introducing an imaginary point at \f$z=-\Delta z\f$ and then
302 eliminating it.
303 
304 In other words, we combine the centered finite difference approximation
305 @f[ \frac{ E_{1} - E_{-1} }{2\dz} = -\frac{\phi}{K} @f]
306 with
307 
308 @f[ -R_{k-\frac12} E_{k-1} + \left( 1 + R_{k-\frac12} + R_{k+\frac12} \right) E_{k} - R_{k+\frac12} E_{k+1} + \text{advective terms} = \dots @f]
309 
310 to get
311 
312 @f{align*}{
313  \frac{E_{1}-E_{-1}}{2\,\Delta z} & = -\frac{\phi}{K_{0}}, \\
314  E_{1}-E_{-1} & = -\frac{2\,\Delta z\,\phi}{K_{0}}, \\
315  E_{-1}\,R_{-\frac12}-R_{-\frac12}\,E_{1} & = \frac{2\,R_{-\frac12}\,\Delta z\,\phi}{K_{0}}, \\
316  -R_{\frac12}\,E_{1}+E_{0}\,\left(R_{\frac12}+R_{-\frac12}+1\right)-E_{-1}\,R_{-\frac12} + \text{advective terms} & = \dots, \\
317  \left(-R_{\frac12}-R_{-\frac12}\right)\,E_{1}+E_{0}\,\left(R_{\frac12}+R_{-\frac12}+1\right) + \text{advective terms} & = \frac{2\,R_{-\frac12}\,\Delta z\,\phi}{K_{0}}+\dots.
318 @f}
319 
320 The error in the pure conductive and smooth conductivity case is @f$ O(\dz^2) @f$.
321 
322 This method should only be called if everything but the basal boundary condition
323 is already set.
324 
325 @param[in] heat_flux prescribed heat flux (positive means flux into the ice)
326 
327  */
328 void enthSystemCtx::set_basal_heat_flux(double heat_flux) {
329  using std::pow;
330  // extract K from R[0], so this code works even if K=K(T)
331  // recall: R = (ice_K / ice_density) * dt / PetscSqr(dz)
332  const double
333  K = (m_ice_density * pow(m_dz, 2) * m_R[0]) / m_dt,
334  G = - heat_flux / K;
335 
336  this->set_basal_neumann_bc(G);
337 }
338 
339 //! Set enthalpy flux at the base.
340 /*! This method should probably be used for debugging only. Its purpose is to allow setting the
341  enthalpy flux even if K == 0, i.e. in a "pure advection" setup.
342  */
344  const bool include_horizontal_advection = not (m_marginal and m_margin_exclude_horizontal_advection);
345  const bool include_strain_heating = not (m_marginal and m_margin_exclude_strain_heat);
346 
347  const double
348  Rminus = m_R[0], // R_{-1/2}
349  Rplus = 0.5 * (m_R[0] + m_R[1]), // R_{+1/2}
350  mu_w = 0.5 * m_nu * m_w[0];
351 
352  const double A_d = m_w[0] < 0.0 ? m_lambda - 1.0 : 1.0 - m_lambda;
353  const double A_u = m_w[0] < 0.0 ? 1.0 - m_lambda : m_lambda - 1.0;
354  const double A_b = m_w[0] < 0.0 ? -m_lambda : m_lambda - 2.0;
355 
356  // diagonal entry
357  m_D0 = 1.0 + Rminus + Rplus + 2.0 * mu_w * A_d;
358  // upper-diagonal entry
359  m_U0 = - Rminus - Rplus + 2.0 * mu_w * A_u;
360  // right-hand side, excluding the strain heating term and the horizontal advection
361  m_B0 = m_Enth[0] + 2.0 * dE * m_dz * (-Rminus + mu_w * A_b);
362 
363  // treat horizontal velocity using first-order upwinding:
364  double upwind_u = 0.0;
365  double upwind_v = 0.0;
366  if (include_horizontal_advection) {
367  upwind_u = upwind(m_u[0], m_E_w[0], m_Enth[0], m_E_e[0], 1.0 / m_dx);
368  upwind_v = upwind(m_v[0], m_E_s[0], m_Enth[0], m_E_n[0], 1.0 / m_dy);
369  }
370  double Sigma = 0.0;
371  if (include_strain_heating) {
372  Sigma = m_strain_heating[0];
373  }
374 
375  m_B0 += m_dt * ((Sigma / m_ice_density) - upwind_u - upwind_v); // = rhs[m_ks]
376 }
377 
378 
379 //! \brief Assemble the R array. The R value switches at the CTS.
380 /*! In a simple abstract diffusion
381  \f[ \frac{\partial u}{\partial t} = D \frac{\partial^2 u}{\partial z^2}, \f]
382 with time steps \f$\Delta t\f$ and spatial steps \f$\Delta z\f$ we define
383  \f[ R = \frac{D \Delta t}{\Delta z^2}. \f]
384 This is used in an implicit method to write each line in the linear system, for
385 example [\ref MortonMayers]:
386  \f[ -R U_{j-1}^{n+1} + (1+2R) U_j^{n+1} - R U_{j+1}^{n+1} = U_j^n. \f]
387 
388 In the case of conservation of energy [\ref AschwandenBuelerKhroulevBlatter],
389  \f[ u=E \qquad \text{ and } \qquad D = \frac{K}{\rho} \qquad \text{ and } \qquad K = \frac{k}{c}. \f]
390 Thus
391  \f[ R = \frac{k \Delta t}{\rho c \Delta z^2}. \f]
392  */
394  if (not m_k_depends_on_T) {
395 
396  for (unsigned int k = 1; k <= m_ks; k++) {
397  m_R[k] = (m_Enth[k] < m_Enth_s[k]) ? m_R_cold : m_R_temp;
398  }
399  //still the cold ice value, if no temperate layer above
400  m_R[0] = (m_Enth[1] < m_Enth_s[1]) ? m_R_cold : m_R_temp;
401 
402  } else {
403 
404  for (unsigned int k = 1; k <= m_ks; k++) {
405  if (m_Enth[k] < m_Enth_s[k]) {
406  // cold case
407  const double depth = m_ice_thickness - k * m_dz;
408  double T = m_EC->temperature(m_Enth[k],
409  m_EC->pressure(depth)); // FIXME: issue #15
410 
411  m_R[k] = ((m_k_depends_on_T ? k_from_T(T) : m_ice_k) / m_EC->c()) * m_R_factor;
412  } else {
413  // temperate case
414  m_R[k] = m_R_temp;
415  }
416  }
417  // still the cold ice value, if no temperate layer above
418  if (m_Enth[1] < m_Enth_s[1]) {
419  double T = m_EC->temperature(m_Enth[0],
420  m_EC->pressure(m_ice_thickness)); // FIXME: issue #15
421  m_R[0] = ((m_k_depends_on_T ? k_from_T(T) : m_ice_k) / m_EC->c()) * m_R_factor;
422  } else {
423  // temperate layer case
424  m_R[0] = m_R_temp;
425  }
426 
427  }
428 
429  // R[k] for k > m_ks are never used
430 #if (Pism_DEBUG==1)
431  for (unsigned int k = m_ks + 1; k < m_R.size(); ++k) {
432  m_R[k] = NAN;
433  }
434 #endif
435 }
436 
437 
438 /*! \brief Solve the tridiagonal system, in a single column, which
439  * determines the new values of the ice enthalpy.
440  *
441  * We are solving a convection-diffusion equation, treating the @f$ z @f$ direction implicitly and
442  * @f$ x, y @f$ directions explicitly. See @ref bombproofenth for the documentation of the treatment
443  * of convection terms. The notes below document the way we treat diffusion using a simplified PDE.
444  *
445  * To discretize
446  * @f[ \diff{}{z} \left( K(E) \diff{E}{z}\right) = \diff{E}{t} @f]
447  *
448  * at a location @f$ k @f$ of the vertical grid, we use centered finite differences in space,
449  * backward differences in time, and evaluate @f$ K(E) @f$ at staggered-grid locations:
450  *
451  * @f[ \frac{K_{k+\frac12}\frac{E_{k+1} - E_{k}}{\dz} - K_{k-\frac12}\frac{E_{k} - E_{k-1}}{\dz}}{\dz} = \frac{E_{k} - E^{n-1}_{k}}{\dt}, @f]
452  *
453  * where @f$ E = E^{n} @f$ for clarity and @f$ K_{k\pm \frac12} = K(E^{n-1}_{k\pm \frac12}) @f$,
454  * %i.e. we linearize this PDE by evaluating @f$ K(E) @f$ at the _previous_ time-step.
455  *
456  * We define
457  *
458  * @f[ R_i = \frac{\dt\, K_i}{\dz^2}, @f]
459  *
460  * and the discretization takes form
461  *
462  * @f[ -R_{k-\frac12} E_{k-1} + \left( 1 + R_{k-\frac12} + R_{k+\frac12} \right) E_{k} - R_{k+\frac12} E_{k+1} = E^{n-1}_{k}. @f]
463  *
464  * In the assembly of the tridiagonal system this corresponds to
465  *
466  * @f{align*}{
467  * L_i &= - \frac12 (R_{i} + R_{i-1}),\\
468  * D_i &= 1 + \frac12 (R_{i} + R_{i-1}) + \frac12 (R_{i} + R_{i+1}),\\
469  * U_i &= - \frac12 (R_{i} + R_{i+1}),\\
470  * b_i &= E^{n-1}_{i},
471  * @f}
472  *
473  * where @f$ L_i, D_i, U_i @f$ are lower-diagonal, diagonal, and upper-diagonal entries
474  * corresponding to an equation @f$ i @f$ and @f$ b_i @f$ is the corresponding right-hand side.
475  * (Staggered-grid values are approximated by interpolating from the regular grid).
476  *
477  * This method is _unconditionally stable_ and has a maximum principle (see [@ref MortonMayers,
478  * section 2.11]).
479  */
480 void enthSystemCtx::solve(std::vector<double> &result) {
481 
483 
484 #if (Pism_DEBUG==1)
486  if (std::isnan(m_D0) || std::isnan(m_U0) || std::isnan(m_B0)) {
488  "solveThisColumn() should only be called after\n"
489  " setting basal boundary condition in enthSystemCtx");
490  }
491 #endif
492 
493  // k=0 equation is already established
494  // L[0] = 0.0; // not used
495  S.D(0) = m_D0;
496  S.U(0) = m_U0;
497  S.RHS(0) = m_B0;
498 
499  const double
500  one_over_rho = 1.0 / m_ice_density,
501  Dx = 1.0 / m_dx,
502  Dy = 1.0 / m_dy;
503 
504  const bool include_horizontal_advection = not (m_marginal and m_margin_exclude_horizontal_advection);
505  const bool include_strain_heating = not (m_marginal and m_margin_exclude_strain_heat);
506 
507  // generic ice segment in k location (if any; only runs if m_ks >= 2)
508  for (unsigned int k = 1; k < m_ks; k++) {
509  const double
510  Rminus = 0.5 * (m_R[k-1] + m_R[k]), // R_{k-1/2}
511  Rplus = 0.5 * (m_R[k] + m_R[k+1]), // R_{k+1/2}
512  nu_w = m_nu * m_w[k];
513 
514  const double
515  A_l = m_w[k] >= 0.0 ? 0.5 * m_lambda - 1.0 : -0.5 * m_lambda,
516  A_d = m_w[k] >= 0.0 ? 1.0 - m_lambda : m_lambda - 1.0,
517  A_u = m_w[k] >= 0.0 ? 0.5 * m_lambda : 1.0 - 0.5 * m_lambda;
518 
519  S.L(k) = - Rminus + nu_w * A_l;
520  S.D(k) = 1.0 + Rminus + Rplus + nu_w * A_d;
521  S.U(k) = - Rplus + nu_w * A_u;
522 
523  // horizontal velocity and strain heating
524  double upwind_u = 0.0;
525  double upwind_v = 0.0;
526  if (include_horizontal_advection) {
527  upwind_u = upwind(m_u[k], m_E_w[k], m_Enth[k], m_E_e[k], Dx);
528  upwind_v = upwind(m_v[k], m_E_s[k], m_Enth[k], m_E_n[k], Dy);
529  }
530  double Sigma = 0.0;
531  if (include_strain_heating) {
532  Sigma = m_strain_heating[k];
533  }
534 
535  S.RHS(k) = m_Enth[k] + m_dt * (one_over_rho * Sigma - upwind_u - upwind_v);
536  }
537 
538  // Assemble the top surface equation. Values m_{L,D,U,B}_ks are set using set_surface_dirichlet()
539  // or set_surface_heat_flux().
540  if (m_ks > 0) {
541  S.L(m_ks) = m_L_ks;
542  }
543  S.D(m_ks) = m_D_ks;
544  if (m_ks < m_z.size() - 1) {
545  S.U(m_ks) = m_U_ks;
546  }
547  S.RHS(m_ks) = m_B_ks;
548 
549  // Solve it; note drainage is not addressed yet and post-processing may occur
550  try {
551  S.solve(m_ks + 1, result);
552  }
553  catch (RuntimeError &e) {
554  e.add_context("solving the tri-diagonal system (enthSystemCtx) at (%d,%d)\n"
555  "saving system to m-file... ", m_i, m_j);
557  throw;
558  }
559 
560  // air above
561  for (unsigned int k = m_ks+1; k < result.size(); k++) {
562  result[k] = m_B_ks;
563  }
564 
565 #if (Pism_DEBUG==1)
566  // if success, mark column as done by making scheme params and b.c. coeffs invalid
567  m_lambda = -1.0;
568  m_D0 = NAN;
569  m_U0 = NAN;
570  m_B0 = NAN;
571  m_L_ks = NAN;
572  m_D_ks = NAN;
573  m_U_ks = NAN;
574  m_B_ks = NAN;
575 #endif
576 }
577 
578 void enthSystemCtx::save_system(std::ostream &output, unsigned int system_size) const {
579  m_solver->save_system(output, system_size);
580  pism::TridiagonalSystem::save_vector(output, m_R, system_size, m_solver->prefix() + "_R");
581 }
582 
583 } // end of namespace energy
584 } // end of namespace pism
double get_number(const std::string &name, UseFlag flag=REMEMBER_THIS_USE) const
bool get_flag(const std::string &name, UseFlag flag=REMEMBER_THIS_USE) const
A class for storing and accessing PISM configuration flags and parameters.
std::shared_ptr< EnthalpyConverter > Ptr
void add_context(const std::string &message)
Add a message providing some context. This way we can (sort of) get a stack trace even though C++ exc...
void save_system(std::ostream &output, unsigned int system_size) const
View the tridiagonal system A x = b to an output stream, both A as a full matrix and b as a vector.
std::string prefix() const
static void save_vector(std::ostream &output, const std::vector< double > &v, unsigned int system_size, const std::string &variable)
Utility for simple ascii view of a vector (one-dimensional column) quantity.
Virtual base class. Abstracts a tridiagonal system to solve in a column of ice and/or bedrock.
Definition: ColumnSystem.hh:86
A virtual class collecting methods common to ice and bedrock 3D fields.
Definition: Array3D.hh:33
const array::Array3D & m_w3
void coarse_to_fine(const array::Array3D &coarse, int i, int j, double *fine) const
void reportColumnZeroPivotErrorMFile(unsigned int M)
Write system matrix and right-hand-side into an Python script. The file name contains ZERO_PIVOT_ERRO...
std::vector< double > m_z
levels of the fine vertical grid
unsigned int m_ks
current system size; corresponds to the highest vertical level within the ice
std::vector< double > m_u
u-component of the ice velocity
const array::Array3D & m_u3
pointers to 3D velocity components
void init_column(int i, int j, double ice_thickness)
const array::Array3D & m_v3
int m_i
current column indexes
TridiagonalSystem * m_solver
std::vector< double > m_w
w-component of the ice velocity
std::vector< double > m_v
v-component of the ice velocity
Base class for tridiagonal systems in the ice.
void assemble_R()
Assemble the R array. The R value switches at the CTS.
Definition: enthSystem.cc:393
std::vector< double > m_Enth_s
Definition: enthSystem.hh:84
const array::Array3D & m_Enth3
Definition: enthSystem.hh:109
std::vector< double > m_E_n
Definition: enthSystem.hh:88
std::vector< double > m_E_w
Definition: enthSystem.hh:88
std::vector< double > m_R
values of
Definition: enthSystem.hh:94
const array::Array3D & m_strain_heating3
Definition: enthSystem.hh:109
std::vector< double > m_Enth
Definition: enthSystem.hh:82
void set_surface_neumann_bc(double dE)
Set enthalpy flux at the surface.
Definition: enthSystem.cc:222
void compute_enthalpy_CTS()
Compute the CTS value of enthalpy in an ice column.
Definition: enthSystem.cc:151
EnthalpyConverter::Ptr m_EC
Definition: enthSystem.hh:110
void set_surface_heat_flux(double heat_flux)
Set the top surface heat flux into the ice.
Definition: enthSystem.cc:207
enthSystemCtx(const std::vector< double > &storage_grid, const std::string &prefix, double dx, double dy, double dt, const Config &config, const array::Array3D &Enth3, const array::Array3D &u3, const array::Array3D &v3, const array::Array3D &w3, const array::Array3D &strain_heating3, EnthalpyConverter::Ptr EC)
Definition: enthSystem.cc:31
void init(int i, int j, bool ismarginal, double ice_thickness)
Definition: enthSystem.cc:110
void set_basal_neumann_bc(double dE)
Set enthalpy flux at the base.
Definition: enthSystem.cc:343
std::vector< double > m_E_e
Definition: enthSystem.hh:88
double k_from_T(double T) const
Definition: enthSystem.cc:101
std::vector< double > m_strain_heating
strain heating in the ice column
Definition: enthSystem.hh:91
virtual void save_system(std::ostream &output, unsigned int M) const
Definition: enthSystem.cc:578
void set_basal_heat_flux(double heat_flux)
Set coefficients in discrete equation for Neumann condition at base of ice.
Definition: enthSystem.cc:328
double m_D0
implicit FD method parameter
Definition: enthSystem.hh:101
std::vector< double > m_E_s
Definition: enthSystem.hh:88
void solve(std::vector< double > &result)
Solve the tridiagonal system, in a single column, which determines the new values of the ice enthalpy...
Definition: enthSystem.cc:480
void checkReadyToSolve() const
Definition: enthSystem.cc:261
double compute_lambda()
Compute the lambda for BOMBPROOF.
Definition: enthSystem.cc:170
void set_basal_dirichlet_bc(double E_basal)
Set coefficients in discrete equation for at base of ice.
Definition: enthSystem.cc:278
void set_surface_dirichlet_bc(double E_surface)
Definition: enthSystem.cc:186
#define PISM_ERROR_LOCATION
const double G
Definition: exactTestK.c:40
double min(const array::Scalar &input)
Finds minimum over all the values in an array::Scalar object. Ignores ghosts.
Definition: Scalar.cc:193
bool marginal(const array::Scalar1 &thickness, int i, int j, double threshold)
Definition: EnergyModel.cc:90
static double upwind(double u, double E_m, double E, double E_p, double delta_inverse)
Definition: enthSystem.cc:199
static double K(double psi_x, double psi_y, double speed, double epsilon)
static const double k
Definition: exactTestP.cc:42
static double S(unsigned n)
Definition: test_cube.c:58