radia 1.0.9__tar.gz → 1.0.10__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 (139) hide show
  1. {radia-1.0.9/src/radia.egg-info → radia-1.0.10}/PKG-INFO +2 -2
  2. {radia-1.0.9 → radia-1.0.10}/README.md +1 -1
  3. {radia-1.0.9 → radia-1.0.10}/README_BUILD.md +1 -1
  4. {radia-1.0.9 → radia-1.0.10}/docs/API_REFERENCE.md +51 -20
  5. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/demo_field_types.py +1 -1
  6. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/export_radia_geometry.py +1 -1
  7. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/visualize_field.py +1 -1
  8. {radia-1.0.9 → radia-1.0.10}/examples/simple_problems/test_update_hmatrix_magnetization.py +3 -3
  9. radia-1.0.10/examples/solver_benchmarks/README.md +36 -0
  10. radia-1.0.10/examples/solver_benchmarks/benchmark_hmatrix_field.py +119 -0
  11. {radia-1.0.9 → radia-1.0.10}/pyproject.toml +1 -1
  12. {radia-1.0.9 → radia-1.0.10}/setup.py +1 -1
  13. radia-1.0.10/src/python/rad_ngsolve.pyd +0 -0
  14. {radia-1.0.9 → radia-1.0.10}/src/python/radia.pyd +0 -0
  15. {radia-1.0.9 → radia-1.0.10/src/radia.egg-info}/PKG-INFO +2 -2
  16. {radia-1.0.9 → radia-1.0.10}/src/radia.egg-info/SOURCES.txt +7 -5
  17. {radia-1.0.9 → radia-1.0.10}/tests/test_advanced.py +2 -2
  18. radia-1.0.10/tests/test_group_operations.py +238 -0
  19. {radia-1.0.9 → radia-1.0.10}/tests/test_magpylib_comparison.py +3 -2
  20. radia-1.0.10/tests/test_materials.py +167 -0
  21. {radia-1.0.9 → radia-1.0.10}/tests/test_parallel_performance.py +19 -11
  22. {radia-1.0.9 → radia-1.0.10}/tests/test_rad_ngsolve.py +2 -2
  23. radia-1.0.10/tests/test_radia.py +226 -0
  24. radia-1.0.10/tests/test_serialization.py +264 -0
  25. {radia-1.0.9 → radia-1.0.10}/tests/test_square_coil_analytical.py +3 -2
  26. radia-1.0.10/tests/test_transformations.py +301 -0
  27. radia-1.0.10/tests/test_type_cast.py +143 -0
  28. radia-1.0.9/docs/HACAPK_INTEGRATION_COMPLETE.md +0 -416
  29. radia-1.0.9/docs/HACAPK_INTEGRATION_PLAN.md +0 -355
  30. radia-1.0.9/docs/NGSOLVE_INTEGRATION_STATUS.md +0 -210
  31. radia-1.0.9/docs/PHASE4_STATUS.md +0 -248
  32. radia-1.0.9/docs/hmatrix_field_progress.md +0 -459
  33. radia-1.0.9/src/python/rad_ngsolve.pyd +0 -0
  34. radia-1.0.9/tests/test_radia.py +0 -261
  35. {radia-1.0.9 → radia-1.0.10}/COPYRIGHT.txt +0 -0
  36. {radia-1.0.9 → radia-1.0.10}/LICENSE +0 -0
  37. {radia-1.0.9 → radia-1.0.10}/MANIFEST.in +0 -0
  38. {radia-1.0.9 → radia-1.0.10}/docs/API_EXTENSIONS.md +0 -0
  39. {radia-1.0.9 → radia-1.0.10}/docs/CF_BACKGROUND_FIELD_IMPLEMENTATION.md +0 -0
  40. {radia-1.0.9 → radia-1.0.10}/docs/CLAUDE.md +0 -0
  41. {radia-1.0.9 → radia-1.0.10}/docs/DIRECTORY_STRUCTURE.md +0 -0
  42. {radia-1.0.9 → radia-1.0.10}/docs/NGSOLVE_CF_BACKGROUND_FIELD_DESIGN.md +0 -0
  43. {radia-1.0.9 → radia-1.0.10}/docs/NGSOLVE_DLL_ISSUE.md +0 -0
  44. {radia-1.0.9 → radia-1.0.10}/docs/NGSOLVE_INTEGRATION.md +0 -0
  45. {radia-1.0.9 → radia-1.0.10}/docs/NGSOLVE_PYTHON_SOLUTION.md +0 -0
  46. {radia-1.0.9 → radia-1.0.10}/docs/NGSOLVE_USAGE_GUIDE.md +0 -0
  47. {radia-1.0.9 → radia-1.0.10}/docs/OPENMP_PERFORMANCE_REPORT.md +0 -0
  48. {radia-1.0.9 → radia-1.0.10}/docs/TAB_CONVERSION_REPORT.md +0 -0
  49. {radia-1.0.9 → radia-1.0.10}/docs/hmatrix_field_design.md +0 -0
  50. {radia-1.0.9 → radia-1.0.10}/docs/scripts/README.md +0 -0
  51. {radia-1.0.9 → radia-1.0.10}/examples/H-matrix/BENCHMARK_RESULTS.md +0 -0
  52. {radia-1.0.9 → radia-1.0.10}/examples/H-matrix/HMATRIX_FIELD_DESIGN.md +0 -0
  53. {radia-1.0.9 → radia-1.0.10}/examples/H-matrix/HMATRIX_FIELD_DESIGN_SIMPLIFIED.md +0 -0
  54. {radia-1.0.9 → radia-1.0.10}/examples/H-matrix/README.md +0 -0
  55. {radia-1.0.9 → radia-1.0.10}/examples/H-matrix/benchmark_field_evaluation.py +0 -0
  56. {radia-1.0.9 → radia-1.0.10}/examples/H-matrix/benchmark_parallel_construction.py +0 -0
  57. {radia-1.0.9 → radia-1.0.10}/examples/H-matrix/benchmark_solver.py +0 -0
  58. {radia-1.0.9 → radia-1.0.10}/examples/H-matrix/plot_benchmark_results.py +0 -0
  59. {radia-1.0.9 → radia-1.0.10}/examples/H-matrix/run_all_benchmarks.py +0 -0
  60. {radia-1.0.9 → radia-1.0.10}/examples/H-matrix/verify_field_accuracy.py +0 -0
  61. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/HMATRIX_ANALYSIS.md +0 -0
  62. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/HMATRIX_FIELD_EVALUATION_ISSUE.md +0 -0
  63. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/H_MATRIX_PARALLEL_OPTIMIZATION.md +0 -0
  64. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/NGBEM_ANALYSIS.md +0 -0
  65. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/NGSOLVE_SET_VS_INTERPOLATE.md +0 -0
  66. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/PROPOSAL_VECTORIZED_API.md +0 -0
  67. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/README.md +0 -0
  68. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/SET_VS_INTERPOLATE_SIMPLE.md +0 -0
  69. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/SOLVER_OPTIMIZATION_PROPOSAL.md +0 -0
  70. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/benchmark_gridfunction_set.py +0 -0
  71. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/debug_field_values.py +0 -0
  72. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/rad.ObjBckgCF/Cubit2Nastran.py +0 -0
  73. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/rad.ObjBckgCF/README.md +0 -0
  74. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/rad.ObjBckgCF/sphere_nastran_analysis.py +0 -0
  75. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/rad.ObjBckgCF/sphere_nastran_field_mu.pvsm +0 -0
  76. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/rad.ObjBckgCF/sphere_nastran_geometry.vtk +0 -0
  77. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/radia_field.pvsm +0 -0
  78. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/test_batch_evaluation.py +0 -0
  79. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/test_batch_fld.py +0 -0
  80. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/test_coordinate_transform.py +0 -0
  81. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/test_mesh_convergence.py +0 -0
  82. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/test_set_vs_interpolate.py +0 -0
  83. {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/verify_curl_A_equals_B.py +0 -0
  84. {radia-1.0.9 → radia-1.0.10}/examples/complex_coil_geometry/README.md +0 -0
  85. {radia-1.0.9 → radia-1.0.10}/examples/complex_coil_geometry/coil_geometry.vtk +0 -0
  86. {radia-1.0.9 → radia-1.0.10}/examples/complex_coil_geometry/coil_model.py +0 -0
  87. {radia-1.0.9 → radia-1.0.10}/examples/complex_coil_geometry/complex_coil.pvsm +0 -0
  88. {radia-1.0.9 → radia-1.0.10}/examples/complex_coil_geometry/field_map.py +0 -0
  89. {radia-1.0.9 → radia-1.0.10}/examples/complex_coil_geometry/field_map.vtk +0 -0
  90. {radia-1.0.9 → radia-1.0.10}/examples/complex_coil_geometry/visualize_coils.py +0 -0
  91. {radia-1.0.9 → radia-1.0.10}/examples/electromagnet/README.md +0 -0
  92. {radia-1.0.9 → radia-1.0.10}/examples/electromagnet/electromagnet.pvsm +0 -0
  93. {radia-1.0.9 → radia-1.0.10}/examples/electromagnet/electromagnet.vtk +0 -0
  94. {radia-1.0.9 → radia-1.0.10}/examples/electromagnet/field_distribution.vtk +0 -0
  95. {radia-1.0.9 → radia-1.0.10}/examples/electromagnet/magnet.py +0 -0
  96. {radia-1.0.9 → radia-1.0.10}/examples/electromagnet/racetrack_coil_model.py +0 -0
  97. {radia-1.0.9 → radia-1.0.10}/examples/electromagnet/yoke_model.py +0 -0
  98. {radia-1.0.9 → radia-1.0.10}/examples/simple_problems/CONVERSION_NOTES.md +0 -0
  99. {radia-1.0.9 → radia-1.0.10}/examples/simple_problems/README.md +0 -0
  100. {radia-1.0.9 → radia-1.0.10}/examples/simple_problems/case0.py +0 -0
  101. {radia-1.0.9 → radia-1.0.10}/examples/simple_problems/case1.py +0 -0
  102. {radia-1.0.9 → radia-1.0.10}/examples/simple_problems/case2.py +0 -0
  103. {radia-1.0.9 → radia-1.0.10}/examples/simple_problems/case3.py +0 -0
  104. {radia-1.0.9 → radia-1.0.10}/examples/smco_magnet_array/README.md +0 -0
  105. {radia-1.0.9 → radia-1.0.10}/examples/smco_magnet_array/smbo.pvsm +0 -0
  106. {radia-1.0.9 → radia-1.0.10}/examples/smco_magnet_array/smco_array.py +0 -0
  107. {radia-1.0.9 → radia-1.0.10}/examples/smco_magnet_array/smco_array.vtk +0 -0
  108. {radia-1.0.9 → radia-1.0.10}/examples/smco_magnet_array/smco_field_distribution.vtk +0 -0
  109. {radia-1.0.9 → radia-1.0.10}/examples/solver_time_evaluation/README.md +0 -0
  110. {radia-1.0.9 → radia-1.0.10}/examples/solver_time_evaluation/benchmark_linear_material.py +0 -0
  111. {radia-1.0.9 → radia-1.0.10}/examples/solver_time_evaluation/benchmark_lu_vs_gs.py +0 -0
  112. {radia-1.0.9 → radia-1.0.10}/examples/solver_time_evaluation/benchmark_matrix_construction.py +0 -0
  113. {radia-1.0.9 → radia-1.0.10}/examples/solver_time_evaluation/benchmark_solver_scaling.py +0 -0
  114. {radia-1.0.9 → radia-1.0.10}/examples/test_hmatrix_accuracy.py +0 -0
  115. {radia-1.0.9 → radia-1.0.10}/examples/test_hmatrix_cube_nonlinear.py +0 -0
  116. {radia-1.0.9 → radia-1.0.10}/examples/test_hmatrix_parallel.py +0 -0
  117. {radia-1.0.9 → radia-1.0.10}/setup.cfg +0 -0
  118. {radia-1.0.9 → radia-1.0.10}/src/python/__init__.py +0 -0
  119. {radia-1.0.9 → radia-1.0.10}/src/python/nastran_reader.py +0 -0
  120. {radia-1.0.9 → radia-1.0.10}/src/python/radia_coil_builder.py +0 -0
  121. {radia-1.0.9 → radia-1.0.10}/src/python/radia_ngsolve_field.py +0 -0
  122. {radia-1.0.9 → radia-1.0.10}/src/python/radia_pyvista_viewer.py +0 -0
  123. {radia-1.0.9 → radia-1.0.10}/src/python/radia_vtk_export.py +0 -0
  124. {radia-1.0.9 → radia-1.0.10}/src/radia.egg-info/dependency_links.txt +0 -0
  125. {radia-1.0.9 → radia-1.0.10}/src/radia.egg-info/not-zip-safe +0 -0
  126. {radia-1.0.9 → radia-1.0.10}/src/radia.egg-info/requires.txt +0 -0
  127. {radia-1.0.9 → radia-1.0.10}/src/radia.egg-info/top_level.txt +0 -0
  128. {radia-1.0.9 → radia-1.0.10}/tests/README.md +0 -0
  129. {radia-1.0.9 → radia-1.0.10}/tests/__init__.py +0 -0
  130. {radia-1.0.9 → radia-1.0.10}/tests/benchmark_hmatrix.py +0 -0
  131. {radia-1.0.9 → radia-1.0.10}/tests/benchmarks/benchmark_correct.py +0 -0
  132. {radia-1.0.9 → radia-1.0.10}/tests/benchmarks/benchmark_heavy.py +0 -0
  133. {radia-1.0.9 → radia-1.0.10}/tests/benchmarks/benchmark_openmp.py +0 -0
  134. {radia-1.0.9 → radia-1.0.10}/tests/benchmarks/benchmark_threads.py +0 -0
  135. {radia-1.0.9 → radia-1.0.10}/tests/conftest.py +0 -0
  136. {radia-1.0.9 → radia-1.0.10}/tests/test_radhmat.py +0 -0
  137. {radia-1.0.9 → radia-1.0.10}/tests/test_simple.py +0 -0
  138. {radia-1.0.9 → radia-1.0.10}/tests/test_utils.py +0 -0
  139. {radia-1.0.9 → radia-1.0.10}/tests/test_vector_potential.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: radia
3
- Version: 1.0.9
3
+ Version: 1.0.10
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
@@ -150,7 +150,7 @@ import rad_ngsolve
150
150
 
151
151
  # Create Radia magnet
152
152
  magnet = rad.ObjRecMag([0,0,0], [20,20,20], [0,0,1.2]) # mm units
153
- rad.MatApl(magnet, rad.MatStd('NdFeB', 1.2))
153
+ rad.MatApl(magnet, rad.MatLin([0.06, 0.17], [0,0,1.2])) # NdFeB
154
154
  rad.Solve(magnet, 0.0001, 10000)
155
155
 
156
156
  # Create NGSolve CoefficientFunction for different field types
@@ -110,7 +110,7 @@ import rad_ngsolve
110
110
 
111
111
  # Create Radia magnet
112
112
  magnet = rad.ObjRecMag([0,0,0], [20,20,20], [0,0,1.2]) # mm units
113
- rad.MatApl(magnet, rad.MatStd('NdFeB', 1.2))
113
+ rad.MatApl(magnet, rad.MatLin([0.06, 0.17], [0,0,1.2])) # NdFeB
114
114
  rad.Solve(magnet, 0.0001, 10000)
115
115
 
116
116
  # Create NGSolve CoefficientFunction for different field types
@@ -349,7 +349,7 @@ import radia as rad
349
349
 
350
350
  # Create Radia magnet
351
351
  magnet = rad.ObjRecMag([0, 0, 0], [20, 20, 30], [0, 0, 1.2])
352
- rad.MatApl(magnet, rad.MatStd('NdFeB', 1.2))
352
+ rad.MatApl(magnet, rad.MatLin([0.06, 0.17], [0, 0, 1.2])) # NdFeB
353
353
  rad.Solve(magnet, 0.0001, 10000)
354
354
 
355
355
  # Create field CoefficientFunctions
@@ -322,33 +322,64 @@ Creates nonlinear anisotropic material with separate parallel/perpendicular char
322
322
 
323
323
  ---
324
324
 
325
- ### MatStd
325
+ ### Common Material Configuration Examples
326
+
327
+ Below are typical material parameters for common magnetic materials. These can be created using `MatLin` for permanent magnets or `MatSatIsoFrm` for soft magnetic materials.
328
+
329
+ #### Permanent Magnet Materials (Linear Anisotropic)
330
+
331
+ **NdFeB** (Neodymium-Iron-Boron):
326
332
  ```python
327
- material = rad.MatStd(name, mr)
333
+ mat = rad.MatLin([0.06, 0.17], [0, 0, 1.2]) # ksi_par=0.06, ksi_perp=0.17, Mr=1.2 T
328
334
  ```
329
- Creates pre-defined magnetic material.
330
335
 
331
- **Parameters**:
332
- - `name`: Material identifier string
333
- - `'NdFeB'`: NdFeB permanent magnet (default mr = 1.2 T)
334
- - `'SmCo5'`: SmCo5 permanent magnet (default mr = 0.85 T)
335
- - `'Sm2Co17'`: Sm2Co17 permanent magnet (default mr = 1.05 T)
336
- - `'Ferrite'`: Ferrite permanent magnet (default mr = 0.35 T)
337
- - `'Xc06'`: Low carbon steel C<0.06%
338
- - `'Steel37'`: Steel C<0.13%
339
- - `'Steel42'`: Steel C<0.19%
340
- - `'AFK502'`: Vanadium Permendur (Fe:49%, Co:49%, V:2%)
341
- - `'AFK1'`: FeCo alloy (Fe:74.2%, Co:25%)
342
- - `mr`: Optional remanent magnetization magnitude (T)
336
+ **SmCo5** (Samarium-Cobalt):
337
+ ```python
338
+ mat = rad.MatLin([0.005, 0.04], [0, 0, 0.85]) # Mr=0.85 T
339
+ ```
343
340
 
344
- **Example**:
341
+ **Sm2Co17** (Samarium-Cobalt 2:17):
342
+ ```python
343
+ mat = rad.MatLin([0.005, 0.04], [0, 0, 1.05]) # Mr=1.05 T
344
+ ```
345
+
346
+ **Ferrite**:
347
+ ```python
348
+ mat = rad.MatLin([0.07, 0.2], [0, 0, 0.35]) # Mr=0.35 T
349
+ ```
350
+
351
+ #### Soft Magnetic Materials (Nonlinear Isotropic)
352
+
353
+ **Xc06** (Low Carbon Steel, C<0.06%):
345
354
  ```python
346
- # NdFeB with default 1.2 T remanence
347
- mat = rad.MatStd('NdFeB')
355
+ mat = rad.MatSatIsoFrm([2118., 1.362], [63.06, 0.2605], [17.138, 0.4917])
356
+ ```
357
+
358
+ **Steel37** (C<0.13%):
359
+ ```python
360
+ mat = rad.MatSatIsoFrm([1596.3, 1.1488], [133.11, 0.4268], [18.713, 0.4759])
361
+ ```
362
+
363
+ **Steel42** (C<0.19%):
364
+ ```python
365
+ mat = rad.MatSatIsoFrm([968.66, 1.441], [24.65, 0.2912], [8.3, 0.3316])
366
+ ```
348
367
 
349
- # Custom remanence
350
- mat = rad.MatStd('NdFeB', 1.4)
368
+ **AFK502** (Vanadium Permendur, Fe:49%, Co:49%, V:2%):
369
+ ```python
370
+ mat = rad.MatSatIsoFrm([10485., 1.788], [241.5, 0.437], [7.43, 0.115])
371
+ ```
372
+
373
+ **AFK1** (FeCo alloy, Fe:74.2%, Co:25%):
374
+ ```python
375
+ mat = rad.MatSatIsoFrm([2001., 1.704], [38.56, 0.493], [1.24, 0.152])
376
+ ```
377
+
378
+ **Note**: For soft magnetic materials, the magnetization is modeled as:
379
+ ```
380
+ M(H) = ms1*tanh(ksi1*H/ms1) + ms2*tanh(ksi2*H/ms2) + ms3*tanh(ksi3*H/ms3)
351
381
  ```
382
+ where H is the magnitude of magnetic field strength in Tesla.
352
383
 
353
384
  ---
354
385
 
@@ -40,7 +40,7 @@ magnet_center = [0, 0, 0]
40
40
  magnet_size = [20, 20, 30]
41
41
 
42
42
  magnet = rad.ObjRecMag(magnet_center, magnet_size, [0, 0, 1.2])
43
- rad.MatApl(magnet, rad.MatStd('NdFeB', 1.2))
43
+ rad.MatApl(magnet, rad.MatLin([0.06, 0.17], [0, 0, 1.2])) # NdFeB
44
44
  rad.Solve(magnet, 0.0001, 10000)
45
45
 
46
46
  print(f"Magnet created: object #{magnet}")
@@ -37,7 +37,7 @@ magnet_center = [0, 0, 0]
37
37
  magnet_size = [20, 20, 30]
38
38
 
39
39
  magnet = rad.ObjRecMag(magnet_center, magnet_size, [0, 0, 1.2])
40
- rad.MatApl(magnet, rad.MatStd('NdFeB', 1.2))
40
+ rad.MatApl(magnet, rad.MatLin([0.06, 0.17], [0, 0, 1.2])) # NdFeB
41
41
  rad.Solve(magnet, 0.0001, 10000)
42
42
 
43
43
  print(f"Magnet created: object #{magnet}")
@@ -52,7 +52,7 @@ magnet_center = [0, 0, 0]
52
52
  magnet_size = [20, 20, 30]
53
53
 
54
54
  magnet = rad.ObjRecMag(magnet_center, magnet_size, [0, 0, 1.2])
55
- rad.MatApl(magnet, rad.MatStd('NdFeB', 1.2))
55
+ rad.MatApl(magnet, rad.MatLin([0.06, 0.17], [0, 0, 1.2])) # NdFeB
56
56
  rad.Solve(magnet, 0.0001, 10000)
57
57
 
58
58
  print(f"Magnet created: object #{magnet}")
@@ -18,9 +18,9 @@ try:
18
18
  print(f" OK: block={block}, container={g}")
19
19
 
20
20
  # Step 2
21
- print("\n[Step 2] Setting initial magnetization...")
22
- rad.MatApl(g, rad.MatStd('NdFeB', 1000.0))
23
- print(" OK: M = [0, 0, 1000] A/m")
21
+ print("\n[Step 2] Setting material...")
22
+ rad.MatApl(g, rad.MatLin([0.06, 0.17], [0, 0, 1.2])) # NdFeB
23
+ print(" OK: Material applied")
24
24
 
25
25
  # Step 3
26
26
  print("\n[Step 3] Enabling H-matrix...")
@@ -0,0 +1,36 @@
1
+ # Solver Benchmarks
2
+
3
+ H-matrix field evaluation benchmark.
4
+
5
+ ## Benchmark
6
+
7
+ ### H-matrix Field Evaluation (`benchmark_hmatrix_field.py`)
8
+
9
+ **Problem:**
10
+ - Grid: 10×10 = 100 rectangular magnets
11
+ - Observation points: 10×10 = 100 points
12
+ - Initial magnetization: [0, 0, 1] T
13
+
14
+ **Comparison:**
15
+ 1. Direct calculation (use_hmatrix=0)
16
+ 2. H-matrix calculation (use_hmatrix=1)
17
+
18
+ **Purpose:**
19
+ Demonstrate H-matrix accuracy and performance for field evaluation.
20
+
21
+ ## Running
22
+
23
+ ```bash
24
+ python benchmark_hmatrix_field.py
25
+ ```
26
+
27
+ ## Expected Results
28
+
29
+ - **Accuracy:** < 1% relative error
30
+ - **Memory:** ~0.2-0.3 MB for N=100
31
+ - **Speedup:** Variable (H-matrix overhead may dominate for small problems)
32
+
33
+ ## References
34
+
35
+ - CLAUDE.md: H-matrix testing guidelines
36
+ - test_hmatrix_large.py: Original test (same as benchmark_hmatrix_field.py)
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ H-matrix accuracy test with 100+ elements and 100+ observation points
4
+
5
+ This test demonstrates proper H-matrix usage as specified in CLAUDE.md:
6
+ - Use rad.SetHMatrixFieldEval() to enable H-matrix explicitly
7
+ - Use at least 100 elements and 100 observation points
8
+ - Compare H-matrix vs direct calculation accuracy
9
+ """
10
+
11
+ import sys
12
+ sys.path.insert(0, r"S:\Radia\01_GitHub\build\Release")
13
+
14
+ import radia as rad
15
+ import numpy as np
16
+
17
+ print("=" * 80)
18
+ print("H-Matrix Accuracy Test (100+ elements, 100+ points)")
19
+ print("=" * 80)
20
+
21
+ # Create geometry: 10x10 grid = 100 rectangular magnets
22
+ rad.UtiDelAll()
23
+
24
+ magnets = []
25
+ grid_size = 10 # 10x10 = 100 magnets
26
+
27
+ for i in range(grid_size):
28
+ for j in range(grid_size):
29
+ x = i * 15.0 # 15mm spacing
30
+ y = j * 15.0
31
+ mag = rad.ObjRecMag([x, y, 0], [10, 10, 10], [0, 0, 1])
32
+ magnets.append(mag)
33
+
34
+ container = rad.ObjCnt(magnets)
35
+
36
+ # Observation points: 10x10 grid = 100 points above the magnets
37
+ obs_points = []
38
+ for i in range(grid_size):
39
+ for j in range(grid_size):
40
+ x = i * 15.0 + 7.5 # Center of each cell
41
+ y = j * 15.0 + 7.5
42
+ z = 25.0 # 25mm above
43
+ obs_points.append([x, y, z])
44
+
45
+ print(f"\n[Setup]")
46
+ print(f" Number of magnets: {len(magnets)}")
47
+ print(f" Number of observation points: {len(obs_points)}")
48
+ print(f" Grid configuration: {grid_size}x{grid_size}")
49
+
50
+ # Test 1: Direct calculation (use_hmatrix=0)
51
+ print("\n[Test 1] Direct calculation")
52
+ print("-" * 80)
53
+
54
+ import time
55
+ t0 = time.time()
56
+ H_direct = rad.FldBatch(container, 'h', obs_points, 0) # use_hmatrix=0
57
+ t_direct = time.time() - t0
58
+
59
+ H_direct = np.array(H_direct).reshape(-1, 3)
60
+
61
+ print(f" Calculation time: {t_direct:.4f} seconds")
62
+ print(f" First point H: [{H_direct[0,0]:.6e}, {H_direct[0,1]:.6e}, {H_direct[0,2]:.6e}] A/m")
63
+ print(f" Mean |H|: {np.mean(np.sqrt(np.sum(H_direct**2, axis=1))):.6e} A/m")
64
+
65
+ # Test 2: H-matrix (use_hmatrix=1)
66
+ print("\n[Test 2] H-matrix calculation")
67
+ print("-" * 80)
68
+
69
+ # Enable H-matrix explicitly as per CLAUDE.md guidelines
70
+ rad.SetHMatrixFieldEval(1, 1e-6)
71
+
72
+ t0 = time.time()
73
+ H_hmat = rad.FldBatch(container, 'h', obs_points, 1) # use_hmatrix=1
74
+ t_hmat = time.time() - t0
75
+
76
+ H_hmat = np.array(H_hmat).reshape(-1, 3)
77
+
78
+ print(f" Calculation time: {t_hmat:.4f} seconds")
79
+ print(f" First point H: [{H_hmat[0,0]:.6e}, {H_hmat[0,1]:.6e}, {H_hmat[0,2]:.6e}] A/m")
80
+ print(f" Mean |H|: {np.mean(np.sqrt(np.sum(H_hmat**2, axis=1))):.6e} A/m")
81
+
82
+ # Compute error
83
+ diff = H_hmat - H_direct
84
+ abs_errors = np.sqrt(np.sum(diff**2, axis=1))
85
+ mag_H = np.sqrt(np.sum(H_direct**2, axis=1))
86
+ rel_errors = abs_errors / (mag_H + 1e-15) # Avoid division by zero
87
+
88
+ max_rel_error = np.max(rel_errors)
89
+ mean_rel_error = np.mean(rel_errors)
90
+
91
+ print(f"\n[Accuracy]")
92
+ print(f" Max relative error: {max_rel_error:.6e} ({max_rel_error*100:.4f}%)")
93
+ print(f" Mean relative error: {mean_rel_error:.6e} ({mean_rel_error*100:.4f}%)")
94
+ print(f" Max absolute error: {np.max(abs_errors):.6e} A/m")
95
+
96
+ print(f"\n[Performance]")
97
+ print(f" Direct time: {t_direct:.4f} seconds")
98
+ print(f" H-matrix time: {t_hmat:.4f} seconds")
99
+ print(f" Speedup: {t_direct/t_hmat:.2f}x")
100
+
101
+ # Get H-matrix stats
102
+ stats = rad.GetHMatrixStats()
103
+ print(f"\n[H-matrix Stats]")
104
+ print(f" Enabled: {stats[0]}")
105
+ print(f" Cached: {stats[1]}")
106
+ print(f" Memory: {stats[2]:.3f} MB")
107
+
108
+ # Pass/Fail criteria
109
+ print("\n" + "=" * 80)
110
+ if max_rel_error < 0.01:
111
+ print(f"PASS: H-matrix accuracy < 1% (actual: {max_rel_error*100:.4f}%)")
112
+ exit_code = 0
113
+ else:
114
+ print(f"FAIL: H-matrix accuracy > 1% (actual: {max_rel_error*100:.4f}%)")
115
+ exit_code = 1
116
+
117
+ print("=" * 80)
118
+
119
+ sys.exit(exit_code)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "radia"
7
- version = "1.0.9"
7
+ version = "1.0.10"
8
8
  description = "Radia 3D Magnetostatics with NGSolve Integration and OpenMP Parallelization"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -16,7 +16,7 @@ import shutil
16
16
  import sys
17
17
 
18
18
  # Read version from pyproject.toml
19
- version = "1.0.9"
19
+ version = "1.0.10"
20
20
 
21
21
  # Read the README file
22
22
  readme_file = Path(__file__).parent / "README.md"
Binary file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: radia
3
- Version: 1.0.9
3
+ Version: 1.0.10
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
@@ -150,7 +150,7 @@ import rad_ngsolve
150
150
 
151
151
  # Create Radia magnet
152
152
  magnet = rad.ObjRecMag([0,0,0], [20,20,20], [0,0,1.2]) # mm units
153
- rad.MatApl(magnet, rad.MatStd('NdFeB', 1.2))
153
+ rad.MatApl(magnet, rad.MatLin([0.06, 0.17], [0,0,1.2])) # NdFeB
154
154
  rad.Solve(magnet, 0.0001, 10000)
155
155
 
156
156
  # Create NGSolve CoefficientFunction for different field types
@@ -10,19 +10,14 @@ docs/API_REFERENCE.md
10
10
  docs/CF_BACKGROUND_FIELD_IMPLEMENTATION.md
11
11
  docs/CLAUDE.md
12
12
  docs/DIRECTORY_STRUCTURE.md
13
- docs/HACAPK_INTEGRATION_COMPLETE.md
14
- docs/HACAPK_INTEGRATION_PLAN.md
15
13
  docs/NGSOLVE_CF_BACKGROUND_FIELD_DESIGN.md
16
14
  docs/NGSOLVE_DLL_ISSUE.md
17
15
  docs/NGSOLVE_INTEGRATION.md
18
- docs/NGSOLVE_INTEGRATION_STATUS.md
19
16
  docs/NGSOLVE_PYTHON_SOLUTION.md
20
17
  docs/NGSOLVE_USAGE_GUIDE.md
21
18
  docs/OPENMP_PERFORMANCE_REPORT.md
22
- docs/PHASE4_STATUS.md
23
19
  docs/TAB_CONVERSION_REPORT.md
24
20
  docs/hmatrix_field_design.md
25
- docs/hmatrix_field_progress.md
26
21
  docs/scripts/README.md
27
22
  examples/test_hmatrix_accuracy.py
28
23
  examples/test_hmatrix_cube_nonlinear.py
@@ -89,6 +84,8 @@ examples/smco_magnet_array/smbo.pvsm
89
84
  examples/smco_magnet_array/smco_array.py
90
85
  examples/smco_magnet_array/smco_array.vtk
91
86
  examples/smco_magnet_array/smco_field_distribution.vtk
87
+ examples/solver_benchmarks/README.md
88
+ examples/solver_benchmarks/benchmark_hmatrix_field.py
92
89
  examples/solver_time_evaluation/README.md
93
90
  examples/solver_time_evaluation/benchmark_linear_material.py
94
91
  examples/solver_time_evaluation/benchmark_lu_vs_gs.py
@@ -113,13 +110,18 @@ tests/__init__.py
113
110
  tests/benchmark_hmatrix.py
114
111
  tests/conftest.py
115
112
  tests/test_advanced.py
113
+ tests/test_group_operations.py
116
114
  tests/test_magpylib_comparison.py
115
+ tests/test_materials.py
117
116
  tests/test_parallel_performance.py
118
117
  tests/test_rad_ngsolve.py
119
118
  tests/test_radhmat.py
120
119
  tests/test_radia.py
120
+ tests/test_serialization.py
121
121
  tests/test_simple.py
122
122
  tests/test_square_coil_analytical.py
123
+ tests/test_transformations.py
124
+ tests/test_type_cast.py
123
125
  tests/test_utils.py
124
126
  tests/test_vector_potential.py
125
127
  tests/benchmarks/benchmark_correct.py
@@ -150,8 +150,8 @@ def test_iron_core():
150
150
  core = rad.ObjRecMag([0, 0, 0], [40, 40, 100])
151
151
  print(f" Core created: ID={core}")
152
152
 
153
- # Create iron material
154
- mat_iron = rad.MatStd('Steel37', 2000)
153
+ # Create iron material (Steel37 equivalent)
154
+ mat_iron = rad.MatSatIsoFrm([1596.3, 1.1488], [133.11, 0.4268], [18.713, 0.4759])
155
155
  rad.MatApl(core, mat_iron)
156
156
  print(f" Iron material applied")
157
157
 
@@ -0,0 +1,238 @@
1
+ """
2
+ Unit tests for radgroup.cpp - Object grouping operations
3
+
4
+ Tests ObjCnt (Container/Group) functionality:
5
+ - Group creation
6
+ - Adding/removing objects
7
+ - Group transformations
8
+ - Nested groups
9
+ """
10
+
11
+ import sys
12
+ sys.path.insert(0, r"S:\Radia\01_GitHub\build\Release")
13
+
14
+ import pytest
15
+ import radia as rad
16
+ import numpy as np
17
+
18
+
19
+ class TestGroupCreation:
20
+ """Test basic group creation and structure"""
21
+
22
+ def test_empty_group(self):
23
+ """Test creating an empty group"""
24
+ rad.UtiDelAll()
25
+ group = rad.ObjCnt([])
26
+ assert group > 0, "Empty group should have valid index"
27
+
28
+ def test_single_object_group(self):
29
+ """Test group with single object"""
30
+ rad.UtiDelAll()
31
+ mag = rad.ObjRecMag([0, 0, 0], [10, 10, 10], [0, 0, 1])
32
+ group = rad.ObjCnt([mag])
33
+ assert group > 0
34
+
35
+ def test_multiple_objects_group(self):
36
+ """Test group with multiple objects"""
37
+ rad.UtiDelAll()
38
+
39
+ mags = []
40
+ for i in range(5):
41
+ mag = rad.ObjRecMag([i*20, 0, 0], [10, 10, 10], [0, 0, 1])
42
+ mags.append(mag)
43
+
44
+ group = rad.ObjCnt(mags)
45
+ assert group > 0
46
+
47
+ # Test field evaluation on group
48
+ H = rad.Fld(group, 'h', [50, 0, 0])
49
+ assert len(H) == 3, "Should return 3D field vector"
50
+
51
+ def test_nested_groups(self):
52
+ """Test creating nested groups"""
53
+ rad.UtiDelAll()
54
+
55
+ # Create two sub-groups
56
+ group1_mags = [
57
+ rad.ObjRecMag([0, 0, 0], [10, 10, 10], [0, 0, 1]),
58
+ rad.ObjRecMag([20, 0, 0], [10, 10, 10], [0, 0, 1])
59
+ ]
60
+ group1 = rad.ObjCnt(group1_mags)
61
+
62
+ group2_mags = [
63
+ rad.ObjRecMag([0, 20, 0], [10, 10, 10], [0, 0, 1]),
64
+ rad.ObjRecMag([20, 20, 0], [10, 10, 10], [0, 0, 1])
65
+ ]
66
+ group2 = rad.ObjCnt(group2_mags)
67
+
68
+ # Create parent group
69
+ parent_group = rad.ObjCnt([group1, group2])
70
+ assert parent_group > 0
71
+
72
+ # Test field from nested group
73
+ H = rad.Fld(parent_group, 'h', [10, 10, 0])
74
+ assert len(H) == 3
75
+
76
+
77
+ class TestGroupTransformations:
78
+ """Test transformations applied to groups"""
79
+
80
+ def test_translate_group(self):
81
+ """Test translating entire group"""
82
+ rad.UtiDelAll()
83
+
84
+ # Create group at origin
85
+ mags = []
86
+ for i in range(3):
87
+ mag = rad.ObjRecMag([i*10, 0, 0], [5, 5, 5], [0, 0, 1])
88
+ mags.append(mag)
89
+ group = rad.ObjCnt(mags)
90
+
91
+ # Field at origin before translation
92
+ H_before = rad.Fld(group, 'h', [0, 0, 20])
93
+
94
+ # Translate group
95
+ rad.TrfOrnt(group, rad.TrfTrsl([0, 100, 0]))
96
+
97
+ # Field at translated position
98
+ H_after = rad.Fld(group, 'h', [0, 100, 20])
99
+
100
+ # Fields should be similar (geometry moved)
101
+ assert np.allclose(H_before, H_after, rtol=1e-6)
102
+
103
+ def test_rotate_group(self):
104
+ """Test rotating entire group"""
105
+ rad.UtiDelAll()
106
+
107
+ # Create asymmetric group
108
+ mag1 = rad.ObjRecMag([10, 0, 0], [5, 5, 5], [1, 0, 0])
109
+ mag2 = rad.ObjRecMag([20, 0, 0], [5, 5, 5], [1, 0, 0])
110
+ group = rad.ObjCnt([mag1, mag2])
111
+
112
+ # Rotate 90 degrees around z-axis
113
+ rad.TrfOrnt(group, rad.TrfRot([0, 0, 0], [0, 0, 1], np.pi/2))
114
+
115
+ # After rotation, magnets should be along y-axis
116
+ # Field at rotated position should make sense
117
+ H = rad.Fld(group, 'h', [0, 15, 0])
118
+ assert len(H) == 3
119
+
120
+ def test_multiple_transformations(self):
121
+ """Test combining multiple transformations"""
122
+ rad.UtiDelAll()
123
+
124
+ mag = rad.ObjRecMag([0, 0, 0], [10, 10, 10], [0, 0, 1])
125
+ group = rad.ObjCnt([mag])
126
+
127
+ # Translate
128
+ rad.TrfOrnt(group, rad.TrfTrsl([10, 0, 0]))
129
+
130
+ # Rotate
131
+ rad.TrfOrnt(group, rad.TrfRot([0, 0, 0], [0, 0, 1], np.pi/4))
132
+
133
+ # Should still compute field
134
+ H = rad.Fld(group, 'h', [20, 0, 0])
135
+ assert len(H) == 3
136
+
137
+
138
+ class TestGroupMaterialApplication:
139
+ """Test applying materials to groups"""
140
+
141
+ def test_material_to_group(self):
142
+ """Test applying material to entire group"""
143
+ rad.UtiDelAll()
144
+
145
+ # Create group without material
146
+ mags = []
147
+ for i in range(3):
148
+ mag = rad.ObjRecMag([i*15, 0, 0], [10, 10, 10], [0, 0, 1])
149
+ mags.append(mag)
150
+ group = rad.ObjCnt(mags)
151
+
152
+ # Apply material to group
153
+ mat = rad.MatLin([1000, 0], [10, 10, 10])
154
+ rad.MatApl(group, mat)
155
+
156
+ # Field should be computed
157
+ H = rad.Fld(group, 'h', [20, 0, 0])
158
+ assert len(H) == 3
159
+ assert not np.allclose(H, [0, 0, 0]) # Should have non-zero field
160
+
161
+
162
+ class TestGroupFieldEvaluation:
163
+ """Test field evaluation from groups"""
164
+
165
+ def test_field_from_group_vs_individual(self):
166
+ """Compare field from group vs sum of individual objects"""
167
+ rad.UtiDelAll()
168
+
169
+ # Create individual magnets
170
+ mag1 = rad.ObjRecMag([0, 0, 0], [10, 10, 10], [0, 0, 1])
171
+ rad.MatApl(mag1, rad.MatLin([1000, 0], [10, 10, 10]))
172
+
173
+ mag2 = rad.ObjRecMag([20, 0, 0], [10, 10, 10], [0, 0, 1])
174
+ rad.MatApl(mag2, rad.MatLin([1000, 0], [10, 10, 10]))
175
+
176
+ # Field from individual objects
177
+ H1 = np.array(rad.Fld(mag1, 'h', [30, 0, 0]))
178
+ H2 = np.array(rad.Fld(mag2, 'h', [30, 0, 0]))
179
+ H_sum = H1 + H2
180
+
181
+ # Field from group
182
+ group = rad.ObjCnt([mag1, mag2])
183
+ H_group = np.array(rad.Fld(group, 'h', [30, 0, 0]))
184
+
185
+ # Should be equal (superposition principle)
186
+ assert np.allclose(H_sum, H_group, rtol=1e-10)
187
+
188
+ def test_batch_field_from_group(self):
189
+ """Test FldBatch with groups"""
190
+ rad.UtiDelAll()
191
+
192
+ mags = []
193
+ for i in range(5):
194
+ mag = rad.ObjRecMag([i*15, 0, 0], [10, 10, 10], [0, 0, 1])
195
+ rad.MatApl(mag, rad.MatLin([1000, 0], [10, 10, 10]))
196
+ mags.append(mag)
197
+
198
+ group = rad.ObjCnt(mags)
199
+
200
+ # Evaluate at multiple points
201
+ points = [[x, 0, 0] for x in [10, 20, 30, 40]]
202
+ H_batch = rad.FldBatch(group, 'h', points, 0) # Direct calculation
203
+
204
+ assert len(H_batch) == 4, "Should return 4 field vectors"
205
+ for H in H_batch:
206
+ assert len(H) == 3, "Each field should be 3D"
207
+
208
+
209
+ class TestGroupEdgeCases:
210
+ """Test edge cases and error handling"""
211
+
212
+ def test_group_with_duplicate_objects(self):
213
+ """Test group containing same object multiple times"""
214
+ rad.UtiDelAll()
215
+
216
+ mag = rad.ObjRecMag([0, 0, 0], [10, 10, 10], [0, 0, 1])
217
+
218
+ # Create group with duplicates (should handle gracefully)
219
+ group = rad.ObjCnt([mag, mag, mag])
220
+ assert group > 0
221
+
222
+ def test_deeply_nested_groups(self):
223
+ """Test deeply nested group hierarchy"""
224
+ rad.UtiDelAll()
225
+
226
+ # Create deep nesting: group -> group -> group -> magnet
227
+ mag = rad.ObjRecMag([0, 0, 0], [10, 10, 10], [0, 0, 1])
228
+ level1 = rad.ObjCnt([mag])
229
+ level2 = rad.ObjCnt([level1])
230
+ level3 = rad.ObjCnt([level2])
231
+
232
+ # Should still compute field
233
+ H = rad.Fld(level3, 'h', [20, 0, 0])
234
+ assert len(H) == 3
235
+
236
+
237
+ if __name__ == "__main__":
238
+ pytest.main([__file__, "-v"])
@@ -228,13 +228,14 @@ def test_cylindrical_magnet_comparison():
228
228
  print(f"[PASS] Radia and magpylib agree within {tolerance_percent}% tolerance")
229
229
  print(f" Difference: {diff_percent:.2f}%")
230
230
  print("=" * 70)
231
- return True
232
231
  else:
233
232
  print(f"[FAIL] Radia and magpylib differ by {diff_percent:.2f}%")
234
233
  print(f" Exceeds {tolerance_percent}% tolerance")
235
234
  print(f" Check magnetization unit conversion!")
236
235
  print("=" * 70)
237
- return False
236
+
237
+ # Use assertion for pytest
238
+ assert passed, f"Radia and magpylib differ by {diff_percent:.2f}%, exceeds {tolerance_percent}% tolerance"
238
239
 
239
240
  def main():
240
241
  """Run the comparison test"""