maxwell.cc 4.3 KB

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