World Builder  1.1.0-pre
A geodynamic initial conditions generator
world.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 
20 #include "world_builder/world.h"
21 
22 
23 #include "world_builder/config.h"
26 #include "world_builder/nan.h"
27 #include "world_builder/point.h"
35 
36 #include <iostream>
39 
40 #ifdef WB_WITH_MPI
41 // we don't need the c++ MPI wrappers
42 #define OMPI_SKIP_MPICXX 1
43 #define MPICH_SKIP_MPICXX
44 #include <mpi.h>
45 #endif
46 
47 #ifndef NDEBUG
48 #ifdef WB_USE_FP_EXCEPTIONS
49 #include <cfenv>
50 #endif
51 #endif
52 
53 namespace WorldBuilder
54 {
55  using namespace Utilities;
56 
57  World::World(std::string filename, bool has_output_dir, const std::string &output_dir, unsigned long random_number_seed, const bool limit_debug_consistency_checks_)
58  :
59  parameters(*this),
60  surface_coord_conversions(invalid),
61  dim(NaN::ISNAN),
62  random_number_engine(random_number_seed),
63  limit_debug_consistency_checks(limit_debug_consistency_checks_)
64  {
65 
66 #ifndef NDEBUG
67 #ifdef WB_USE_FP_EXCEPTIONS
68  // Some implementations seem to not initialize the floating point exception
69  // bits to zero. Make sure we start from a clean state.
70  feclearexcept(FE_DIVBYZERO|FE_INVALID);
71 
72  // enable floating point exceptions
73  feenableexcept(FE_DIVBYZERO|FE_INVALID);
74 #endif
75 #endif
76 
77 #ifdef WB_WITH_MPI
78  int mpi_initialized;
79  MPI_Initialized(&mpi_initialized);
80  if (mpi_initialized == 0)
81  {
82  MPI_RANK = 0;
83  MPI_SIZE = 1;
84  }
85  else
86  {
87  MPI_Comm_rank(MPI_COMM_WORLD, &MPI_RANK);
88  MPI_Comm_size(MPI_COMM_WORLD, &MPI_SIZE);
89  }
90 #else
91  MPI_RANK = 0;
92  MPI_SIZE = 1;
93 #endif
94 
96 
97  parameters.initialize(filename, has_output_dir, output_dir);
98 
100  }
101 
102  World::~World()
103  = default;
104 
106  {
107  prm.enter_subsection("properties");
108  {
109  prm.declare_entry("", Types::Object({"version", "features"}), "Root object");
110 
111  prm.declare_entry("version", Types::String(""),"The major and minor version number for which the input file was written.");
112 
113  prm.declare_entry("$schema", Types::String(""),"The optional filename or https address to a JSON schema file");
114 
115  prm.declare_entry("cross section", Types::Array(Types::Point<2>(),2,2),"This is an array of two points along where the cross section is taken");
116 
117  prm.declare_entry("potential mantle temperature", Types::Double(1600),
118  "The potential temperature of the mantle at the surface in Kelvin.");
119  prm.declare_entry("surface temperature", Types::Double(293.15),
120  "The temperature at the surface in Kelvin.");
121  prm.declare_entry("force surface temperature", Types::Bool(false),
122  "Force the provided surface temperature to be set at the surface");
123  prm.declare_entry("thermal expansion coefficient", Types::Double(3.5e-5),
124  "The thermal expansion coefficient in $K^{-1}$.");
125  prm.declare_entry("specific heat", Types::Double(1250),
126  "The specific heat in $J kg^{-1} K^{-1}.$");
127  prm.declare_entry("thermal diffusivity", Types::Double(0.804e-6),
128  "The thermal diffusivity in $m^{2} s^{-1}$.");
129 
130  prm.declare_entry("maximum distance between coordinates",Types::Double(0),
131  "This enforces a maximum distance (in degree for spherical coordinates "
132  "or meter in cartesian coordinates) between coordinates in the model. "
133  "If the distance is larger, extra points are added by interpolation. "
134  "Requires interpolation to be not 'none'.");
135 
136  prm.declare_entry("interpolation",Types::String("continuous monotone spline"),
137  "What type of interpolation should be used to enforce the minimum points per "
138  "distance parameter. Options are none, linear, monotone spline and "
139  "continuous monotone spline interpolation.");
140 
141 
142  prm.declare_entry("coordinate system", Types::PluginSystem("cartesian", CoordinateSystems::Interface::declare_entries, {"model"}, false),"A coordinate system. Cartesian or spherical.");
143 
144  prm.declare_entry("gravity model", Types::PluginSystem("uniform", GravityModel::Interface::declare_entries, {"model"}, false),"A gravity model for the world.");
145 
146  prm.declare_entry("features", Types::PluginSystem("",Features::Interface::declare_entries, {"model"}),"A list of features.");
147 
148  prm.declare_entry("random number seed", Types::Int(-1),
149  "This allows the input of a preferred random number seed to generate random numbers."
150  " If no input is given, this value is -1 and triggers the use of default seed = 1.");
151 
152  }
153  prm.leave_subsection();
154 
155  }
156 
157 
159  {
160  using namespace rapidjson;
161 
167  WBAssertThrow((prm.get<std::string>("version") == Version::MAJOR + "." + Version::MINOR),
168  "The major and minor version combination for which is input file was written "
169  "is not the same as the version of the World Builder you are running. This means "
170  "That there may have been incompatible changes made between the versions. \n\n"
171  "Verify those changes and whether they affect your model. If this is not "
172  "the case, adjust the version number in the input file. \n\nThe provided version "
173  "number is \"" << prm.get<std::string>("version") << "\", while the used world builder "
174  "has (major.minor) version \"" << Version::MAJOR << "." << Version::MINOR << "\". "
175  "If you created this file from scratch, fill set the version number to \"" <<
176  Version::MAJOR << "." << Version::MINOR << "\" to continue. If you got the world builder "
177  "file from somewhere, make sure that the output is what you expect it to be, because "
178  "backwards incompatible changes may have been made to the code.");
179 
183  prm.coordinate_system = prm.get_unique_pointer<CoordinateSystems::Interface>("coordinate system");
184  prm.coordinate_system->parse_entries(prm);
185 
189  prm.gravity_model = prm.get_unique_pointer<GravityModel::Interface>("gravity model");
190 
191  prm.enter_subsection("gravity model");
192  {
193  prm.gravity_model->parse_entries(prm);
194  }
195  prm.leave_subsection();
196 
197  prm.get_unique_pointers<Features::Interface>("features",prm.features);
198 
199  const bool set_cross_section = prm.check_entry("cross section");
200 
201  const CoordinateSystem coordinate_system = prm.coordinate_system->natural_coordinate_system();
202 
203  if (set_cross_section)
204  {
205  dim = 2;
206  const std::vector<Point<2> > cross_section_natural = prm.get_vector<Point<2> >("cross section");
207 
208  WBAssertThrow(cross_section_natural.size() == 2, "The cross section should contain two points, but it contains "
209  << cross_section.size() << " points.");
210 
211  for (const auto &it : cross_section_natural)
212  cross_section.push_back(it * (coordinate_system == spherical ? Consts::PI / 180.0 : 1.0));
213 
214 
220 
221 
222  }
223  else
224  {
225  dim = 3;
226  }
227 
231  potential_mantle_temperature = prm.get<double>("potential mantle temperature");
232  surface_temperature = prm.get<double>("surface temperature");
233  surface_temperature = prm.get<double>("surface temperature");
234  force_surface_temperature = prm.get<bool>("force surface temperature");
235  thermal_expansion_coefficient = prm.get<double>("thermal expansion coefficient");
236  specific_heat = prm.get<double>("specific heat");
237  thermal_diffusivity = prm.get<double>("thermal diffusivity");
238 
242  maximum_distance_between_coordinates = prm.get<double>("maximum distance between coordinates");
243  interpolation = prm.get<std::string>("interpolation");
244 
248  const int local_seed = prm.get<int>("random number seed");
249 
250  if (local_seed>=0)
251  random_number_engine.seed(static_cast<unsigned int>(local_seed+MPI_RANK));
252 
257  prm.enter_subsection("features");
258  {
259  for (unsigned int i = 0; i < prm.features.size(); ++i)
260  {
261  prm.enter_subsection(std::to_string(i));
262  {
263  prm.features[i]->parse_entries(prm);
264  }
265  prm.leave_subsection();
266  }
267  }
268  prm.leave_subsection();
269  }
270 
271 
272 
273  unsigned int
274  World::properties_output_size(const std::vector<std::array<unsigned int,3>> &properties) const
275  {
276  unsigned int n_output_entries = 0;
277  for (auto property : properties)
278  {
279  switch (property[0])
280  {
281  case 1: // Temperature
282  n_output_entries += 1;
283  break;
284  case 2: // composition
285  n_output_entries += 1;
286  break;
287  case 3: // grains (10 entries per grain)
288  {
289  n_output_entries += property[2]*10;
290  break;
291  }
292  case 4: // tag
293  {
294  n_output_entries += 1;
295  break;
296  }
297  case 5: // velocity (3 entries)
298  {
299  n_output_entries += 3;
300  break;
301  }
302  default:
303  WBAssertThrow(false,
304  "Internal error: Unimplemented property provided. " <<
305  "Only temperature (1), composition (2), grains (3), tag (4) or velocity (5) are allowed. "
306  "Provided property number was: " << property[0]);
307  }
308  }
309  return n_output_entries;
310  }
311 
312 
313 
314  std::vector<double>
315  World::properties(const std::array<double, 2> &point,
316  const double depth,
317  const std::vector<std::array<unsigned int,3>> &properties) const
318  {
319  // turn it into a 3d coordinate and call the 3d temperature function
320  WBAssertThrow(dim == 2, "This function can only be called when the cross section "
321  "variable in the world builder file has been set. Dim is "
322  << dim << '.');
323 
324  const CoordinateSystem coordinate_system = this->parameters.coordinate_system->natural_coordinate_system();
325 
326  Point<2> point_natural(point[0], point[1],coordinate_system);
327  if (coordinate_system == spherical)
328  {
329  point_natural[1] = std::sqrt(point[0]*point[0]+point[1]*point[1]);
330  point_natural[0] = std::atan2(point[1],point[0]);
331  }
332 
333  Point<3> coord_3d(coordinate_system);
334  if (coordinate_system == spherical)
335  {
336  coord_3d[0] = point_natural[1];
337  coord_3d[1] = cross_section[0][0] + point_natural[0] * surface_coord_conversions[0];
338  coord_3d[2] = cross_section[0][1] + point_natural[0] * surface_coord_conversions[1];
339  }
340  else
341  {
342  coord_3d[0] = cross_section[0][0] + point_natural[0] * surface_coord_conversions[0];
343  coord_3d[1] = cross_section[0][1] + point_natural[0] * surface_coord_conversions[1];
344  coord_3d[2] = point_natural[1];
345  }
346 
347 
348  const std::array<double, 3> point_3d_cartesian = this->parameters.coordinate_system->natural_to_cartesian_coordinates(coord_3d.get_array());
349 
350  std::vector<double> results = this->properties(point_3d_cartesian, depth, properties);
351  unsigned int counter = 0;
352  for (auto property : properties)
353  {
354  switch (property[0])
355  {
356  case 1: // temperature
357  {
358  counter += 1;
359  break;
360  }
361  case 2: // composition
362  {
363  counter += 1;
364  break;
365  }
366  case 3: // grains
367  {
368  counter += 10;
369  break;
370  }
371  case 4: // tag
372  {
373  counter += 1;
374  break;
375  }
376  case 5:
377  {
378  // convert 3d velocity vector to a 2d one
379  Point<2> vector = Point<2>(cartesian);
380  vector[0] = surface_coord_conversions[0]*results[counter]+surface_coord_conversions[1]*results[counter+1];
381  vector[1] = results[counter+2];
382  results[counter] = surface_coord_conversions[0]*results[counter]+surface_coord_conversions[1]*results[counter+1];
383  results[counter+1] = results[counter+2];
384  results[counter+2] = 0;
385  counter += 3;
386  break;
387  }
388  default:
389  {
390  WBAssert(false,
391  "Internal error: Unimplemented property provided by internal process. " <<
392  "Only temperature (1), composition (2), grains (3), tag (4) or velocity (5) are allowed. "
393  "Provided property number was: " << property[0]);
394  }
395  }
396 
397  }
398  return results;
399  }
400 
401 
402  std::vector<double>
403  World::properties(const std::array<double, 3> &point_,
404  const double depth,
405  const std::vector<std::array<unsigned int,3>> &properties) const
406  {
407  // We receive the cartesian points from the user.
408  const Point<3> point(point_,cartesian);
409  (void) this->limit_debug_consistency_checks;
410  WBAssert(this->limit_debug_consistency_checks || this->parameters.coordinate_system->natural_coordinate_system() == cartesian
411  || approx(depth, this->parameters.coordinate_system->max_model_depth()-sqrt(point_[0]*point_[0]+point_[1]*point_[1]+point_[2]*point_[2])),
412  "Inconsistent input. Please check whether the radius in the spherical coordinates is consistent with the radius of the planet as defined "
413  << "in the program that uses the Geodynamic World Builder. This is a debug check in GWB and can be disabled by setting "
414  << "limit_debug_consistency_checks to true. "
415  << "Depth = " << depth << ", radius = " << this->parameters.coordinate_system->max_model_depth()
416  << ", point = " << point_[0] << " " << point_[1] << " " << point_[2]
417  << ", radius-point.norm() = " << this->parameters.coordinate_system->max_model_depth()-sqrt(point_[0]*point_[0]+point_[1]*point_[1]+point_[2]*point_[2]));
418 
419  const Objects::NaturalCoordinate natural_coordinate = Objects::NaturalCoordinate(point,*(this->parameters.coordinate_system));
420 
421  // create output vector
422  std::vector<double> output;
423  std::vector<size_t> entry_in_output;
424  std::vector<std::array<unsigned int,3>> properties_local;
425  const double gravity_norm = this->parameters.gravity_model->gravity_norm(point);
426  for (unsigned int i_property = 0; i_property < properties.size(); ++i_property)
427  {
428  switch (properties[i_property][0])
429  {
430  case 1: // Temperature
431  if (std::fabs(depth) < 2.0 * std::numeric_limits<double>::epsilon() && force_surface_temperature)
432  {
433  entry_in_output.emplace_back(output.size());
434  output.emplace_back(this->surface_temperature);
435  if (properties.size() == 1)
436  return output;
437  }
438  else
439  {
440  entry_in_output.emplace_back(output.size());
441  output.emplace_back(potential_mantle_temperature * std::exp(((thermal_expansion_coefficient * gravity_norm) / specific_heat) * depth));
442  }
443  properties_local.emplace_back(properties[i_property]);
444  break;
445  case 2: // composition
446  entry_in_output.emplace_back(output.size());
447  output.emplace_back(0.);
448  properties_local.emplace_back(properties[i_property]);
449  break;
450  case 3: // grains (10 entries per grain)
451  {
452  entry_in_output.emplace_back(output.size());
453  const std::vector<double> tmp_vector(properties[i_property][2]*10,0.);
454  output.insert(output.end(), tmp_vector.begin(), tmp_vector.end());
455  properties_local.emplace_back(properties[i_property]);
456  break;
457  }
458  case 4: // tag
459  {
460  entry_in_output.emplace_back(output.size());
461  output.emplace_back(-1);
462  properties_local.emplace_back(properties[i_property]);
463  break;
464  }
465  case 5: // velocity
466  {
467  entry_in_output.emplace_back(output.size());
468  output.emplace_back(0);
469  output.emplace_back(0);
470  output.emplace_back(0);
471  properties_local.emplace_back(properties[i_property]);
472  break;
473  }
474  default:
475  WBAssertThrow(false,
476  "Internal error: Unimplemented property provided. " <<
477  "Only temperature (1), composition (2), grains (3), tag (4) or velocity (5) are allowed. "
478  "Provided property number was: " << properties[i_property][0]);
479  }
480  }
481  for (auto &&it : parameters.features)
482  {
483  it->properties(point, natural_coordinate, depth, properties_local, gravity_norm, entry_in_output, output);
484  }
485 
486  return output;
487  }
488 
489  double
490  World::temperature(const std::array<double,2> &point,
491  const double depth) const
492  {
493  return properties(point, depth, {{{1,0,0}}})[0];
494  }
495 
496  double
497  World::temperature(const std::array<double,2> &point,
498  const double depth,
499  const double /*gravity_norm*/) const
500  {
501  return properties(point, depth, {{{1,0,0}}})[0];
502  }
503 
504  double
505  World::temperature(const std::array<double,3> &point,
506  const double depth) const
507  {
508  return properties(point, depth, {{{1,0,0}}})[0];
509  }
510 
511  double
512  World::temperature(const std::array<double,3> &point,
513  const double depth,
514  const double /*gravity_norm*/) const
515  {
516  return properties(point, depth, {{{1,0,0}}})[0];
517  }
518 
519  double
520  World::composition(const std::array<double,2> &point,
521  const double depth,
522  const unsigned int composition_number) const
523  {
524  return properties(point, depth, {{{2,composition_number,0}}})[0];
525  }
526 
527  double
528  World::composition(const std::array<double,3> &point,
529  const double depth,
530  const unsigned int composition_number) const
531  {
532  return properties(point, depth, {{{2,composition_number,0}}})[0];
533  }
534 
535 
536 
538  World::grains(const std::array<double,2> &point,
539  const double depth,
540  const unsigned int composition_number,
541  size_t number_of_grains) const
542  {
543  return WorldBuilder::grains(properties(point, depth, {{{3,composition_number,static_cast<unsigned int>(number_of_grains)}}}),static_cast<unsigned int>(number_of_grains),0);
544  }
545 
547  World::grains(const std::array<double,3> &point,
548  const double depth,
549  const unsigned int composition_number,
550  size_t number_of_grains) const
551  {
552  return WorldBuilder::grains(properties(point, depth, {{{3,composition_number,static_cast<unsigned int>(number_of_grains)}}}),static_cast<unsigned int>(number_of_grains),0);
553  }
554 
555  std::mt19937 &
557  {
558  return random_number_engine;
559  }
560 
562  World::distance_to_plane(const std::array<double, 3> &point_,
563  const double depth,
564  const std::string &name) const
565  {
566  // We receive the cartesian points from the user.
567  const Point<3> point(point_,cartesian);
568 
569  WBAssert(this->limit_debug_consistency_checks || this->parameters.coordinate_system->natural_coordinate_system() == cartesian
570  || approx(depth, this->parameters.coordinate_system->max_model_depth()-sqrt(point_[0]*point_[0]+point_[1]*point_[1]+point_[2]*point_[2])),
571  "Inconsistent input. Please check whether the radius in the spherical coordinates is consistent with the radius of the planet as defined "
572  << "in the program that uses the Geodynamic World Builder. This is a debug check in GWB and can be disabled by setting "
573  << "limit_debug_consistency_checks to true. "
574  << "Depth = " << depth << ", radius = " << this->parameters.coordinate_system->max_model_depth()
575  << ", point = " << point_[0] << " " << point_[1] << " " << point_[2]
576  << ", radius-point.norm() = " << this->parameters.coordinate_system->max_model_depth()-sqrt(point_[0]*point_[0]+point_[1]*point_[1]+point_[2]*point_[2]));
577 
578  const Objects::NaturalCoordinate natural_coordinate = Objects::NaturalCoordinate(point,*(this->parameters.coordinate_system));
579 
580  Objects::PlaneDistances plane_distances(0.0, 0.0);
581  for (auto &&it : this->parameters.features)
582  {
583  if (it->get_name() == name)
584  {
585  plane_distances = it->distance_to_feature_plane(point, natural_coordinate, depth);
586  break;
587  }
588  }
589  return plane_distances;
590  }
591 
592 } // namespace WorldBuilder
593 
bool check_entry(const std::string &name) const
Definition: parameters.cc:205
void parse_entries(Parameters &prm)
Definition: world.cc:158
Objects::PlaneDistances distance_to_plane(const std::array< double, 3 > &point, const double depth, const std::string &name) const
Definition: world.cc:562
static void declare_entries(Parameters &prm, const std::string &parent_name, const std::vector< std::string > &required_entries)
Definition: interface.cc:38
double composition(const std::array< double, 2 > &point, const double depth, const unsigned int composition_number) const
Definition: world.cc:520
unsigned int dim
Definition: world.h:317
double maximum_distance_between_coordinates
Definition: world.h:298
bool approx(double a, double b, double error_factor=1e4)
Definition: utilities.h:48
void enter_subsection(const std::string &name)
Definition: parameters.cc:1871
std::unique_ptr< WorldBuilder::GravityModel::Interface > gravity_model
Definition: parameters.h:276
double potential_mantle_temperature
Definition: world.h:268
std::mt19937 & get_random_number_engine()
Definition: world.cc:556
bool limit_debug_consistency_checks
Definition: world.h:332
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
std::unique_ptr< T > get_unique_pointer(const std::string &name)
Definition: parameters.cc:1738
Point< 2 > surface_coord_conversions
Definition: world.h:263
static void declare_entries(Parameters &prm)
Definition: world.cc:105
void initialize(std::string &filename, bool has_output_dir=false, const std::string &output_dir="")
Definition: parameters.cc:88
World(std::string filename, bool has_output_dir=false, const std::string &output_dir="", unsigned long random_number_seed=1, const bool limit_debug_consistency_checks=true)
Definition: world.cc:57
const unsigned int ISNAN
Definition: nan.h:46
const std::array< double, dim > & get_array() const
Definition: point.h:393
unsigned int properties_output_size(const std::vector< std::array< unsigned int, 3 >> &properties) const
Definition: world.cc:274
double temperature(const std::array< double, 2 > &point, const double depth) const
Definition: world.cc:490
#define WBAssert(condition, message)
Definition: assert.h:27
WorldBuilder::grains grains(const std::array< double, 2 > &point, const double depth, const unsigned int composition_number, size_t number_of_grains) const
Definition: world.cc:538
double norm() const
Definition: point.h:413
std::mt19937 random_number_engine
Definition: world.h:323
static void declare_entries(Parameters &prm, const std::string &parent_name, const std::vector< std::string > &required_entries)
Definition: interface.cc:67
double thermal_diffusivity
Definition: world.h:293
double specific_heat
Definition: world.h:288
double surface_temperature
Definition: world.h:273
#define WBAssertThrow(condition, message)
Definition: assert.h:40
bool get_unique_pointers(const std::string &name, std::vector< std::unique_ptr< T > > &vector)
Definition: parameters.cc:1770
bool force_surface_temperature
Definition: world.h:278
std::vector< std::unique_ptr< WorldBuilder::Features::Interface > > features
Definition: parameters.h:260
std::unique_ptr< WorldBuilder::CoordinateSystems::Interface > coordinate_system
Definition: parameters.h:268
double thermal_expansion_coefficient
Definition: world.h:283
static void declare_entries(Parameters &prm, const std::string &parent_name, const std::vector< std::string > &required_entries)
Definition: interface.cc:39
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
std::vector< Point< 2 > > cross_section
Definition: world.h:258
Parameters parameters
Definition: world.h:253
std::vector< T > get_vector(const std::string &name)
T get(const std::string &name)