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.
- {radia-1.0.9/src/radia.egg-info → radia-1.0.10}/PKG-INFO +2 -2
- {radia-1.0.9 → radia-1.0.10}/README.md +1 -1
- {radia-1.0.9 → radia-1.0.10}/README_BUILD.md +1 -1
- {radia-1.0.9 → radia-1.0.10}/docs/API_REFERENCE.md +51 -20
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/demo_field_types.py +1 -1
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/export_radia_geometry.py +1 -1
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/visualize_field.py +1 -1
- {radia-1.0.9 → radia-1.0.10}/examples/simple_problems/test_update_hmatrix_magnetization.py +3 -3
- radia-1.0.10/examples/solver_benchmarks/README.md +36 -0
- radia-1.0.10/examples/solver_benchmarks/benchmark_hmatrix_field.py +119 -0
- {radia-1.0.9 → radia-1.0.10}/pyproject.toml +1 -1
- {radia-1.0.9 → radia-1.0.10}/setup.py +1 -1
- radia-1.0.10/src/python/rad_ngsolve.pyd +0 -0
- {radia-1.0.9 → radia-1.0.10}/src/python/radia.pyd +0 -0
- {radia-1.0.9 → radia-1.0.10/src/radia.egg-info}/PKG-INFO +2 -2
- {radia-1.0.9 → radia-1.0.10}/src/radia.egg-info/SOURCES.txt +7 -5
- {radia-1.0.9 → radia-1.0.10}/tests/test_advanced.py +2 -2
- radia-1.0.10/tests/test_group_operations.py +238 -0
- {radia-1.0.9 → radia-1.0.10}/tests/test_magpylib_comparison.py +3 -2
- radia-1.0.10/tests/test_materials.py +167 -0
- {radia-1.0.9 → radia-1.0.10}/tests/test_parallel_performance.py +19 -11
- {radia-1.0.9 → radia-1.0.10}/tests/test_rad_ngsolve.py +2 -2
- radia-1.0.10/tests/test_radia.py +226 -0
- radia-1.0.10/tests/test_serialization.py +264 -0
- {radia-1.0.9 → radia-1.0.10}/tests/test_square_coil_analytical.py +3 -2
- radia-1.0.10/tests/test_transformations.py +301 -0
- radia-1.0.10/tests/test_type_cast.py +143 -0
- radia-1.0.9/docs/HACAPK_INTEGRATION_COMPLETE.md +0 -416
- radia-1.0.9/docs/HACAPK_INTEGRATION_PLAN.md +0 -355
- radia-1.0.9/docs/NGSOLVE_INTEGRATION_STATUS.md +0 -210
- radia-1.0.9/docs/PHASE4_STATUS.md +0 -248
- radia-1.0.9/docs/hmatrix_field_progress.md +0 -459
- radia-1.0.9/src/python/rad_ngsolve.pyd +0 -0
- radia-1.0.9/tests/test_radia.py +0 -261
- {radia-1.0.9 → radia-1.0.10}/COPYRIGHT.txt +0 -0
- {radia-1.0.9 → radia-1.0.10}/LICENSE +0 -0
- {radia-1.0.9 → radia-1.0.10}/MANIFEST.in +0 -0
- {radia-1.0.9 → radia-1.0.10}/docs/API_EXTENSIONS.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/docs/CF_BACKGROUND_FIELD_IMPLEMENTATION.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/docs/CLAUDE.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/docs/DIRECTORY_STRUCTURE.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/docs/NGSOLVE_CF_BACKGROUND_FIELD_DESIGN.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/docs/NGSOLVE_DLL_ISSUE.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/docs/NGSOLVE_INTEGRATION.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/docs/NGSOLVE_PYTHON_SOLUTION.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/docs/NGSOLVE_USAGE_GUIDE.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/docs/OPENMP_PERFORMANCE_REPORT.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/docs/TAB_CONVERSION_REPORT.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/docs/hmatrix_field_design.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/docs/scripts/README.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/H-matrix/BENCHMARK_RESULTS.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/H-matrix/HMATRIX_FIELD_DESIGN.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/H-matrix/HMATRIX_FIELD_DESIGN_SIMPLIFIED.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/H-matrix/README.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/H-matrix/benchmark_field_evaluation.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/H-matrix/benchmark_parallel_construction.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/H-matrix/benchmark_solver.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/H-matrix/plot_benchmark_results.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/H-matrix/run_all_benchmarks.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/H-matrix/verify_field_accuracy.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/HMATRIX_ANALYSIS.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/HMATRIX_FIELD_EVALUATION_ISSUE.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/H_MATRIX_PARALLEL_OPTIMIZATION.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/NGBEM_ANALYSIS.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/NGSOLVE_SET_VS_INTERPOLATE.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/PROPOSAL_VECTORIZED_API.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/README.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/SET_VS_INTERPOLATE_SIMPLE.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/SOLVER_OPTIMIZATION_PROPOSAL.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/benchmark_gridfunction_set.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/debug_field_values.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/rad.ObjBckgCF/Cubit2Nastran.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/rad.ObjBckgCF/README.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/rad.ObjBckgCF/sphere_nastran_analysis.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/rad.ObjBckgCF/sphere_nastran_field_mu.pvsm +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/rad.ObjBckgCF/sphere_nastran_geometry.vtk +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/radia_field.pvsm +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/test_batch_evaluation.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/test_batch_fld.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/test_coordinate_transform.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/test_mesh_convergence.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/test_set_vs_interpolate.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/NGSolve_Integration/verify_curl_A_equals_B.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/complex_coil_geometry/README.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/complex_coil_geometry/coil_geometry.vtk +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/complex_coil_geometry/coil_model.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/complex_coil_geometry/complex_coil.pvsm +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/complex_coil_geometry/field_map.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/complex_coil_geometry/field_map.vtk +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/complex_coil_geometry/visualize_coils.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/electromagnet/README.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/electromagnet/electromagnet.pvsm +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/electromagnet/electromagnet.vtk +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/electromagnet/field_distribution.vtk +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/electromagnet/magnet.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/electromagnet/racetrack_coil_model.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/electromagnet/yoke_model.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/simple_problems/CONVERSION_NOTES.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/simple_problems/README.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/simple_problems/case0.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/simple_problems/case1.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/simple_problems/case2.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/simple_problems/case3.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/smco_magnet_array/README.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/smco_magnet_array/smbo.pvsm +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/smco_magnet_array/smco_array.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/smco_magnet_array/smco_array.vtk +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/smco_magnet_array/smco_field_distribution.vtk +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/solver_time_evaluation/README.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/solver_time_evaluation/benchmark_linear_material.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/solver_time_evaluation/benchmark_lu_vs_gs.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/solver_time_evaluation/benchmark_matrix_construction.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/solver_time_evaluation/benchmark_solver_scaling.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/test_hmatrix_accuracy.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/test_hmatrix_cube_nonlinear.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/examples/test_hmatrix_parallel.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/setup.cfg +0 -0
- {radia-1.0.9 → radia-1.0.10}/src/python/__init__.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/src/python/nastran_reader.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/src/python/radia_coil_builder.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/src/python/radia_ngsolve_field.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/src/python/radia_pyvista_viewer.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/src/python/radia_vtk_export.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/src/radia.egg-info/dependency_links.txt +0 -0
- {radia-1.0.9 → radia-1.0.10}/src/radia.egg-info/not-zip-safe +0 -0
- {radia-1.0.9 → radia-1.0.10}/src/radia.egg-info/requires.txt +0 -0
- {radia-1.0.9 → radia-1.0.10}/src/radia.egg-info/top_level.txt +0 -0
- {radia-1.0.9 → radia-1.0.10}/tests/README.md +0 -0
- {radia-1.0.9 → radia-1.0.10}/tests/__init__.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/tests/benchmark_hmatrix.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/tests/benchmarks/benchmark_correct.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/tests/benchmarks/benchmark_heavy.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/tests/benchmarks/benchmark_openmp.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/tests/benchmarks/benchmark_threads.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/tests/conftest.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/tests/test_radhmat.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/tests/test_simple.py +0 -0
- {radia-1.0.9 → radia-1.0.10}/tests/test_utils.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
-
**
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
347
|
-
|
|
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
|
-
|
|
350
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
22
|
-
rad.MatApl(g, rad.
|
|
23
|
-
print(" OK:
|
|
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)
|
|
Binary file
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: radia
|
|
3
|
-
Version: 1.0.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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"""
|