World Builder  1.1.0-pre
A geodynamic initial conditions generator
tian2019_water_content.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2018 - 2024 by the authors of the World Builder code.
3 
4  This file is part of the World Builder.
5 
6  This program is free software: you can redistribute it and/or modify
7  it under the terms of the GNU Lesser General Public License as published
8  by the Free Software Foundation, either version 2 of the License, or
9  (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public License
17  along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19 
21 
22 #include "world_builder/kd_tree.h"
23 #include "world_builder/nan.h"
31 #include "world_builder/world.h"
32 
33 
34 namespace WorldBuilder
35 {
36 
37  using namespace Utilities;
38 
39  namespace Features
40  {
41  namespace OceanicPlateModels
42  {
43  namespace Composition
44  {
45  TianWaterContent::TianWaterContent(WorldBuilder::World *world_)
46  :
47  min_depth(NaN::DSNAN),
48  max_depth(NaN::DSNAN),
49  density(NaN::DSNAN)
50  {
51  this->world = world_;
52  this->name = "water content";
53  }
54 
56  = default;
57 
58  void
59  TianWaterContent::declare_entries(Parameters &prm, const std::string & /*unused*/)
60  {
61  // Document plugin and require entries if needed.
62  // Add compositions to the required parameters.
63  prm.declare_entry("", Types::Object({"compositions"}),
64  "TianWaterContent compositional model. Sets bound water content as a compositional field. The returned "
65  "water content is based on the the temperature and pressure at a point within the world. Currently, "
66  "the bound water content can be determined for four different lithologies: 'sediment', mid-ocean "
67  "ridge basalt ('MORB'), 'gabbro', and 'peridotite', using parameterized phase diagrams from Tian et al., 2019 "
68  "(https://doi.org/10.1029/2019GC008488). The pressure is lithostatic, calculated with a constant user defined "
69  "density, and is limited by a user defined cutoff pressure (in GPa) for each lithology. This is required because the "
70  "parameterization breaks down at large pressures. Recommended cutoff pressures are 10 GPa is used for 'peridotite', "
71  "26 GPa is used for 'gabbro', 16 GPa is used for 'MORB', and 1 GPa is used for 'sediment'.");
72 
73  // Declare entries of this plugin
75  "The depth in meters from which the composition of this feature is present.");
76  prm.declare_entry("max depth", Types::OneOf(Types::Double(std::numeric_limits<double>::max()),Types::Array(Types::ValueAtPoints(std::numeric_limits<double>::max(), 2.))),
77  "The depth in meters to which the composition of this feature is present.");
78  prm.declare_entry("compositions", Types::Array(Types::UnsignedInt(),0),
79  "A list with the labels of the composition which are present there.");
80  prm.declare_entry("density", Types::Double(3000.0),
81  "The reference density used for determining the lithostatic pressure for calculating "
82  "the bound water content.");
83  prm.declare_entry("lithology", Types::String("peridotite"),
84  "The lithology used to determine which polynomials to use for calculating the water content. Valid options are: "
85  "'sediment', 'MORB', 'gabbro', and 'peridotite'.");
86  prm.declare_entry("initial water content", Types::Double(5),
87  "The value of the initial water content (in wt%) for the lithology at the trench. This represents the "
88  "max value applied to this lithology.");
89  prm.declare_entry("cutoff pressure", Types::Double(10),
90  "The upper bound for the pressure, in GPa, for the specified lithology in the Tian parameterization. This is necessary because "
91  "the parameterization breaks down for high pressures. It is recommended that 10 GPa is used for 'peridotite', 26 GPa is used for "
92  "'gabbro', 16 GPa is used for 'MORB', and 1 GPa is used for 'sediment'.");
93  prm.declare_entry("operation", Types::String("replace", std::vector<std::string> {"replace", "replace defined only", "add", "subtract"}),
94  "Whether the value should replace any value previously defined at this location (replace) or "
95  "add the value to the previously define value. Replacing implies that all compositions not "
96  "explicitly defined are set to zero. To only replace the defined compositions use the replace only defined option.");
97  }
98 
99  void
100  TianWaterContent::parse_entries(Parameters &prm, const std::vector<Point<2>> &coordinates)
101  {
102  min_depth_surface = Objects::Surface(prm.get("min depth",coordinates));
104  max_depth_surface = Objects::Surface(prm.get("max depth",coordinates));
106  density = prm.get<double>("density");
107  compositions = prm.get_vector<unsigned int>("compositions");
108  max_water_content = prm.get<double>("initial water content");
109  operation = string_operations_to_enum(prm.get<std::string>("operation"));
110  cutoff_pressure = prm.get<double>("cutoff pressure");
111  std::string lithology_string = prm.get<std::string>("lithology");
112 
113  if (lithology_string=="peridotite")
115  else if (lithology_string=="gabbro")
117  else if (lithology_string=="MORB")
119  else if (lithology_string=="sediment")
121  }
122 
123 
124  double
126  double temperature) const
127  {
128  double ln_LR_value = 0;
129  double ln_c_sat_value = 0;
130  double Td_value = 0;
131  std::vector<double> LR_polynomial_coeffs;
132  std::vector<double> c_sat_polynomial_coeffs;
133  std::vector<double> Td_polynomial_coeffs;
134 
135  // Calculate the c_sat value from Tian et al., 2019
136  if (lithology_type == sediment)
137  {
138  for (unsigned int c_sat_index = 0; c_sat_index < c_sat_poly[lithology_type].size(); ++c_sat_index)
139  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));
140  }
141  else
142  {
143  for (unsigned int c_sat_index = 0; c_sat_index < c_sat_poly[lithology_type].size(); ++c_sat_index)
144  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));
145  }
146 
147  // Calculate the LR value from Tian et al., 2019
148  for (unsigned int LR_coeff_index = 0; LR_coeff_index < LR_poly[lithology_type].size(); ++LR_coeff_index)
149  ln_LR_value += LR_poly[lithology_type][LR_coeff_index] * (std::pow(1/pressure, LR_poly[lithology_type].size() - 1 - LR_coeff_index));
150 
151  // Calculate the Td value from Tian et al., 2019
152  for (unsigned int Td_coeff_index = 0; Td_coeff_index < Td_poly[lithology_type].size(); ++Td_coeff_index)
153  Td_value += Td_poly[lithology_type][Td_coeff_index] * (std::pow(pressure, Td_poly[lithology_type].size() - 1 - Td_coeff_index));
154 
155  double partition_coeff = std::exp(ln_c_sat_value) * std::exp(std::exp(ln_LR_value) * (1/temperature - 1/Td_value));
156  return partition_coeff;
157  }
158 
159 
160  double
161  TianWaterContent::get_composition(const Point<3> &position_in_cartesian_coordinates,
162  const Objects::NaturalCoordinate &position_in_natural_coordinates,
163  const double depth,
164  const unsigned int composition_number,
165  double composition,
166  const double /*feature_min_depth*/,
167  const double /*feature_max_depth*/) const
168  {
169  if (depth <= max_depth && depth >= min_depth)
170  {
171  const double min_depth_local = min_depth_surface.constant_value ? min_depth : min_depth_surface.local_value(position_in_natural_coordinates.get_surface_point()).interpolated_value;
172  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;
173  if (depth <= max_depth_local && depth >= min_depth_local)
174  {
175  // The polynomials break down for pressures less than 0.5 GPa, and for pressures above a user defined cutoff pressure
176  // ensure that the pressure is never below 0.5 GPa
177  const double lithostatic_pressure = std::max(0.5, std::min(density * 9.81 * depth / 1e9, cutoff_pressure)); // GPa
178  const double slab_temperature = world->properties(position_in_cartesian_coordinates.get_array(), depth, {{{1,0,0}}})[0];
179  double partition_coefficient = calculate_water_content(lithostatic_pressure,
180  slab_temperature);
181  // The partition_coefficient is output as a percentage, but geodynamic modeling software
182  // typically deal with fractions, so we divide by 100 below
183  partition_coefficient = std::min(max_water_content, partition_coefficient) / 100;
184 
185  for (unsigned int i = 0; i < compositions.size(); ++i)
186  {
187  if (compositions[i] == composition_number)
188  {
189  return apply_operation(operation,composition, partition_coefficient);
190  }
191  }
192 
193  if (operation == Operations::REPLACE)
194  {
195  return 0.0;
196  }
197  }
198  }
199  return composition;
200  }
202  } // namespace Composition
203  } // namespace OceanicPlateModels
204  } // namespace Features
205 } // namespace WorldBuilder
206 
207 
double calculate_water_content(double pressure, double temperature) const
#define WB_REGISTER_FEATURE_OCEANIC_PLATE_COMPOSITION_MODEL(classname, name)
Definition: interface.h:150
const double DSNAN
Definition: nan.h:41
static void declare_entries(Parameters &prm, const std::string &parent_name="")
std::vector< double > properties(const std::array< double, 2 > &point, const double depth, const std::vector< std::array< unsigned int, 3 >> &properties) const
Definition: world.cc:315
const std::array< double, dim > & get_array() const
Definition: point.h:393
Operations string_operations_to_enum(const std::string &operation)
void parse_entries(Parameters &prm, const std::vector< Point< 2 >> &coordinates) override final
SurfaceValueInfo local_value(const Point< 2 > &check_point) const
Definition: surface.cc:149
double get_composition(const Point< 3 > &position, 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 override final
double apply_operation(const Operations operation, const double old_value, const double new_value)
void declare_entry(const std::string &name, const Types::Interface &type, const std::string &documentation)
Definition: parameters.cc:197
std::vector< T > get_vector(const std::string &name)
T get(const std::string &name)