World Builder  1.1.0-pre
A geodynamic initial conditions generator
plate_model.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/nan.h"
27 #include "world_builder/world.h"
28 
29 namespace WorldBuilder
30 {
31  using namespace Utilities;
32 
33  namespace Features
34  {
35  namespace SubductingPlateModels
36  {
37  namespace Temperature
38  {
39  PlateModel::PlateModel(WorldBuilder::World *world_)
40  :
41  min_depth(NaN::DSNAN),
42  max_depth(NaN::DSNAN),
43  density(NaN::DSNAN),
44  plate_velocity(NaN::DSNAN),
45  thermal_conductivity(NaN::DSNAN),
46  thermal_expansion_coefficient(NaN::DSNAN),
47  specific_heat(NaN::DSNAN),
48  potential_mantle_temperature(NaN::DSNAN),
49  surface_temperature(NaN::DSNAN),
50  adiabatic_heating(true),
51  operation(Operations::REPLACE)
52  {
53  this->world = world_;
54  this->name = "plate model";
55  }
56 
58  = default;
59 
60  void
61  PlateModel::declare_entries(Parameters &prm, const std::string & /*unused*/)
62  {
63  // Document plugin and require entries if needed.
64  // Add `plate velocity` to the required parameters.
65  prm.declare_entry("", Types::Object({"plate velocity"}),
66  "Plate model (based on McKenzie, 1970).");
67 
68  // Declare entries of this plugin
69  prm.declare_entry("min distance slab top", Types::Double(0),
70  "todo The depth in meters from which the composition of this feature is present.");
71 
72  prm.declare_entry("max distance slab top", Types::Double(std::numeric_limits<double>::max()),
73  "todo The depth in meters to which the composition of this feature is present.");
74 
75  prm.declare_entry("density", Types::Double(3300),
76  "The reference density of the subducting plate in $kg/m^3$");
77 
78  prm.declare_entry("plate velocity", Types::Double(NaN::DQNAN),
79  "The velocity in meters per year with which the plate subducts in meters per year.");
80 
81  prm.declare_entry("thermal conductivity", Types::Double(2.0),
82  "The thermal conductivity of the subducting plate material in $W m^{-1} K^{-1}$.");
83 
84  prm.declare_entry("thermal expansion coefficient", Types::Double(-1),
85  "The thermal expansivity of the subducting plate material in $K^{-1}$. If smaller than zero, the global value is used.");
86 
87  prm.declare_entry("specific heat", Types::Double(-1),
88  "The specific heat of the subducting plate material in $J kg^{-1} K^{-1}$. If smaller than zero, the global value is used.");
89 
90  prm.declare_entry("adiabatic heating", Types::Bool(true),
91  "Whether adiabatic heating should be used for the slab. Setting the parameter to false leads to equation 26 from McKenzie (1970),"
92  "which is the result obtained from McKenzie 1969.");
93 
94  prm.declare_entry("potential mantle temperature", Types::Double(-1),
95  "The potential temperature of the mantle at the surface in Kelvin. If smaller than zero, the global value is used.");
96  }
97 
98  void
100  {
101 
102  min_depth = prm.get<double>("min distance slab top");
103  max_depth = prm.get<double>("max distance slab top");
104  operation = string_operations_to_enum(prm.get<std::string>("operation"));
105 
106  density = prm.get<double>("density");
107  plate_velocity = prm.get<double>("plate velocity");
108  thermal_conductivity = prm.get<double>("thermal conductivity");
109  thermal_expansion_coefficient = prm.get<double>("thermal expansion coefficient");
110 
111  if (thermal_expansion_coefficient < 0 )
112  thermal_expansion_coefficient = this->world->thermal_expansion_coefficient;
113 
114  specific_heat = prm.get<double>("specific heat");
115 
116  if (specific_heat < 0)
118 
119  adiabatic_heating = prm.get<bool>("adiabatic heating");
120 
122  ?
124  :
125  prm.get<double>("potential mantle temperature");
127  }
128 
129 
130  double
131  PlateModel::get_temperature(const Point<3> & /*position_in_cartesian_coordinates*/,
132  const double depth,
133  const double gravity_norm,
134  double temperature_,
135  const double /*feature_min_depth*/,
136  const double /*feature_max_depth*/,
137  const WorldBuilder::Utilities::PointDistanceFromCurvedPlanes &distance_from_planes,
138  const AdditionalParameters &additional_parameters) const
139  {
140  const double thickness_local = std::min(additional_parameters.local_thickness, max_depth);
141  const double distance_from_plane = distance_from_planes.distance_from_plane;
142  const double distance_along_plane = distance_from_planes.distance_along_plane;
143 
144  if (distance_from_plane <= max_depth && distance_from_plane >= min_depth)
145  {
146  /*
147  * We now use the McKenzie (1970) equation to determine the
148  * temperature inside the slab. The McKenzie equation was
149  * designed for a straight slab, but we have a potentially
150  * curved slab. Since the angle is used to compute the depth
151  * of the point, we directly use the depth.
152  */
153  const double R = (density * specific_heat
154  * (plate_velocity /(365.25 * 24.0 * 60.0 * 60.0))
155  * thickness_local) / (2.0 * thermal_conductivity);
156 
157  WBAssert(!std::isnan(R), "Internal error: R is not a number: " << R << '.');
158 
159  const int n_sum = 500;
160  // distance_from_plane can be zero, so protect division.
161  const double z_scaled = 1 - (std::fabs(distance_from_plane) < 2.0 * std::numeric_limits<double>::epsilon() ?
162  2.0 * std::numeric_limits<double>::epsilon()
163  :
164  distance_from_plane/thickness_local);
165 
166  // distance_along_plane can be zero, so protect division.
167  const double x_scaled = (std::fabs(distance_along_plane) < 2.0 * std::numeric_limits<double>::epsilon() ?
168  2.0 *std::numeric_limits<double>::epsilon()
169  :
170  distance_along_plane/thickness_local);
171 
172  // the paper uses `(x_scaled * sin(average_angle) - z_scaled * cos(average_angle))` to compute the
173  // depth (except that you do not use average angles since they only have on angle). On recomputing
174  // their result it seems to me (Menno) that it should have been `(1-z_scaled)` instead of `z_scaled`.
175  // To avoid this whole problem we just use the depth directly since we have that.
176  // todo: get the local thickniss out of H, that prevents an other division.
177  // If we want to specify the bottom temperature, because we have defined a linear temperature increase in the
178  // mantle and/or oceanic plate, we have to switch off adiabatic heating for now.
179  // Todo: there may be a better way to deal with this.
180  ;
181  const double temp = adiabatic_heating ? std::exp(((thermal_expansion_coefficient * gravity_norm * depth) / specific_heat)) : 1;
182 
183  WBAssert(!std::isnan(temp), "Internal error: temp is not a number: " << temp << ". In exponent: "
184  << std::exp(((thermal_expansion_coefficient * gravity_norm) / specific_heat) * depth)
185  << ", thermal_expansion_coefficient = " << thermal_expansion_coefficient << ", gravity_norm = " << gravity_norm
186  << ", specific_heat = "<< specific_heat << ", depth = " << depth );
187 
188  double sum=0;
189  for (int i=1; i<=n_sum; i++)
190  {
191  sum += (std::pow((-1.0),i)/(i*Consts::PI)) *
192  (exp((R - std::pow(R * R + i * i * Consts::PI * Consts::PI, 0.5)) * x_scaled))
193  * (sin(i * Consts::PI * z_scaled));
194  }
195  // todo: investigate whether this 273.15 should just be the surface temperature.
196  const double temperature = temp * (potential_mantle_temperature
197  + 2.0 * (potential_mantle_temperature - 273.15) * sum);
198 
199  WBAssert(!std::isnan(temperature), "Internal error: temperature is not a number: " << temperature << '.');
200  WBAssert(std::isfinite(temperature), "Internal error: temperature is not finite: " << temperature << '.');
201 
202 
203  return apply_operation(operation,temperature_,temperature);
204  }
205 
206  return temperature_;
207  }
208 
210  } // namespace Temperature
211  } // namespace SubductingPlateModels
212  } // namespace Features
213 } // namespace WorldBuilder
214 
const double DSNAN
Definition: nan.h:41
double potential_mantle_temperature
Definition: world.h:268
Operations string_operations_to_enum(const std::string &operation)
const double DQNAN
Definition: nan.h:32
#define WBAssert(condition, message)
Definition: assert.h:27
double specific_heat
Definition: world.h:288
double surface_temperature
Definition: world.h:273
#define WB_REGISTER_FEATURE_SUBDUCTING_PLATE_TEMPERATURE_MODEL(classname, name)
Definition: interface.h:156
double get_temperature(const Point< 3 > &position, const double depth, const double gravity, double temperature, const double feature_min_depth, const double feature_max_depth, const WorldBuilder::Utilities::PointDistanceFromCurvedPlanes &distance_from_planes, const AdditionalParameters &additional_parameters) const override final
Definition: plate_model.cc:131
double thermal_expansion_coefficient
Definition: world.h:283
double apply_operation(const Operations operation, const double old_value, const double new_value)
double sin(const double raw_angle)
Definition: point.h:81
constexpr double PI
Definition: consts.h:30
void declare_entry(const std::string &name, const Types::Interface &type, const std::string &documentation)
Definition: parameters.cc:197
static void declare_entries(Parameters &prm, const std::string &parent_name="")
Definition: plate_model.cc:61
T get(const std::string &name)