From 4f435a09b757d49561dbc0a583dd94ff24b57872 Mon Sep 17 00:00:00 2001 From: danieldouglas92 Date: Fri, 16 Aug 2024 13:48:26 -0600 Subject: [PATCH] Add test --- ...ter_content.h => tian2019_water_content.h} | 84 ++++++---- ...ter_content.h => tian2019_water_content.h} | 81 ++++++---- ...r_content.cc => tian2019_water_content.cc} | 147 +++++++---------- ...r_content.cc => tian2019_water_content.cc} | 151 +++++++----------- .../water_content_subducting_plate.dat | 29 ++++ .../gwb-dat/water_content_subducting_plate.wb | 55 +++++++ .../screen-output.log | 24 +++ 7 files changed, 327 insertions(+), 244 deletions(-) rename include/world_builder/features/oceanic_plate_models/composition/{water_content.h => tian2019_water_content.h} (59%) rename include/world_builder/features/subducting_plate_models/composition/{water_content.h => tian2019_water_content.h} (59%) rename source/world_builder/features/oceanic_plate_models/composition/{water_content.cc => tian2019_water_content.cc} (58%) rename source/world_builder/features/subducting_plate_models/composition/{water_content.cc => tian2019_water_content.cc} (53%) create mode 100644 tests/gwb-dat/water_content_subducting_plate.dat create mode 100644 tests/gwb-dat/water_content_subducting_plate.wb create mode 100644 tests/gwb-dat/water_content_subducting_plate/screen-output.log diff --git a/include/world_builder/features/oceanic_plate_models/composition/water_content.h b/include/world_builder/features/oceanic_plate_models/composition/tian2019_water_content.h similarity index 59% rename from include/world_builder/features/oceanic_plate_models/composition/water_content.h rename to include/world_builder/features/oceanic_plate_models/composition/tian2019_water_content.h index 4d0b82c1e..0ae3dd84a 100644 --- a/include/world_builder/features/oceanic_plate_models/composition/water_content.h +++ b/include/world_builder/features/oceanic_plate_models/composition/tian2019_water_content.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2018 - 2021 by the authors of the World Builder code. + Copyright (C) 2018 - 2024 by the authors of the World Builder code. This file is part of the World Builder. @@ -36,23 +36,25 @@ namespace WorldBuilder namespace Composition { /** - * This class represents the bound water content in an oceanic plate and can implement + * This class represents the bound water content in a subducting plate and can implement * submodules for computing this bound water content. These submodules determine what * the returned water content will be based on the the temperature and pressure at a point - * within the world. + * within the world. Currently, the bound water content can be determined for four different + * lithologies: sediments, mid-ocean ridge basalts, gabbros, and peridotites, using + * parameterized phase diagrams from Tian et al., 2019 (https://doi.org/10.1029/2019GC008488). */ - class WaterContent final: public Interface + class TianWaterContent final: public Interface { public: /** * constructor */ - WaterContent(WorldBuilder::World *world); + TianWaterContent(WorldBuilder::World *world); /** * Destructor */ - ~WaterContent() override final; + ~TianWaterContent() override final; /** * declare and read in the world builder file into the parameters class @@ -71,8 +73,7 @@ namespace WorldBuilder * "MORB", and "peridotite" */ double calculate_water_content(double pressure, - double temperature, - std::string lithology) const; + double temperature) const; /** * Returns a value for the bound water contend based on the given position, depth in the model, @@ -88,7 +89,7 @@ namespace WorldBuilder private: - // WaterContent composition submodule parameters + // TianWaterContent composition submodule parameters double min_depth; Objects::Surface min_depth_surface; double max_depth; @@ -96,32 +97,49 @@ namespace WorldBuilder Objects::Surface max_depth_surface; std::vector compositions; Operations operation; - std::string lithology_str; double max_water_content; - // Peridotite polynomial coefficients - std::vector LR_poly_peridotite = {-19.0609, 168.983, -630.032, 1281.84, -1543.14, 1111.88, -459.142, 95.4143, 1.97246}; - std::vector c_sat_poly_peridotite = {0.00115628, 2.42179}; - std::vector Td_poly_peridotite = {-15.4627, 94.9716, 636.603}; - - // Gabbro polynomial coefficients - std::vector LR_poly_gabbro = {-1.81745, 7.67198, -10.8507, 5.09329, 8.14519}; - std::vector c_sat_poly_gabbro = {-0.0176673, 0.0893044, 1.52732}; - std::vector Td_poly_gabbro = {-1.72277, 20.5898, 637.517}; - - // MORB polynomial coefficients - std::vector LR_poly_MORB = {-1.78177, 7.50871, -10.4840, 5.19725, 7.96365}; - std::vector c_sat_poly_MORB = {0.0102725, -0.115390, 0.324452, 1.41588}; - std::vector Td_poly_MORB = {-3.81280, 22.7809, 638.049}; - - // Sediment polynomial coefficients - std::vector LR_poly_sediment = {-2.03283, 10.8186, -21.2119, 18.3351, -6.48711, 8.32459}; - std::vector c_sat_poly_sediment = {-0.150662, 0.301807, 1.01867}; - std::vector Td_poly_sediment = {2.83277, -24.7593, 85.9090, 524.898}; - - // Maximum pressure for the lithologies (Peridotite, Gabbro, MORB, Sediment). Above these - // pressures, the parameterized phase diagrams break down and the solubility goes to infinity. - std::vector pressure_cutoffs {10, 26, 16, 1.0}; + enum LithologyName + { + peridotite, + gabbro, + MORB, + sediment + }; + LithologyName lithology_type; + + // Define the coefficients for the polynomials for 3 quantities: LR which represents the + // enthalpy change of the dehydration reactions, c_sat which represents the volatile saturation + // content, and Td which represents the onset temperature of dehydration. The first row is for + // 'peridotite', the second row is for 'gabbro', the third row is for 'MORB', and the fourth row + // is for 'sediment'. + std::vector> LR_poly = + { + {-19.0609, 168.983, -630.032, 1281.84, -1543.14, 1111.88, -459.142, 95.4143, 1.97246}, + {-1.81745, 7.67198, -10.8507, 5.09329, 8.14519}, + {-1.78177, 7.50871, -10.4840, 5.19725, 7.96365}, + {-2.03283, 10.8186, -21.2119, 18.3351, -6.48711, 8.32459} + }; + + std::vector> c_sat_poly = + { + {0.00115628, 2.42179}, + {-0.0176673, 0.0893044, 1.52732}, + {0.0102725, -0.115390, 0.324452, 1.41588}, + {-0.150662, 0.301807, 1.01867} + }; + + std::vector> Td_poly = + { + {-15.4627, 94.9716, 636.603}, + {-1.72277, 20.5898, 637.517}, + {-3.81280, 22.7809, 638.049}, + {2.83277, -24.7593, 85.9090, 524.898} + }; + + // Maximum pressure for the lithologies (Peridotite, Gabbro, MORB, Sediment). These are required because + // Above these pressures, the parameterized phase diagrams break down and the solubility goes to infinity. + double cutoff_pressure; }; } // namespace Composition } // namespace OceanicPlateModels diff --git a/include/world_builder/features/subducting_plate_models/composition/water_content.h b/include/world_builder/features/subducting_plate_models/composition/tian2019_water_content.h similarity index 59% rename from include/world_builder/features/subducting_plate_models/composition/water_content.h rename to include/world_builder/features/subducting_plate_models/composition/tian2019_water_content.h index 4a1adb67e..94092b3c3 100644 --- a/include/world_builder/features/subducting_plate_models/composition/water_content.h +++ b/include/world_builder/features/subducting_plate_models/composition/tian2019_water_content.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2018 - 2021 by the authors of the World Builder code. + Copyright (C) 2018 - 2024 by the authors of the World Builder code. This file is part of the World Builder. @@ -38,20 +38,22 @@ namespace WorldBuilder * This class represents the bound water content in a subducting plate and can implement * submodules for computing this bound water content. These submodules determine what * the returned water content will be based on the the temperature and pressure at a point - * within the world. + * within the world. Currently, the bound water content can be determined for four different + * lithologies: sediments, mid-ocean ridge basalts, gabbros, and peridotites, using + * parameterized phase diagrams from Tian et al., 2019 (https://doi.org/10.1029/2019GC008488). */ - class WaterContent final: public Interface + class TianWaterContent final: public Interface { public: /** * constructor */ - WaterContent(WorldBuilder::World *world); + TianWaterContent(WorldBuilder::World *world); /** * Destructor */ - ~WaterContent() override final; + ~TianWaterContent() override final; /** * declare and read in the world builder file into the parameters class @@ -70,8 +72,7 @@ namespace WorldBuilder * "MORB", and "peridotite" */ double calculate_water_content(double pressure, - double temperature, - std::string lithology) const; + double temperature) const; /** * Returns a value for the bound water contend based on the given position, depth in the model, * gravity, and the temperature at that point. @@ -87,7 +88,7 @@ namespace WorldBuilder private: - // water_content composition submodule parameters + // TianWaterContent composition submodule parameters double min_depth; double max_depth; double density; // Density used to calculate the lithostatic pressure @@ -96,29 +97,47 @@ namespace WorldBuilder std::string lithology_str; double max_water_content; - // Peridotite polynomial coefficients - std::vector LR_poly_peridotite = {-19.0609, 168.983, -630.032, 1281.84, -1543.14, 1111.88, -459.142, 95.4143, 1.97246}; - std::vector c_sat_poly_peridotite = {0.00115628, 2.42179}; - std::vector Td_poly_peridotite = {-15.4627, 94.9716, 636.603}; - - // Gabbro polynomial coefficients - std::vector LR_poly_gabbro = {-1.81745, 7.67198, -10.8507, 5.09329, 8.14519}; - std::vector c_sat_poly_gabbro = {-0.0176673, 0.0893044, 1.52732}; - std::vector Td_poly_gabbro = {-1.72277, 20.5898, 637.517}; - - // MORB polynomial coefficients - std::vector LR_poly_MORB = {-1.78177, 7.50871, -10.4840, 5.19725, 7.96365}; - std::vector c_sat_poly_MORB = {0.0102725, -0.115390, 0.324452, 1.41588}; - std::vector Td_poly_MORB = {-3.81280, 22.7809, 638.049}; - - // Sediment polynomial coefficients - std::vector LR_poly_sediment = {-2.03283, 10.8186, -21.2119, 18.3351, -6.48711, 8.32459}; - std::vector c_sat_poly_sediment = {-0.150662, 0.301807, 1.01867}; - std::vector Td_poly_sediment = {2.83277, -24.7593, 85.9090, 524.898}; - - // Maximum pressure for the lithologies (Peridotite, Gabbro, MORB, Sediment). Above these - // pressures, the parameterized phase diagrams break down and the solubility goes to infinity. - std::vector pressure_cutoffs {10, 26, 16, 1.0}; + enum LithologyName + { + peridotite, + gabbro, + MORB, + sediment + }; + LithologyName lithology_type; + + // Define the coefficients for the polynomials for 3 quantities: LR which represents the + // enthalpy change of the dehydration reactions, c_sat which represents the volatile saturation + // content, and Td which represents the onset temperature of dehydration. The first row is for + // 'peridotite', the second row is for 'gabbro', the third row is for 'MORB', and the fourth row + // is for 'sediment'. + std::vector> LR_poly = + { + {-19.0609, 168.983, -630.032, 1281.84, -1543.14, 1111.88, -459.142, 95.4143, 1.97246}, + {-1.81745, 7.67198, -10.8507, 5.09329, 8.14519}, + {-1.78177, 7.50871, -10.4840, 5.19725, 7.96365}, + {-2.03283, 10.8186, -21.2119, 18.3351, -6.48711, 8.32459} + }; + + std::vector> c_sat_poly = + { + {0.00115628, 2.42179}, + {-0.0176673, 0.0893044, 1.52732}, + {0.0102725, -0.115390, 0.324452, 1.41588}, + {-0.150662, 0.301807, 1.01867} + }; + + std::vector> Td_poly = + { + {-15.4627, 94.9716, 636.603}, + {-1.72277, 20.5898, 637.517}, + {-3.81280, 22.7809, 638.049}, + {2.83277, -24.7593, 85.9090, 524.898} + }; + + // Maximum pressure for the lithologies (Peridotite, Gabbro, MORB, Sediment). These are required because + // Above these pressures, the parameterized phase diagrams break down and the solubility goes to infinity. + double cutoff_pressure; }; } // namespace Composition } // namespace SubductingPlateModels diff --git a/source/world_builder/features/oceanic_plate_models/composition/water_content.cc b/source/world_builder/features/oceanic_plate_models/composition/tian2019_water_content.cc similarity index 58% rename from source/world_builder/features/oceanic_plate_models/composition/water_content.cc rename to source/world_builder/features/oceanic_plate_models/composition/tian2019_water_content.cc index 9946ad90c..4794a54f5 100644 --- a/source/world_builder/features/oceanic_plate_models/composition/water_content.cc +++ b/source/world_builder/features/oceanic_plate_models/composition/tian2019_water_content.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2018 - 2021 by the authors of the World Builder code. + Copyright (C) 2018 - 2024 by the authors of the World Builder code. This file is part of the World Builder. @@ -17,7 +17,7 @@ along with this program. If not, see . */ -#include "world_builder/features/oceanic_plate_models/composition/water_content.h" +#include "world_builder/features/oceanic_plate_models/composition/tian2019_water_content.h" #include "world_builder/kd_tree.h" #include "world_builder/nan.h" @@ -28,6 +28,7 @@ #include "world_builder/types/unsigned_int.h" #include "world_builder/types/value_at_points.h" #include "world_builder/utilities.h" +#include "world_builder/world.h" namespace WorldBuilder @@ -41,7 +42,7 @@ namespace WorldBuilder { namespace Composition { - WaterContent::WaterContent(WorldBuilder::World *world_) + TianWaterContent::TianWaterContent(WorldBuilder::World *world_) : min_depth(NaN::DSNAN), max_depth(NaN::DSNAN), @@ -51,16 +52,23 @@ namespace WorldBuilder this->name = "water content"; } - WaterContent::~WaterContent() + TianWaterContent::~TianWaterContent() = default; void - WaterContent::declare_entries(Parameters &prm, const std::string & /*unused*/) + TianWaterContent::declare_entries(Parameters &prm, const std::string & /*unused*/) { // Document plugin and require entries if needed. // Add compositions to the required parameters. prm.declare_entry("", Types::Object({"compositions"}), - "WaterContent compositional model. Sets water content as a compositional field."); + "TianWaterContent compositional model. Sets bound water content as a compositional field. The returned " + "water content is based on the the temperature and pressure at a point within the world. Currently, " + "the bound water content can be determined for four different lithologies: 'sediment', mid-ocean " + "ridge basalt ('MORB'), 'gabbro', and 'peridotite', using parameterized phase diagrams from Tian et al., 2019 " + "(https://doi.org/10.1029/2019GC008488). The pressure is lithostatic, calculated with a constant user defined " + "density, and is limited by a user defined cutoff pressure (in GPa) for each lithology. This is required because the " + "parameterization breaks down at large pressures. Recommended cutoff pressures are 10 GPa is used for 'peridotite', " + "26 GPa is used for 'gabbro', 16 GPa is used for 'MORB', and 1 GPa is used for 'sediment'."); // Declare entries of this plugin prm.declare_entry("min depth", Types::OneOf(Types::Double(0),Types::Array(Types::ValueAtPoints(0., 2.))), @@ -70,22 +78,26 @@ namespace WorldBuilder prm.declare_entry("compositions", Types::Array(Types::UnsignedInt(),0), "A list with the labels of the composition which are present there."); prm.declare_entry("density", Types::Double(3000.0), - "The reference density used to estimate the lithostatic pressure for calculating " + "The reference density used for determining the lithostatic pressure for calculating " "the bound water content."); prm.declare_entry("lithology", Types::String("peridotite"), - "The lithology used to determine which polynomials to use for calculating the water content."); + "The lithology used to determine which polynomials to use for calculating the water content. Valid options are: " + "'sediment', 'MORB', 'gabbro', and 'peridotite'."); prm.declare_entry("initial water content", Types::Double(5), - "The value of the initial water content (in wt%) for the lithology at the trench. This is essentially the " + "The value of the initial water content (in wt%) for the lithology at the trench. This represents the " "max value applied to this lithology."); + prm.declare_entry("cutoff pressure", Types::Double(10), + "The upper bound for the pressure, in GPa, for the specified lithology in the Tian parameterization. This is necessary because " + "the parameterization breaks down for high pressures. It is recommended that 10 GPa is used for 'peridotite', 26 GPa is used for " + "'gabbro', 16 GPa is used for 'MORB', and 1 GPa is used for 'sediment'."); prm.declare_entry("operation", Types::String("replace", std::vector {"replace", "replace defined only", "add", "subtract"}), "Whether the value should replace any value previously defined at this location (replace) or " "add the value to the previously define value. Replacing implies that all compositions not " "explicitly defined are set to zero. To only replace the defined compositions use the replace only defined option."); - } void - WaterContent::parse_entries(Parameters &prm, const std::vector> &coordinates) + TianWaterContent::parse_entries(Parameters &prm, const std::vector> &coordinates) { min_depth_surface = Objects::Surface(prm.get("min depth",coordinates)); min_depth = min_depth_surface.minimum; @@ -95,16 +107,24 @@ namespace WorldBuilder compositions = prm.get_vector("compositions"); max_water_content = prm.get("initial water content"); operation = string_operations_to_enum(prm.get("operation")); - lithology_str = prm.get("lithology"); + cutoff_pressure = prm.get("cutoff pressure"); + std::string lithology_string = prm.get("lithology"); + + if (lithology_string=="peridotite") + lithology_type = peridotite; + else if (lithology_string=="gabbro") + lithology_type = gabbro; + else if (lithology_string=="MORB") + lithology_type = MORB; + else if (lithology_string=="sediment") + lithology_type = sediment; } double - WaterContent::calculate_water_content(double pressure, - double temperature, - std::string lithology_string) const + TianWaterContent::calculate_water_content(double pressure, + double temperature) const { - pressure = pressure <= 0.5 ? 0.5 : pressure; double ln_LR_value = 0; double ln_c_sat_value = 0; double Td_value = 0; @@ -112,77 +132,25 @@ namespace WorldBuilder std::vector c_sat_polynomial_coeffs; std::vector Td_polynomial_coeffs; - enum LithologyName - { - peridotite, - gabbro, - MORB, - sediment - }; - LithologyName lithology = peridotite; - - if (lithology_string=="peridotite") - lithology = peridotite; - else if (lithology_string=="gabbro") - lithology = gabbro; - else if (lithology_string=="MORB") - lithology = MORB; - else if (lithology_string=="sediment") - lithology = sediment; - - if (lithology == peridotite) - { - LR_polynomial_coeffs = LR_poly_peridotite; - c_sat_polynomial_coeffs = c_sat_poly_peridotite; - Td_polynomial_coeffs = Td_poly_peridotite; - pressure = pressure > pressure_cutoffs[0] ? pressure_cutoffs[0] : pressure; - } - - if (lithology == gabbro) - { - LR_polynomial_coeffs = LR_poly_gabbro; - c_sat_polynomial_coeffs = c_sat_poly_gabbro; - Td_polynomial_coeffs = Td_poly_gabbro; - pressure = pressure > pressure_cutoffs[1] ? pressure_cutoffs[1] : pressure; - } - - if (lithology == MORB) - { - LR_polynomial_coeffs = LR_poly_MORB; - c_sat_polynomial_coeffs = c_sat_poly_MORB; - Td_polynomial_coeffs = Td_poly_MORB; - pressure = pressure > pressure_cutoffs[2] ? pressure_cutoffs[2] : pressure; - } - - if (lithology == sediment) - { - LR_polynomial_coeffs = LR_poly_sediment; - c_sat_polynomial_coeffs = c_sat_poly_sediment; - Td_polynomial_coeffs = Td_poly_sediment; - pressure = pressure > pressure_cutoffs[3] ? pressure_cutoffs[3] : pressure; - } - - double inv_pressure = 1/pressure; - // Calculate the c_sat value from Tian et al., 2019 - if (lithology == sediment) + if (lithology_type == sediment) { - for (unsigned int c_sat_index = 0; c_sat_index < c_sat_polynomial_coeffs.size(); ++c_sat_index) - ln_c_sat_value += c_sat_polynomial_coeffs[c_sat_index] * (std::pow(std::log10(pressure), c_sat_polynomial_coeffs.size() - 1 - c_sat_index)); + for (unsigned int c_sat_index = 0; c_sat_index < c_sat_poly[lithology_type].size(); ++c_sat_index) + ln_c_sat_value += c_sat_poly[lithology_type][c_sat_index] * (std::pow(std::log10(pressure), c_sat_poly[lithology_type].size() - 1 - c_sat_index)); } else { - for (unsigned int c_sat_index = 0; c_sat_index < c_sat_polynomial_coeffs.size(); ++c_sat_index) - ln_c_sat_value += c_sat_polynomial_coeffs[c_sat_index] * (std::pow(pressure, c_sat_polynomial_coeffs.size() - 1 - c_sat_index)); + for (unsigned int c_sat_index = 0; c_sat_index < c_sat_poly[lithology_type].size(); ++c_sat_index) + ln_c_sat_value += c_sat_poly[lithology_type][c_sat_index] * (std::pow(pressure, c_sat_poly[lithology_type].size() - 1 - c_sat_index)); } // Calculate the LR value from Tian et al., 2019 - for (unsigned int LR_coeff_index = 0; LR_coeff_index < LR_polynomial_coeffs.size(); ++LR_coeff_index) - ln_LR_value += LR_polynomial_coeffs[LR_coeff_index] * (std::pow(inv_pressure, LR_polynomial_coeffs.size() - 1 - LR_coeff_index)); + for (unsigned int LR_coeff_index = 0; LR_coeff_index < LR_poly[lithology_type].size(); ++LR_coeff_index) + ln_LR_value += LR_poly[lithology_type][LR_coeff_index] * (std::pow(1/pressure, LR_poly[lithology_type].size() - 1 - LR_coeff_index)); // Calculate the Td value from Tian et al., 2019 - for (unsigned int Td_coeff_index = 0; Td_coeff_index < Td_polynomial_coeffs.size(); ++Td_coeff_index) - Td_value += Td_polynomial_coeffs[Td_coeff_index] * (std::pow(pressure, Td_polynomial_coeffs.size() - 1 - Td_coeff_index)); + for (unsigned int Td_coeff_index = 0; Td_coeff_index < Td_poly[lithology_type].size(); ++Td_coeff_index) + Td_value += Td_poly[lithology_type][Td_coeff_index] * (std::pow(pressure, Td_poly[lithology_type].size() - 1 - Td_coeff_index)); double partition_coeff = std::exp(ln_c_sat_value) * std::exp(std::exp(ln_LR_value) * (1/temperature - 1/Td_value)); return partition_coeff; @@ -190,13 +158,13 @@ namespace WorldBuilder double - WaterContent::get_composition(const Point<3> &position_in_cartesian_coordinates, - const Objects::NaturalCoordinate &position_in_natural_coordinates, - const double depth, - const unsigned int composition_number, - double composition, - const double /*feature_min_depth*/, - const double /*feature_max_depth*/) const + TianWaterContent::get_composition(const Point<3> &position_in_cartesian_coordinates, + const Objects::NaturalCoordinate &position_in_natural_coordinates, + const double depth, + const unsigned int composition_number, + double composition, + const double /*feature_min_depth*/, + const double /*feature_max_depth*/) const { if (depth <= max_depth && depth >= min_depth) { @@ -204,11 +172,12 @@ namespace WorldBuilder const double max_depth_local = max_depth_surface.constant_value ? max_depth : max_depth_surface.local_value(position_in_natural_coordinates.get_surface_point()).interpolated_value; if (depth <= max_depth_local && depth >= min_depth_local) { - double lithostatic_pressure = density * 9.81 * depth / 1e9; // GPa - double slab_temperature = this->world->properties(position_in_cartesian_coordinates.get_array(), depth, {{{1,0,0}}})[0]; + // The polynomials break down for pressures less than 0.5 GPa, and for pressures above a user defined cutoff pressure + // ensure that the pressure is never below 0.5 GPa + const double lithostatic_pressure = std::max(0.5, std::min(density * 9.81 * depth / 1e9, cutoff_pressure)); // GPa + const double slab_temperature = world->properties(position_in_cartesian_coordinates.get_array(), depth, {{{1,0,0}}})[0]; double partition_coefficient = calculate_water_content(lithostatic_pressure, - slab_temperature, - lithology_str); + slab_temperature); partition_coefficient = std::min(max_water_content, partition_coefficient); @@ -228,7 +197,7 @@ namespace WorldBuilder } return composition; } - WB_REGISTER_FEATURE_OCEANIC_PLATE_COMPOSITION_MODEL(WaterContent, water content) + WB_REGISTER_FEATURE_OCEANIC_PLATE_COMPOSITION_MODEL(TianWaterContent, tian water content) } // namespace Composition } // namespace OceanicPlateModels } // namespace Features diff --git a/source/world_builder/features/subducting_plate_models/composition/water_content.cc b/source/world_builder/features/subducting_plate_models/composition/tian2019_water_content.cc similarity index 53% rename from source/world_builder/features/subducting_plate_models/composition/water_content.cc rename to source/world_builder/features/subducting_plate_models/composition/tian2019_water_content.cc index b10b0a0b9..0835b4c4c 100644 --- a/source/world_builder/features/subducting_plate_models/composition/water_content.cc +++ b/source/world_builder/features/subducting_plate_models/composition/tian2019_water_content.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2018 - 2021 by the authors of the World Builder code. + Copyright (C) 2018 - 2024 by the authors of the World Builder code. This file is part of the World Builder. @@ -17,7 +17,7 @@ along with this program. If not, see . */ -#include "world_builder/features/subducting_plate_models/composition/water_content.h" +#include "world_builder/features/subducting_plate_models/composition/tian2019_water_content.h" #include "world_builder/nan.h" #include "world_builder/types/array.h" @@ -25,6 +25,7 @@ #include "world_builder/types/object.h" #include "world_builder/types/unsigned_int.h" #include "world_builder/utilities.h" +#include "world_builder/world.h" namespace WorldBuilder @@ -38,7 +39,7 @@ namespace WorldBuilder { namespace Composition { - WaterContent::WaterContent(WorldBuilder::World *world_) + TianWaterContent::TianWaterContent(WorldBuilder::World *world_) : min_depth(NaN::DSNAN), max_depth(NaN::DSNAN), @@ -48,16 +49,23 @@ namespace WorldBuilder this->name = "water content"; } - WaterContent::~WaterContent() + TianWaterContent::~TianWaterContent() = default; void - WaterContent::declare_entries(Parameters &prm, const std::string & /*unused*/) + TianWaterContent::declare_entries(Parameters &prm, const std::string & /*unused*/) { // Document plugin and require entries if needed. // Add compositions to the required parameters. prm.declare_entry("", Types::Object({"compositions"}), - "WaterContent compositional model. Sets constant compositional field."); + "TianWaterContent compositional model. Sets bound water content as a compositional field. The returned " + "water content is based on the the temperature and pressure at a point within the world. Currently, " + "the bound water content can be determined for four different lithologies: 'sediment', mid-ocean " + "ridge basalt ('MORB'), 'gabbro', and 'peridotite', using parameterized phase diagrams from Tian et al., 2019 " + "(https://doi.org/10.1029/2019GC008488). The pressure is lithostatic, calculated with a constant user defined " + "density, and is limited by a user defined cutoff pressure (in GPa) for each lithology. This is required because the " + "parameterization breaks down at large pressures. Recommended cutoff pressures are 10 GPa is used for 'peridotite', " + "26 GPa is used for 'gabbro', 16 GPa is used for 'MORB', and 1 GPa is used for 'sediment'."); // Declare entries of this plugin prm.declare_entry("min distance slab top", Types::Double(0), @@ -65,16 +73,21 @@ namespace WorldBuilder prm.declare_entry("max distance slab top", Types::Double(std::numeric_limits::max()), "todo The depth in meters to which the composition of this feature is present."); prm.declare_entry("density", Types::Double(3000.0), - "The reference density used to estimate the lithostatic pressure for calculating " + "The reference density used for determining the lithostatic pressure for calculating " "the bound water content."); prm.declare_entry("compositions", Types::Array(Types::UnsignedInt(),0), "A list with the labels of the composition which are present there."); prm.declare_entry("lithology", Types::String("peridotite"), - "The lithology used to determine which polynomials to use for calculating the water content."); + "The lithology used to determine which polynomials to use for calculating the water content. Valid options are: " + "'sediment', 'MORB', 'gabbro', and 'peridotite'."); prm.declare_entry("initial water content", Types::Double(5), - "The value of the initial water content (in wt%) for the lithology at the trench. This is essentially the " + "The value of the initial water content (in wt%) for the lithology at the trench. This represents the " "max value applied to this lithology."); - prm.declare_entry("operation", Types::String("add", std::vector {"replace", "replace defined only", "add", "subtract"}), + prm.declare_entry("cutoff pressure", Types::Double(10), + "The upper bound for the pressure, in GPa, for the specified lithology in the Tian parameterization. This is necessary because " + "the parameterization breaks down for high pressures. It is recommended that 10 GPa is used for 'peridotite', 26 GPa is used for " + "'gabbro', 16 GPa is used for 'MORB', and 1 GPa is used for 'sediment'."); + prm.declare_entry("operation", Types::String("replace", std::vector {"replace", "replace defined only", "add", "subtract"}), "Whether the value should replace any value previously defined at this location (replace) or " "add the value to the previously define value. Replacing implies that all compositions not " "explicitly defined are set to zero. To only replace the defined compositions use the replace only defined option."); @@ -82,24 +95,32 @@ namespace WorldBuilder } void - WaterContent::parse_entries(Parameters &prm) + TianWaterContent::parse_entries(Parameters &prm) { min_depth = prm.get("min distance slab top"); max_depth = prm.get("max distance slab top"); density = prm.get("density"); compositions = prm.get_vector("compositions"); max_water_content = prm.get("initial water content"); + cutoff_pressure = prm.get("cutoff pressure"); operation = string_operations_to_enum(prm.get("operation")); - lithology_str = prm.get("lithology"); + std::string lithology_string = prm.get("lithology"); + + if (lithology_string=="peridotite") + lithology_type = peridotite; + else if (lithology_string=="gabbro") + lithology_type = gabbro; + else if (lithology_string=="MORB") + lithology_type = MORB; + else if (lithology_string=="sediment") + lithology_type = sediment; } double - WaterContent::calculate_water_content(double pressure, - double temperature, - std::string lithology_string) const + TianWaterContent::calculate_water_content(double pressure, + double temperature) const { - pressure = pressure <= 0.5 ? 0.5 : pressure; double ln_LR_value = 0; double ln_c_sat_value = 0; double Td_value = 0; @@ -107,78 +128,25 @@ namespace WorldBuilder std::vector c_sat_polynomial_coeffs; std::vector Td_polynomial_coeffs; - enum LithologyName - { - peridotite, - gabbro, - MORB, - sediment - }; - - LithologyName lithology = peridotite; - - if (lithology_string=="peridotite") - lithology = peridotite; - else if (lithology_string=="gabbro") - lithology = gabbro; - else if (lithology_string=="MORB") - lithology = MORB; - else if (lithology_string=="sediment") - lithology = sediment; - - if (lithology == peridotite) - { - LR_polynomial_coeffs = LR_poly_peridotite; - c_sat_polynomial_coeffs = c_sat_poly_peridotite; - Td_polynomial_coeffs = Td_poly_peridotite; - pressure = pressure > pressure_cutoffs[0] ? pressure_cutoffs[0] : pressure; - } - - if (lithology == gabbro) - { - LR_polynomial_coeffs = LR_poly_gabbro; - c_sat_polynomial_coeffs = c_sat_poly_gabbro; - Td_polynomial_coeffs = Td_poly_gabbro; - pressure = pressure > pressure_cutoffs[1] ? pressure_cutoffs[1] : pressure; - } - - if (lithology == MORB) - { - LR_polynomial_coeffs = LR_poly_MORB; - c_sat_polynomial_coeffs = c_sat_poly_MORB; - Td_polynomial_coeffs = Td_poly_MORB; - pressure = pressure > pressure_cutoffs[2] ? pressure_cutoffs[2] : pressure; - } - - if (lithology == sediment) - { - LR_polynomial_coeffs = LR_poly_sediment; - c_sat_polynomial_coeffs = c_sat_poly_sediment; - Td_polynomial_coeffs = Td_poly_sediment; - pressure = pressure > pressure_cutoffs[3] ? 1.0 : pressure; - } - - double inv_pressure = 1/pressure; - // Calculate the c_sat value from Tian et al., 2019 - if (lithology == sediment) + if (lithology_type == sediment) { - for (unsigned int c_sat_index = 0; c_sat_index < c_sat_polynomial_coeffs.size(); ++c_sat_index) - ln_c_sat_value += c_sat_polynomial_coeffs[c_sat_index] * (std::pow(std::log10(pressure), c_sat_polynomial_coeffs.size() - 1 - c_sat_index)); + for (unsigned int c_sat_index = 0; c_sat_index < c_sat_poly[lithology_type].size(); ++c_sat_index) + ln_c_sat_value += c_sat_poly[lithology_type][c_sat_index] * (std::pow(std::log10(pressure), c_sat_poly[lithology_type].size() - 1 - c_sat_index)); } else { - for (unsigned int c_sat_index = 0; c_sat_index < c_sat_polynomial_coeffs.size(); ++c_sat_index) - ln_c_sat_value += c_sat_polynomial_coeffs[c_sat_index] * (std::pow(pressure, c_sat_polynomial_coeffs.size() - 1 - c_sat_index)); + for (unsigned int c_sat_index = 0; c_sat_index < c_sat_poly[lithology_type].size(); ++c_sat_index) + ln_c_sat_value += c_sat_poly[lithology_type][c_sat_index] * (std::pow(pressure, c_sat_poly[lithology_type].size() - 1 - c_sat_index)); } // Calculate the LR value from Tian et al., 2019 - for (unsigned int LR_coeff_index = 0; LR_coeff_index < LR_polynomial_coeffs.size(); ++LR_coeff_index) - ln_LR_value += LR_polynomial_coeffs[LR_coeff_index] * (std::pow(inv_pressure, LR_polynomial_coeffs.size() - 1 - LR_coeff_index)); + for (unsigned int LR_coeff_index = 0; LR_coeff_index < LR_poly[lithology_type].size(); ++LR_coeff_index) + ln_LR_value += LR_poly[lithology_type][LR_coeff_index] * (std::pow(1/pressure, LR_poly[lithology_type].size() - 1 - LR_coeff_index)); // Calculate the Td value from Tian et al., 2019 - for (unsigned int Td_coeff_index = 0; Td_coeff_index < Td_polynomial_coeffs.size(); ++Td_coeff_index) - Td_value += Td_polynomial_coeffs[Td_coeff_index] * (std::pow(pressure, Td_polynomial_coeffs.size() - 1 - Td_coeff_index)); + for (unsigned int Td_coeff_index = 0; Td_coeff_index < Td_poly[lithology_type].size(); ++Td_coeff_index) + Td_value += Td_poly[lithology_type][Td_coeff_index] * (std::pow(pressure, Td_poly[lithology_type].size() - 1 - Td_coeff_index)); double partition_coeff = std::exp(ln_c_sat_value) * std::exp(std::exp(ln_LR_value) * (1/temperature - 1/Td_value)); return partition_coeff; @@ -186,22 +154,23 @@ namespace WorldBuilder double - WaterContent::get_composition(const Point<3> &position_in_cartesian_coordinates, - const double depth, - const unsigned int composition_number, - double composition, - const double /*feature_min_depth*/, - const double /*feature_max_depth*/, - const WorldBuilder::Utilities::PointDistanceFromCurvedPlanes &distance_from_plane, - const AdditionalParameters & /*additional_parameters*/) const + TianWaterContent::get_composition(const Point<3> &position_in_cartesian_coordinates, + const double depth, + const unsigned int composition_number, + double composition, + const double /*feature_min_depth*/, + const double /*feature_max_depth*/, + const WorldBuilder::Utilities::PointDistanceFromCurvedPlanes &distance_from_plane, + const AdditionalParameters & /*additional_parameters*/) const { if (distance_from_plane.distance_from_plane <= max_depth && distance_from_plane.distance_from_plane >= min_depth) { - double lithostatic_pressure = density * 9.81 * depth / 1e9; // GPa - double slab_temperature = this->world->properties(position_in_cartesian_coordinates.get_array(), depth, {{{1,0,0}}})[0]; + // The polynomials break down for pressures less than 0.5 GPa, and for pressures above a user defined cutoff pressure + // ensure that the pressure is never below 0.5 GPa + const double lithostatic_pressure = std::max(0.5, std::min(density * 9.81 * depth / 1e9, cutoff_pressure)); // GPa + const double slab_temperature = world->properties(position_in_cartesian_coordinates.get_array(), depth, {{{1,0,0}}})[0]; double partition_coefficient = calculate_water_content(lithostatic_pressure, - slab_temperature, - lithology_str); + slab_temperature); partition_coefficient = std::min(max_water_content, partition_coefficient); @@ -220,7 +189,7 @@ namespace WorldBuilder } return composition; } - WB_REGISTER_FEATURE_SUBDUCTING_PLATE_COMPOSITION_MODEL(WaterContent, water content) + WB_REGISTER_FEATURE_SUBDUCTING_PLATE_COMPOSITION_MODEL(TianWaterContent, tian water content) } // namespace Composition } // namespace SubductingPlateModels } // namespace Features diff --git a/tests/gwb-dat/water_content_subducting_plate.dat b/tests/gwb-dat/water_content_subducting_plate.dat new file mode 100644 index 000000000..0a66e195d --- /dev/null +++ b/tests/gwb-dat/water_content_subducting_plate.dat @@ -0,0 +1,29 @@ +# This is a comment in the data +# file. +# Now define parameters: +# dim = 2 +# compositions = 1 +# x y d T C0 +50e3 0e3 1e3 +50e3 0e3 4e3 +50e3 0e3 8e3 +50e3 0e3 11e3 +210e3 0e3 55e3 +210e3 0e3 57.5e3 +210e3 0e3 60e3 +210e3 0e3 62.5e3 +210e3 0e3 65e3 +210e3 0e3 67.5e3 +210e3 0e3 70e3 +210e3 0e3 72.5e3 +210e3 0e3 75e3 +210e3 0e3 77.5e3 +210e3 0e3 80e3 +210e3 0e3 82.5e3 +210e3 0e3 85e3 +210e3 0e3 87.5e3 +210e3 0e3 90e3 +210e3 0e3 92.5e3 +210e3 0e3 95e3 +210e3 0e3 97.5e3 +210e3 0e3 100e3 \ No newline at end of file diff --git a/tests/gwb-dat/water_content_subducting_plate.wb b/tests/gwb-dat/water_content_subducting_plate.wb new file mode 100644 index 000000000..372a42170 --- /dev/null +++ b/tests/gwb-dat/water_content_subducting_plate.wb @@ -0,0 +1,55 @@ +{ + "version": "1.0", + "gravity model":{"model":"uniform", "magnitude":10}, + "cross section":[[0,0],[50e3,0]], + "surface temperature":273, "potential mantle temperature":1573, + "thermal expansion coefficient":3.1e-5, "specific heat":1000, "thermal diffusivity":1.0e-6, + "features": + [ + + {"model": "oceanic plate", "name": "test", + "coordinates": [[-110e3, -100e3], [-110e3, 100e3], [100e3, 100e3], [100e3, -100e3]], + "min depth":0.0, "max depth":100e3, + "composition models": [{"model":"tian water content", "compositions":[0], "min depth": 0, "max depth": 2e3, "lithology":"sediment", "initial water content":3, "cutoff pressure":1}, + {"model":"tian water content", "compositions":[0], "min depth": 2e3, "max depth": 6e3, "lithology":"MORB", "initial water content":1, "cutoff pressure":16}, + {"model":"tian water content", "compositions":[0], "min depth": 6e3, "max depth": 10e3, "lithology":"gabbro", "initial water content":0.5, "cutoff pressure":26}, + {"model":"tian water content", "compositions":[0], "min depth": 10e3, "max depth": 20e3, "lithology":"peridotite", "initial water content":2, "cutoff pressure":10}], + "temperature models": [{"model":"plate model", "bottom temperature": -1, "top temperature": 273, "max depth":100e3, "min depth": 0.0, + "ridge coordinates": [[[-500e3,-100e3],[-500e3,100e3]]], "spreading velocity": 0.05}]}, + + {"model": "oceanic plate", "name": "test", + "coordinates": [[1100e3, -100e3], [1100e3, 100e3], [100e3, 100e3], [100e3, -100e3]], + "min depth":0.0, "max depth":100e3, + "temperature models": [{"model": "plate model", "bottom temperature": -1, "top temperature": 273, "max depth":100e3, "min depth": 0.0, + "ridge coordinates": [[[1100e3,-100e3],[1100e3,100e3]]], "spreading velocity": 0.05}]}, + + {"model":"subducting plate", "name":"Slab", + + "coordinates":[[100e3,-100e3],[100e3,100e3]], + + "dip point":[1e7,0],"max depth":1000e3, + + "segments":[{"length":200e3,"thickness":[100e3],"top truncation":[-50e3],"angle":[0,85]}, + {"length":200e3,"thickness":[100e3],"top truncation":[-50e3],"angle":[85,0]}, + {"length":100e3,"thickness":[100e3],"top truncation":[-50e3],"angle":[0,90]}, + {"length":200e3,"thickness":[100e3],"top truncation":[-50e3],"angle":[90,90]}], + + "composition models":[ + {"model":"tian water content", "compositions":[0], "density":3300, "min distance slab top":0, "max distance slab top":2e3, "lithology":"sediment", "initial water content":3, "cutoff pressure":1}, + {"model":"tian water content", "compositions":[0], "density":3300, "min distance slab top":2e3, "max distance slab top":6e3, "lithology":"MORB", "initial water content":1, "cutoff pressure":16}, + {"model":"tian water content", "compositions":[0], "density":3300, "min distance slab top":6e3, "max distance slab top":10e3, "lithology":"gabbro", "initial water content":0.5, "cutoff pressure":26}, + {"model":"tian water content", "compositions":[0], "density":3300, "min distance slab top":10e3, "max distance slab top":20e3, "lithology":"peridotite", "initial water content":2, "cutoff pressure":10}], + + "temperature models":[{"model":"mass conserving", + "reference model name": "plate model", + "adiabatic heating":true, + "subducting velocity":0.05, + "spreading velocity":0.05, + "ridge coordinates":[[[-500e3,-100e3],[-500e3,100e3]]], + "coupling depth":80e3, + "forearc cooling factor":1.0, + "taper distance":0, + "min distance slab top":-200e3, "max distance slab top":300e3}] + } + ] +} diff --git a/tests/gwb-dat/water_content_subducting_plate/screen-output.log b/tests/gwb-dat/water_content_subducting_plate/screen-output.log new file mode 100644 index 000000000..385f3c400 --- /dev/null +++ b/tests/gwb-dat/water_content_subducting_plate/screen-output.log @@ -0,0 +1,24 @@ +# x z d T c0 tag +50e3 0e3 1e3 312.405 3 0 +50e3 0e3 4e3 430.229 1 0 +50e3 0e3 8e3 584.338 0.5 0 +50e3 0e3 11e3 695.831 2 0 +210e3 0e3 55e3 1322.25 0 1 +210e3 0e3 57.5e3 1203.62 0.372427 1 +210e3 0e3 60e3 1034.33 0.510104 1 +210e3 0e3 62.5e3 849.688 0.689769 1 +210e3 0e3 65e3 697.68 1 1 +210e3 0e3 67.5e3 614.688 1 1 +210e3 0e3 70e3 616.639 0.5 1 +210e3 0e3 72.5e3 652.908 0.5 1 +210e3 0e3 75e3 687.783 0.5 1 +210e3 0e3 77.5e3 721.222 2 1 +210e3 0e3 80e3 753.193 2 1 +210e3 0e3 82.5e3 783.675 2 1 +210e3 0e3 85e3 812.658 2 1 +210e3 0e3 87.5e3 839.142 2 1 +210e3 0e3 90e3 863.691 2 1 +210e3 0e3 92.5e3 886.838 1.91775 1 +210e3 0e3 95e3 908.585 1.30946 1 +210e3 0e3 97.5e3 928.938 0.908444 1 +210e3 0e3 100e3 947.908 0.640881 1