AlmaBTE  1.3
A solver of the space- and time-dependent Boltzmann transport equation for phonons
utilities.hpp
Go to the documentation of this file.
1 // Copyright 2015-2018 The ALMA Project Developers
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12 // implied. See the License for the specific language governing
13 // permissions and limitations under the License.
14 
15 #pragma once
16 
19 
20 #include <tuple>
21 #include <vector>
22 #include <string>
23 #include <memory>
24 #include <limits>
25 #include <algorithm>
26 #include <iostream>
27 #include <cstdlib>
28 #include <cmath>
29 #include <ctime>
30 #include <boost/math/common_factor.hpp>
31 #include <boost/algorithm/string.hpp>
32 #include <boost/lexical_cast.hpp>
33 #include <boost/tokenizer.hpp>
34 #include <boost/mpi.hpp>
35 #include <constants.hpp>
36 #include <Eigen/Dense>
37 #include <Eigen/Sparse>
38 #include <unsupported/Eigen/Splines>
39 
40 namespace alma {
42 template <typename T, typename... Args>
43 std::unique_ptr<T> make_unique(Args&&... args) {
44  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
45 }
46 
47 
50 template <typename T> class Container_comparator {
51 public:
52  inline bool operator()(const T& op1, const T& op2) const {
53  return std::lexicographical_compare(
54  std::begin(op1), std::end(op1), std::begin(op2), std::end(op2));
55  }
56 };
57 
60 template <typename T>
61 std::tuple<std::vector<typename T::key_type>,
62  std::vector<typename T::mapped_type>>
63 split_keys_and_values(const T& input) {
64  auto size = input.size();
65 
66  std::vector<typename T::key_type> keys;
67  keys.reserve(size);
68  std::vector<typename T::mapped_type> values;
69  values.reserve(size);
70 
71  for (auto& i : input) {
72  keys.emplace_back(i.first);
73  values.emplace_back(i.second);
74  }
75  return std::make_tuple(keys, values);
76 }
77 
78 
85 inline void flush_istream(std::istream& f) {
86  if (f.peek() == '\n') {
87  f.ignore(1, '\n');
88  }
89 }
90 
91 
101 template <typename T>
102 inline std::vector<T> tokenize_homogeneous_line(std::string& line,
103  const std::string sep = " \t") {
104  std::vector<T> nruter;
105  boost::char_separator<char> separator(sep.c_str());
106  boost::tokenizer<boost::char_separator<char>> tokenizer(line, separator);
107 
108  for (auto i : tokenizer)
109  nruter.emplace_back(boost::lexical_cast<T>(i));
110  return nruter;
111 }
112 
113 
119 inline bool starts_with_character(const std::string& line,
120  const std::string& chars) {
121  if (line.length() == 0)
122  return false;
123 
124  for (auto c : chars) {
125  if (line[0] == c)
126  return true;
127  }
128  return false;
129 }
130 
131 
139 inline bool almost_equal(const double a,
140  const double b,
141  const double abstol = 1e-8,
142  const double reltol = 1e-5) {
143  // Method and default values inspired by
144  // numpy.allclose().
145  const auto tol = std::fabs(abstol) + std::fabs(reltol * b);
146 
147  return std::fabs(a - b) < tol;
148 }
149 
150 
153 template <typename T> class Min_keeper {
154 public:
156  Min_keeper(const double _abstol = 1e-8, const double _reltol = 1e-5)
157  : abstol(_abstol), reltol(_reltol) {
158  }
159 
160 
162  double get_value() const {
163  return this->minimum;
164  }
165 
166 
169  std::vector<T> get_vector() const {
170  return this->objects;
171  }
172 
173 
185  void update(const T& obj, double value) {
186  if (almost_equal(value, this->minimum, this->abstol, this->reltol)) {
187  this->objects.emplace_back(obj);
188  }
189  else if (value < this->minimum) {
190  this->minimum = value;
191  this->objects.clear();
192  this->objects.emplace_back(obj);
193  }
194  }
195 
196 
197 private:
199  double minimum = std::numeric_limits<double>::infinity();
201  std::vector<T> objects;
203  const double abstol;
205  const double reltol;
206 };
207 
214 inline double ssqrt(double x) {
215  return std::copysign(std::sqrt(std::fabs(x)), x);
216 }
217 
218 
224 template <typename T> inline int signum(const T& arg) {
225  T zero(0);
226 
227  return (zero < arg) - (arg < zero);
228 }
229 
230 
234 inline std::string get_timestamp() {
235  // A much more elegant solution using put_time() exists.
236  // We chose the traditional implementation because put time
237  // is not implemented in GCC < 5.
238  auto t = std::time(nullptr);
239  auto localtime = std::localtime(&t);
240  std::size_t size = 0;
241 
242  std::vector<char> buffer;
243 
244  do {
245  size += 64;
246  buffer.reserve(size);
247  // 64 bytesshould be enough for everybody, but we
248  // check the return code of std::strftime anyway to see
249  // if we need to allocate more space.
250  } while (std::strftime(
251  buffer.data(), size, "%Y-%m-%dT%H:%M:%S", localtime) == 0u);
252  return std::string(buffer.data());
253 }
254 
255 
268 inline std::array<std::size_t, 2> my_jobs(std::size_t njobs,
269  std::size_t nprocs,
270  std::size_t my_id) {
271  // If nprocs divides njobs, each process gets exactly its
272  // fair share of jobs. If there is a remainder R , an extra
273  // job is assigned to the first R processes. If njobs > nprocs,
274  // some processes do not get any job.
275  auto quot = njobs / nprocs;
276  auto rem = njobs % nprocs;
277 
278  std::array<std::size_t, 2> nruter;
279  nruter[0] = quot * my_id + std::min(rem, my_id);
280  nruter[1] = quot * (my_id + 1) + std::min(rem, my_id + 1);
281  return nruter;
282 }
283 
284 
292 template <typename T, typename U> inline U python_mod(const T& n, const U& d) {
293  if (d < 0)
294  return python_mod(-n, -d);
295  else {
296  auto nruter = n % d;
297 
298  if (nruter < 0)
299  nruter += d;
300  return nruter;
301  }
302 }
303 
304 
310 inline double bose_einstein(double omega, double T) {
311  constexpr double prefactor = 1e12 * constants::hbar / constants::kB;
312 
313  return 1. / (std::exp(prefactor * omega / T) - 1.);
314 }
315 
316 
326 inline double bose_einstein_kernel(double omega, double T) {
327  constexpr double prefactor = 0.5 * 1e12 * constants::hbar / constants::kB;
328  auto x = prefactor * omega / T;
329 
330  if (x < 1e-320) {
331  return 1.;
332  }
333  else if (x > 300.) {
334  return 0.;
335  }
336  else {
337  auto s = x / std::sinh(x);
338  return s * s;
339  }
340 }
341 
342 
347 inline Eigen::VectorXi reduce_integers(
348  const Eigen::Ref<const Eigen::VectorXi>& vector) {
349  std::size_t d = vector.size();
350  Eigen::VectorXi::Scalar gcd;
351 
352  if (d == 0)
353  gcd = 1;
354  else {
355  gcd = std::abs(vector(0));
356 
357  for (std::size_t i = 1; i < d; ++i)
358  gcd = boost::math::gcd(gcd, vector(i));
359  }
360  return Eigen::VectorXi(vector / gcd);
361 }
362 
363 
365 inline std::string engineer_format(double x, bool insert_space = false) {
366  std::vector<char> prefix(
367  {'f', 'p', 'n', 'u', 'm', ' ', 'k', 'M', 'G', 'T'});
368  std::vector<double> scaling(
369  {1e-15, 1e-12, 1e-9, 1e-6, 1e-3, 1.0, 1e3, 1e6, 1e9, 1e12, 1e15});
370 
371  int idx =
372  static_cast<int>(std::floor((std::log10(x) + 15.0) / (3.0 - 1e-12)));
373 
374  if (idx < 0) {
375  idx = 0;
376  }
377 
378  if (idx > 9) {
379  idx = 9;
380  }
381  std::stringstream result_builder;
382  result_builder << x / scaling.at(idx);
383 
384  if (idx != 5) { // avoid introducing blank space
385  if (insert_space) {
386  result_builder << " ";
387  }
388  result_builder << prefix.at(idx);
389  }
390  return result_builder.str();
391 }
392 
393 
396 inline Eigen::VectorXd logSpace(double min, double max, int N) {
397  Eigen::VectorXd logresult(N);
398 
399  logresult.setLinSpaced(N, std::log10(min), std::log10(max));
400  return Eigen::pow(10.0, logresult.array());
401 }
402 
403 
406 inline Eigen::VectorXd splineInterpolation(
407  const Eigen::Ref<const Eigen::VectorXd>& X,
408  const Eigen::Ref<const Eigen::VectorXd>& Y,
409  const Eigen::Ref<const Eigen::VectorXd>& Xtarget) {
410  Eigen::VectorXd result(Xtarget.size());
411 
412  // map X values to 1D chord lengths ranging from 0 to 1
413  Eigen::VectorXd X_norm =
414  (X.array() - X.minCoeff()) / (X.maxCoeff() - X.minCoeff());
415 
416  // construct cubic spline over the base points
417  typedef Eigen::Spline<double, 1> Spline1D;
418  Spline1D S(Eigen::SplineFitting<Spline1D>::Interpolate(
419  Y.transpose(), 3, X_norm.transpose()));
420 
421  // convert Xtarget values to their equivalent chord length
422  Eigen::VectorXd Xtarget_norm =
423  (Xtarget.array() - X.minCoeff()) / (X.maxCoeff() - X.minCoeff());
424 
425  // obtain spline value at each of these chord lengths
426 
427  for (int nx = 0; nx < Xtarget.size(); nx++) {
428  result(nx) = S(Xtarget_norm(nx))(0);
429  }
430 
431  return result;
432 }
433 
434 
438 inline Eigen::VectorXd linearInterpolation(
439  const Eigen::Ref<const Eigen::VectorXd>& X,
440  const Eigen::Ref<const Eigen::VectorXd>& Y,
441  const Eigen::Ref<const Eigen::VectorXd>& Xtarget) {
442  Eigen::VectorXd result(Xtarget.size());
443  int N = X.size();
444 
445  // perform linear interpolation for each of the specified targets
446 
447  for (int nx = 0; nx < Xtarget.size(); nx++) {
448  if (Xtarget(nx) < X(0)) {
449  std::cout << "alma::linearInterpolation > " << std::endl;
450  std::cout << "WARNING: encountered target element out of bounds."
451  << std::endl;
452  std::cout << "Interpolated result will be clipped." << std::endl;
453 
454  result(nx) = X(0);
455  }
456 
457  else if (Xtarget(nx) > X(N - 1)) {
458  std::cout << "alma::linearInterpolation > " << std::endl;
459  std::cout << "WARNING: encountered target element out of bounds."
460  << std::endl;
461  std::cout << "Interpolated result will be clipped." << std::endl;
462 
463  result(nx) = X(N - 1);
464  }
465 
466  else {
467  // use bisection algorithm to determine base array bin the target
468  // sits
469  // in.
470 
471  int leftindex = 0;
472  int rightindex = N - 1;
473 
474  while (rightindex - leftindex > 1) {
475  int midindex = (leftindex + rightindex) / 2;
476 
477  if (X(midindex) > Xtarget(nx)) {
478  rightindex = midindex;
479  }
480  else {
481  leftindex = midindex;
482  }
483  }
484 
485  double yleft = Y(leftindex);
486  double yright = Y(rightindex);
487  double xleft = X(leftindex);
488  double xright = X(rightindex);
489 
490  result(nx) = yleft + (Xtarget(nx) - xleft) * (yright - yleft) /
491  (xright - xleft);
492  }
493  }
494 
495  return result;
496 }
497 } // namespace alma
498 
499 namespace boost {
500 namespace serialization {
502 template <class Archive, typename S>
503 void save(Archive& ar, const Eigen::Triplet<S>& t, const unsigned int version) {
504  ar& t.row();
505  ar& t.col();
506  ar& t.value();
507 }
508 
509 
511 template <class Archive, typename S>
512 void load(Archive& ar, Eigen::Triplet<S>& t, const unsigned int version) {
513  int row;
514  ar& row;
515  int col;
516  ar& col;
517  S value;
518  ar& value;
519 
520  t = Eigen::Triplet<S>(row, col, value);
521 }
522 
523 
526 template <class Archive, typename S>
527 void serialize(Archive& ar, Eigen::Triplet<S>& t, const unsigned int version) {
528  split_free(ar, t, version);
529 }
530 } // namespace serialization
531 } // namespace boost
std::array< std::size_t, 2 > my_jobs(std::size_t njobs, std::size_t nprocs, std::size_t my_id)
Compute the minimum and maximum indices of the jobs that must be executed in the current process...
Definition: utilities.hpp:268
Eigen::VectorXd linearInterpolation(const Eigen::Ref< const Eigen::VectorXd > &X, const Eigen::Ref< const Eigen::VectorXd > &Y, const Eigen::Ref< const Eigen::VectorXd > &Xtarget)
Perform linear interpolation of a tabulated function Y(X) at Xtarget.
Definition: utilities.hpp:438
void update(const T &obj, double value)
Update the minimum value with a new one if the latter is lower.
Definition: utilities.hpp:185
bool almost_equal(const double a, const double b, const double abstol=1e-8, const double reltol=1e-5)
Approximate comparation between doubles.
Definition: utilities.hpp:139
Definition: analytic1d.hpp:26
std::string engineer_format(double x, bool insert_space=false)
Convert a numerical value to a string that uses engineering prefix.
Definition: utilities.hpp:365
Min_keeper(const double _abstol=1e-8, const double _reltol=1e-5)
Basic constructor.
Definition: utilities.hpp:156
U python_mod(const T &n, const U &d)
Alternative modulus operation with a behavior similar to the % operator in Python.
Definition: utilities.hpp:292
Definition: isotopic_scattering.hpp:58
std::unique_ptr< T > make_unique(Args &&... args)
Emulation of C++14&#39;s make_unique.
Definition: utilities.hpp:43
std::string get_timestamp()
Build a timestamp in our preferred format.
Definition: utilities.hpp:234
void flush_istream(std::istream &f)
Read from f until &#39; &#39; is found.
Definition: utilities.hpp:85
double ssqrt(double x)
"Signed square root" of real numbers, that converts purely imaginary numbers to negative values...
Definition: utilities.hpp:214
Eigen::VectorXd splineInterpolation(const Eigen::Ref< const Eigen::VectorXd > &X, const Eigen::Ref< const Eigen::VectorXd > &Y, const Eigen::Ref< const Eigen::VectorXd > &Xtarget)
Perform spline interpolation of a tabulated function Y(X) at Xtarget.
Definition: utilities.hpp:406
double get_value() const
Definition: utilities.hpp:162
void load(Archive &ar, Eigen::Triplet< S > &t, const unsigned int version)
Allow boost to unserialize an Eigen::Triplet.
Definition: utilities.hpp:512
Eigen::VectorXd logSpace(double min, double max, int N)
Construct a vector with N logarithmically spaced values from min to max.
Definition: utilities.hpp:396
bool starts_with_character(const std::string &line, const std::string &chars)
Check whether a line starts with a character from a given set.
Definition: utilities.hpp:119
void serialize(Archive &ar, Eigen::Triplet< S > &t, const unsigned int version)
Dummy function allowing us to implement save() and load() separately.
Definition: utilities.hpp:527
Comparator function object template for a container of comparable objects.
Definition: utilities.hpp:50
double bose_einstein(double omega, double T)
Bose-Einstein distribution.
Definition: utilities.hpp:310
Physical, mathematical and miscellaneous constants used in alma.
void save(Archive &ar, const Eigen::Triplet< S > &t, const unsigned int version)
Allow boost to serialize an Eigen::Triplet.
Definition: utilities.hpp:503
std::vector< T > get_vector() const
Definition: utilities.hpp:169
Eigen::VectorXi reduce_integers(const Eigen::Ref< const Eigen::VectorXi > &vector)
Divide a vector of integers by the GCD of their coefficients.
Definition: utilities.hpp:347
double bose_einstein_kernel(double omega, double T)
Integration kernel for the specific heat, thermal conductivity and other integrals.
Definition: utilities.hpp:326
std::vector< T > tokenize_homogeneous_line(std::string &line, const std::string sep=" \)
Try to tokenize a line into homogeneous tokens.
Definition: utilities.hpp:102
std::tuple< std::vector< typename T::key_type >, std::vector< typename T::mapped_type > > split_keys_and_values(const T &input)
Put the keys and values of a map (or similar) into separate vectors.
Definition: utilities.hpp:63
Convenience class for updating a minimum value and keeping track of all the objects associated to it...
Definition: utilities.hpp:153
int signum(const T &arg)
Compute the sign of an argument.
Definition: utilities.hpp:224