maxwell.cc 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. // -*- mode: c++; coding: utf-8 -*-
  2. // Maxwell — 4-vector potential vacuum field equations
  3. // After Chaitin1986, p. 14. Attempt at straight translation from APL.
  4. // (c) Daniel Llorens - 2016
  5. // This library is free software; you can redistribute it and/or modify it under
  6. // the terms of the GNU Lesser General Public License as published by the Free
  7. // Software Foundation; either version 3 of the License, or (at your option) any
  8. // later version.
  9. #include <iostream>
  10. #include <thread>
  11. #include <string>
  12. #include "ra/test.hh"
  13. #include "ra/ra.hh"
  14. #include "ra/bench.hh"
  15. using real = double;
  16. template <class T, int rank> using array = ra::Big<T, rank>;
  17. using ra::iota;
  18. auto H = ra::all;
  19. template <int n> constexpr ra::dots_t<n> HH = ra::dots<n>;
  20. using std::cout, std::endl, ra::PI, ra::TestRecorder;
  21. int main()
  22. {
  23. TestRecorder tr(cout);
  24. real delta = 1;
  25. int o=20, n=20, m=2, l=2;
  26. array<real, 5> A({o, n, m, l, 4}, 0.);
  27. array<real, 6> DA({o, n, m, l, 4, 4}, 0.);
  28. array<real, 6> F({o, n, m, l, 4, 4}, 0.);
  29. array<real, 4> divA({o, n, m, l}, 0.);
  30. array<real, 4> X({n, m, l, 4}, 0.), Y({n, m, l, 4}, 0.);
  31. A(0, H, H, H, 2) = -cos(iota(n)*(2*PI/n))/(2*PI/n);
  32. A(1, H, H, H, 2) = -cos((iota(n)-delta)*(2*PI/n))/(2*PI/n);
  33. auto t0 = Benchmark::clock::now();
  34. // FIXME this is painful without a roll operator, but we need a roll operator without temps.
  35. for (int t=1; t+1<o; ++t) {
  36. // X←(1⌽[0]A[T;;;;])+(1⌽[1]A[T;;;;])+(1⌽[2]A[T;;;;])
  37. X(iota(n-1)) = A(t, iota(n-1, 1));
  38. X(n-1) = A(t, 0);
  39. X(H, iota(m-1)) += A(t, H, iota(m-1, 1));
  40. X(H, m-1) += A(t, H, 0);
  41. X(H, H, iota(l-1)) += A(t, H, H, iota(l-1, 1));
  42. X(H, H, l-1) += A(t, H, H, 0);
  43. // Y←(¯1⌽[0]A[T;;;;])+(¯1⌽[1]A[T;;;;])+(¯1⌽[2]A[T;;;;])
  44. Y(iota(n-1, 1)) = A(t, iota(n-1));
  45. Y(0) = A(t, n-1);
  46. Y(H, iota(m-1, 1)) += A(t, H, iota(m-1));
  47. Y(H, 0) += A(t, H, m-1);
  48. Y(H, H, iota(l-1, 1)) += A(t, H, H, iota(l-1));
  49. Y(H, H, 0) += A(t, H, H, l-1);
  50. A(t+1) = X + Y - A(t-1) - 4*A(t);
  51. }
  52. auto time_A = Benchmark::clock::now()-t0;
  53. // FIXME should try to traverse the array once, e.g. explode() = pack(...). The need to wrap around boundaries complicates this greatly.
  54. auto diff = [&DA, &A, &delta](auto k_, real factor)
  55. {
  56. constexpr int k = decltype(k_)::value;
  57. const int o = DA.size(k);
  58. if (o>=2) {
  59. DA(HH<k>, iota(o-2, 1), HH<4-k>, k) = (A(HH<k>, iota(o-2, 2)) - A(HH<k>, iota(o-2, 0)));
  60. DA(HH<k>, 0, HH<4-k>, k) = (A(HH<k>, 1) - A(HH<k>, o-1));
  61. DA(HH<k>, o-1, HH<4-k>, k) = (A(HH<k>, 0) - A(HH<k>, o-2));
  62. DA(HH<5>, k) *= factor;
  63. }
  64. };
  65. t0 = Benchmark::clock::now();
  66. diff(mp::int_t<0>(), +1/(2*delta));
  67. diff(mp::int_t<1>(), -1/(2*delta));
  68. diff(mp::int_t<2>(), -1/(2*delta));
  69. diff(mp::int_t<3>(), -1/(2*delta));
  70. auto time_DA = Benchmark::clock::now()-t0;
  71. F = ra::transpose<0, 1, 2, 3, 5, 4>(DA) - DA;
  72. // abuse shape matching to reduce last axis.
  73. divA += ra::transpose<0, 1, 2, 3, 4, 4>(DA);
  74. tr.info("Lorentz test max div A (1)").test_eq(0., amax(divA));
  75. // an alternative without a temporary.
  76. tr.info("Lorentz test max div A (2)")
  77. .test_eq(0., amax(map([](auto && a) { return sum(a); },
  78. iter<1>(ra::transpose<0, 1, 2, 3, 4, 4>(DA)))));
  79. tr.quiet().test_eq(0.3039588939177449, F(19, 0, 0, 0, 2, 1));
  80. auto show = [&tr, &delta](char const * name, int t, auto && F)
  81. {
  82. tr.quiet().test(amin(F)>=-1);
  83. tr.quiet().test(amax(F)<=+1);
  84. cout << name << "(0)=" << std::setprecision(10) << std::setw(12) << F(0) << " t=" << (t*delta) << ":\n";
  85. for_each([](auto && F) { cout << std::string(int(round(20*(clamp(F, -1., 1.)+1))), ' ') << "*\n"; }, F);
  86. };
  87. for (int t=0; t<o; ++t) {
  88. show("Ey", t, F(t, H, 0, 0, 2, 0));
  89. show("Bz", t, F(t, H, 0, 0, 2, 1));
  90. std::this_thread::sleep_for(std::chrono::milliseconds(50));
  91. cout << endl;
  92. }
  93. cout << Benchmark::toseconds(time_A)/1e-6 << " μs time_A" << endl;
  94. cout << Benchmark::toseconds(time_DA)/1e-6 << " μs time_DA" << endl;
  95. return tr.summary();
  96. }