33#include <gsl/gsl_math.h>
35#include "pism/coupler/util/options.hh"
36#include "pism/geometry/Geometry.hh"
37#include "pism/util/Config.hh"
38#include "pism/util/Grid.hh"
39#include "pism/util/Mask.hh"
40#include "pism/util/Time.hh"
42#include "pism/coupler/ocean/Pico.hh"
43#include "pism/coupler/ocean/PicoGeometry.hh"
44#include "pism/coupler/ocean/PicoPhysics.hh"
45#include "pism/util/array/Forcing.hh"
46#include "pism/util/Logger.hh"
47#include "pism/util/io/IO_Flags.hh"
54 m_Soc(m_grid,
"pico_salinity"),
55 m_Soc_box0(m_grid,
"pico_salinity_box0"),
56 m_Toc(m_grid,
"pico_temperature"),
57 m_Toc_box0(m_grid,
"pico_temperature_box0"),
58 m_T_star(m_grid,
"pico_T_star"),
59 m_overturning(m_grid,
"pico_overturning"),
60 m_basal_melt_rate(m_grid,
"pico_basal_melt_rate"),
69 auto buffer_size =
static_cast<int>(
m_config->get_number(
"input.forcing.buffer_size"));
91 .long_name(
"potential temperature of the adjacent ocean")
95 .long_name(
"salinity of the adjacent ocean")
128 m_n_boxes =
static_cast<int>(
m_config->get_number(
"ocean.pico.number_of_boxes"));
134 m_log->message(2,
"* Initializing the Potsdam Ice-shelf Cavity mOdel for the ocean ...\n");
147 m_log->message(4,
"PICO basin min=%f, max=%f\n",
153 m_log->message(2,
" -Using %d drainage basins and values: \n"
154 " gamma_T= %.2e, overturning_coeff = %.2e... \n",
157 m_log->message(2,
" -Depth of continental shelf for computation of temperature and salinity input\n"
158 " is set for whole domain to continental_shelf_depth=%.0f meter\n",
168 ice_density =
m_config->get_number(
"constants.ice.density"),
169 water_density =
m_config->get_number(
"constants.sea_water.density"),
170 g =
m_config->get_number(
"constants.standard_gravity");
201 auto grid = basal_melt_rate.
grid();
209 for (
auto p : grid->points()) {
211 const int i = p.i(), j = p.j();
213 auto M = cell_type.
box(i, j);
215 bool potential_partially_filled_cell =
222 if (potential_partially_filled_cell) {
223 auto BMR = basal_melt_rate.
box(i, j);
226 double melt_sum = 0.0;
238 basal_melt_rate(i, j) = melt_sum / N;
254 double T_fill_value =
m_config->get_number(
"constants.fresh_water.melting_point_temperature");
285 std::vector<double> basin_temperature(
m_n_basins);
286 std::vector<double> basin_salinity(
m_n_basins);
351 ice_density =
m_config->get_number(
"constants.ice.density"),
352 water_density =
m_config->get_number(
"constants.sea_water.density"),
353 g =
m_config->get_number(
"constants.standard_gravity");
378 std::vector<double> &temperature,
379 std::vector<double> &salinity)
const {
388 for (
int basin_id = 0; basin_id <
m_n_basins; basin_id++) {
389 temperature[basin_id] = 0.0;
390 salinity[basin_id] = 0.0;
393 array::AccessScope list{ &theta_ocean, &salinity_ocean, &basin_mask, &continental_shelf_mask };
398 for (
auto p :
m_grid->points()) {
399 const int i = p.i(), j = p.j();
401 if (continental_shelf_mask.
as_int(i, j) == 2) {
402 int basin_id = basin_mask.
as_int(i, j);
404 count[basin_id] += 1;
405 salinity[basin_id] += salinity_ocean(i, j);
406 temperature[basin_id] += theta_ocean(i, j);
420 salinity = salinityr;
421 temperature = temperaturer;
425 temperature[0] = physics.
T_dummy();
426 salinity[0] = physics.
S_dummy();
429 for (
int basin_id = 1; basin_id <
m_n_basins; basin_id++) {
431 if (
count[basin_id] != 0) {
432 salinity[basin_id] /=
count[basin_id];
433 temperature[basin_id] /=
count[basin_id];
435 m_log->message(5,
" %d: temp =%.3f, salinity=%.3f\n", basin_id, temperature[basin_id], salinity[basin_id]);
439 "PICO WARNING: basin %d contains no cells with ocean data on continental shelf\n"
440 " (no values with ocean_contshelf_mask=2).\n"
441 " Using default temperature (%.3f K) and salinity (%.3f g/kg)\n"
442 " since mean salinity and temperature cannot be computed.\n"
443 " This may bias the basal melt rate estimate.\n"
444 " Please check your input data.\n",
447 temperature[basin_id] = physics.
T_dummy();
448 salinity[basin_id] = physics.
S_dummy();
465 const std::vector<double> &basin_temperature,
466 const std::vector<double> &basin_salinity,
470 array::AccessScope list{ &ice_thickness, &basin_mask, &Soc_box0, &Toc_box0, &mask, &shelf_mask };
483 for (
auto p :
m_grid->points()) {
484 const int i = p.i(), j = p.j();
485 int s = shelf_mask.
as_int(i, j);
486 int b = basin_mask.
as_int(i, j);
492 auto M = mask.
star(i, j);
497 if (cfs_in_basins_per_shelf[s *
m_n_basins + b] != b) {
498 cfs_in_basins_per_shelf[s *
m_n_basins + b] = b;
511 n_shelf_cells = n_shelf_cellsr;
512 n_shelf_cells_per_basin = n_shelf_cells_per_basinr;
513 cfs_in_basins_per_shelf = cfs_in_basins_per_shelfr;
519 if (n_shelf_cells_per_basin[sb] > 0 and cfs_in_basins_per_shelf[sb] == 0) {
520 n_shelf_cells[s] -= n_shelf_cells_per_basin[sb];
521 n_shelf_cells_per_basin[sb] = 0;
529 int low_temperature_counter = 0;
530 for (
auto p :
m_grid->points()) {
531 const int i = p.i(), j = p.j();
534 Toc_box0(i, j) = 0.0;
535 Soc_box0(i, j) = 0.0;
537 int s = shelf_mask.
as_int(i, j);
542 assert(n_shelf_cells[s] > 0);
543 double N = std::max(n_shelf_cells[s], 1);
548 Toc_box0(i, j) += basin_temperature[b] * n_shelf_cells_per_basin[sb] / N;
549 Soc_box0(i, j) += basin_salinity[b] * n_shelf_cells_per_basin[sb] / N;
552 double theta_pm = physics.
theta_pm(Soc_box0(i, j), physics.
pressure(ice_thickness(i, j)));
555 if (Toc_box0(i, j) < theta_pm) {
556 const double eps = 0.001;
559 Toc_box0(i, j) = theta_pm + eps;
560 low_temperature_counter += 1;
565 low_temperature_counter =
GlobalSum(
m_grid->com, low_temperature_counter);
566 if (low_temperature_counter > 0) {
569 "PICO WARNING: temperature has been below pressure melting temperature in %d cases,\n"
570 " setting it to pressure melting temperature\n",
571 low_temperature_counter);
593 const double T0 =
m_config->get_number(
"constants.fresh_water.melting_point_temperature"),
594 beta_CC =
m_config->get_number(
"constants.ice.beta_Clausius_Clapeyron"),
595 g =
m_config->get_number(
"constants.standard_gravity"),
596 ice_density =
m_config->get_number(
"constants.ice.density");
598 array::AccessScope list{ &ice_thickness, &cell_type, &shelf_mask, &Toc_box0, &Soc_box0,
599 &Toc, &Soc, &basal_melt_rate, &basal_temperature };
601 for (
auto p :
m_grid->points()) {
602 const int i = p.i(), j = p.j();
605 if (shelf_mask.
as_int(i, j) > 0) {
606 double pressure = physics.
pressure(ice_thickness(i, j));
608 basal_melt_rate(i, j) =
610 basal_temperature(i, j) = physics.
T_pm(Soc_box0(i, j), pressure);
613 Toc(i, j) = Toc_box0(i, j);
614 Soc(i, j) = Soc_box0(i, j);
617 const double pressure = ice_density *
g * ice_thickness(i, j);
619 basal_temperature(i, j) = T0 -
beta_CC * pressure;
620 basal_melt_rate(i, j) = 0.0;
644 array::AccessScope list{ &ice_thickness, &shelf_mask, &box_mask, &T_star, &Toc_box0, &Toc,
645 &Soc_box0, &Soc, &overturning, &basal_melt_rate, &basal_temperature };
647 int n_Toc_failures = 0;
651 for (
auto p :
m_grid->points()) {
652 const int i = p.i(), j = p.j();
654 int shelf_id = shelf_mask.
as_int(i, j);
656 if (box_mask.
as_int(i, j) == 1 and shelf_id > 0) {
658 const double pressure = physics.
pressure(ice_thickness(i, j));
660 T_star(i, j) = physics.
T_star(Soc_box0(i, j), Toc_box0(i, j), pressure);
662 auto Toc_box1 = physics.
Toc_box1(box1_area[shelf_id], T_star(i, j), Soc_box0(i, j), Toc_box0(i, j));
666 if (Toc_box1.failed) {
668 "PICO WARNING: negative square root argument at %d, %d\n"
669 " probably because of positive T_star=%f \n"
670 " Not aborting, but setting square root to 0... \n",
676 Toc(i, j) = Toc_box1.value;
677 Soc(i, j) = physics.
Soc_box1(Toc_box0(i, j), Soc_box0(i, j), Toc(i, j));
679 overturning(i, j) = physics.
overturning(Soc_box0(i, j), Soc(i, j), Toc_box0(i, j), Toc(i, j));
682 basal_melt_rate(i, j) = physics.
melt_rate(physics.
theta_pm(Soc(i, j), pressure), Toc(i, j));
683 basal_temperature(i, j) = physics.
T_pm(Soc(i, j), pressure);
688 if (n_Toc_failures > 0) {
690 "PICO WARNING: square-root argument for temperature calculation\n"
691 " has been negative in %d cases.\n",
713 std::vector<bool> use_beckmann_goosse(
m_n_shelves);
716 &Soc, &basal_melt_rate, &basal_temperature };
719 for (
int box = 2; box <=
m_n_boxes; ++box) {
727 use_beckmann_goosse[s] = (salinity[s] == 0.0 or
728 temperature[s] == 0.0 or
729 overturning[s] == 0.0);
732 std::vector<double> box_area;
735 int n_beckmann_goosse_cells = 0;
737 for (
auto p :
m_grid->points()) {
738 const int i = p.i(), j = p.j();
740 int shelf_id = shelf_mask.
as_int(i, j);
742 if (box_mask.
as_int(i, j) == box and shelf_id > 0) {
744 if (use_beckmann_goosse[shelf_id]) {
745 n_beckmann_goosse_cells += 1;
751 S_previous = salinity[shelf_id],
752 T_previous = temperature[shelf_id],
753 overturning_box1 = overturning[shelf_id];
756 double pressure = physics.
pressure(ice_thickness(i, j));
759 T_star(i, j) = physics.
T_star(S_previous, T_previous, pressure);
760 Toc(i, j) = physics.
Toc(box_area[shelf_id], T_previous, T_star(i, j), overturning_box1, S_previous);
761 Soc(i, j) = physics.
Soc(S_previous, T_previous, Toc(i, j));
764 basal_melt_rate(i, j) = physics.
melt_rate(physics.
theta_pm(Soc(i, j), pressure), Toc(i, j));
765 basal_temperature(i, j) = physics.
T_pm(Soc(i, j), pressure);
770 n_beckmann_goosse_cells =
GlobalSum(
m_grid->com, n_beckmann_goosse_cells);
771 if (n_beckmann_goosse_cells > 0) {
774 "PICO WARNING: [box %d]: switched to the Beckmann Goosse (2003) model at %d locations\n"
775 " (no boundary data from the previous box)\n",
776 box, n_beckmann_goosse_cells);
814 std::vector<double> &result)
const {
828 for (
auto p :
m_grid->points()) {
829 const int i = p.i(), j = p.j();
831 int shelf_id = shelf_mask.
as_int(i, j);
833 if (box_mask.
as_int(i, j) == box_id) {
834 n_cells_per_box[shelf_id] += 1;
835 result[shelf_id] += field(i, j);
849 if (n_cells[s] > 0) {
850 result[s] /=
static_cast<double>(n_cells[s]);
866 std::vector<double> &result)
const {
870 auto cell_area =
m_grid->cell_area();
872 for (
auto p :
m_grid->points()) {
873 const int i = p.i(), j = p.j();
875 int shelf_id = shelf_mask.
as_int(i, j);
877 if (shelf_id > 0 and box_mask.
as_int(i, j) == box_id) {
878 result[shelf_id] += cell_area;
887 result[i] = result1[i];
const Time & time() 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)
static Ptr wrap(const T &input)
High-level PISM I/O class.
array::CellType2 cell_type
array::Scalar2 ice_thickness
array::Scalar2 bed_elevation
Combines the max. time step with the flag indicating if a restriction is active. Makes is possible to...
Makes sure that we call begin_access() and end_access() for all accessed array::Arrays.
stencils::Star< T > star(int i, int j) const
stencils::Box< T > box(int i, int j) const
void write(const OutputFile &file) const
std::shared_ptr< const Grid > grid() const
void set(double c)
Result: v[j] <- c for all j.
void update_ghosts()
Updates ghost points.
VariableMetadata & metadata(unsigned int N=0)
Returns a reference to the VariableMetadata object containing metadata for the compoment N.
bool floating_ice(int i, int j) const
"Cell type" mask. Adds convenience methods to array::Scalar.
int as_int(int i, int j) const
std::shared_ptr< array::Scalar > m_shelf_base_mass_flux
std::shared_ptr< array::Scalar > m_shelf_base_temperature
std::shared_ptr< array::Scalar > m_water_column_pressure
virtual void write_state_impl(const OutputFile &output) const
The default (empty implementation).
virtual DiagnosticList spatial_diagnostics_impl() const
virtual std::set< VariableMetadata > state_impl() const
A very rudimentary PISM ocean model.
const array::Scalar & continental_shelf_mask() const
const array::Scalar & ice_shelf_mask() const
const array::Scalar & ice_rise_mask() const
void update(const array::Scalar &bed_elevation, const array::CellType1 &cell_type)
const array::Scalar & basin_mask() const
const array::Scalar & box_mask() const
double pressure(double ice_thickness) const
double overturning_coeff() const
double ice_density() const
TocBox1 Toc_box1(double area, double T_star, double Soc_box0, double Toc_box0) const
double Toc(double box_area, double temperature, double T_star, double overturning, double salinity) const
double continental_shelf_depth() const
double melt_rate(double pm_point, double Toc) const
equation 8 in the PICO paper.
double overturning(double Soc_box0, double Soc, double Toc_box0, double Toc) const
equation 3 in the PICO paper. See also equation 4.
double theta_pm(double salinity, double pressure) const
double Soc_box1(double Toc_box0, double Soc_box0, double Toc) const
double T_pm(double salinity, double pressure) const
double melt_rate_beckmann_goosse(double pot_pm_point, double Toc) const
Beckmann & Goosse meltrate.
double T_star(double salinity, double temperature, double pressure) const
See equation A6 and lines before in PICO paper.
double Soc(double salinity, double temperature, double Toc) const
void set_ocean_input_fields(const PicoPhysics &physics, const array::Scalar &ice_thickness, const array::CellType1 &mask, const array::Scalar &basin_mask, const array::Scalar &shelf_mask, const std::vector< double > &basin_temperature, const std::vector< double > &basin_salinity, array::Scalar &Toc_box0, array::Scalar &Soc_box0) const
Set ocean ocean input from box 0 as boundary condition for box 1.
void write_state_impl(const OutputFile &output) const
The default (empty implementation).
std::shared_ptr< array::Forcing > m_salinity_ocean
std::map< std::string, Diagnostic::Ptr > spatial_diagnostics_impl() const
MaxTimestep max_timestep_impl(double t) const
void process_box1(const PicoPhysics &physics, const array::Scalar &ice_thickness, const array::Scalar &shelf_mask, const array::Scalar &box_mask, const array::Scalar &Toc_box0, const array::Scalar &Soc_box0, array::Scalar &basal_melt_rate, array::Scalar &basal_temperature, array::Scalar &T_star, array::Scalar &Toc, array::Scalar &Soc, array::Scalar &overturning)
void compute_box_area(int box_id, const array::Scalar &shelf_mask, const array::Scalar &box_mask, std::vector< double > &result) const
Pico(std::shared_ptr< const Grid > g)
array::Scalar1 m_basal_melt_rate
void compute_ocean_input_per_basin(const PicoPhysics &physics, const array::Scalar &basin_mask, const array::Scalar &continental_shelf_mask, const array::Scalar &salinity_ocean, const array::Scalar &theta_ocean, std::vector< double > &temperature, std::vector< double > &salinity) const
Compute temperature and salinity input from ocean data by averaging.
void compute_box_average(int box_id, const array::Scalar &field, const array::Scalar &shelf_mask, const array::Scalar &box_mask, std::vector< double > &result) const
std::shared_ptr< array::Forcing > m_theta_ocean
std::set< VariableMetadata > state_impl() const
void process_other_boxes(const PicoPhysics &physics, const array::Scalar &ice_thickness, const array::Scalar &shelf_mask, const array::Scalar &box_mask, array::Scalar &basal_melt_rate, array::Scalar &basal_temperature, array::Scalar &T_star, array::Scalar &Toc, array::Scalar &Soc) const
void init_impl(const Geometry &geometry)
void update_impl(const Inputs &inputs, double t, double dt)
void beckmann_goosse(const PicoPhysics &physics, const array::Scalar &ice_thickness, const array::Scalar &shelf_mask, const array::CellType &cell_type, const array::Scalar &Toc_box0, const array::Scalar &Soc_box0, array::Scalar &basal_melt_rate, array::Scalar &basal_temperature, array::Scalar &Toc, array::Scalar &Soc)
array::Scalar m_overturning
static const double beta_CC
std::set< VariableMetadata > metadata(std::initializer_list< const Array * > vecs)
@ PISM_READONLY
open an existing file for reading only
bool ocean(int M)
An ocean cell (floating ice or ice-free).
static void extend_basal_melt_rates(const array::CellType1 &cell_type, array::Scalar1 &basal_melt_rate)
void compute_average_water_column_pressure(const Geometry &geometry, double ice_density, double water_density, double standard_gravity, array::Scalar &result)
std::map< std::string, Diagnostic::Ptr > DiagnosticList
T combine(const T &a, const T &b)
void GlobalSum(MPI_Comm comm, double *local, double *result, int count)