23#include <gsl/gsl_interp.h>
28#include "pism/age/Isochrones.hh"
29#include "pism/util/Context.hh"
30#include "pism/util/MaxTimestep.hh"
31#include "pism/util/Time.hh"
32#include "pism/stressbalance/StressBalance.hh"
33#include "pism/util/array/Array3D.hh"
34#include "pism/util/array/Scalar.hh"
35#include "pism/util/Interpolation1D.hh"
36#include "pism/util/petscwrappers/Vec.hh"
37#include "pism/util/Logger.hh"
38#include "pism/util/io/LocalInterpCtx.hh"
39#include "pism/util/io/IO_Flags.hh"
40#include "pism/util/io/io_helpers.hh"
62 for (
size_t k = 0;
k < N - 1;
k++) {
63 if (a[
k] > a[
k + 1]) {
77 const std::vector<double> ×) {
78 using namespace details;
81 if ((
int)times.size() > N_max) {
83 "the total number of isochronal layers (%d) exceeds '%s' = %d",
87 const auto &time = grid->ctx()->time();
92 result->metadata().long_name(
"thicknesses of isochronal layers").units(
"m");
95 pism::printf(
"times for isochrones in '%s'; earliest deposition times for layers in '%s'",
97 auto &z = result->metadata(0).dimension(
"z");
100 .long_name(z_description)
101 .units(time->units());
103 z[
"calendar"] = time->calendar();
115static std::shared_ptr<array::Array3D>
117 const std::vector<double> &requested_times) {
119 auto grid = input.
grid();
121 const auto &input_times = input.
levels();
127 "deposition times in '%s' have to be non-decreasing",
132 for (
auto t : input_times) {
140 for (
auto t : requested_times) {
141 if (t > last_kept_time) {
150 for (
auto p : grid->points()) {
151 const int i = p.i(), j = p.j();
154 auto *out = result->get_column(i, j);
156 for (
size_t k = 0;
k < N_layers_to_copy; ++
k) {
168 const std::vector<double> ×) {
169 double T_start = time.
start(), T_end = time.
end();
171 std::vector<double> result;
172 for (
auto t : times) {
173 if (t >= T_start and t <= T_end) {
186 using namespace details;
190 std::vector<double> result(n_deposition_times);
202 using namespace details;
216 if (N_deposition_times == 0) {
218 "'%s' = '%s' has no times within the modeled time span [%s, %s]",
223 if ((
int)N_deposition_times > N_max) {
226 "the number of times (%d) in '%s' exceeds the allowed maximum ('%s' = %d)",
243 const File &file,
int record) {
247 auto N = (
int)result->levels().size();
249 auto metadata = result->metadata(0);
251 auto variable_info = file.
find_variable(metadata.get_name(), metadata[
"standard_name"]);
254 grid->registration());
266 std::vector<double> Z(N);
267 for (
int k = 0;
k < N; ++
k) {
272 lic.
z = std::make_shared<Interpolation1D>(
NEAREST, Z, Z);
287 const File &input_file,
int record) {
290 result->read(input_file, record);
303 if (t <= current_time) {
316 auto regrid_file = config.
get_string(
"input.regrid.file");
318 if (regrid_file.empty()) {
335 auto grid = layer_thickness.
grid();
337 auto N = layer_thickness.
levels().size();
341 for (
auto p : grid->points()) {
342 const int i = p.i(), j = p.j();
346 double H_total = 0.0;
347 for (
int k = 0;
k < (
int)N; ++
k) {
353 double S = ice_thickness(i, j) / H_total;
354 for (
size_t k = 0;
k < N; ++
k) {
365 std::shared_ptr<const stressbalance::StressBalance> stress_balance)
366 :
Component(grid), m_stress_balance(stress_balance) {
392 using namespace details;
401 initialize(regrid_file, (
int)last_record,
true);
410 auto requested_times = deposition_times(*
m_config, *
time);
412 auto N_bootstrap =
static_cast<int>(
m_config->get_number(N_boot_parameter));
413 auto N_max =
static_cast<int>(
m_config->get_number(N_max_parameter));
414 auto N_deposition_times = requested_times.size();
416 if (N_bootstrap < 0) {
418 N_boot_parameter, N_bootstrap);
422 "* Bootstrapping the isochrone tracking model, adding %d isochronal layers...\n",
425 if (N_bootstrap + (
int)N_deposition_times > N_max) {
426 auto times =
m_config->get_string(times_parameter);
429 "%s (%d) + %s (%d) exceeds the allowed maximum (%s = %d)", N_boot_parameter,
430 (
int)N_bootstrap, times_parameter, (
int)N_deposition_times, N_max_parameter, (
int)N_max);
433 if (N_bootstrap > 0) {
436 std::vector<double> deposition_times(N_bootstrap,
time->
start());
438 for (
const auto &t : requested_times) {
439 deposition_times.push_back(t);
448 for (
auto p :
m_grid->points()) {
449 const int i = p.i(), j = p.j();
451 double H = ice_thickness(i, j);
454 for (
int k = 0;
k < N_bootstrap; ++
k) {
455 column[
k] = H /
static_cast<double>(N_bootstrap);
465 for (
auto p :
m_grid->points()) {
466 const int i = p.i(), j = p.j();
474 std::vector<std::string> dates;
478 m_log->message(3,
"Deposition times: %s\n",
join(dates,
", ").c_str());
483 e.
add_context(
"bootstrapping the isochrone tracking model");
494 using namespace details;
496 m_log->message(2,
"* Initializing the isochrone tracking model from '%s'...\n",
497 input_file.
name().c_str());
499 if (use_interpolation) {
500 m_log->message(2,
" [using bilinear interpolation to read layer thicknesses]\n");
506 std::shared_ptr<array::Array3D> tmp;
508 if (use_interpolation) {
509 tmp = regrid_layer_thickness(
m_grid, input_file, record);
511 tmp = read_layer_thickness(
m_grid, input_file, record);
524 std::vector<std::string> dates;
528 m_log->message(3,
"Deposition times: %s\n",
join(dates,
", ").c_str());
534 e.
add_context(
"initializing the isochrone tracking model");
549 initialize(regrid_file, (
int)last_record,
true);
578 for (
auto p :
m_grid->points()) {
579 const int i = p.i(), j = p.j();
585 double dH = top_surface_mass_balance(i, j);
589 if (H[
k] + dH >= 0.0) {
602 double dH = bottom_surface_mass_balance(i, j);
606 if (H[
k] + dH >= 0.0) {
630 double H_min =
m_config->get_number(
"geometry.ice_free_thickness_standard");
634 auto Q = [](
double U,
double f_n,
double f_p) {
return U * (U >= 0 ? f_n : f_p); };
636 for (
auto p :
m_grid->points()) {
637 const int i = p.i(), j = p.j();
639 const double *d_c =
m_tmp->get_column(i, j), *d_n =
m_tmp->get_column(i, j + 1),
640 *d_e =
m_tmp->get_column(i + 1, j), *d_s =
m_tmp->get_column(i, j - 1),
641 *d_w =
m_tmp->get_column(i - 1, j);
646 double d_total = 0.0;
677 double Q_n = Q(0.5 * (V + V_n), d_c[
k], d_n[
k]), Q_e = Q(0.5 * (U + U_e), d_c[
k], d_e[
k]),
678 Q_s = Q(0.5 * (V + V_s), d_s[
k], d_c[
k]), Q_w = Q(0.5 * (U + U_w), d_w[
k], d_c[
k]);
680 d[
k] = d_c[
k] - dt * ((Q_e - Q_w) / dx + (Q_n - Q_s) / dy);
685 d[
k] = std::max(d[
k], 0.0);
698 double S = ice_thickness(i, j) / d_total;
703 assert(ice_thickness(i, j) < H_min);
712 size_t N = deposition_times.size();
720 size_t k = gsl_interp_bsearch(deposition_times.data(), T, 0, N);
722 double T_k = deposition_times[
k];
738 "Isochrone tracking: reached isochrones.max_n_layers and can't add more.\n"
739 " SMB will contribute to the current top layer.");
753 "Isochrone tracking: no stress balance provided. "
754 "Cannot compute the maximum time step.");
768 double t0 = deposition_times[0];
770 return { t0 - t,
"isochrones" };
773 if (t >= deposition_times.back()) {
774 return {
"isochrones" };
777 auto N = deposition_times.size();
785 size_t k = gsl_interp_bsearch(deposition_times.data(), t, 0, N - 1);
787 return { deposition_times[
k + 1] - t,
"isochrones" };
810namespace diagnostics {
816 using namespace details;
823 auto description =
pism::printf(
"depth below surface of isochrones for times in '%s'",
824 deposition_time_variable_name);
826 m_vars[0].long_name(description).units(
"m");
827 auto &z =
m_vars[0].dimension(
"z");
829 .set_name(deposition_time_variable_name)
831 pism::printf(
"deposition times for isochrones in '%s'", isochrone_depth_variable_name))
832 .units(time->units());
833 z[
"calendar"] = time->calendar();
841 auto result = layer_thicknesses.
duplicate();
842 result->metadata(0) =
m_vars[0];
844 size_t N = result->levels().size();
848 for (
auto p :
m_grid->points()) {
849 const int i = p.i(), j = p.j();
851 double *column = result->get_column(i, j);
852 const double *d = layer_thicknesses.get_column(i, j);
854 double total_depth = 0.0;
855 for (
int k = (
int)N - 1;
k >= 0; --
k) {
857 column[
k] = total_depth;
const units::System::Ptr m_sys
unit system used by this component
const Time & time() const
std::shared_ptr< const Grid > grid() const
std::shared_ptr< const Config > m_config
configuration database used by this component
const std::shared_ptr< const Grid > m_grid
grid used by this component
std::shared_ptr< const Logger > m_log
logger (for easy access)
A class defining a common interface for most PISM sub-models.
double get_number(const std::string &name, UseFlag flag=REMEMBER_THIS_USE) const
std::string get_string(const std::string &name, UseFlag flag=REMEMBER_THIS_USE) const
A class for storing and accessing PISM configuration flags and parameters.
A template derived from Diagnostic, adding a "Model".
std::vector< VariableMetadata > m_vars
metadata corresponding to NetCDF variables
static Ptr wrap(const T &input)
const units::System::Ptr m_sys
the unit system
std::shared_ptr< Diagnostic > Ptr
std::shared_ptr< const Grid > m_grid
the grid
void read_variable(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, double *ip) const
VariableLookupData find_variable(const std::string &short_name, const std::string &std_name) const
Find a variable using its standard name and/or short name.
unsigned int nrecords() const
Get the number of records. Uses the length of an unlimited dimension.
unsigned int dimension_length(const std::string &name) const
Get the length of a dimension.
High-level PISM I/O class.
std::shared_ptr< array::Array3D > m_layer_thickness
isochronal layer thicknesses
void bootstrap(const array::Scalar &ice_thickness)
MaxTimestep max_timestep_deposition_times(double t) const
void update(double t, double dt, const array::Array3D &u, const array::Array3D &v, const array::Scalar &ice_thickness, const array::Scalar &top_surface_mass_balance, const array::Scalar &bottom_surface_mass_balance)
void write_state_impl(const OutputFile &output) const
MaxTimestep max_timestep_impl(double t) const
DiagnosticList spatial_diagnostics_impl() const
std::shared_ptr< array::Array3D > m_tmp
temporary storage needed for time stepping
std::shared_ptr< const stressbalance::StressBalance > m_stress_balance
Isochrones(std::shared_ptr< const Grid > grid, std::shared_ptr< const stressbalance::StressBalance > stress_balance)
std::set< VariableMetadata > state_impl() const
size_t m_top_layer_index
The index of the topmost isochronal layer.
double top_layer_deposition_time() const
void restart(const File &input_file, int record)
const array::Array3D & layer_thicknesses() const
void initialize(const File &input_file, int record, bool use_interpolation)
MaxTimestep max_timestep_cfl() const
std::array< int, 4 > count
std::shared_ptr< Interpolation1D > z
std::array< int, 4 > start
Combines the max. time step with the flag indicating if a restriction is active. Makes is possible to...
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...
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
double current() const
Current time, in seconds.
std::vector< double > parse_times(const std::string &spec) const
std::string date(double T) const
Returns the date corresponding to time T.
Makes sure that we call begin_access() and end_access() for all accessed array::Arrays.
double interpolate(int i, int j, double z) const
Return value of scalar quantity at level z (m) above base of ice (by linear interpolation).
std::shared_ptr< Array3D > duplicate(Kind ghostedp=WITHOUT_GHOSTS) const
double * get_column(int i, int j)
A virtual class collecting methods common to ice and bedrock 3D fields.
const std::vector< double > & levels() const
const std::string & get_name() const
Get the name of an Array object.
std::shared_ptr< const Grid > grid() const
IsochroneDepths(const Isochrones *m)
std::shared_ptr< array::Array > compute_impl() const
Report isochrone depth below surface, in meters.
Wrapper around VecGetArray and VecRestoreArray.
#define PISM_ERROR_LOCATION
std::set< VariableMetadata > metadata(std::initializer_list< const Array * > vecs)
static std::shared_ptr< array::Array3D > read_layer_thickness(std::shared_ptr< const Grid > grid, const File &input_file, int record)
static const char * N_max_parameter
static std::shared_ptr< array::Array3D > allocate_layer_thickness(std::shared_ptr< const Grid > grid, const std::vector< double > ×)
static void renormalize(const array::Scalar &ice_thickness, array::Array3D &layer_thickness)
static const char * isochrone_depth_variable_name
static std::vector< double > prune_deposition_times(const Time &time, const std::vector< double > ×)
static size_t n_active_layers(std::vector< double > deposition_times, double current_time)
static std::shared_ptr< array::Array3D > regrid_layer_thickness(std::shared_ptr< const Grid > grid, const File &file, int record)
static const char * layer_thickness_variable_name
static std::vector< double > deposition_times(const File &input_file)
static const char * N_boot_parameter
static const char * deposition_time_variable_name
static bool regridp(const Config &config)
static const char * times_parameter
static bool non_decreasing(const std::vector< double > &a)
Checks if a vector of doubles is not decreasing.
@ PISM_READONLY
open an existing file for reading only
void regrid_spatial_variable(const VariableMetadata &variable, const Grid &target_grid, const LocalInterpCtx &interp_context, const File &file, const Logger &log, double *output)
Regrid from a NetCDF file into a distributed array output.
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.
std::string join(const std::vector< std::string > &strings, const std::string &separator)
Concatenate strings, inserting separator between elements.
bool set_member(const std::string &string, const std::set< std::string > &set)
Star stencil points (in the map-plane).
static double S(unsigned n)