World Builder  1.1.0-pre
A geodynamic initial conditions generator
random_uniform_distribution_deflected.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"
31 #include "world_builder/world.h"
32 
33 namespace WorldBuilder
34 {
35 
36  using namespace Utilities;
37 
38  namespace Features
39  {
40  namespace PlumeModels
41  {
42  namespace Grains
43  {
44  RandomUniformDistributionDeflected::RandomUniformDistributionDeflected(WorldBuilder::World *world_)
45  :
46  min_depth(NaN::DSNAN),
47  max_depth(NaN::DSNAN)
48 
49  {
50  this->world = world_;
51  this->name = "random uniform distribution deflected";
52  }
53 
55  = default;
56 
57  void
59  {
60  // Document plugin and require entries if needed.
61  // Add compositions to the required parameters.
62  prm.declare_entry("", Types::Object({"compositions"}),
63  "Random uniform distribution grains model. The size of the grains can be independently set "
64  "to a single value or to a random distribution.");
65 
66  // Declare entries of this plugin
67  prm.declare_entry("min depth", Types::Double(0),
68  "The depth in meters from which the grains of this feature are present.");
69  prm.declare_entry("max depth", Types::Double(std::numeric_limits<double>::max()),
70  "The depth in meters to which the grains of this feature are present.");
71 
72  prm.declare_entry("compositions", Types::Array(Types::UnsignedInt(),0),
73  "A list with the integer labels of the composition which are present there.");
74 
75  prm.declare_entry("orientation operation", Types::String("replace", std::vector<std::string> {"replace", "multiply"}),
76  "Whether the value should replace any value previously defined at this location (replace) or "
77  "add the value to the previously define value (add, not implemented). Replacing implies that all values not "
78  "explicitly defined are set to zero.");
79 
80  prm.declare_entry("grain sizes",
82  "A list of the size of all of the grains in each composition. If set to <0, the size will be randomized between 0 and 1.");
83 
84  prm.declare_entry("normalize grain sizes",
85  Types::Array(Types::Bool(true),0),
86  "A list of whether the sizes of the grains should be normalized or not. If normalized, the total of the grains of a composition will be equal to 1.");
87 
88  prm.declare_entry("deflections",
90  "A list of the deflections of all of the grains in each composition between 0 and 1.");
91 
92  prm.declare_entry("basis rotation matrices", Types::Array(Types::Array(Types::Array(Types::Double(0),3,3),3,3),0),
93  "A list with the rotation matrices of the grains which are present there for each compositions.");
94 
95  prm.declare_entry("basis Euler angles z-x-z", Types::Array(Types::Array(Types::Double(0),3,3),0),
96  "A list with the z-x-z Euler angles of the grains which are present there for each compositions.");
97 
98 
99  }
100 
101  void
103  {
104  min_depth = prm.get<double>("min depth");
105  max_depth = prm.get<double>("max depth");
106  compositions = prm.get_vector<unsigned int>("compositions");
107 
108  const bool set_euler_angles = prm.check_entry("basis Euler angles z-x-z");
109  const bool set_rotation_matrices = prm.check_entry("basis rotation matrices");
110 
111  WBAssertThrow(!(set_euler_angles == true && set_rotation_matrices == true),
112  "Only Euler angles or Rotation matrices may be set, but both are set for " << prm.get_full_json_path());
113 
114 
115  WBAssertThrow(!(set_euler_angles == false && set_rotation_matrices == false),
116  "Euler angles or Rotation matrices have to be set, but neither are set for " << prm.get_full_json_path());
117 
118  if (set_euler_angles)
119  {
120  std::vector<std::array<double,3> > basis_euler_angles_vector = prm.get_vector<std::array<double,3> >("basis Euler angles z-x-z");
121  basis_rotation_matrices.resize(basis_euler_angles_vector.size());
122  for (size_t i = 0; i<basis_euler_angles_vector.size(); ++i)
123  {
124  basis_rotation_matrices[i] = Utilities::euler_angles_to_rotation_matrix(basis_euler_angles_vector[i][0],basis_euler_angles_vector[i][1],basis_euler_angles_vector[i][2]);
125  }
126 
127  }
128  else
129  {
130  basis_rotation_matrices = prm.get_vector<std::array<std::array<double,3>,3> >("basis rotation matrices");
131  }
132 
133  operation = prm.get<std::string>("orientation operation");
134  grain_sizes = prm.get_vector<double>("grain sizes");
135  normalize_grain_sizes = prm.get_vector<bool>("normalize grain sizes");
136  deflections = prm.get_vector<double>("deflections");
137 
138 
139  WBAssertThrow(compositions.size() == grain_sizes.size(),
140  "There are not the same amount of compositions (" << compositions.size()
141  << ") and grain_sizes (" << grain_sizes.size() << ").");
142  WBAssertThrow(compositions.size() == normalize_grain_sizes.size(),
143  "There are not the same amount of compositions (" << compositions.size()
144  << ") and normalize_grain_sizes (" << normalize_grain_sizes.size() << ").");
145  WBAssertThrow(compositions.size() == deflections.size(),
146  "There are not the same amount of compositions (" << compositions.size()
147  << ") and deflections (" << deflections.size() << ").");
148  WBAssertThrow(compositions.size() == basis_rotation_matrices.size(),
149  "There are not the same amount of compositions (" << compositions.size()
150  << ") and rotation_matrices (" << basis_rotation_matrices.size() << ").");
151  }
152 
154  RandomUniformDistributionDeflected::get_grains(const Point<3> & /*position_in_cartesian_coordinates*/,
155  const Objects::NaturalCoordinate & /*position_in_natural_coordinates*/,
156  const double depth,
157  const unsigned int composition_number,
158  WorldBuilder::grains grains_,
159  const double /*feature_min_depth*/,
160  const double /*feature_max_depth*/) const
161  {
162  WorldBuilder::grains grains_local = grains_;
163  if (depth <= max_depth && depth >= min_depth)
164  {
165  for (unsigned int i =0; i < compositions.size(); ++i)
166  {
167  if (compositions[i] == composition_number)
168  {
169  std::uniform_real_distribution<> dist(0.0,1.0);
170  for (auto &&it_rotation_matrices : grains_local.rotation_matrices)
171  {
172  // set a uniform random a_cosine_matrix per grain
173  // This function is based on an article in Graphic Gems III, written by James Arvo, Cornell University (p 116-120).
174  // The original code can be found on http://www.realtimerendering.com/resources/GraphicsGems/gemsiii/rand_rotation.c
175  // and is licenend accourding to this website with the following licence:
176  //
177  // "The Graphics Gems code is copyright-protected. In other words, you cannot claim the text of the code as your own and
178  // resell it. Using the code is permitted in any program, product, or library, non-commercial or commercial. Giving credit
179  // is not required, though is a nice gesture. The code comes as-is, and if there are any flaws or problems with any Gems
180  // code, nobody involved with Gems - authors, editors, publishers, or webmasters - are to be held responsible. Basically,
181  // don't be a jerk, and remember that anything free comes with no guarantee.""
182  //
183  // The book saids in the preface the following: "As in the first two volumes, all of the C and C++ code in this book is in
184  // the public domain, and is yours to study, modify, and use."
185 
186  // first generate three random numbers between 0 and 1 and multiply them with 2 PI or 2 for z. Note that these are not the same as phi_1, theta and phi_2.
187  const double one = dist(world->get_random_number_engine());
188  const double two = dist(world->get_random_number_engine());
189  const double three = dist(world->get_random_number_engine());
190 
191  // the distribution is restricted by the deflection (between 0 and 1)
192  const double theta = 2.0 * Consts::PI * one * deflections[i]; // Rotation about the pole (Z)
193  const double phi = 2.0 * Consts::PI * two; // For direction of pole deflection.
194  const double z = 2.0 * three * deflections[i]; //For magnitude of pole deflection.
195 
196  // Compute a vector V used for distributing points over the sphere
197  // via the reflection I - V Transpose(V). This formulation of V
198  // will guarantee that if x[1] and x[2] are uniformly distributed,
199  // the reflected points will be uniform on the sphere. Note that V
200  // has length sqrt(2) to eliminate the 2 in the Householder matrix.
201 
202  const double r = std::sqrt( z );
203  const double Vx = std::sin( phi ) * r;
204  const double Vy = std::cos( phi ) * r;
205  const double Vz = std::sqrt( 2.F - z );
206 
207  // Compute the row vector S = Transpose(V) * R, where R is a simple
208  // rotation by theta about the z-axis. No need to compute Sz since
209  // it's just Vz.
210 
211  const double st = std::sin( theta );
212  const double ct = std::cos( theta );
213  const double Sx = Vx * ct - Vy * st;
214  const double Sy = Vx * st + Vy * ct;
215 
216  // Construct the rotation matrix ( V Transpose(V) - I ) R, which
217  // is equivalent to V S - R.
218 
219  std::array<std::array<double,3>,3> rotation_matrices;
220  rotation_matrices[0][0] = (Vx * Sx - ct);
221  rotation_matrices[0][1] = (Vx * Sy - st);
222  rotation_matrices[0][2] = Vx * Vz;
223 
224  rotation_matrices[1][0] = (Vy * Sx + st);
225  rotation_matrices[1][1] = (Vy * Sy - ct);
226  rotation_matrices[1][2] = Vy * Vz;
227 
228  rotation_matrices[2][0] = Vz * Sx;
229  rotation_matrices[2][1] = Vz * Sy;
230  rotation_matrices[2][2] = 1.0 - z; // This equals Vz * Vz - 1.0
231 
232  // Rotate the basis rotation matrix with the random uniform distribution rotation matrix
233  // First get the transpose of the rotation matrix
234  std::array<std::array<double, 3>, 3> rot_T= rotation_matrices;
235  rot_T[0][1] = rotation_matrices[1][0];
236  rot_T[1][0] = rotation_matrices[0][1];
237  rot_T[1][2] = rotation_matrices[2][1];
238  rot_T[2][1] = rotation_matrices[1][2];
239  rot_T[0][2] = rotation_matrices[2][0];
240  rot_T[2][0] = rotation_matrices[0][2];
241 
242  // Then U' = R * U * R^T
243  std::array<std::array<double,3>,3> result1 = multiply_3x3_matrices(rotation_matrices, basis_rotation_matrices[i]);
244 
245  it_rotation_matrices = result1;
246  }
247 
248  double total_size = 0;
249  for (auto &&it_sizes : grains_local.sizes)
250  {
251  it_sizes = grain_sizes[i] < 0 ? dist(world->get_random_number_engine()) : grain_sizes[i];
252  total_size += it_sizes;
253  }
254 
255  if (normalize_grain_sizes[i])
256  {
257  const double one_over_total_size = 1/total_size;
258  // std::transform is a c++17 feature, while current GWB is c++14
259  // update this after switching to c+=17
260  // std::transform(grains_local.sizes.begin(), grains_local.sizes.end(), grains_local.sizes.begin(),
261  // [one_over_total_size](double sizes) -> double { return sizes *one_over_total_size; });
262  // Apply the transformation using a loop
263  for (auto &&size : grains_local.sizes)
264  {
265  size = size*one_over_total_size;
266  }
267  }
268 
269 
270  return grains_local;
271  }
272  }
273  }
274  return grains_local;
275  }
276  WB_REGISTER_FEATURE_PLUME_GRAINS_MODEL(RandomUniformDistributionDeflected, random uniform distribution deflected)
277  } // namespace Grains
278  } // namespace PlumeModels
279  } // namespace Features
280 } // namespace WorldBuilder
281 
282 
bool check_entry(const std::string &name) const
Definition: parameters.cc:205
const double DSNAN
Definition: nan.h:41
std::array< std::array< double, 3 >, 3 > multiply_3x3_matrices(const std::array< std::array< double, 3 >, 3 > mat1, const std::array< std::array< double, 3 >, 3 > mat2)
Definition: utilities.cc:1510
std::mt19937 & get_random_number_engine()
Definition: world.cc:556
std::vector< double > sizes
Definition: grains.h:47
#define WB_REGISTER_FEATURE_PLUME_GRAINS_MODEL(classname, name)
Definition: interface.h:152
std::string get_full_json_path(size_t max_size=std::numeric_limits< size_t >::max()) const
Definition: parameters.cc:1933
double cos(const double angle)
Definition: point.h:97
#define WBAssertThrow(condition, message)
Definition: assert.h:40
WorldBuilder::grains get_grains(const Point< 3 > &position, const Objects::NaturalCoordinate &position_in_natural_coordinates, const double depth, const unsigned int composition_number, WorldBuilder::grains grains, const double feature_min_depth, const double feature_max_depth) const override final
std::array< std::array< double, 3 >, 3 > euler_angles_to_rotation_matrix(double phi1_d, double theta_d, double phi2_d)
Definition: utilities.cc:1147
double sin(const double raw_angle)
Definition: point.h:81
std::vector< std::array< std::array< double, 3 >, 3 > > rotation_matrices
Definition: grains.h:51
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< T > get_vector(const std::string &name)
T get(const std::string &name)