ngsolve 6.2.2505.post105.dev0__cp312-cp312-macosx_10_15_universal2.whl → 6.2.2506.post33.dev0__cp312-cp312-macosx_10_15_universal2.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ngsolve might be problematic. Click here for more details.

Files changed (67) hide show
  1. netgen/include/analytic_integrals.hpp +10 -0
  2. netgen/include/bdbequations.hpp +20 -0
  3. netgen/include/bem_diffops.hpp +475 -0
  4. netgen/include/bspline.hpp +2 -0
  5. netgen/include/contact.hpp +4 -0
  6. netgen/include/h1lumping.hpp +6 -0
  7. netgen/include/hcurl_equations.hpp +29 -0
  8. netgen/include/hdivfe_utils.hpp +1 -0
  9. netgen/include/kernels.hpp +654 -0
  10. netgen/include/mp_coefficient.hpp +20 -20
  11. netgen/include/mptools.hpp +268 -123
  12. netgen/include/potentialtools.hpp +2 -2
  13. netgen/include/thdivfe_impl.hpp +66 -0
  14. netgen/libngbla.dylib +0 -0
  15. netgen/libngcomp.dylib +0 -0
  16. netgen/libngfem.dylib +0 -0
  17. netgen/libngla.dylib +0 -0
  18. netgen/libngsbem.dylib +0 -0
  19. netgen/libngsolve.dylib +0 -0
  20. netgen/libngstd.dylib +0 -0
  21. ngsolve/__init__.pyi +2 -2
  22. ngsolve/cmake/NGSolveConfig.cmake +1 -1
  23. ngsolve/config/__init__.pyi +7 -7
  24. ngsolve/config/config.py +6 -6
  25. ngsolve/config/config.pyi +7 -7
  26. ngsolve/demos/intro/cmagnet.py +19 -22
  27. ngsolve/solve.pyi +1 -1
  28. ngsolve/solve_implementation.py +4 -0
  29. ngsolve/utils.pyi +1 -1
  30. {ngsolve-6.2.2505.post105.dev0.dist-info → ngsolve-6.2.2506.post33.dev0.dist-info}/METADATA +2 -2
  31. {ngsolve-6.2.2505.post105.dev0.dist-info → ngsolve-6.2.2506.post33.dev0.dist-info}/RECORD +67 -64
  32. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/Netgen.icns +0 -0
  33. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/bin/ngscxx +0 -0
  34. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/bin/ngsld +0 -0
  35. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/bin/ngsolve.tcl +0 -0
  36. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/bin/ngspy +0 -0
  37. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/beam.geo +0 -0
  38. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/beam.vol +0 -0
  39. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/chip.in2d +0 -0
  40. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/chip.vol +0 -0
  41. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/coil.geo +0 -0
  42. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/coil.vol +0 -0
  43. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/coilshield.geo +0 -0
  44. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/coilshield.vol +0 -0
  45. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/cube.geo +0 -0
  46. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/cube.vol +0 -0
  47. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/d10_DGdoubleglazing.pde +0 -0
  48. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/d11_chip_nitsche.pde +0 -0
  49. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/d1_square.pde +0 -0
  50. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/d2_chip.pde +0 -0
  51. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/d3_helmholtz.pde +0 -0
  52. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/d4_cube.pde +0 -0
  53. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/d5_beam.pde +0 -0
  54. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/d6_shaft.pde +0 -0
  55. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/d7_coil.pde +0 -0
  56. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/d8_coilshield.pde +0 -0
  57. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/d9_hybridDG.pde +0 -0
  58. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/doubleglazing.in2d +0 -0
  59. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/doubleglazing.vol +0 -0
  60. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/piezo2d40round4.vol.gz +0 -0
  61. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/shaft.geo +0 -0
  62. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/shaft.vol +0 -0
  63. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/square.in2d +0 -0
  64. {ngsolve-6.2.2505.post105.dev0.data → ngsolve-6.2.2506.post33.dev0.data}/data/share/ngsolve/square.vol +0 -0
  65. {ngsolve-6.2.2505.post105.dev0.dist-info → ngsolve-6.2.2506.post33.dev0.dist-info}/LICENSE +0 -0
  66. {ngsolve-6.2.2505.post105.dev0.dist-info → ngsolve-6.2.2506.post33.dev0.dist-info}/WHEEL +0 -0
  67. {ngsolve-6.2.2505.post105.dev0.dist-info → ngsolve-6.2.2506.post33.dev0.dist-info}/top_level.txt +0 -0
@@ -303,7 +303,7 @@ namespace ngsbem
303
303
 
304
304
 
305
305
  // hn1 = jn+ i*yn
306
- class MPSingular
306
+ class Singular
307
307
  {
308
308
  public:
309
309
  template <typename T>
@@ -329,7 +329,7 @@ namespace ngsbem
329
329
 
330
330
 
331
331
  // jn
332
- class MPRegular
332
+ class Regular
333
333
  {
334
334
  public:
335
335
  template <typename T>
@@ -357,14 +357,14 @@ namespace ngsbem
357
357
 
358
358
 
359
359
  template <typename RADIAL, typename entry_type=Complex>
360
- class NGS_DLL_HEADER MultiPole
360
+ class NGS_DLL_HEADER SphericalExpansion
361
361
  {
362
362
  SphericalHarmonics<entry_type> sh;
363
363
  double kappa;
364
364
  double rtyp;
365
365
  public:
366
366
 
367
- MultiPole (int aorder, double akappa, double artyp)
367
+ SphericalExpansion (int aorder, double akappa, double artyp)
368
368
  : sh(aorder), kappa(akappa), rtyp(artyp) { }
369
369
 
370
370
 
@@ -376,15 +376,15 @@ namespace ngsbem
376
376
  double RTyp() const { return rtyp; }
377
377
  int Order() const { return sh.Order(); }
378
378
 
379
- MultiPole Truncate(int neworder) const
379
+ SphericalExpansion Truncate(int neworder) const
380
380
  {
381
381
  if (neworder > sh.Order()) neworder=sh.Order();
382
- MultiPole nmp(neworder, kappa, rtyp);
382
+ SphericalExpansion nmp(neworder, kappa, rtyp);
383
383
  nmp.sh.Coefs() = sh.Coefs().Range(sqr(neworder+1));
384
384
  return nmp;
385
385
  }
386
386
 
387
- MultiPole & operator+= (const MultiPole & mp2)
387
+ SphericalExpansion & operator+= (const SphericalExpansion & mp2)
388
388
  {
389
389
  size_t commonsize = min(SH().Coefs().Size(), mp2.SH().Coefs().Size());
390
390
  SH().Coefs().Range(commonsize) += mp2.SH().Coefs().Range(commonsize);
@@ -395,27 +395,24 @@ namespace ngsbem
395
395
  entry_type EvalDirectionalDerivative (Vec<3> x, Vec<3> d) const;
396
396
 
397
397
  void AddCharge (Vec<3> x, entry_type c);
398
- void AddDipole (Vec<3> x, Vec<3> d, entry_type c);
399
- void AddCurrent (Vec<3> ap, Vec<3> ep, Complex j, int num=100);
400
-
401
- /*
402
- void ChangeScaleTo (double newscale)
398
+ void AddDipole (Vec<3> x, Vec<3> dir, entry_type c);
399
+ void AddChargeDipole (Vec<3> x, entry_type c, Vec<3> dir, entry_type c2)
403
400
  {
404
- double fac = Scale()/newscale;
405
- double prod = 1;
406
- for (int n = 0; n <= sh.Order(); n++, prod*= fac)
407
- sh.CoefsN(n) *= prod;
408
- scale = newscale;
401
+ // TODO: add them at once
402
+ AddCharge (x, c);
403
+ AddDipole (x, dir, c2);
409
404
  }
410
- */
405
+
406
+ void AddPlaneWave (Vec<3> d, entry_type c);
407
+ void AddCurrent (Vec<3> ap, Vec<3> ep, Complex j, int num=100);
408
+
409
+
411
410
  void ChangeRTypTo (double new_rtyp)
412
411
  {
413
- // double fac = Scale()/newscale;
414
412
  double fac = RADIAL::Scale(kappa, rtyp) / RADIAL::Scale(kappa, new_rtyp);
415
413
  double prod = 1;
416
414
  for (int n = 0; n <= sh.Order(); n++, prod*= fac)
417
415
  sh.CoefsN(n) *= prod;
418
- // scale = newscale;
419
416
  rtyp = new_rtyp;
420
417
  }
421
418
 
@@ -434,7 +431,7 @@ namespace ngsbem
434
431
 
435
432
 
436
433
  template <typename TARGET>
437
- void Transform (MultiPole<TARGET,entry_type> & target, Vec<3> dist) const
434
+ void Transform (SphericalExpansion<TARGET,entry_type> & target, Vec<3> dist) const
438
435
  {
439
436
  if (target.SH().Order() < 0) return;
440
437
  if (SH().Order() < 0)
@@ -449,8 +446,8 @@ namespace ngsbem
449
446
  auto [len, theta, phi] = SphericalCoordinates(dist);
450
447
 
451
448
 
452
- // MultiPole<RADIAL,entry_type> tmp{*this};
453
- MultiPole<RADIAL,entry_type> tmp(Order(), kappa, rtyp);
449
+ // SphericalExpansion<RADIAL,entry_type> tmp{*this};
450
+ SphericalExpansion<RADIAL,entry_type> tmp(Order(), kappa, rtyp);
454
451
  tmp.SH().Coefs() = SH().Coefs();
455
452
 
456
453
  tmp.SH().RotateZ(phi);
@@ -463,12 +460,12 @@ namespace ngsbem
463
460
  }
464
461
 
465
462
  template <typename TARGET>
466
- void TransformAdd (MultiPole<TARGET,entry_type> & target, Vec<3> dist, bool atomic = false) const
463
+ void TransformAdd (SphericalExpansion<TARGET,entry_type> & target, Vec<3> dist, bool atomic = false) const
467
464
  {
468
465
  if (SH().Order() < 0) return;
469
466
  if (target.SH().Order() < 0) return;
470
467
 
471
- MultiPole<TARGET,entry_type> tmp{target};
468
+ SphericalExpansion<TARGET,entry_type> tmp{target};
472
469
  Transform(tmp, dist);
473
470
  if (!atomic)
474
471
  target.SH().Coefs() += tmp.SH().Coefs();
@@ -478,8 +475,20 @@ namespace ngsbem
478
475
  }
479
476
 
480
477
  template <typename TARGET>
481
- void ShiftZ (double z, MultiPole<TARGET,entry_type> & target);
478
+ void ShiftZ (double z, SphericalExpansion<TARGET,entry_type> & target);
479
+
482
480
 
481
+ template <typename TARGET>
482
+ void In2Out (SphericalExpansion<TARGET,entry_type> & target, double r) const
483
+ {
484
+ Vector<Complex> rad(Order()+1);
485
+ Vector<Complex> radout(target.Order()+1);
486
+ RADIAL::Eval(Order(), kappa, r, RTyp(), rad);
487
+ TARGET::Eval(target.Order(), kappa, r, target.RTyp(), radout);
488
+ target.SH().Coefs() = 0;
489
+ for (int j = 0; j <= std::min(Order(), target.Order()); j++)
490
+ target.SH().CoefsN(j) = rad(j)/radout(j) * SH().CoefsN(j);
491
+ }
483
492
  };
484
493
 
485
494
 
@@ -488,7 +497,8 @@ namespace ngsbem
488
497
 
489
498
  static constexpr int MPOrder (double rho_kappa)
490
499
  {
491
- return max (20, int(2*rho_kappa));
500
+ // return max (20, int(2*rho_kappa));
501
+ return 20+int(2*rho_kappa);
492
502
  }
493
503
  static constexpr int maxdirect = 100;
494
504
 
@@ -506,22 +516,22 @@ namespace ngsbem
506
516
 
507
517
 
508
518
  template <typename entry_type=Complex>
509
- class SingularMLMultiPole
519
+ class SingularMLExpansion
510
520
  {
511
521
  using simd_entry_type = decltype(MakeSimd(declval<std::array<entry_type,FMM_SW>>()));
512
522
  static Array<size_t> nodes_on_level;
513
523
 
514
524
  struct RecordingSS
515
525
  {
516
- const MultiPole<MPSingular,entry_type> * mp_source;
517
- MultiPole<MPSingular,entry_type> * mp_target;
526
+ const SphericalExpansion<Singular,entry_type> * mp_source;
527
+ SphericalExpansion<Singular,entry_type> * mp_target;
518
528
  Vec<3> dist;
519
529
  double len, theta, phi;
520
530
  bool flipz;
521
531
  public:
522
532
  RecordingSS() = default;
523
- RecordingSS (const MultiPole<MPSingular,entry_type> * amp_source,
524
- MultiPole<MPSingular,entry_type> * amp_target,
533
+ RecordingSS (const SphericalExpansion<Singular,entry_type> * amp_source,
534
+ SphericalExpansion<Singular,entry_type> * amp_target,
525
535
  Vec<3> adist)
526
536
  : mp_source(amp_source), mp_target(amp_target), dist(adist)
527
537
  {
@@ -579,8 +589,8 @@ namespace ngsbem
579
589
  static void ProcessVectorizedBatch(FlatArray<RecordingSS*> batch, double len, double theta) {
580
590
 
581
591
  // *testout << "Processing vectorized S->S batch of size " << batch.Size() << ", with N = " << N << ", vec_length = " << vec_length << ", len = " << len << ", theta = " << theta << endl;
582
- MultiPole<MPSingular, Vec<N,Complex>> vec_source(batch[0]->mp_source->Order(), batch[0]->mp_source->Kappa(), batch[0]->mp_source->RTyp());
583
- MultiPole<MPSingular, Vec<N,Complex>> vec_target(batch[0]->mp_target->Order(), batch[0]->mp_target->Kappa(), batch[0]->mp_target->RTyp());
592
+ SphericalExpansion<Singular, Vec<N,Complex>> vec_source(batch[0]->mp_source->Order(), batch[0]->mp_source->Kappa(), batch[0]->mp_source->RTyp());
593
+ SphericalExpansion<Singular, Vec<N,Complex>> vec_target(batch[0]->mp_target->Order(), batch[0]->mp_target->Kappa(), batch[0]->mp_target->RTyp());
584
594
 
585
595
  // Copy multipoles into vectorized multipole
586
596
  for (int i = 0; i < batch.Size(); i++)
@@ -617,15 +627,18 @@ namespace ngsbem
617
627
  double r;
618
628
  int level;
619
629
  std::array<unique_ptr<Node>,8> childs;
620
- MultiPole<MPSingular, entry_type> mp;
630
+ SphericalExpansion<Singular, entry_type> mp;
621
631
 
622
632
  Array<tuple<Vec<3>, entry_type>> charges;
623
633
  Array<tuple<Vec<3>, Vec<3>, entry_type>> dipoles;
634
+ Array<tuple<Vec<3>, entry_type, Vec<3>, entry_type>> chargedipoles;
624
635
  Array<tuple<Vec<3>, Vec<3>, Complex,int>> currents;
625
636
 
626
637
  using simd_entry_type = decltype(MakeSimd(declval<std::array<entry_type,FMM_SW>>()));
627
638
  Array<tuple<Vec<3,SIMD<double,FMM_SW>>, simd_entry_type>> simd_charges;
628
639
  Array<tuple<Vec<3,SIMD<double,FMM_SW>>, Vec<3,SIMD<double,FMM_SW>>, simd_entry_type>> simd_dipoles;
640
+ Array<tuple<Vec<3,SIMD<double,FMM_SW>>, simd_entry_type,
641
+ Vec<3,SIMD<double,FMM_SW>>, simd_entry_type>> simd_chargedipoles;
629
642
 
630
643
  int total_sources;
631
644
  std::mutex node_mutex;
@@ -638,7 +651,15 @@ namespace ngsbem
638
651
  nodes_on_level[level]++;
639
652
  }
640
653
 
641
-
654
+ int GetChildNum (Vec<3> x) const
655
+ {
656
+ int childnum = 0;
657
+ if (x(0) > center(0)) childnum += 1;
658
+ if (x(1) > center(1)) childnum += 2;
659
+ if (x(2) > center(2)) childnum += 4;
660
+ return childnum;
661
+ }
662
+
642
663
  void CreateChilds()
643
664
  {
644
665
  if (childs[0]) throw Exception("have already childs");
@@ -654,15 +675,32 @@ namespace ngsbem
654
675
  }
655
676
 
656
677
 
678
+ void SendSourcesToChilds()
679
+ {
680
+ CreateChilds();
681
+
682
+ for (auto [x,c] : charges)
683
+ AddCharge (x,c);
684
+ for (auto [x,d,c] : dipoles)
685
+ AddDipole (x,d,c);
686
+ for (auto [x,c,d,c2] : chargedipoles)
687
+ AddChargeDipole (x,c,d,c2);
688
+ for (auto [sp,ep,j,num] : currents)
689
+ AddCurrent (sp,ep,j,num);
690
+
691
+ charges.SetSize0();
692
+ dipoles.SetSize0();
693
+ chargedipoles.SetSize0();
694
+ currents.SetSize0();
695
+ }
696
+
697
+
657
698
  void AddCharge (Vec<3> x, entry_type c)
658
699
  {
659
700
  if (have_childs) // quick check without locking
660
701
  {
661
702
  // directly send to childs:
662
- int childnum = 0;
663
- if (x(0) > center(0)) childnum += 1;
664
- if (x(1) > center(1)) childnum += 2;
665
- if (x(2) > center(2)) childnum += 4;
703
+ int childnum = GetChildNum(x);
666
704
  childs[childnum] -> AddCharge(x, c);
667
705
  return;
668
706
  }
@@ -671,36 +709,19 @@ namespace ngsbem
671
709
 
672
710
  if (have_childs) // test again after locking
673
711
  {
674
- // directly send to childs:
675
- int childnum = 0;
676
- if (x(0) > center(0)) childnum += 1;
677
- if (x(1) > center(1)) childnum += 2;
678
- if (x(2) > center(2)) childnum += 4;
712
+ int childnum = GetChildNum(x);
679
713
  childs[childnum] -> AddCharge(x, c);
680
714
  return;
681
715
  }
682
716
 
683
-
684
-
685
717
  charges.Append( tuple{x,c} );
686
718
 
687
719
  // if (r*mp.Kappa() < 1e-8) return;
688
720
  if (level > 20) return;
689
721
  if (charges.Size() < maxdirect && r*mp.Kappa() < 1)
690
722
  return;
691
-
692
- CreateChilds();
693
-
694
- for (auto [x,c] : charges)
695
- AddCharge (x,c);
696
- for (auto [x,d,c] : dipoles)
697
- AddDipole (x,d,c);
698
- for (auto [sp,ep,j,num] : currents)
699
- AddCurrent (sp,ep,j,num);
700
723
 
701
- charges.SetSize0();
702
- dipoles.SetSize0();
703
- currents.SetSize0();
724
+ SendSourcesToChilds();
704
725
  }
705
726
 
706
727
 
@@ -709,11 +730,7 @@ namespace ngsbem
709
730
  if (have_childs)
710
731
  {
711
732
  // directly send to childs:
712
-
713
- int childnum = 0;
714
- if (x(0) > center(0)) childnum += 1;
715
- if (x(1) > center(1)) childnum += 2;
716
- if (x(2) > center(2)) childnum += 4;
733
+ int childnum = GetChildNum(x);
717
734
  childs[childnum] -> AddDipole(x, d, c);
718
735
  return;
719
736
  }
@@ -723,37 +740,54 @@ namespace ngsbem
723
740
  if (have_childs)
724
741
  {
725
742
  // directly send to childs:
726
-
727
- int childnum = 0;
728
- if (x(0) > center(0)) childnum += 1;
729
- if (x(1) > center(1)) childnum += 2;
730
- if (x(2) > center(2)) childnum += 4;
743
+ int childnum = GetChildNum(x);
731
744
  childs[childnum] -> AddDipole(x, d, c);
732
745
  return;
733
746
  }
734
-
735
-
736
-
737
747
 
738
748
  dipoles.Append (tuple{x,d,c});
739
749
 
740
750
  if (dipoles.Size() < maxdirect || r < 1e-8)
741
751
  return;
752
+
753
+ SendSourcesToChilds();
754
+ }
755
+
756
+
757
+ void AddChargeDipole (Vec<3> x, entry_type c, Vec<3> dir, entry_type c2)
758
+ {
759
+ if (have_childs)
760
+ {
761
+ // directly send to childs:
762
+ int childnum = GetChildNum(x);
763
+ childs[childnum] -> AddChargeDipole(x, c, dir, c2);
764
+ return;
765
+ }
766
+
767
+ lock_guard<mutex> guard(node_mutex);
768
+
769
+ if (have_childs)
770
+ {
771
+ // directly send to childs:
772
+ int childnum = GetChildNum(x);
773
+ childs[childnum] -> AddChargeDipole(x, c, dir, c2);
774
+ return;
775
+ }
742
776
 
743
- CreateChilds();
777
+ chargedipoles.Append (tuple{x,c,dir,c2});
744
778
 
745
- for (auto [x,c] : charges)
746
- AddCharge (x,c);
747
- for (auto [x,d,c] : dipoles)
748
- AddDipole (x,d,c);
749
- for (auto [sp,ep,j,num] : currents)
750
- AddCurrent (sp,ep,j,num);
779
+ if (chargedipoles.Size() < maxdirect || r < 1e-8)
780
+ return;
751
781
 
752
- charges.SetSize0();
753
- dipoles.SetSize0();
754
- currents.SetSize0();
782
+ SendSourcesToChilds();
783
+
784
+ /*
785
+ AddCharge (x, c);
786
+ AddDipole (x, dir, c2);
787
+ */
755
788
  }
756
789
 
790
+
757
791
  // not parallel yet
758
792
  void AddCurrent (Vec<3> sp, Vec<3> ep, Complex j, int num)
759
793
  {
@@ -787,6 +821,12 @@ namespace ngsbem
787
821
 
788
822
  currents.Append (tuple{sp,ep,j,num});
789
823
 
824
+ // if (currents.Size() < maxdirect || r < 1e-8)
825
+ if (currents.Size() < 4 || r < 1e-8)
826
+ return;
827
+
828
+ SendSourcesToChilds();
829
+ /*
790
830
  // if (currents.Size() < maxdirect || r < 1e-8)
791
831
  if (currents.Size() < 4 || r < 1e-8)
792
832
  return;
@@ -803,6 +843,7 @@ namespace ngsbem
803
843
  charges.SetSize0();
804
844
  dipoles.SetSize0();
805
845
  currents.SetSize0();
846
+ */
806
847
  }
807
848
 
808
849
 
@@ -823,7 +864,20 @@ namespace ngsbem
823
864
  // t.AddFlops (charges.Size());
824
865
  if (simd_charges.Size())
825
866
  {
867
+ // static Timer t("mptool singmp, evaluate, simd charges"); RegionTimer r(t);
868
+
826
869
  simd_entry_type vsum{0.0};
870
+ if (mp.Kappa() < 1e-12)
871
+ {
872
+ for (auto [x,c] : simd_charges)
873
+ {
874
+ auto rho2 = L2Norm2(p-x);
875
+ auto kernel = (1/(4*M_PI)) * rsqrt(rho2);
876
+ kernel = If(rho2 > 0.0, kernel, SIMD<double,FMM_SW>(0.0));
877
+ vsum += kernel * c;
878
+ }
879
+ }
880
+ else
827
881
  if (mp.Kappa() < 1e-8)
828
882
  for (auto [x,c] : simd_charges)
829
883
  {
@@ -861,6 +915,8 @@ namespace ngsbem
861
915
 
862
916
  if (simd_dipoles.Size())
863
917
  {
918
+ // static Timer t("mptool singmp, evaluate, simd dipoles"); RegionTimer r(t);
919
+
864
920
  simd_entry_type vsum{0.0};
865
921
  for (auto [x,d,c] : simd_dipoles)
866
922
  {
@@ -887,6 +943,52 @@ namespace ngsbem
887
943
  }
888
944
  }
889
945
 
946
+
947
+
948
+ if (simd_chargedipoles.Size())
949
+ {
950
+ // static Timer t("mptool singmp, evaluate, simd chargedipoles"); RegionTimer r(t);
951
+
952
+ simd_entry_type vsum{0.0};
953
+ for (auto [x,c,d,c2] : simd_chargedipoles)
954
+ {
955
+ auto rho = L2Norm(p-x);
956
+ auto rhokappa = rho*mp.Kappa();
957
+ auto invrho = If(rho>0.0, 1.0/rho, SIMD<double,FMM_SW>(0.0));
958
+ auto [si,co] = sincos(rhokappa);
959
+
960
+ auto kernelc = (1/(4*M_PI))*invrho*SIMD<Complex,FMM_SW>(co,si);
961
+ vsum += kernelc * c;
962
+
963
+ auto kernel =
964
+ invrho*invrho * InnerProduct(p-x, d) *
965
+ kernelc * SIMD<Complex,FMM_SW>(-1.0, rhokappa);
966
+
967
+ vsum += kernel * c2;
968
+ }
969
+ sum += HSum(vsum);
970
+ }
971
+ else
972
+ {
973
+ // static Timer t("mptool singmp, evaluate, chargedipoles"); RegionTimer r(t);
974
+
975
+ for (auto [x,c,d,c2] : chargedipoles)
976
+ if (double rho = L2Norm(p-x); rho > 0)
977
+ {
978
+ sum += (1/(4*M_PI))*exp(Complex(0,rho*mp.Kappa())) / rho * c;
979
+
980
+ Vec<3> drhodp = 1.0/rho * (p-x);
981
+ Complex dGdrho = (1/(4*M_PI))*exp(Complex(0,rho*mp.Kappa())) *
982
+ (Complex(0, mp.Kappa())/rho - 1.0/sqr(rho));
983
+
984
+ sum += dGdrho * InnerProduct(drhodp, d) * c2;
985
+ }
986
+ }
987
+
988
+
989
+
990
+
991
+
890
992
  for (auto [sp,ep,j,num] : currents)
891
993
  {
892
994
  // should use explizit formula instead ...
@@ -923,7 +1025,9 @@ namespace ngsbem
923
1025
  }
924
1026
 
925
1027
  if (dipoles.Size())
926
- throw Exception("EvaluateDeriv not implemented for dipoles in SingularMLMultiPole");
1028
+ throw Exception("EvaluateDeriv not implemented for dipoles in SingularMLExpansion");
1029
+ if (chargedipoles.Size())
1030
+ throw Exception("EvaluateDeriv not implemented for dipoles in SingularMLExpansion");
927
1031
 
928
1032
  for (auto [x,c] : charges)
929
1033
  if (double rho = L2Norm(p-x); rho > 0)
@@ -938,7 +1042,7 @@ namespace ngsbem
938
1042
 
939
1043
  void CalcTotalSources()
940
1044
  {
941
- total_sources = charges.Size() + dipoles.Size();
1045
+ total_sources = charges.Size() + dipoles.Size() + chargedipoles.Size();
942
1046
  for (auto & child : childs)
943
1047
  if (child)
944
1048
  {
@@ -971,9 +1075,9 @@ namespace ngsbem
971
1075
  }
972
1076
  else
973
1077
  {
974
- if (charges.Size()+dipoles.Size()+currents.Size() == 0)
1078
+ if (charges.Size()+dipoles.Size()+chargedipoles.Size()+currents.Size() == 0)
975
1079
  {
976
- mp = MultiPole<MPSingular,entry_type> (-1, mp.Kappa(), 1.);
1080
+ mp = SphericalExpansion<Singular,entry_type> (-1, mp.Kappa(), 1.);
977
1081
  return;
978
1082
  }
979
1083
 
@@ -1012,6 +1116,26 @@ namespace ngsbem
1012
1116
  simd_dipoles[ii] = MakeSimd(di);
1013
1117
  }
1014
1118
 
1119
+
1120
+ simd_chargedipoles.SetSize( (chargedipoles.Size()+FMM_SW-1)/FMM_SW);
1121
+ i = 0, ii = 0;
1122
+ for ( ; i+FMM_SW <= chargedipoles.Size(); i+=FMM_SW, ii++)
1123
+ {
1124
+ std::array<tuple<Vec<3>,entry_type,Vec<3>,entry_type>, FMM_SW> di;
1125
+ for (int j = 0; j < FMM_SW; j++) di[j] = chargedipoles[i+j];
1126
+ simd_chargedipoles[ii] = MakeSimd(di);
1127
+ }
1128
+ if (i < chargedipoles.Size())
1129
+ {
1130
+ std::array<tuple<Vec<3>,entry_type,Vec<3>,entry_type>, FMM_SW> di;
1131
+ int j = 0;
1132
+ for ( ; i+j < chargedipoles.Size(); j++) di[j] = chargedipoles[i+j];
1133
+ for ( ; j < FMM_SW; j++) di[j] = tuple( get<0>(di[0]), entry_type{0.0}, get<2>(di[0]), entry_type{0.0} );
1134
+ simd_chargedipoles[ii] = MakeSimd(di);
1135
+ }
1136
+
1137
+
1138
+
1015
1139
 
1016
1140
  if (nodes_to_process)
1017
1141
  *nodes_to_process += this;
@@ -1022,6 +1146,9 @@ namespace ngsbem
1022
1146
  for (auto [x,d,c] : dipoles)
1023
1147
  mp.AddDipole (x-center, d, c);
1024
1148
 
1149
+ for (auto [x,c,d,c2] : chargedipoles)
1150
+ mp.AddChargeDipole (x-center, c, d, c2);
1151
+
1025
1152
  for (auto [sp,ep,j,num] : currents)
1026
1153
  mp.AddCurrent (sp-center, ep-center, j, num);
1027
1154
  }
@@ -1030,7 +1157,7 @@ namespace ngsbem
1030
1157
 
1031
1158
  entry_type EvaluateMP(Vec<3> p) const
1032
1159
  {
1033
- if (charges.Size() || dipoles.Size())
1160
+ if (charges.Size() || dipoles.Size() || chargedipoles.Size())
1034
1161
  return Evaluate(p);
1035
1162
 
1036
1163
  if (L2Norm(p-center) > 3*r)
@@ -1050,7 +1177,7 @@ namespace ngsbem
1050
1177
  // cout << "EvaluateMPDeriv Singular, p = " << p << ", d = " << d << ", r = " << r << ", center = " << center << endl;
1051
1178
  // cout << "Norm: " << L2Norm(p-center) << " > " << 3*r << endl;
1052
1179
  // cout << "charges.Size() = " << charges.Size() << ", dipoles.Size() = " << dipoles.Size() << endl;
1053
- if (charges.Size() || dipoles.Size() || !childs[0])
1180
+ if (charges.Size() || dipoles.Size() || chargedipoles.Size() || !childs[0])
1054
1181
  return EvaluateDeriv(p, d);
1055
1182
 
1056
1183
  if (L2Norm(p-center) > 3*r)
@@ -1073,6 +1200,8 @@ namespace ngsbem
1073
1200
  ost << "xi = " << x << ", ci = " << c << endl;
1074
1201
  for (auto [x,d,c] : dipoles)
1075
1202
  ost << "xi = " << x << ", di = " << d << ", ci = " << c << endl;
1203
+ for (auto [x,c,d,c2] : chargedipoles)
1204
+ ost << "xi = " << x << ", c = " << c << ", di = " << d << ", ci = " << c2 << endl;
1076
1205
 
1077
1206
  for (int i = 0; i < 8; i++)
1078
1207
  if (childs[i]) childs[i] -> Print (ost, i);
@@ -1101,7 +1230,7 @@ namespace ngsbem
1101
1230
  bool havemp = false;
1102
1231
 
1103
1232
  public:
1104
- SingularMLMultiPole (Vec<3> center, double r, double kappa)
1233
+ SingularMLExpansion (Vec<3> center, double r, double kappa)
1105
1234
  : root(center, r, 0, kappa)
1106
1235
  {
1107
1236
  nodes_on_level = 0;
@@ -1120,6 +1249,11 @@ namespace ngsbem
1120
1249
  root.AddDipole(x, d, c);
1121
1250
  }
1122
1251
 
1252
+ void AddChargeDipole(Vec<3> x, entry_type c, Vec<3> dir, entry_type c2)
1253
+ {
1254
+ root.AddChargeDipole(x, c, dir, c2);
1255
+ }
1256
+
1123
1257
  void AddCurrent (Vec<3> sp, Vec<3> ep, Complex j, int num)
1124
1258
  {
1125
1259
  if constexpr (!std::is_same<entry_type, Vec<3,Complex>>())
@@ -1201,6 +1335,8 @@ namespace ngsbem
1201
1335
  node->mp.AddCharge(x-node->center, c);
1202
1336
  for (auto [x,d,c]: node->dipoles)
1203
1337
  node->mp.AddDipole(x-node->center, d, c);
1338
+ for (auto [x,c,d,c2]: node->chargedipoles)
1339
+ node->mp.AddChargeDipole(x-node->center, c, d, c2);
1204
1340
  for (auto [sp,ep,j,num]: node->currents)
1205
1341
  node->mp.AddCurrent(sp-node->center, ep-node->center, j, num);
1206
1342
  }, TasksPerThread(4));
@@ -1272,12 +1408,12 @@ namespace ngsbem
1272
1408
  }
1273
1409
 
1274
1410
  template <typename entry_type2>
1275
- friend class RegularMLMultiPole;
1411
+ friend class RegularMLExpansion;
1276
1412
  };
1277
1413
 
1278
1414
 
1279
1415
  template <typename entry_type>
1280
- inline ostream & operator<< (ostream & ost, const SingularMLMultiPole<entry_type> & mlmp)
1416
+ inline ostream & operator<< (ostream & ost, const SingularMLExpansion<entry_type> & mlmp)
1281
1417
  {
1282
1418
  mlmp.Print(ost);
1283
1419
  return ost;
@@ -1285,21 +1421,21 @@ namespace ngsbem
1285
1421
 
1286
1422
 
1287
1423
  template <typename elem_type=Complex>
1288
- class NGS_DLL_HEADER RegularMLMultiPole
1424
+ class NGS_DLL_HEADER RegularMLExpansion
1289
1425
  {
1290
1426
  static Array<size_t> nodes_on_level;
1291
1427
 
1292
1428
 
1293
1429
  struct RecordingRS
1294
1430
  {
1295
- const MultiPole<MPSingular,elem_type> * mpS;
1296
- MultiPole<MPRegular,elem_type> * mpR;
1431
+ const SphericalExpansion<Singular,elem_type> * mpS;
1432
+ SphericalExpansion<Regular,elem_type> * mpR;
1297
1433
  Vec<3> dist;
1298
1434
  double len, theta, phi;
1299
1435
  public:
1300
1436
  RecordingRS() = default;
1301
- RecordingRS (const MultiPole<MPSingular,elem_type> * ampS,
1302
- MultiPole<MPRegular,elem_type> * ampR,
1437
+ RecordingRS (const SphericalExpansion<Singular,elem_type> * ampS,
1438
+ SphericalExpansion<Regular,elem_type> * ampR,
1303
1439
  Vec<3> adist)
1304
1440
  : mpS(ampS), mpR(ampR), dist(adist)
1305
1441
  {
@@ -1381,10 +1517,10 @@ namespace ngsbem
1381
1517
  // static Timer tfrombatch("mptools - copy from batch 2");
1382
1518
 
1383
1519
  // *testout << "Processing vectorized batch of size " << batch.Size() << ", with N = " << N << ", vec_length = " << vec_length << ", len = " << len << ", theta = " << theta << endl;
1384
- MultiPole<MPSingular, Vec<N,Complex>> vec_source(batch[0]->mpS->Order(), batch[0]->mpS->Kappa(), batch[0]->mpS->RTyp());
1385
- // MultiPole<MPSingular, elem_type> tmp_source{*batch[0]->mpS};
1386
- MultiPole<MPRegular, elem_type> tmp_target{*batch[0]->mpR};
1387
- MultiPole<MPRegular, Vec<N,Complex>> vec_target(batch[0]->mpR->Order(), batch[0]->mpR->Kappa(), batch[0]->mpR->RTyp());
1520
+ SphericalExpansion<Singular, Vec<N,Complex>> vec_source(batch[0]->mpS->Order(), batch[0]->mpS->Kappa(), batch[0]->mpS->RTyp());
1521
+ // SphericalExpansion<Singular, elem_type> tmp_source{*batch[0]->mpS};
1522
+ SphericalExpansion<Regular, elem_type> tmp_target{*batch[0]->mpR};
1523
+ SphericalExpansion<Regular, Vec<N,Complex>> vec_target(batch[0]->mpR->Order(), batch[0]->mpR->Kappa(), batch[0]->mpR->RTyp());
1388
1524
 
1389
1525
  // Copy multipoles into vectorized multipole
1390
1526
  // ttobatch.Start();
@@ -1432,13 +1568,13 @@ namespace ngsbem
1432
1568
  double r;
1433
1569
  int level;
1434
1570
  std::array<unique_ptr<Node>,8> childs;
1435
- MultiPole<MPRegular,elem_type> mp;
1571
+ SphericalExpansion<Regular,elem_type> mp;
1436
1572
  Array<Vec<3>> targets;
1437
1573
  int total_targets;
1438
1574
  std::mutex node_mutex;
1439
1575
  atomic<bool> have_childs{false};
1440
1576
 
1441
- Array<const typename SingularMLMultiPole<elem_type>::Node*> singnodes;
1577
+ Array<const typename SingularMLExpansion<elem_type>::Node*> singnodes;
1442
1578
 
1443
1579
  Node (Vec<3> acenter, double ar, int alevel, double kappa)
1444
1580
  : center(acenter), r(ar), level(alevel), mp(MPOrder(ar*kappa), kappa, ar) // 1.0/min(1.0, 0.25*r*kappa))
@@ -1464,7 +1600,7 @@ namespace ngsbem
1464
1600
  have_childs = true;
1465
1601
  }
1466
1602
 
1467
- void AddSingularNode (const typename SingularMLMultiPole<elem_type>::Node & singnode, bool allow_refine,
1603
+ void AddSingularNode (const typename SingularMLExpansion<elem_type>::Node & singnode, bool allow_refine,
1468
1604
  Array<RecordingRS> * recording)
1469
1605
  {
1470
1606
  if (mp.SH().Order() < 0) return;
@@ -1546,7 +1682,7 @@ namespace ngsbem
1546
1682
  void LocalizeExpansion(bool allow_refine)
1547
1683
  {
1548
1684
  if (allow_refine)
1549
- if (mp.Order() > 20 && !childs[0])
1685
+ if (mp.Order() > 30 && !childs[0])
1550
1686
  CreateChilds();
1551
1687
 
1552
1688
  if (childs[0])
@@ -1567,7 +1703,7 @@ namespace ngsbem
1567
1703
  mp.TransformAdd (childs[nr]->mp, childs[nr]->center-center);
1568
1704
  childs[nr]->LocalizeExpansion(allow_refine);
1569
1705
  });
1570
- mp = MultiPole<MPRegular,elem_type>(-1, mp.Kappa(), 1.);
1706
+ mp = SphericalExpansion<Regular,elem_type>(-1, mp.Kappa(), 1.);
1571
1707
  //mp.SH().Coefs()=0.0;
1572
1708
  }
1573
1709
  }
@@ -1583,11 +1719,16 @@ namespace ngsbem
1583
1719
  if (childs[childnum])
1584
1720
  sum = childs[childnum]->Evaluate(p);
1585
1721
  else
1586
- sum = mp.Eval(p-center);
1587
-
1588
- for (auto sn : singnodes)
1589
- sum += sn->EvaluateMP(p);
1722
+ {
1723
+ // static Timer t("mptool regmp, evaluate reg"); RegionTimer r(t);
1724
+ sum = mp.Eval(p-center);
1725
+ }
1590
1726
 
1727
+ {
1728
+ // static Timer t("mptool regmp, evaluate, singnode"); RegionTimer r(t);
1729
+ for (auto sn : singnodes)
1730
+ sum += sn->EvaluateMP(p);
1731
+ }
1591
1732
  return sum;
1592
1733
  }
1593
1734
 
@@ -1695,7 +1836,7 @@ namespace ngsbem
1695
1836
  }
1696
1837
 
1697
1838
  if (total_targets == 0)
1698
- mp = MultiPole<MPRegular,elem_type>(-1, mp.Kappa(),1.);
1839
+ mp = SphericalExpansion<Regular,elem_type>(-1, mp.Kappa(),1.);
1699
1840
  }
1700
1841
 
1701
1842
 
@@ -1715,10 +1856,10 @@ namespace ngsbem
1715
1856
  };
1716
1857
 
1717
1858
  Node root;
1718
- shared_ptr<SingularMLMultiPole<elem_type>> singmp;
1859
+ shared_ptr<SingularMLExpansion<elem_type>> singmp;
1719
1860
 
1720
1861
  public:
1721
- RegularMLMultiPole (shared_ptr<SingularMLMultiPole<elem_type>> asingmp, Vec<3> center, double r)
1862
+ RegularMLExpansion (shared_ptr<SingularMLExpansion<elem_type>> asingmp, Vec<3> center, double r)
1722
1863
  : root(center, r, 0, asingmp->Kappa()), singmp(asingmp)
1723
1864
  {
1724
1865
  if (!singmp->havemp) throw Exception("first call Calc for singular MP");
@@ -1748,7 +1889,7 @@ namespace ngsbem
1748
1889
  }
1749
1890
  }
1750
1891
 
1751
- RegularMLMultiPole (Vec<3> center, double r, double kappa)
1892
+ RegularMLExpansion (Vec<3> center, double r, double kappa)
1752
1893
  : root(center, r, 0, kappa)
1753
1894
  {
1754
1895
  nodes_on_level = 0;
@@ -1760,7 +1901,7 @@ namespace ngsbem
1760
1901
  root.AddTarget (t);
1761
1902
  }
1762
1903
 
1763
- void CalcMP(shared_ptr<SingularMLMultiPole<elem_type>> asingmp)
1904
+ void CalcMP(shared_ptr<SingularMLExpansion<elem_type>> asingmp, bool onlytargets = true)
1764
1905
  {
1765
1906
  static Timer t("mptool regular MLMP"); RegionTimer rg(t);
1766
1907
  static Timer trec("mptool regular MLMP - recording");
@@ -1769,15 +1910,17 @@ namespace ngsbem
1769
1910
  singmp = asingmp;
1770
1911
 
1771
1912
  root.CalcTotalTargets();
1772
- root.RemoveEmptyTrees();
1913
+ if (onlytargets)
1914
+ root.RemoveEmptyTrees();
1773
1915
 
1774
1916
 
1775
- // root.AddSingularNode(singmp->root, false, nullptr);
1917
+ // root.AddSingularNode(singmp->root, !onlytargets, nullptr);
1918
+
1776
1919
  // /*
1777
1920
  Array<RecordingRS> recording;
1778
1921
  {
1779
1922
  RegionTimer rrec(trec);
1780
- root.AddSingularNode(singmp->root, false, &recording);
1923
+ root.AddSingularNode(singmp->root, !onlytargets, &recording);
1781
1924
  }
1782
1925
 
1783
1926
  // cout << "recorded: " << recording.Size() << endl;
@@ -1828,15 +1971,15 @@ namespace ngsbem
1828
1971
 
1829
1972
  /*
1830
1973
  int maxlevel = 0;
1831
- for (auto [i,num] : Enumerate(RegularMLMultiPole::nodes_on_level))
1974
+ for (auto [i,num] : Enumerate(RegularMLExpansion::nodes_on_level))
1832
1975
  if (num > 0) maxlevel = i;
1833
1976
 
1834
1977
  for (int i = 0; i <= maxlevel; i++)
1835
- cout << "reg " << i << ": " << RegularMLMultiPole::nodes_on_level[i] << endl;
1978
+ cout << "reg " << i << ": " << RegularMLExpansion::nodes_on_level[i] << endl;
1836
1979
  */
1837
1980
 
1838
1981
  static Timer tloc("mptool regular localize expansion"); RegionTimer rloc(tloc);
1839
- root.LocalizeExpansion(false);
1982
+ root.LocalizeExpansion(!onlytargets);
1840
1983
  }
1841
1984
 
1842
1985
  void Print (ostream & ost) const
@@ -1857,7 +2000,9 @@ namespace ngsbem
1857
2000
  elem_type Evaluate (Vec<3> p) const
1858
2001
  {
1859
2002
  // static Timer t("mptool Eval MLMP regular"); RegionTimer r(t);
1860
- if (L2Norm(p-root.center) > root.r) return elem_type{0.0};
2003
+ // if (L2Norm(p-root.center) > root.r) return elem_type{0.0};
2004
+ if (MaxNorm(p-root.center) > root.r)
2005
+ return singmp->Evaluate(p);
1861
2006
  return root.Evaluate(p);
1862
2007
  }
1863
2008
 
@@ -1871,10 +2016,10 @@ namespace ngsbem
1871
2016
 
1872
2017
 
1873
2018
  template <typename elem_type>
1874
- inline ostream & operator<< (ostream & ost, const RegularMLMultiPole<elem_type> & mlmp)
2019
+ inline ostream & operator<< (ostream & ost, const RegularMLExpansion<elem_type> & mlmp)
1875
2020
  {
1876
2021
  mlmp.Print(ost);
1877
- // ost << "RegularMLMultiPole" << endl;
2022
+ // ost << "RegularMLExpansion" << endl;
1878
2023
  return ost;
1879
2024
  }
1880
2025