radia 1.3.15__tar.gz → 1.3.16__tar.gz

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.
Files changed (188) hide show
  1. {radia-1.3.15/src/radia.egg-info → radia-1.3.16}/PKG-INFO +1 -1
  2. {radia-1.3.15 → radia-1.3.16}/docs/API_REFERENCE.md +107 -0
  3. {radia-1.3.15 → radia-1.3.16}/examples/cube_uniform_field/linear/benchmark_conditions.py +3 -3
  4. radia-1.3.16/examples/cube_uniform_field/nonlinear/README.md +229 -0
  5. {radia-1.3.15 → radia-1.3.16}/examples/cube_uniform_field/nonlinear/benchmark_conditions.py +1 -1
  6. {radia-1.3.15 → radia-1.3.16}/examples/cube_uniform_field/nonlinear/benchmark_hexahedron_msc.py +121 -18
  7. radia-1.3.16/examples/cube_uniform_field/nonlinear/benchmark_tetra_all_solvers.py +327 -0
  8. {radia-1.3.15 → radia-1.3.16}/examples/cube_uniform_field/nonlinear/benchmark_tetrahedron_msc_netgen.py +93 -12
  9. {radia-1.3.15 → radia-1.3.16}/pyproject.toml +1 -1
  10. {radia-1.3.15 → radia-1.3.16}/src/radia/__init__.py +1 -1
  11. radia-1.3.16/src/radia/radia.pyd +0 -0
  12. {radia-1.3.15 → radia-1.3.16}/src/radia/radia_ngsolve.pyd +0 -0
  13. {radia-1.3.15 → radia-1.3.16/src/radia.egg-info}/PKG-INFO +1 -1
  14. {radia-1.3.15 → radia-1.3.16}/src/radia.egg-info/SOURCES.txt +1 -2
  15. radia-1.3.15/examples/cube_uniform_field/nonlinear/README.md +0 -137
  16. radia-1.3.15/examples/ngsolve_integration/test_batch_evaluation.py +0 -143
  17. radia-1.3.15/examples/ngsolve_integration/test_unit_conversion.py +0 -173
  18. radia-1.3.15/src/radia/radia.pyd +0 -0
  19. {radia-1.3.15 → radia-1.3.16}/COPYRIGHT.txt +0 -0
  20. {radia-1.3.15 → radia-1.3.16}/LICENSE +0 -0
  21. {radia-1.3.15 → radia-1.3.16}/MANIFEST.in +0 -0
  22. {radia-1.3.15 → radia-1.3.16}/README.md +0 -0
  23. {radia-1.3.15 → radia-1.3.16}/docs/HMATRIX_EVALUATION.md +0 -0
  24. {radia-1.3.15 → radia-1.3.16}/docs/README.md +0 -0
  25. {radia-1.3.15 → radia-1.3.16}/docs/scripts/README.md +0 -0
  26. {radia-1.3.15 → radia-1.3.16}/examples/README.md +0 -0
  27. {radia-1.3.15 → radia-1.3.16}/examples/background_fields/README.md +0 -0
  28. {radia-1.3.15 → radia-1.3.16}/examples/background_fields/cubit_to_nastran.py +0 -0
  29. {radia-1.3.15 → radia-1.3.16}/examples/background_fields/permeability_comparison.py +0 -0
  30. {radia-1.3.15 → radia-1.3.16}/examples/background_fields/quadrupole_analytical.py +0 -0
  31. {radia-1.3.15 → radia-1.3.16}/examples/background_fields/quadrupole_analytical.vtk.vtk +0 -0
  32. {radia-1.3.15 → radia-1.3.16}/examples/background_fields/sphere_in_quadrupole.py +0 -0
  33. {radia-1.3.15 → radia-1.3.16}/examples/background_fields/sphere_nastran_analysis.py +0 -0
  34. {radia-1.3.15 → radia-1.3.16}/examples/background_fields/sphere_nastran_field_mu.pvsm +0 -0
  35. {radia-1.3.15 → radia-1.3.16}/examples/background_fields/sphere_nastran_geometry.vtk +0 -0
  36. {radia-1.3.15 → radia-1.3.16}/examples/complex_coil_geometry/README.md +0 -0
  37. {radia-1.3.15 → radia-1.3.16}/examples/complex_coil_geometry/coil_geometry.vtk +0 -0
  38. {radia-1.3.15 → radia-1.3.16}/examples/complex_coil_geometry/coil_model.py +0 -0
  39. {radia-1.3.15 → radia-1.3.16}/examples/complex_coil_geometry/complex_coil.pvsm +0 -0
  40. {radia-1.3.15 → radia-1.3.16}/examples/complex_coil_geometry/field_map.py +0 -0
  41. {radia-1.3.15 → radia-1.3.16}/examples/complex_coil_geometry/field_map.vtk +0 -0
  42. {radia-1.3.15 → radia-1.3.16}/examples/complex_coil_geometry/visualize_coils.py +0 -0
  43. {radia-1.3.15 → radia-1.3.16}/examples/cube_uniform_field/README.md +0 -0
  44. {radia-1.3.15 → radia-1.3.16}/examples/cube_uniform_field/linear/README.md +0 -0
  45. {radia-1.3.15 → radia-1.3.16}/examples/cube_uniform_field/linear/benchmark_hexa_unified.py +0 -0
  46. {radia-1.3.15 → radia-1.3.16}/examples/cube_uniform_field/linear/benchmark_tetra_unified.py +0 -0
  47. {radia-1.3.15 → radia-1.3.16}/examples/cube_uniform_field/linear/compare_radia_elf.py +0 -0
  48. {radia-1.3.15 → radia-1.3.16}/examples/cube_uniform_field/linear/verify_radia_vs_elf.py +0 -0
  49. {radia-1.3.15 → radia-1.3.16}/examples/cube_uniform_field/nonlinear/benchmark_hexahedron_hacapk.py +0 -0
  50. {radia-1.3.15 → radia-1.3.16}/examples/electromagnet/README.md +0 -0
  51. {radia-1.3.15 → radia-1.3.16}/examples/electromagnet/Radia_model.vtk +0 -0
  52. {radia-1.3.15 → radia-1.3.16}/examples/electromagnet/York.vtk +0 -0
  53. {radia-1.3.15 → radia-1.3.16}/examples/electromagnet/field_distribution.vtk +0 -0
  54. {radia-1.3.15 → radia-1.3.16}/examples/electromagnet/main_simulation_workflow.py +0 -0
  55. {radia-1.3.15 → radia-1.3.16}/examples/electromagnet/visualize_paraview.py +0 -0
  56. {radia-1.3.15 → radia-1.3.16}/examples/electromagnet/york_cubit_mesh.py +0 -0
  57. {radia-1.3.15 → radia-1.3.16}/examples/magpylib_integration/README.md +0 -0
  58. {radia-1.3.15 → radia-1.3.16}/examples/magpylib_integration/cylinder_magnet_examples.py +0 -0
  59. {radia-1.3.15 → radia-1.3.16}/examples/magpylib_integration/demo_magpylib_integration.py +0 -0
  60. {radia-1.3.15 → radia-1.3.16}/examples/magpylib_integration/sphere_in_halbach_cylinder.py +0 -0
  61. {radia-1.3.15 → radia-1.3.16}/examples/ngsolve_integration/README.md +0 -0
  62. {radia-1.3.15 → radia-1.3.16}/examples/ngsolve_integration/RadiaField_HACApK/verify_batch_evaluation.py +0 -0
  63. {radia-1.3.15 → radia-1.3.16}/examples/ngsolve_integration/benchmark_gridfunction_set.py +0 -0
  64. {radia-1.3.15 → radia-1.3.16}/examples/ngsolve_integration/demo_basic_field.py +0 -0
  65. {radia-1.3.15 → radia-1.3.16}/examples/ngsolve_integration/demo_batch_evaluation.py +0 -0
  66. {radia-1.3.15 → radia-1.3.16}/examples/ngsolve_integration/demo_coordinate_transform.py +0 -0
  67. {radia-1.3.15 → radia-1.3.16}/examples/ngsolve_integration/demo_field_types.py +0 -0
  68. {radia-1.3.15 → radia-1.3.16}/examples/ngsolve_integration/demo_hdiv_projection.py +0 -0
  69. {radia-1.3.15 → radia-1.3.16}/examples/ngsolve_integration/mesh_magnetization_import/README.md +0 -0
  70. {radia-1.3.15 → radia-1.3.16}/examples/ngsolve_integration/mesh_magnetization_import/ngsolve_cube_uniform_field.py +0 -0
  71. {radia-1.3.15 → radia-1.3.16}/examples/ngsolve_integration/mesh_magnetization_import/sphere_analytical_to_radia.py +0 -0
  72. {radia-1.3.15 → radia-1.3.16}/examples/ngsolve_integration/mesh_magnetization_import/verified_ngsolve_to_radia.py +0 -0
  73. {radia-1.3.15 → radia-1.3.16}/examples/ngsolve_integration/verify_curl_A_equals_B/verify_curl_A_equals_B.py +0 -0
  74. {radia-1.3.15 → radia-1.3.16}/examples/ngsolve_integration/visualize_field.py +0 -0
  75. {radia-1.3.15 → radia-1.3.16}/examples/simple_problems/CONVERSION_NOTES.md +0 -0
  76. {radia-1.3.15 → radia-1.3.16}/examples/simple_problems/README.md +0 -0
  77. {radia-1.3.15 → radia-1.3.16}/examples/simple_problems/arc_current_dual_magnets.py +0 -0
  78. {radia-1.3.15 → radia-1.3.16}/examples/simple_problems/arc_current_with_magnet.py +0 -0
  79. {radia-1.3.15 → radia-1.3.16}/examples/simple_problems/chamfered_pole_piece.py +0 -0
  80. {radia-1.3.15 → radia-1.3.16}/examples/simple_problems/compare_magpylib.py +0 -0
  81. {radia-1.3.15 → radia-1.3.16}/examples/simple_problems/cubic_polyhedron_magnet.py +0 -0
  82. {radia-1.3.15 → radia-1.3.16}/examples/smco_magnet_array/README.md +0 -0
  83. {radia-1.3.15 → radia-1.3.16}/examples/smco_magnet_array/smbo.pvsm +0 -0
  84. {radia-1.3.15 → radia-1.3.16}/examples/smco_magnet_array/smco_array.py +0 -0
  85. {radia-1.3.15 → radia-1.3.16}/examples/smco_magnet_array/smco_array.vtk +0 -0
  86. {radia-1.3.15 → radia-1.3.16}/examples/smco_magnet_array/smco_field_distribution.vtk +0 -0
  87. {radia-1.3.15 → radia-1.3.16}/examples/solver_benchmarks/BENCHMARK_RESULTS.md +0 -0
  88. {radia-1.3.15 → radia-1.3.16}/examples/solver_benchmarks/README.md +0 -0
  89. {radia-1.3.15 → radia-1.3.16}/examples/solver_benchmarks/benchmark_field_evaluation.py +0 -0
  90. {radia-1.3.15 → radia-1.3.16}/examples/solver_benchmarks/benchmark_large_scale_comparison.py +0 -0
  91. {radia-1.3.15 → radia-1.3.16}/examples/solver_benchmarks/benchmark_linear_material.py +0 -0
  92. {radia-1.3.15 → radia-1.3.16}/examples/solver_benchmarks/benchmark_lu_vs_hmatrix.py +0 -0
  93. {radia-1.3.15 → radia-1.3.16}/examples/solver_benchmarks/benchmark_matrix_construction.py +0 -0
  94. {radia-1.3.15 → radia-1.3.16}/examples/solver_benchmarks/benchmark_parallel_construction.py +0 -0
  95. {radia-1.3.15 → radia-1.3.16}/examples/solver_benchmarks/benchmark_solver.py +0 -0
  96. {radia-1.3.15 → radia-1.3.16}/examples/solver_benchmarks/benchmark_solver_comparison.py +0 -0
  97. {radia-1.3.15 → radia-1.3.16}/examples/solver_benchmarks/benchmark_solver_methods.py +0 -0
  98. {radia-1.3.15 → radia-1.3.16}/examples/solver_benchmarks/benchmark_solver_scaling.py +0 -0
  99. {radia-1.3.15 → radia-1.3.16}/examples/solver_benchmarks/benchmark_solver_scaling_extended.py +0 -0
  100. {radia-1.3.15 → radia-1.3.16}/examples/solver_benchmarks/plot_benchmark_results.py +0 -0
  101. {radia-1.3.15 → radia-1.3.16}/examples/solver_benchmarks/run_all_benchmarks.py +0 -0
  102. {radia-1.3.15 → radia-1.3.16}/examples/solver_benchmarks/verify_field_accuracy.py +0 -0
  103. {radia-1.3.15 → radia-1.3.16}/examples/tetra_field_accuracy_evaluation/README.md +0 -0
  104. {radia-1.3.15 → radia-1.3.16}/examples/tetra_field_accuracy_evaluation/analytical_reference.py +0 -0
  105. {radia-1.3.15 → radia-1.3.16}/examples/tetra_field_accuracy_evaluation/compare_all.py +0 -0
  106. {radia-1.3.15 → radia-1.3.16}/examples/tetra_field_accuracy_evaluation/evaluate_radia_solvers.py +0 -0
  107. {radia-1.3.15 → radia-1.3.16}/examples/tetra_field_accuracy_evaluation/evaluate_tetra_field.py +0 -0
  108. {radia-1.3.15 → radia-1.3.16}/examples/tetra_field_accuracy_evaluation/ngsolve_reference.py +0 -0
  109. {radia-1.3.15 → radia-1.3.16}/setup.cfg +0 -0
  110. {radia-1.3.15 → radia-1.3.16}/setup.py +0 -0
  111. {radia-1.3.15 → radia-1.3.16}/src/radia/libopenblas.dll +0 -0
  112. {radia-1.3.15 → radia-1.3.16}/src/radia/nastran_mesh_import.py +0 -0
  113. {radia-1.3.15 → radia-1.3.16}/src/radia/netgen_mesh_import.py +0 -0
  114. {radia-1.3.15 → radia-1.3.16}/src/radia/rad_ngsolve_fast.py +0 -0
  115. {radia-1.3.15 → radia-1.3.16}/src/radia/radia_coil_builder.py +0 -0
  116. {radia-1.3.15 → radia-1.3.16}/src/radia/radia_field_cached.py +0 -0
  117. {radia-1.3.15 → radia-1.3.16}/src/radia/radia_ngsolve_field.py +0 -0
  118. {radia-1.3.15 → radia-1.3.16}/src/radia/radia_ngsolve_utils.py +0 -0
  119. {radia-1.3.15 → radia-1.3.16}/src/radia/radia_pyvista_viewer.py +0 -0
  120. {radia-1.3.15 → radia-1.3.16}/src/radia/radia_vtk_export.py +0 -0
  121. {radia-1.3.15 → radia-1.3.16}/src/radia.egg-info/dependency_links.txt +0 -0
  122. {radia-1.3.15 → radia-1.3.16}/src/radia.egg-info/not-zip-safe +0 -0
  123. {radia-1.3.15 → radia-1.3.16}/src/radia.egg-info/requires.txt +0 -0
  124. {radia-1.3.15 → radia-1.3.16}/src/radia.egg-info/top_level.txt +0 -0
  125. {radia-1.3.15 → radia-1.3.16}/tests/README.md +0 -0
  126. {radia-1.3.15 → radia-1.3.16}/tests/__init__.py +0 -0
  127. {radia-1.3.15 → radia-1.3.16}/tests/benchmarks/benchmark_correct.py +0 -0
  128. {radia-1.3.15 → radia-1.3.16}/tests/benchmarks/benchmark_heavy.py +0 -0
  129. {radia-1.3.15 → radia-1.3.16}/tests/benchmarks/benchmark_openmp.py +0 -0
  130. {radia-1.3.15 → radia-1.3.16}/tests/benchmarks/benchmark_threads.py +0 -0
  131. {radia-1.3.15 → radia-1.3.16}/tests/conftest.py +0 -0
  132. {radia-1.3.15 → radia-1.3.16}/tests/profile_batch_performance.py +0 -0
  133. {radia-1.3.15 → radia-1.3.16}/tests/test_advanced.py +0 -0
  134. {radia-1.3.15 → radia-1.3.16}/tests/test_all_spaces.py +0 -0
  135. {radia-1.3.15 → radia-1.3.16}/tests/test_background_field_debug.py +0 -0
  136. {radia-1.3.15 → radia-1.3.16}/tests/test_batch_evaluation.py +0 -0
  137. {radia-1.3.15 → radia-1.3.16}/tests/test_cf_direct.py +0 -0
  138. {radia-1.3.15 → radia-1.3.16}/tests/test_convergence_hdiv.py +0 -0
  139. {radia-1.3.15 → radia-1.3.16}/tests/test_curlA_equals_B.py +0 -0
  140. {radia-1.3.15 → radia-1.3.16}/tests/test_curl_A_detailed.py +0 -0
  141. {radia-1.3.15 → radia-1.3.16}/tests/test_far_field_accuracy.py +0 -0
  142. {radia-1.3.15 → radia-1.3.16}/tests/test_fast_preparecache.py +0 -0
  143. {radia-1.3.15 → radia-1.3.16}/tests/test_fast_simple.py +0 -0
  144. {radia-1.3.15 → radia-1.3.16}/tests/test_group_operations.py +0 -0
  145. {radia-1.3.15 → radia-1.3.16}/tests/test_hcurl_vs_hdiv.py +0 -0
  146. {radia-1.3.15 → radia-1.3.16}/tests/test_l2_norm_debug.py +0 -0
  147. {radia-1.3.15 → radia-1.3.16}/tests/test_magpylib_comparison.py +0 -0
  148. {radia-1.3.15 → radia-1.3.16}/tests/test_materials.py +0 -0
  149. {radia-1.3.15 → radia-1.3.16}/tests/test_memory_allocation_tracking.py +0 -0
  150. {radia-1.3.15 → radia-1.3.16}/tests/test_mesh_import.py +0 -0
  151. {radia-1.3.15 → radia-1.3.16}/tests/test_minimal_cached.py +0 -0
  152. {radia-1.3.15 → radia-1.3.16}/tests/test_moving_magnet_memory.py +0 -0
  153. {radia-1.3.15 → radia-1.3.16}/tests/test_moving_magnet_memory_diagnosis.py +0 -0
  154. {radia-1.3.15 → radia-1.3.16}/tests/test_new_material_api.py +0 -0
  155. {radia-1.3.15 → radia-1.3.16}/tests/test_ngsolve_integration.py +0 -0
  156. {radia-1.3.15 → radia-1.3.16}/tests/test_objbckg_simple.py +0 -0
  157. {radia-1.3.15 → radia-1.3.16}/tests/test_objbckgcf_alone.py +0 -0
  158. {radia-1.3.15 → radia-1.3.16}/tests/test_order1.py +0 -0
  159. {radia-1.3.15 → radia-1.3.16}/tests/test_parallel_performance.py +0 -0
  160. {radia-1.3.15 → radia-1.3.16}/tests/test_preparecache_performance.py +0 -0
  161. {radia-1.3.15 → radia-1.3.16}/tests/test_process_memory.py +0 -0
  162. {radia-1.3.15 → radia-1.3.16}/tests/test_python_cached_field.py +0 -0
  163. {radia-1.3.15 → radia-1.3.16}/tests/test_python_cached_simple.py +0 -0
  164. {radia-1.3.15 → radia-1.3.16}/tests/test_rad_ngsolve.py +0 -0
  165. {radia-1.3.15 → radia-1.3.16}/tests/test_rad_ngsolve_diagnostic.py +0 -0
  166. {radia-1.3.15 → radia-1.3.16}/tests/test_rad_ngsolve_function.py +0 -0
  167. {radia-1.3.15 → radia-1.3.16}/tests/test_radhmat.py +0 -0
  168. {radia-1.3.15 → radia-1.3.16}/tests/test_radia.py +0 -0
  169. {radia-1.3.15 → radia-1.3.16}/tests/test_radia_core_memory.py +0 -0
  170. {radia-1.3.15 → radia-1.3.16}/tests/test_radia_field_computation_memory.py +0 -0
  171. {radia-1.3.15 → radia-1.3.16}/tests/test_radia_ngsolve_longrun.py +0 -0
  172. {radia-1.3.15 → radia-1.3.16}/tests/test_radia_ngsolve_memory_leak.py +0 -0
  173. {radia-1.3.15 → radia-1.3.16}/tests/test_radia_ngsolve_with_cache.py +0 -0
  174. {radia-1.3.15 → radia-1.3.16}/tests/test_radia_only_memory.py +0 -0
  175. {radia-1.3.15 → radia-1.3.16}/tests/test_serialization.py +0 -0
  176. {radia-1.3.15 → radia-1.3.16}/tests/test_set_vs_interpolate.py +0 -0
  177. {radia-1.3.15 → radia-1.3.16}/tests/test_simple.py +0 -0
  178. {radia-1.3.15 → radia-1.3.16}/tests/test_simple_fld_leak.py +0 -0
  179. {radia-1.3.15 → radia-1.3.16}/tests/test_square_coil_analytical.py +0 -0
  180. {radia-1.3.15 → radia-1.3.16}/tests/test_tetrahedral_solver.py +0 -0
  181. {radia-1.3.15 → radia-1.3.16}/tests/test_transformations.py +0 -0
  182. {radia-1.3.15 → radia-1.3.16}/tests/test_type_cast.py +0 -0
  183. {radia-1.3.15 → radia-1.3.16}/tests/test_unit_conversion_verify.py +0 -0
  184. {radia-1.3.15 → radia-1.3.16}/tests/test_utils.py +0 -0
  185. {radia-1.3.15 → radia-1.3.16}/tests/test_vector_potential.py +0 -0
  186. {radia-1.3.15 → radia-1.3.16}/tests/test_without_B_projection.py +0 -0
  187. {radia-1.3.15 → radia-1.3.16}/tests/test_without_gridfunction.py +0 -0
  188. {radia-1.3.15 → radia-1.3.16}/tests/verify_curl_A_equals_B.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: radia
3
- Version: 1.3.15
3
+ Version: 1.3.16
4
4
  Summary: Radia 3D Magnetostatics with NGSolve Integration and OpenMP Parallelization
5
5
  Home-page: https://github.com/ksugahar/Radia_NGSolve
6
6
  Author: Pascal Elleaume
@@ -307,6 +307,113 @@ rel_change = |B_new - B_old| / B_sat
307
307
 
308
308
  This method provides fast Newton-Raphson convergence and matches industry-standard solvers.
309
309
 
310
+ ### Solver Tolerance Parameters
311
+
312
+ Radia provides three tolerance parameters for controlling solver behavior:
313
+
314
+ ```python
315
+ # 1. Nonlinear iteration tolerance (outer loop)
316
+ # Set via Solve() - controls when Newton-Raphson iterations stop
317
+ rad.Solve(obj, nonl_tol, max_iter, method) # nonl_tol = 0.001 recommended
318
+
319
+ # 2. BiCGSTAB inner loop tolerance
320
+ # Set via SetBiCGSTABTol() BEFORE Solve() - controls linear system accuracy
321
+ rad.SetBiCGSTABTol(bicg_tol) # Default: 1e-4
322
+
323
+ # 3. H-matrix ACA tolerance (Method 2 only)
324
+ # Set via SetHACApKParams() BEFORE Solve() - controls low-rank approximation
325
+ rad.SetHACApKParams(hmat_eps, leaf_size, eta) # Default: 1e-4, 10, 2.0
326
+ ```
327
+
328
+ | Parameter | API | Default | Description |
329
+ |-----------|-----|---------|-------------|
330
+ | `nonl_tol` | `rad.Solve(obj, nonl_tol, ...)` | 0.001 | Nonlinear convergence threshold |
331
+ | `bicg_tol` | `rad.SetBiCGSTABTol(tol)` | 1e-4 | BiCGSTAB relative residual tolerance |
332
+ | `hmat_eps` | `rad.SetHACApKParams(eps, ...)` | 1e-4 | H-matrix ACA compression tolerance |
333
+
334
+ **Example - Full solver configuration:**
335
+
336
+ ```python
337
+ import radia as rad
338
+
339
+ # Configure tolerances BEFORE Solve()
340
+ rad.SetBiCGSTABTol(1e-4) # BiCGSTAB tolerance
341
+ rad.SetHACApKParams(1e-4, 10, 2.0) # H-matrix: eps=1e-4, leaf=10, eta=2.0
342
+
343
+ # Solve with nonlinear tolerance
344
+ rad.Solve(grp, 0.001, 100, 2) # nonl_tol=0.001, max_iter=100, method=2 (HACApK)
345
+ ```
346
+
347
+ ### SetBiCGSTABTol - BiCGSTAB Inner Loop Tolerance
348
+
349
+ ```python
350
+ rad.SetBiCGSTABTol(tol)
351
+ ```
352
+
353
+ | Parameter | Type | Default | Description |
354
+ |-----------|------|---------|-------------|
355
+ | `tol` | float | 1e-4 | Relative residual tolerance for BiCGSTAB |
356
+
357
+ **Notes:**
358
+ - Affects Method 1 (BiCGSTAB) and Method 2 (HACApK)
359
+ - Lower values = higher accuracy but more iterations
360
+ - Call BEFORE `rad.Solve()`
361
+
362
+ ### SetHACApKParams - H-Matrix Parameters (Method 2)
363
+
364
+ ```python
365
+ rad.SetHACApKParams(eps, leaf_size, eta)
366
+ ```
367
+
368
+ | Parameter | Type | Default | Description |
369
+ |-----------|------|---------|-------------|
370
+ | `eps` | float | 1e-4 | ACA+ compression tolerance |
371
+ | `leaf_size` | int | 10 | Minimum cluster size in elements |
372
+ | `eta` | float | 2.0 | Admissibility parameter |
373
+
374
+ **Notes:**
375
+ - Only affects Method 2 (HACApK H-matrix solver)
376
+ - Lower `eps` = higher accuracy, larger ranks, more memory
377
+ - Call BEFORE `rad.Solve()`
378
+
379
+ **Parameter Rationale:**
380
+
381
+ | Parameter | Default | Rationale |
382
+ |-----------|---------|-----------|
383
+ | `eps` | 1e-4 | Balance between accuracy and compression. Lower values (1e-6, 1e-8) for higher accuracy, higher values (1e-3) for faster computation. |
384
+ | `leaf_size` | 10 | Minimum cluster size. Smaller values allow deeper tree but increase H-matrix overhead. 10 provides good balance for typical element counts. ELF-compatible default. |
385
+ | `eta` | 2.0 | Standard admissibility criterion: clusters are "well-separated" when `dist(c1,c2) >= eta * max(diam(c1), diam(c2))`. eta=2.0 is conservative, ensuring accurate low-rank approximations. Lower values (1.0) allow more aggressive compression but may reduce accuracy. |
386
+
387
+ ### SetRelaxParam - Under-Relaxation Coefficient
388
+
389
+ ```python
390
+ rad.SetRelaxParam(relax)
391
+ ```
392
+
393
+ | Parameter | Type | Default | Description |
394
+ |-----------|------|---------|-------------|
395
+ | `relax` | float | 0.0 | Under-relaxation coefficient (0.0-1.0) |
396
+
397
+ **Notes:**
398
+ - Affects all solver methods (0=LU, 1=BiCGSTAB, 2=HACApK)
399
+ - `relax=0.0`: Full Newton step (default, fastest convergence when stable)
400
+ - `relax>0.0`: Damped update: `chi_new = chi_new*(1-relax) + chi_old*relax`
401
+ - Use under-relaxation (e.g., 0.2-0.5) when:
402
+ - Convergence is slow or oscillating
403
+ - Material has steep B-H curve
404
+ - Problem is highly nonlinear
405
+ - Call BEFORE `rad.Solve()`
406
+
407
+ **Example:**
408
+ ```python
409
+ # For difficult nonlinear problems, use under-relaxation
410
+ rad.SetRelaxParam(0.3) # 30% damping
411
+ rad.Solve(container, 0.001, 100, 1)
412
+
413
+ # Reset to full step for normal cases
414
+ rad.SetRelaxParam(0.0)
415
+ ```
416
+
310
417
  ### BiCGSTAB Performance
311
418
 
312
419
  Typical solve times (nonlinear BH curve material):
@@ -33,7 +33,7 @@ CHI = MU_R - 1 # For reference/calculations only
33
33
  # =============================================================================
34
34
  # External Field
35
35
  # =============================================================================
36
- H_EXT = 50000.0 # External H field magnitude [A/m]
36
+ H_EXT = 200000.0 # External H field magnitude [A/m] (same as nonlinear benchmark)
37
37
  B_EXT = MU_0 * H_EXT # External B field magnitude [T]
38
38
 
39
39
  # =============================================================================
@@ -49,8 +49,8 @@ MAX_ITERATIONS = 1000 # Maximum iterations
49
49
  # Tetrahedron: maxh = CUBE_SIZE / n_div (approximate correspondence)
50
50
 
51
51
  # Standard mesh sizes for comparison
52
- STANDARD_N_DIVS = [3, 4, 5, 6, 7, 8]
53
- STANDARD_MAXH = [0.5, 0.4, 0.35, 0.3, 0.25, 0.2]
52
+ STANDARD_N_DIVS = [5, 10, 15]
53
+ STANDARD_MAXH = [0.35, 0.25, 0.2, 0.15, 0.1]
54
54
 
55
55
  def n_div_to_maxh(n_div):
56
56
  """Convert hexahedron n_div to approximate tetrahedral maxh."""
@@ -0,0 +1,229 @@
1
+ # Nonlinear Material Benchmark
2
+
3
+ Benchmark for Radia's nonlinear solver with saturable BH curves using MSC (Magnetic Surface Charge) method.
4
+
5
+ ## Problem Setup
6
+
7
+ - **Geometry**: 1.0 m x 1.0 m x 1.0 m cube centered at origin
8
+ - **Material**: Nonlinear BH curve (soft iron with saturation)
9
+ - **External Field**: Hz = 200,000 A/m (uniform field along z-axis)
10
+
11
+ ## Element Types
12
+
13
+ | Element | Mesh Tool | DOF | Benchmark Script |
14
+ |---------|-----------|-----|------------------|
15
+ | Hexahedron MSC | ObjPolyhdr | 6 (sigma per face) | `benchmark_hexahedron_msc.py` |
16
+ | Tetrahedron MSC | Netgen | 3 (Mx, My, Mz) | `benchmark_tetrahedron_msc_netgen.py` |
17
+
18
+ ## BH Curve (Soft Iron)
19
+
20
+ ```
21
+ H [A/m] B [T]
22
+ 0 0.0
23
+ 100 0.1
24
+ 200 0.3
25
+ 500 0.8
26
+ 1000 1.2
27
+ 2000 1.5
28
+ 5000 1.7
29
+ 10000 1.8
30
+ 50000 2.0
31
+ 100000 2.1
32
+ ```
33
+
34
+ - Initial permeability: mu_r ~= 800
35
+ - Saturation onset: B ~= 1.5 T
36
+
37
+ ---
38
+
39
+ ## Hexahedron MSC Benchmarks
40
+
41
+ Results stored in `hexahedron_msc/{lu,bicgstab,hacapk}/` directories.
42
+
43
+ ### Solver Methods
44
+
45
+ | Method | Description | Complexity |
46
+ |--------|-------------|------------|
47
+ | LU | Dense LU decomposition | O(N^3) |
48
+ | BiCGSTAB | Iterative BiCGSTAB | O(N^2) per iteration |
49
+ | HACApK | H-matrix accelerated BiCGSTAB | O(N log N) per iteration |
50
+
51
+ ### LU Solver Results (H_ext = 200,000 A/m)
52
+
53
+ | N | Elements | DOF | Time (s) | Iter | M_avg_z (A/m) | Memory (MB) |
54
+ |---|----------|-----|----------|------|---------------|-------------|
55
+ | 5 | 125 | 750 | 0.30 | 6 | 702,132 | 49 |
56
+ | 10 | 1,000 | 6,000 | 46.26 | 13 | 716,281 | 1,174 |
57
+
58
+ **Note**: LU solver for N >= 15 is prohibitively slow (O(N^6) time complexity for nonlinear).
59
+
60
+ ### BiCGSTAB Solver Results (H_ext = 200,000 A/m)
61
+
62
+ | N | Elements | DOF | Time (s) | Iter | M_avg_z (A/m) | Memory (MB) |
63
+ |---|----------|-----|----------|------|---------------|-------------|
64
+ | 5 | 125 | 750 | 0.24 | 3 | 702,114 | 35 |
65
+ | 10 | 1,000 | 6,000 | 17.95 | 5 | 716,316 | 344 |
66
+
67
+ **Note**: BiCGSTAB uses previous solution as initial guess (ELF-compatible). Iteration count matches ELF.
68
+
69
+ ### HACApK Solver Results (H_ext = 200,000 A/m, eps = 1e-4)
70
+
71
+ | N | Elements | DOF | Time (s) | Iter | M_avg_z (A/m) | Memory (MB) | Compression | Leaves |
72
+ |---|----------|-----|----------|------|---------------|-------------|-------------|--------|
73
+ | 5 | 125 | 750 | 0.14 | 4 | 701,757 | 35 | 0.0219 | 184 |
74
+ | 10 | 1,000 | 6,000 | 6.09 | 5 | 715,885 | 208 | 0.0059 | 3,190 |
75
+
76
+ ### Speedup: HACApK vs LU
77
+
78
+ | N | LU Time (s) | HACApK Time (s) | Speedup |
79
+ |---|-------------|-----------------|---------|
80
+ | 5 | 0.30 | 0.14 | 2.1x |
81
+ | 10 | 46.26 | 6.09 | 7.6x |
82
+ | 15 | (est. hours) | - | >>10x |
83
+ | 20 | (impractical) | - | >>100x |
84
+
85
+ ---
86
+
87
+ ## Radia vs ELF/MAGIC Comparison
88
+
89
+ Comparison of Radia and ELF/MAGIC solvers on identical hexahedral mesh (1.0m cube, H_ext = 200,000 A/m).
90
+
91
+ ### Magnetization Accuracy (M_avg_z)
92
+
93
+ | N | Elements | Radia LU | ELF LU | Radia HACApK | ELF HACApK | Difference (%) |
94
+ |---|----------|----------|--------|--------------|------------|----------------|
95
+ | 5 | 125 | 702,132 | 702,132 | 701,757 | 702,110 | < 0.1% |
96
+ | 10 | 1,000 | 716,281 | 716,281 | 715,885 | 716,360 | < 0.1% |
97
+
98
+ **Note**: LU results match exactly. Small differences in HACApK are due to H-matrix compression (eps=1e-4).
99
+
100
+ ### Performance Comparison
101
+
102
+ #### LU Solver
103
+
104
+ | N | Elements | Radia Time (s) | ELF Time (s) | Radia Iter | ELF Iter |
105
+ |---|----------|----------------|--------------|------------|----------|
106
+ | 5 | 125 | 0.30 | 0.13 | 6 | 6 |
107
+ | 10 | 1,000 | 46.26 | 13.42 | 13 | 13 |
108
+
109
+ **Note**: ELF LU is faster due to Fortran LAPACK optimizations. Iteration counts match exactly.
110
+
111
+ #### BiCGSTAB Solver
112
+
113
+ | N | Elements | Radia Time (s) | ELF Time (s) | Radia Iter | ELF Iter |
114
+ |---|----------|----------------|--------------|------------|----------|
115
+ | 5 | 125 | 0.24 | 0.08 | 3 | 3 |
116
+ | 10 | 1,000 | 17.95 | 4.93 | 5 | 4 |
117
+
118
+ **Note**: Radia BiCGSTAB now uses previous solution as initial guess, matching ELF's convergence behavior.
119
+
120
+ #### HACApK Solver (eps = 1e-4)
121
+
122
+ | N | Elements | Radia Time (s) | ELF Time (s) | Radia Iter | ELF Iter |
123
+ |---|----------|----------------|--------------|------------|----------|
124
+ | 5 | 125 | 0.14 | 0.15 | 4 | 3 |
125
+ | 10 | 1,000 | 6.09 | 4.07 | 5 | 4 |
126
+
127
+ **Note**: HACApK performance is comparable. Both use ACA+ compression with identical parameters.
128
+
129
+ ### Key Observations
130
+
131
+ 1. **Accuracy**: LU results are identical between Radia and ELF (M_avg_z matches to all digits)
132
+ 2. **Convergence**: Both use similar Newton-Raphson (mucal2) convergence with B-field criterion
133
+ 3. **HACApK**: Performance is comparable; Radia's H-matrix overhead is similar to ELF
134
+ 4. **BiCGSTAB**: Iteration counts now match ELF (3-5 iterations vs previous 50)
135
+
136
+ ---
137
+
138
+ ## Tetrahedron MSC Benchmarks
139
+
140
+ Results stored in `tetrahedron_msc/{lu,bicgstab,hacapk}/` directories.
141
+
142
+ ### HACApK Solver Results (H_ext = 200,000 A/m, eps = 1e-4)
143
+
144
+ | maxh | Elements | DOF | Time (s) | Iter | M_avg_z (A/m) | Memory (MB) | Leaves |
145
+ |------|----------|-----|----------|------|---------------|-------------|--------|
146
+ | 0.40 | 104 | 312 | 0.29 | 2 | 588,673 | 66 | 172 |
147
+ | 0.20 | 627 | 1,881 | 12.50 | 2 | 568,973 | 134 | 2,098 |
148
+ | 0.15 | 2,211 | 6,633 | 135.86 | 2 | 587,299 | 775 | 14,386 |
149
+ | 0.10 | 4,994 | 14,982 | 757.65 | 2 | 570,603 | 3,298 | 36,475 |
150
+
151
+ **Note**: 3DOF tetrahedra require pre-computed dense InteractMatrix (O(N^2) memory), which is the performance bottleneck. H-matrix is used for MatVec acceleration only.
152
+
153
+ ---
154
+
155
+ ## Convergence Criterion
156
+
157
+ Radia v1.3.15+ uses **B-field based convergence** (mucal2) for nonlinear materials, matching the Newton-Raphson method used in industry-standard solvers.
158
+
159
+ ### Convergence Methods
160
+
161
+ | Method | Stage | Formula | Description |
162
+ |--------|-------|---------|-------------|
163
+ | **mucal0** | Initialization | `chi = B[1] / (mu_0 * H[1]) - 1` | Initial chi from 2nd BH point |
164
+ | mucal1 | Iteration | `rel_change = |chi_new - chi_old| / chi_old` | Legacy relaxation method |
165
+ | **mucal2** | Iteration | `rel_change = |B_new - B_old| / B_sat` | Newton-Raphson, fast convergence |
166
+
167
+ ---
168
+
169
+ ## Solver Methods
170
+
171
+ Radia provides three solver methods:
172
+
173
+ | Method | Name | Description | Recommendation |
174
+ |--------|------|-------------|----------------|
175
+ | 0 | LU | Direct LU decomposition | Small problems (N < 12) |
176
+ | 1 | BiCGSTAB | Iterative solver | Medium problems |
177
+ | 2 | HACApK | H-matrix accelerated BiCGSTAB | Large problems (N >= 10) |
178
+
179
+ ```python
180
+ # Method 0: Direct LU solver
181
+ res = rad.Solve(grp, 0.001, 1000, 0)
182
+
183
+ # Method 1: BiCGSTAB iterative solver
184
+ res = rad.Solve(grp, 0.001, 1000, 1)
185
+
186
+ # Method 2: HACApK (H-matrix + BiCGSTAB)
187
+ rad.SetHACApKParams(1e-4, 10, 2.0) # eps, leaf_size, eta
188
+ res = rad.Solve(grp, 0.001, 1000, 2)
189
+ ```
190
+
191
+ ### When to Use Each Method
192
+
193
+ | Problem Size | Recommended Solver | Rationale |
194
+ |--------------|-------------------|-----------|
195
+ | N < 10 (< 1,000 elem) | LU (Method 0) | Fast, direct solution |
196
+ | 10 <= N < 15 | HACApK (Method 2) | 8x faster than LU |
197
+ | N >= 15 | HACApK (Method 2) | Only practical option |
198
+
199
+ ---
200
+
201
+ ## Usage
202
+
203
+ ### Hexahedron Benchmarks
204
+
205
+ ```bash
206
+ # Run specific solver
207
+ python benchmark_hexahedron_msc.py --lu 5 10
208
+ python benchmark_hexahedron_msc.py --bicgstab 5 10
209
+ python benchmark_hexahedron_msc.py --hacapk 5 10 15 20
210
+
211
+ # Custom ACA tolerance
212
+ python benchmark_hexahedron_msc.py --hacapk --eps 1e-3 5 10
213
+ ```
214
+
215
+ ### Tetrahedron Benchmarks
216
+
217
+ ```bash
218
+ # Run specific solver
219
+ python benchmark_tetrahedron_msc_netgen.py --lu 0.4 0.2
220
+ python benchmark_tetrahedron_msc_netgen.py --hacapk 0.4 0.2 0.15 0.10
221
+
222
+ # Custom ACA tolerance
223
+ python benchmark_tetrahedron_msc_netgen.py --hacapk --eps 1e-3 0.15
224
+ ```
225
+
226
+ ---
227
+
228
+ **Last Updated**: 2025-12-24
229
+ **Author**: Claude Code
@@ -25,7 +25,7 @@ CUBE_HALF = 0.5 # Half of cube edge [m]
25
25
  # =============================================================================
26
26
  # External Field
27
27
  # =============================================================================
28
- H_EXT = 50000.0 # External H field magnitude [A/m]
28
+ H_EXT = 200000.0 # External H field magnitude [A/m] (unified with linear benchmark)
29
29
  B_EXT = MU_0 * H_EXT # External B field magnitude [T]
30
30
 
31
31
  # =============================================================================
@@ -17,11 +17,13 @@ Solver types:
17
17
  Usage:
18
18
  python benchmark_hexahedron_msc.py --lu 5 10 15 20
19
19
  python benchmark_hexahedron_msc.py --bicgstab 5 10 15 20
20
- python benchmark_hexahedron_msc.py 5 10 15 20 # runs both
20
+ python benchmark_hexahedron_msc.py --hacapk 5 10 15 20
21
+ python benchmark_hexahedron_msc.py --hacapk --eps 1e-3 5 10 15 # custom ACA tolerance
21
22
 
22
23
  Examples:
23
24
  python benchmark_hexahedron_msc.py --lu 5 10 15 20
24
25
  python benchmark_hexahedron_msc.py --bicgstab 5 10 15 20
26
+ python benchmark_hexahedron_msc.py --hacapk --eps 1e-4 5 10 15 # ELF-compatible default
25
27
  """
26
28
 
27
29
  import sys
@@ -132,14 +134,18 @@ def create_hexahedron_msc_mesh(n_div):
132
134
  return elements
133
135
 
134
136
 
135
- def benchmark_hexahedron_msc(n_div, solver_method=1, use_hmatrix=False):
137
+ def benchmark_hexahedron_msc(n_div, solver_method=1, use_hmatrix=False,
138
+ hmat_eps=1e-4, leaf_size=10, eta=2.0, relax=0.0):
136
139
  """
137
140
  Benchmark hexahedral MSC mesh with specified solver.
138
141
 
139
142
  Args:
140
143
  n_div: Number of divisions per edge
141
- solver_method: 0=LU, 1=BiCGSTAB
144
+ solver_method: 0=LU, 1=BiCGSTAB, 2=HACApK
142
145
  use_hmatrix: Enable H-matrix acceleration (BiCGSTAB only)
146
+ hmat_eps: ACA tolerance for HACApK solver (default: 1e-4)
147
+ leaf_size: Minimum cluster size in elements (default: 10)
148
+ eta: Admissibility parameter (default: 2.0)
143
149
  """
144
150
  rad.FldUnits('m')
145
151
  rad.UtiDelAll()
@@ -147,6 +153,8 @@ def benchmark_hexahedron_msc(n_div, solver_method=1, use_hmatrix=False):
147
153
  # Determine solver name
148
154
  if solver_method == 0:
149
155
  solver_name = 'lu'
156
+ elif solver_method == 2:
157
+ solver_name = 'hacapk'
150
158
  elif use_hmatrix:
151
159
  solver_name = 'bicgstab_hmatrix'
152
160
  else:
@@ -175,30 +183,52 @@ def benchmark_hexahedron_msc(n_div, solver_method=1, use_hmatrix=False):
175
183
  ext = rad.ObjBckg([0, 0, B_ext])
176
184
  grp = rad.ObjCnt([container, ext])
177
185
 
178
- # Configure H-matrix if requested
179
- # Note: H-matrix API may vary - check if available
186
+ # Configure H-matrix if using HACApK (method=2)
180
187
  hmatrix_enabled = False
181
- if use_hmatrix and solver_method == 1:
188
+ hmatrix_stats = None
189
+ if solver_method == 2:
182
190
  try:
183
- rad.SolverHMatrixEnable()
191
+ # Set HACApK parameters
192
+ # eps: ACA tolerance (lower = more accurate, higher = faster)
193
+ # leaf_size: minimum cluster size in elements
194
+ # eta: admissibility parameter
195
+ rad.SetHACApKParams(hmat_eps, leaf_size, eta)
184
196
  hmatrix_enabled = True
185
- print('H-matrix: Enabled')
197
+ print(f'H-matrix: Enabled (eps={hmat_eps:.0e}, leaf_size={leaf_size}, eta={eta})')
186
198
  except AttributeError:
187
199
  print('H-matrix: Not available (API not found)')
188
200
 
201
+ # Tolerance parameters (all explicitly set for reproducibility)
202
+ NONL_TOL = 0.001 # Nonlinear iteration tolerance (outer loop)
203
+ BICG_TOL = 1e-4 # BiCGSTAB inner loop tolerance (Method 1 and 2)
204
+ MAX_ITER = 100 # Maximum nonlinear iterations
205
+
206
+ # Set BiCGSTAB inner loop tolerance explicitly (ELF-compatible default)
207
+ rad.SetBiCGSTABTol(BICG_TOL)
208
+
209
+ # Set under-relaxation coefficient (0.0 = full step, 0.0-1.0 = under-relaxation)
210
+ rad.SetRelaxParam(relax)
211
+
189
212
  # Solve
190
- print(f'Solving...')
213
+ relax_str = f', relax={relax:.1f}' if relax > 0 else ''
214
+ print(f'Solving... (nonl_tol={NONL_TOL:.0e}, bicg_tol={BICG_TOL:.0e}{relax_str})')
191
215
  t_solve_start = time.time()
192
- result = rad.Solve(grp, 0.001, 1000, solver_method)
216
+ result = rad.Solve(grp, NONL_TOL, MAX_ITER, solver_method)
193
217
  t_solve = time.time() - t_solve_start
194
218
 
195
219
  # Measure peak memory after solve
196
220
  peak_memory_mb = get_peak_memory_mb()
197
221
 
198
- # Disable H-matrix after solve
222
+ # Get H-matrix statistics if HACApK was used
199
223
  if hmatrix_enabled:
200
224
  try:
201
- rad.SolverHMatrixDisable()
225
+ hmatrix_stats = rad.GetHACApKStats()
226
+ if hmatrix_stats:
227
+ print(f'H-matrix stats:')
228
+ print(f' Leaf nodes: {hmatrix_stats["n_leaves"]} (low-rank: {hmatrix_stats["n_lowrank"]}, dense: {hmatrix_stats["n_dense"]})')
229
+ print(f' Max rank: {hmatrix_stats["max_rank"]}')
230
+ print(f' Compression: {hmatrix_stats["compression"]:.4f}')
231
+ print(f' Build time: {hmatrix_stats["build_time"]:.4f} s')
202
232
  except AttributeError:
203
233
  pass
204
234
 
@@ -208,7 +238,7 @@ def benchmark_hexahedron_msc(n_div, solver_method=1, use_hmatrix=False):
208
238
  M_avg_z = np.mean([m[2] for m in M_list]) if M_list else 0.0
209
239
 
210
240
  n_iter = int(result[3]) if result[3] else 0
211
- converged = n_iter < 1000
241
+ converged = n_iter < MAX_ITER
212
242
  residual = result[0] if result[0] else 0.0
213
243
 
214
244
  print(f'Time: {t_solve:.3f} s')
@@ -230,6 +260,13 @@ def benchmark_hexahedron_msc(n_div, solver_method=1, use_hmatrix=False):
230
260
  't_solve': t_solve,
231
261
  'solver_method': solver_method,
232
262
  'solver_name': solver_name,
263
+ # Tolerance parameters (all explicitly recorded)
264
+ 'nonl_tol': NONL_TOL, # Nonlinear iteration tolerance (outer loop)
265
+ 'bicg_tol': BICG_TOL, # BiCGSTAB inner loop tolerance
266
+ # H-matrix parameters (Method 2 only)
267
+ 'hmat_eps': hmat_eps if solver_method == 2 else None,
268
+ 'leaf_size': leaf_size if solver_method == 2 else None,
269
+ 'eta': eta if solver_method == 2 else None,
233
270
  'hmatrix_enabled': hmatrix_enabled,
234
271
  'converged': converged,
235
272
  'residual': residual,
@@ -238,6 +275,23 @@ def benchmark_hexahedron_msc(n_div, solver_method=1, use_hmatrix=False):
238
275
  }
239
276
  if peak_memory_mb is not None:
240
277
  result_data['peak_memory_mb'] = peak_memory_mb
278
+ if hmatrix_stats is not None:
279
+ hmatrix_data = {
280
+ 'n_lowrank': hmatrix_stats['n_lowrank'],
281
+ 'n_dense': hmatrix_stats['n_dense'],
282
+ 'max_rank': hmatrix_stats['max_rank'],
283
+ 'compression_ratio': hmatrix_stats['compression'],
284
+ 'build_time': hmatrix_stats['build_time'],
285
+ 'nlf': hmatrix_stats['n_leaves'],
286
+ }
287
+ # Add timing statistics if available (v1.3.16+)
288
+ if 't_hmatrix_build' in hmatrix_stats:
289
+ hmatrix_data['t_hmatrix_build'] = hmatrix_stats['t_hmatrix_build']
290
+ if 't_linear_solve' in hmatrix_stats:
291
+ hmatrix_data['t_linear_solve'] = hmatrix_stats['t_linear_solve']
292
+ if 'linear_iterations' in hmatrix_stats:
293
+ hmatrix_data['linear_iterations'] = hmatrix_stats['linear_iterations']
294
+ result_data['hmatrix'] = hmatrix_data
241
295
  return result_data
242
296
 
243
297
 
@@ -245,15 +299,25 @@ def main():
245
299
  parser = argparse.ArgumentParser(description='Hexahedron MSC benchmark (Radia)')
246
300
  parser.add_argument('--lu', action='store_true', help='Run LU solver benchmark')
247
301
  parser.add_argument('--bicgstab', action='store_true', help='Run BiCGSTAB solver benchmark')
302
+ parser.add_argument('--hacapk', action='store_true', help='Run HACApK (H-matrix) solver benchmark')
303
+ parser.add_argument('--eps', type=float, default=1e-4,
304
+ help='ACA tolerance for HACApK (default: 1e-4, lower=accurate, higher=fast)')
305
+ parser.add_argument('--leaf_size', type=int, default=10,
306
+ help='Minimum cluster size in elements (default: 10)')
307
+ parser.add_argument('--eta', type=float, default=2.0,
308
+ help='Admissibility parameter (default: 2.0)')
309
+ parser.add_argument('--relax', type=float, default=0.0,
310
+ help='Under-relaxation coefficient (default: 0.0 = full step, 0.0-1.0)')
248
311
  parser.add_argument('sizes', nargs='*', type=int, default=[5, 10, 15, 20],
249
312
  help='Mesh sizes (N values, default: 5 10 15 20)')
250
313
 
251
314
  args = parser.parse_args()
252
315
 
253
- # Default to both if none specified
254
- any_solver = args.lu or args.bicgstab
316
+ # Default to LU only if none specified
317
+ any_solver = args.lu or args.bicgstab or args.hacapk
255
318
  run_lu = args.lu or not any_solver
256
- run_bicgstab = args.bicgstab or not any_solver
319
+ run_bicgstab = args.bicgstab
320
+ run_hacapk = args.hacapk
257
321
 
258
322
  script_dir = os.path.dirname(os.path.abspath(__file__))
259
323
 
@@ -263,10 +327,15 @@ def main():
263
327
  print('Cube size: 1.0 m')
264
328
  print('H_ext: %.0f A/m' % H_EXT)
265
329
  print('N values: %s' % args.sizes)
330
+ if args.relax > 0:
331
+ print('Relaxation: %.2f (under-relaxation)' % args.relax)
332
+ if run_hacapk:
333
+ print('HACApK: eps=%.0e, leaf_size=%d, eta=%.1f' % (args.eps, args.leaf_size, args.eta))
266
334
  print()
267
335
 
268
336
  results_lu = []
269
337
  results_bicgstab = []
338
+ results_hacapk = []
270
339
 
271
340
  for n in args.sizes:
272
341
  # LU Benchmark
@@ -278,7 +347,7 @@ def main():
278
347
  output_dir = os.path.join(script_dir, 'hexahedron_msc', 'lu')
279
348
  os.makedirs(output_dir, exist_ok=True)
280
349
 
281
- result = benchmark_hexahedron_msc(n, solver_method=0, use_hmatrix=False)
350
+ result = benchmark_hexahedron_msc(n, solver_method=0, use_hmatrix=False, relax=args.relax)
282
351
  results_lu.append(result)
283
352
 
284
353
  filename = 'msc_N%d_results.json' % n
@@ -296,7 +365,7 @@ def main():
296
365
  output_dir = os.path.join(script_dir, 'hexahedron_msc', 'bicgstab')
297
366
  os.makedirs(output_dir, exist_ok=True)
298
367
 
299
- result = benchmark_hexahedron_msc(n, solver_method=1, use_hmatrix=False)
368
+ result = benchmark_hexahedron_msc(n, solver_method=1, use_hmatrix=False, relax=args.relax)
300
369
  results_bicgstab.append(result)
301
370
 
302
371
  filename = 'msc_N%d_results.json' % n
@@ -305,6 +374,25 @@ def main():
305
374
  json.dump(result, f, indent=2)
306
375
  print('Saved: %s\n' % filepath)
307
376
 
377
+ # HACApK Benchmark (Method 2)
378
+ if run_hacapk:
379
+ print('\n' + '=' * 70)
380
+ print('HACApK SOLVER: N=%d (eps=%.0e, leaf=%d, eta=%.1f)' % (n, args.eps, args.leaf_size, args.eta))
381
+ print('=' * 70 + '\n')
382
+
383
+ output_dir = os.path.join(script_dir, 'hexahedron_msc', 'hacapk')
384
+ os.makedirs(output_dir, exist_ok=True)
385
+
386
+ result = benchmark_hexahedron_msc(n, solver_method=2, use_hmatrix=False,
387
+ hmat_eps=args.eps, leaf_size=args.leaf_size, eta=args.eta, relax=args.relax)
388
+ results_hacapk.append(result)
389
+
390
+ filename = 'msc_N%d_results.json' % n
391
+ filepath = os.path.join(output_dir, filename)
392
+ with open(filepath, 'w') as f:
393
+ json.dump(result, f, indent=2)
394
+ print('Saved: %s\n' % filepath)
395
+
308
396
  # Summary
309
397
  print('\n' + '=' * 70)
310
398
  print('SUMMARY')
@@ -330,6 +418,21 @@ def main():
330
418
  r['nonl_iterations'], r['M_avg_z'],
331
419
  'Yes' if r['converged'] else 'No'))
332
420
 
421
+ if results_hacapk:
422
+ print('\nHACApK Solver (hexahedron_msc/hacapk/):\n')
423
+ print('%-6s %8s %10s %8s %10s %10s %8s %10s' % (
424
+ 'N', 'Elements', 'Time (s)', 'Nonl It', 'M_avg_z', 'Compress', 'Leaves', 'Conv'))
425
+ print('-' * 85)
426
+ for r in results_hacapk:
427
+ hm = r.get('hmatrix', {})
428
+ compression = hm.get('compression_ratio', 0.0)
429
+ n_leaves = hm.get('nlf', 0)
430
+ print('%-6d %8d %10.3f %8d %10.0f %10.4f %8d %10s' % (
431
+ r['n_div'], r['n_elements'], r['t_solve'],
432
+ r['nonl_iterations'], r['M_avg_z'],
433
+ compression, n_leaves,
434
+ 'Yes' if r['converged'] else 'No'))
435
+
333
436
  print('=' * 70)
334
437
 
335
438