maxwell.cc 4.3 KB

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