nmag-python-3 0.0.6__tar.gz → 0.0.7__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.
- {nmag_python_3-0.0.6/src/nmag_python_3.egg-info → nmag_python_3-0.0.7}/PKG-INFO +3 -3
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/pyproject.toml +3 -3
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/mag_material/mag_material.py +80 -10
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/demag/linear.py +264 -17
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/__init__.py +52 -15
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/anisotropy/fields.py +8 -2
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/anisotropy/materials.py +5 -3
- nmag_python_3-0.0.7/src/nmag/simulation/demag/bem/dirichlet.py +127 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/demag/bem/operator.py +38 -4
- nmag_python_3-0.0.7/src/nmag/simulation/demag/fem/assembly.py +183 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/demag/fem/geometry.py +81 -12
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/demag/fields/auxiliary.py +43 -15
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/demag/fields/probe.py +54 -6
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/demag/fields/recovery.py +81 -16
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/exchange/coefficients.py +6 -4
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/exchange/fields.py +61 -19
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/exchange/llg_rhs.py +20 -7
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/fields/arrays.py +25 -6
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/fields/derived.py +5 -1
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/implicit_dynamics.py +6 -1
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/mesh/geometry.py +67 -6
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/mesh/materials.py +189 -15
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7/src/nmag_python_3.egg-info}/PKG-INFO +3 -3
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag_python_3.egg-info/requires.txt +2 -2
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/backend.py +1 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesh_generation.py +7 -7
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesh_io.py +4 -1
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/si/physical.py +17 -2
- nmag_python_3-0.0.6/src/nmag/simulation/demag/bem/dirichlet.py +0 -85
- nmag_python_3-0.0.6/src/nmag/simulation/demag/fem/assembly.py +0 -114
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/LICENSE +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/README.md +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/setup.cfg +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/anisotropy/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/anisotropy/anisotropy.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/anisotropy/evaluation.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/anisotropy/model.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/anisotropy/predefined.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/anisotropy/py.typed +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/anisotropy/values.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/mag_material/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/mag_material/parameters.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/mag_material/py.typed +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/backends.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/checkpoint.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/cli/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/cli/nmagpp.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/config.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/demag/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/demag/bem_operator.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/demag/geometry.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/demag/lindholm.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/demag/lindholm_fast.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/dynamics/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/output.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/parallel.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/py.typed +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/resources.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/anisotropy/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/demag/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/demag/bem/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/demag/bem/diagnostics.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/demag/bem/hierarchical.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/demag/fem/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/demag/fem/charges.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/demag/fields/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/demag/solver.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/dynamics/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/dynamics/advance.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/dynamics/integrator.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/exchange/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/fields/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/fields/availability.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/fields/averages.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/fields/maxangle.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/fields/probes.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/mesh/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/mesh/probe.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/restart.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/simulation/support.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag/vtk_export.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag_python_3.egg-info/SOURCES.txt +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag_python_3.egg-info/dependency_links.txt +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag_python_3.egg-info/entry_points.txt +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmag_python_3.egg-info/top_level.txt +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/cli/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/cli/nmeshimport.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/geometry/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/geometry/boolean_operations.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/geometry/primitives.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/geometry/transform.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/io/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/io/ascii.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/io/legacy_nmesh_hdf5.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/io/meshio_support.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/io/netgen.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesh_model.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesh_utilities.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/driver.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/meshing_defaults.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/meshing_parameters.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/parity.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/parity_canonical.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/parity_comparison.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/parity_metrics.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/periodic.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/_constants.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/_types.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/density.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/engine/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/engine/state.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/engine/steps.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/engine/topology.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/forces/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/forces/jit.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/forces/neighbors.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/forces/simplex.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/forces/summary.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/forces/types.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/geometry/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/geometry/builder.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/geometry/model.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/seeding/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/seeding/periodic.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/seeding/points.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/seeding/sampling.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/topology/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/topology/finalize.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/relaxation/topology/recovery.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/mesher/sectioned_config.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/nmesh.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/py.typed +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/utils/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/utils/array_list_utils.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/utils/constants.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/utils/timing_memory_utils.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/nmesh/utils/types.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/si/constants.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/si/py.typed +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/simulation/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/simulation/clock.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/simulation/data_writer.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/simulation/data_writer_collection.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/simulation/hysteresis.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/simulation/hysteresis_runner.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/simulation/hysteresis_schedule.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/simulation/inference/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/simulation/inference/inference.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/simulation/py.typed +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/simulation/quantity.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/simulation/simulation_core.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/throttler/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/throttler/py.typed +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/throttler/throttler.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/when/__init__.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/when/py.typed +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/src/when/when.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/tests/test_docs_examples.py +0 -0
- {nmag_python_3-0.0.6 → nmag_python_3-0.0.7}/tests/test_packaging_hygiene.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nmag-python-3
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.7
|
|
4
4
|
Summary: Standalone Python 3 implementation of the Nmag micromagnetic simulation interface
|
|
5
5
|
Author-email: TriMagnetix <info@trimagnetix.com>
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -24,8 +24,8 @@ Requires-Dist: maturin<2,>=1.14; extra == "rust"
|
|
|
24
24
|
Provides-Extra: docs
|
|
25
25
|
Requires-Dist: mkdocs==1.6.1; extra == "docs"
|
|
26
26
|
Requires-Dist: mkdocs-material==9.7.7; extra == "docs"
|
|
27
|
-
Requires-Dist: mkdocstrings==1.0.
|
|
28
|
-
Requires-Dist: mkdocstrings-python==2.0.
|
|
27
|
+
Requires-Dist: mkdocstrings==1.0.8; extra == "docs"
|
|
28
|
+
Requires-Dist: mkdocstrings-python==2.0.10; extra == "docs"
|
|
29
29
|
Provides-Extra: dev
|
|
30
30
|
Requires-Dist: maturin<2,>=1.14; extra == "dev"
|
|
31
31
|
Requires-Dist: h5py-stubs<0.2,>=0.1.2; extra == "dev"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nmag-python-3"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.7"
|
|
8
8
|
authors = [{ name = "TriMagnetix", email = "info@trimagnetix.com" }]
|
|
9
9
|
description = "Standalone Python 3 implementation of the Nmag micromagnetic simulation interface"
|
|
10
10
|
readme = "README.md"
|
|
@@ -32,8 +32,8 @@ rust = ["maturin>=1.14,<2"]
|
|
|
32
32
|
docs = [
|
|
33
33
|
"mkdocs==1.6.1",
|
|
34
34
|
"mkdocs-material==9.7.7",
|
|
35
|
-
"mkdocstrings==1.0.
|
|
36
|
-
"mkdocstrings-python==2.0.
|
|
35
|
+
"mkdocstrings==1.0.8",
|
|
36
|
+
"mkdocstrings-python==2.0.10",
|
|
37
37
|
]
|
|
38
38
|
dev = [
|
|
39
39
|
"maturin>=1.14,<2",
|
|
@@ -131,18 +131,85 @@ class MagMaterial:
|
|
|
131
131
|
|
|
132
132
|
self.su_anisotropy = self.anisotropy
|
|
133
133
|
self._derived_su_constants: dict[str, MaterialScalar] | None = None
|
|
134
|
+
self._derived_su_token: tuple[int, ...] | None = None
|
|
134
135
|
|
|
135
136
|
self.extended_print = False
|
|
137
|
+
self._initialized = True
|
|
136
138
|
if log.isEnabledFor(logging.INFO):
|
|
137
139
|
log.info("Created new Material:\n %s", self)
|
|
138
140
|
|
|
141
|
+
def _sync_llg_coeffs(self) -> None:
|
|
142
|
+
gilbert_to_ll = 1.0 / (1.0 + self.su_llg_damping**2)
|
|
143
|
+
base_coeff1 = -self.su_llg_gamma_G * gilbert_to_ll
|
|
144
|
+
object.__setattr__(self, "su_llg_coeff2", base_coeff1 * self.su_llg_damping)
|
|
145
|
+
object.__setattr__(
|
|
146
|
+
self,
|
|
147
|
+
"su_llg_coeff1",
|
|
148
|
+
0.0 if not self.do_precession else base_coeff1,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
def __setattr__(self, name: str, value: Any) -> None:
|
|
152
|
+
object.__setattr__(self, name, value)
|
|
153
|
+
if not getattr(self, "_initialized", False):
|
|
154
|
+
return
|
|
155
|
+
if name == "Ms":
|
|
156
|
+
object.__setattr__(self, "su_Ms", value)
|
|
157
|
+
elif name == "llg_gamma_G":
|
|
158
|
+
object.__setattr__(self, "su_llg_gamma_G", value)
|
|
159
|
+
self._sync_llg_coeffs()
|
|
160
|
+
elif name == "llg_damping":
|
|
161
|
+
object.__setattr__(self, "su_llg_damping", value)
|
|
162
|
+
self._sync_llg_coeffs()
|
|
163
|
+
elif name in {"su_llg_gamma_G", "su_llg_damping", "do_precession"}:
|
|
164
|
+
self._sync_llg_coeffs()
|
|
165
|
+
elif name == "llg_normalisationfactor":
|
|
166
|
+
object.__setattr__(self, "su_llg_normalisationfactor", value)
|
|
167
|
+
elif name == "exchange_coupling":
|
|
168
|
+
object.__setattr__(self, "su_exchange_coupling", value)
|
|
169
|
+
elif name == "anisotropy":
|
|
170
|
+
object.__setattr__(self, "su_anisotropy", value)
|
|
171
|
+
if not name.startswith("_"):
|
|
172
|
+
object.__setattr__(self, "_derived_su_constants", None)
|
|
173
|
+
object.__setattr__(self, "_derived_su_token", None)
|
|
174
|
+
|
|
139
175
|
def _derived_su_values(self) -> dict[str, MaterialScalar]:
|
|
140
|
-
|
|
176
|
+
token = (
|
|
177
|
+
id(self.Ms),
|
|
178
|
+
id(self.llg_damping),
|
|
179
|
+
id(self.su_llg_damping),
|
|
180
|
+
id(self.llg_gamma_G),
|
|
181
|
+
id(self.su_llg_gamma_G),
|
|
182
|
+
id(self.llg_normalisationfactor),
|
|
183
|
+
id(self.su_llg_normalisationfactor),
|
|
184
|
+
id(self.llg_polarisation),
|
|
185
|
+
id(self.llg_xi),
|
|
186
|
+
id(self.exchange_coupling),
|
|
187
|
+
id(self.su_exchange_coupling),
|
|
188
|
+
id(self.su_Ms),
|
|
189
|
+
bool(self.do_precession),
|
|
190
|
+
id(self.anisotropy),
|
|
191
|
+
id(self.su_anisotropy),
|
|
192
|
+
)
|
|
193
|
+
if self._derived_su_constants is None or self._derived_su_token != token:
|
|
141
194
|
constants = _si_constants()
|
|
142
|
-
|
|
143
|
-
|
|
195
|
+
ms = self.Ms.in_units_of(SI("A/m"))
|
|
196
|
+
su_ms = self.su_Ms.in_units_of(SI("A/m"))
|
|
197
|
+
thermal_factor = (
|
|
198
|
+
(
|
|
199
|
+
(2.0 * constants.boltzmann_constant * self.llg_damping)
|
|
200
|
+
/ (-constants.gamma0 * constants.mu0 * SI(1.0, "A/m"))
|
|
201
|
+
)
|
|
202
|
+
* 0.0
|
|
203
|
+
if ms == 0.0
|
|
204
|
+
else (2.0 * constants.boltzmann_constant * self.llg_damping)
|
|
205
|
+
/ (-constants.gamma0 * constants.mu0 * self.Ms)
|
|
206
|
+
)
|
|
207
|
+
damping = (
|
|
208
|
+
self.su_llg_damping.in_units_of(SI(1))
|
|
209
|
+
if isinstance(self.su_llg_damping, SI)
|
|
210
|
+
else float(self.su_llg_damping)
|
|
144
211
|
)
|
|
145
|
-
gilbert_to_ll = 1.0 / (1.0 +
|
|
212
|
+
gilbert_to_ll = 1.0 / (1.0 + damping * damping)
|
|
146
213
|
polarisation = (
|
|
147
214
|
self.llg_polarisation.in_units_of(SI(1))
|
|
148
215
|
if isinstance(self.llg_polarisation, SI)
|
|
@@ -153,7 +220,6 @@ class MagMaterial:
|
|
|
153
220
|
cast(Any, 1.0 * constants.bohr_magneton).to("J/T").magnitude
|
|
154
221
|
)
|
|
155
222
|
charge = float(cast(Any, 1.0 * constants.positron_charge).to("C").magnitude)
|
|
156
|
-
ms = self.Ms.in_units_of(SI("A/m"))
|
|
157
223
|
su_f = (
|
|
158
224
|
SI(0.0, "m^3/A/s")
|
|
159
225
|
if ms == 0.0
|
|
@@ -165,16 +231,20 @@ class MagMaterial:
|
|
|
165
231
|
"m^3/A/s",
|
|
166
232
|
)
|
|
167
233
|
)
|
|
234
|
+
su_exch_prefactor = (
|
|
235
|
+
(2.0 * self.su_exchange_coupling / (constants.mu0 * SI(1.0, "A/m"))) * 0.0
|
|
236
|
+
if su_ms == 0.0
|
|
237
|
+
else 2.0 * self.su_exchange_coupling / (constants.mu0 * self.su_Ms)
|
|
238
|
+
)
|
|
168
239
|
self._derived_su_constants = {
|
|
169
240
|
"thermal_factor": thermal_factor,
|
|
170
241
|
"su_thermal_factor": thermal_factor,
|
|
171
242
|
"su_llg_stt_prefactor": 0.0 if su_f == 0.0 else 1.0,
|
|
172
|
-
"su_llg_stt_nadiab": su_f * (xi -
|
|
173
|
-
"su_llg_stt_adiab": su_f * (1.0 +
|
|
174
|
-
"su_exch_prefactor":
|
|
175
|
-
2.0 * self.su_exchange_coupling / (constants.mu0 * self.su_Ms)
|
|
176
|
-
),
|
|
243
|
+
"su_llg_stt_nadiab": su_f * (xi - damping),
|
|
244
|
+
"su_llg_stt_adiab": su_f * (1.0 + damping * xi),
|
|
245
|
+
"su_exch_prefactor": su_exch_prefactor,
|
|
177
246
|
}
|
|
247
|
+
self._derived_su_token = token
|
|
178
248
|
return self._derived_su_constants
|
|
179
249
|
|
|
180
250
|
@property
|
|
@@ -11,6 +11,7 @@ import numpy as np
|
|
|
11
11
|
|
|
12
12
|
from ..backends import (
|
|
13
13
|
DEMAG_DENSE_MAX_POINTS_ENV,
|
|
14
|
+
DEMAG_FEM_MATRIX_BACKEND_ENV,
|
|
14
15
|
LEAST_SQUARES_RELATIVE_RESIDUAL_TOLERANCE,
|
|
15
16
|
_demag_solve_condition_diagnostics_enabled,
|
|
16
17
|
_selected_demag_linear_solver_backend,
|
|
@@ -20,41 +21,267 @@ LOGGER = logging.getLogger(__name__)
|
|
|
20
21
|
_scipy_linalg_cache: Any | None = None
|
|
21
22
|
|
|
22
23
|
|
|
24
|
+
_SPARSE_FACTORIZATION_MIN_POINTS = 64
|
|
25
|
+
_SPARSE_FACTORIZATION_MAX_DENSITY = 0.25
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _allow_sparse_factorization() -> bool:
|
|
29
|
+
return os.environ.get(DEMAG_FEM_MATRIX_BACKEND_ENV, "auto").strip().lower() != "dense"
|
|
30
|
+
|
|
31
|
+
|
|
23
32
|
@dataclass(frozen=True, slots=True)
|
|
24
33
|
class ScipyLUFactorization:
|
|
25
|
-
"""Reusable LU factorization for a geometry-dependent
|
|
34
|
+
"""Reusable LU factorization for a geometry-dependent linear system."""
|
|
26
35
|
|
|
27
36
|
lu: np.ndarray
|
|
28
37
|
pivots: np.ndarray
|
|
38
|
+
splu: Any | None = None
|
|
39
|
+
gauge_fixed: bool = False
|
|
40
|
+
matrix: np.ndarray | None = None
|
|
41
|
+
constraint_scale: float = 1.0
|
|
29
42
|
|
|
30
43
|
def solve(self, rhs: np.ndarray) -> np.ndarray:
|
|
31
|
-
|
|
44
|
+
rhs_array = np.asarray(rhs, dtype=float)
|
|
45
|
+
if not np.all(np.isfinite(rhs_array)):
|
|
46
|
+
raise np.linalg.LinAlgError("Cannot solve linear system with non-finite RHS.")
|
|
47
|
+
if self.splu is not None:
|
|
48
|
+
if self.gauge_fixed:
|
|
49
|
+
n = rhs_array.shape[0] - 1
|
|
50
|
+
if n < 0:
|
|
51
|
+
raise ValueError(f"Expected rhs of length >= 1, got {rhs_array.shape}.")
|
|
52
|
+
rhs_body = rhs_array[:n]
|
|
53
|
+
rhs_gauge = rhs_array[n]
|
|
54
|
+
if n == 0 and np.any(rhs_gauge != 0.0):
|
|
55
|
+
raise np.linalg.LinAlgError(
|
|
56
|
+
"Singular gauge-fixed system with non-zero constraint RHS."
|
|
57
|
+
)
|
|
58
|
+
lam = np.mean(rhs_body, axis=0) if n > 0 else np.zeros_like(rhs_gauge)
|
|
59
|
+
rhs_centered = rhs_body - lam
|
|
60
|
+
out = np.zeros_like(rhs_array, dtype=float)
|
|
61
|
+
if n > 1:
|
|
62
|
+
out[1:n] = self.splu.solve(rhs_centered[1:])
|
|
63
|
+
if n > 0:
|
|
64
|
+
out[:n] += rhs_gauge / n - np.mean(out[:n], axis=0)
|
|
65
|
+
out[n] = lam
|
|
66
|
+
if not np.all(np.isfinite(out)):
|
|
67
|
+
raise np.linalg.LinAlgError("Sparse gauge-fixed LU solve produced non-finite values.")
|
|
68
|
+
return out
|
|
69
|
+
out = np.asarray(self.splu.solve(rhs_array), dtype=float)
|
|
70
|
+
if not np.all(np.isfinite(out)):
|
|
71
|
+
raise np.linalg.LinAlgError("Sparse LU solve produced non-finite values.")
|
|
72
|
+
return out
|
|
73
|
+
solve_rhs = rhs_array
|
|
74
|
+
if self.gauge_fixed and self.constraint_scale != 1.0 and rhs_array.shape[0] > 0:
|
|
75
|
+
solve_rhs = rhs_array.copy()
|
|
76
|
+
solve_rhs[-1] *= self.constraint_scale
|
|
77
|
+
if self.matrix is not None:
|
|
78
|
+
linalg = _scipy_linalg()
|
|
79
|
+
out = _validated_least_squares_solution(
|
|
80
|
+
self.matrix,
|
|
81
|
+
solve_rhs,
|
|
82
|
+
np.asarray(linalg.lstsq(self.matrix, solve_rhs, check_finite=False)[0], dtype=float),
|
|
83
|
+
)
|
|
84
|
+
if self.gauge_fixed and self.constraint_scale != 1.0 and out.shape[0] > 0:
|
|
85
|
+
out = out.copy()
|
|
86
|
+
out[-1] *= self.constraint_scale
|
|
87
|
+
return out
|
|
88
|
+
out = np.asarray(
|
|
32
89
|
_scipy_linalg().lu_solve(
|
|
33
90
|
(self.lu, self.pivots),
|
|
34
|
-
|
|
91
|
+
solve_rhs,
|
|
35
92
|
check_finite=False,
|
|
36
93
|
),
|
|
37
94
|
dtype=float,
|
|
38
95
|
)
|
|
96
|
+
if not np.all(np.isfinite(out)):
|
|
97
|
+
raise np.linalg.LinAlgError("Dense LU solve produced non-finite values.")
|
|
98
|
+
if self.gauge_fixed and self.constraint_scale != 1.0 and out.shape[0] > 0:
|
|
99
|
+
out = out.copy()
|
|
100
|
+
out[-1] *= self.constraint_scale
|
|
101
|
+
return out
|
|
102
|
+
|
|
103
|
+
def solve_gauge(self, divergence: np.ndarray) -> np.ndarray:
|
|
104
|
+
div_array = np.asarray(divergence, dtype=float)
|
|
105
|
+
if not np.all(np.isfinite(div_array)):
|
|
106
|
+
raise np.linalg.LinAlgError("Cannot solve gauge-fixed system with non-finite RHS.")
|
|
107
|
+
if self.splu is not None and self.gauge_fixed:
|
|
108
|
+
n = div_array.shape[0]
|
|
109
|
+
if n <= 1:
|
|
110
|
+
return np.zeros_like(div_array, dtype=float)
|
|
111
|
+
rhs_centered = div_array - np.mean(div_array, axis=0)
|
|
112
|
+
phi = np.zeros_like(div_array, dtype=float)
|
|
113
|
+
phi[1:] = self.splu.solve(rhs_centered[1:])
|
|
114
|
+
phi -= np.mean(phi, axis=0)
|
|
115
|
+
if not np.all(np.isfinite(phi)):
|
|
116
|
+
raise np.linalg.LinAlgError("Sparse gauge-fixed LU solve produced non-finite values.")
|
|
117
|
+
return phi
|
|
118
|
+
augmented_shape = (div_array.shape[0] + 1, *div_array.shape[1:])
|
|
119
|
+
augmented_rhs = np.zeros(augmented_shape, dtype=float)
|
|
120
|
+
augmented_rhs[:-1] = div_array
|
|
121
|
+
return self.solve(augmented_rhs)[:-1]
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _is_well_conditioned_splu(splu: Any, matrix_scale: float) -> bool:
|
|
125
|
+
if matrix_scale <= np.finfo(float).tiny or not np.isfinite(matrix_scale):
|
|
126
|
+
return False
|
|
127
|
+
u_diag = np.asarray(splu.U.diagonal(), dtype=float)
|
|
128
|
+
if u_diag.size == 0 or not np.all(np.isfinite(u_diag)):
|
|
129
|
+
return False
|
|
130
|
+
if not np.all(np.isfinite(splu.U.data)) or not np.all(np.isfinite(splu.L.data)):
|
|
131
|
+
return False
|
|
132
|
+
max_u = max(float(np.max(np.abs(splu.U.data))) if splu.U.nnz else 0.0, matrix_scale)
|
|
133
|
+
pivot_tol = 100.0 * np.finfo(float).eps * max_u
|
|
134
|
+
return bool(np.all(np.abs(u_diag) > pivot_tol))
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _factorize_dense_scipy(matrix_array: np.ndarray) -> ScipyLUFactorization:
|
|
138
|
+
import warnings
|
|
139
|
+
|
|
140
|
+
if not np.all(np.isfinite(matrix_array)):
|
|
141
|
+
raise np.linalg.LinAlgError("Cannot factorize a matrix with non-finite entries.")
|
|
142
|
+
|
|
143
|
+
linalg = _scipy_linalg()
|
|
144
|
+
with warnings.catch_warnings():
|
|
145
|
+
warnings.simplefilter("ignore", getattr(linalg, "LinAlgWarning", Warning))
|
|
146
|
+
lu, pivots = linalg.lu_factor(
|
|
147
|
+
matrix_array,
|
|
148
|
+
check_finite=False,
|
|
149
|
+
)
|
|
150
|
+
lu_array = np.asarray(lu, dtype=float)
|
|
151
|
+
pivots_array = np.asarray(pivots, dtype=int)
|
|
152
|
+
finite_lu = bool(np.all(np.isfinite(lu_array)))
|
|
153
|
+
max_entry = float(np.max(np.abs(lu_array))) if (lu_array.size and finite_lu) else 0.0
|
|
154
|
+
retained_matrix = (
|
|
155
|
+
matrix_array
|
|
156
|
+
if (
|
|
157
|
+
lu_array.size == 0
|
|
158
|
+
or not finite_lu
|
|
159
|
+
or max_entry <= np.finfo(float).tiny
|
|
160
|
+
or np.any(np.abs(np.diag(lu_array)) <= 100.0 * np.finfo(float).eps * max_entry)
|
|
161
|
+
)
|
|
162
|
+
else None
|
|
163
|
+
)
|
|
164
|
+
return ScipyLUFactorization(
|
|
165
|
+
lu_array,
|
|
166
|
+
pivots_array,
|
|
167
|
+
matrix=retained_matrix,
|
|
168
|
+
)
|
|
39
169
|
|
|
40
170
|
|
|
41
171
|
def _factorize_scipy(matrix: np.ndarray) -> ScipyLUFactorization:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
172
|
+
matrix_array = np.asarray(matrix, dtype=float)
|
|
173
|
+
if (
|
|
174
|
+
_allow_sparse_factorization()
|
|
175
|
+
and matrix_array.ndim == 2
|
|
176
|
+
and matrix_array.shape[0] == matrix_array.shape[1]
|
|
177
|
+
and matrix_array.shape[0] >= _SPARSE_FACTORIZATION_MIN_POINTS
|
|
178
|
+
and np.all(np.isfinite(matrix_array))
|
|
179
|
+
):
|
|
180
|
+
sparse, sparse_linalg = _scipy_sparse_modules()
|
|
181
|
+
csc = sparse.csc_matrix(matrix_array)
|
|
182
|
+
max_abs = float(np.max(np.abs(csc.data))) if csc.nnz else 0.0
|
|
183
|
+
if (
|
|
184
|
+
max_abs > np.finfo(float).tiny
|
|
185
|
+
and csc.nnz <= _SPARSE_FACTORIZATION_MAX_DENSITY * matrix_array.size
|
|
186
|
+
):
|
|
187
|
+
try:
|
|
188
|
+
splu = sparse_linalg.splu(csc)
|
|
189
|
+
if _is_well_conditioned_splu(splu, max_abs):
|
|
190
|
+
return ScipyLUFactorization(
|
|
191
|
+
np.empty((0, 0), dtype=float),
|
|
192
|
+
np.empty(0, dtype=int),
|
|
193
|
+
splu=splu,
|
|
194
|
+
gauge_fixed=False,
|
|
195
|
+
)
|
|
196
|
+
except (RuntimeError, np.linalg.LinAlgError):
|
|
197
|
+
LOGGER.debug(
|
|
198
|
+
"Sparse LU factorization failed; falling back to dense LU.",
|
|
199
|
+
exc_info=True,
|
|
200
|
+
)
|
|
201
|
+
return _factorize_dense_scipy(matrix_array)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _gauge_constraint_scale(matrix: np.ndarray) -> float:
|
|
205
|
+
if matrix.size == 0 or not np.all(np.isfinite(matrix)):
|
|
206
|
+
return 1.0
|
|
207
|
+
if matrix.ndim == 2 and matrix.shape[0] == matrix.shape[1]:
|
|
208
|
+
diag_max = float(np.max(np.abs(np.diag(matrix))))
|
|
209
|
+
if diag_max > np.finfo(float).tiny:
|
|
210
|
+
return diag_max
|
|
211
|
+
entry_max = float(np.max(np.abs(matrix)))
|
|
212
|
+
if entry_max > np.finfo(float).tiny:
|
|
213
|
+
return entry_max
|
|
214
|
+
return 1.0
|
|
47
215
|
|
|
48
216
|
|
|
49
217
|
def _factorize_gauge_fixed(matrix: np.ndarray) -> ScipyLUFactorization:
|
|
50
|
-
|
|
218
|
+
matrix_array = np.asarray(matrix, dtype=float)
|
|
219
|
+
n = matrix_array.shape[0]
|
|
220
|
+
if (
|
|
221
|
+
_allow_sparse_factorization()
|
|
222
|
+
and matrix_array.ndim == 2
|
|
223
|
+
and matrix_array.shape[1] == n
|
|
224
|
+
and n >= _SPARSE_FACTORIZATION_MIN_POINTS
|
|
225
|
+
and np.all(np.isfinite(matrix_array))
|
|
226
|
+
):
|
|
227
|
+
sparse, sparse_linalg = _scipy_sparse_modules()
|
|
228
|
+
csc = sparse.csc_matrix(matrix_array)
|
|
229
|
+
max_abs = float(np.max(np.abs(csc.data))) if csc.nnz else 0.0
|
|
230
|
+
if (
|
|
231
|
+
max_abs > np.finfo(float).tiny
|
|
232
|
+
and csc.nnz <= _SPARSE_FACTORIZATION_MAX_DENSITY * matrix_array.size
|
|
233
|
+
):
|
|
234
|
+
n_components = sparse.csgraph.connected_components(
|
|
235
|
+
csc,
|
|
236
|
+
directed=False,
|
|
237
|
+
return_labels=False,
|
|
238
|
+
)
|
|
239
|
+
row_col_tol = 100.0 * np.finfo(float).eps * max_abs
|
|
240
|
+
if (
|
|
241
|
+
n_components == 1
|
|
242
|
+
and np.allclose(
|
|
243
|
+
np.asarray(csc.sum(axis=1)).ravel(),
|
|
244
|
+
0.0,
|
|
245
|
+
atol=row_col_tol,
|
|
246
|
+
rtol=0.0,
|
|
247
|
+
)
|
|
248
|
+
and np.allclose(
|
|
249
|
+
np.asarray(csc.sum(axis=0)).ravel(),
|
|
250
|
+
0.0,
|
|
251
|
+
atol=row_col_tol,
|
|
252
|
+
rtol=0.0,
|
|
253
|
+
)
|
|
254
|
+
):
|
|
255
|
+
try:
|
|
256
|
+
splu = sparse_linalg.splu(csc[1:, 1:])
|
|
257
|
+
if _is_well_conditioned_splu(splu, max_abs):
|
|
258
|
+
return ScipyLUFactorization(
|
|
259
|
+
np.empty((0, 0), dtype=float),
|
|
260
|
+
np.empty(0, dtype=int),
|
|
261
|
+
splu=splu,
|
|
262
|
+
gauge_fixed=True,
|
|
263
|
+
)
|
|
264
|
+
except (RuntimeError, np.linalg.LinAlgError):
|
|
265
|
+
LOGGER.debug(
|
|
266
|
+
"Sparse gauge-fixed LU factorization failed; falling back to dense LU.",
|
|
267
|
+
exc_info=True,
|
|
268
|
+
)
|
|
269
|
+
constraint_scale = _gauge_constraint_scale(matrix_array)
|
|
270
|
+
constraint = np.full(n, constraint_scale, dtype=float)
|
|
51
271
|
augmented = np.block(
|
|
52
272
|
[
|
|
53
|
-
[
|
|
273
|
+
[matrix_array, constraint[:, np.newaxis]],
|
|
54
274
|
[constraint[np.newaxis, :], np.zeros((1, 1), dtype=float)],
|
|
55
275
|
]
|
|
56
276
|
)
|
|
57
|
-
|
|
277
|
+
dense = _factorize_dense_scipy(augmented)
|
|
278
|
+
return ScipyLUFactorization(
|
|
279
|
+
dense.lu,
|
|
280
|
+
dense.pivots,
|
|
281
|
+
matrix=dense.matrix,
|
|
282
|
+
gauge_fixed=True,
|
|
283
|
+
constraint_scale=constraint_scale,
|
|
284
|
+
)
|
|
58
285
|
|
|
59
286
|
|
|
60
287
|
def __getattr__(name: str) -> Any:
|
|
@@ -304,10 +531,12 @@ def _gauge_augmented_system(
|
|
|
304
531
|
) -> tuple[np.ndarray, np.ndarray]:
|
|
305
532
|
started = time.perf_counter()
|
|
306
533
|
try:
|
|
307
|
-
|
|
534
|
+
matrix_array = np.asarray(matrix, dtype=float)
|
|
535
|
+
constraint_scale = _gauge_constraint_scale(matrix_array)
|
|
536
|
+
constraint = np.full(len(rhs), constraint_scale, dtype=float)
|
|
308
537
|
augmented = np.block(
|
|
309
538
|
[
|
|
310
|
-
[
|
|
539
|
+
[matrix_array, constraint[:, np.newaxis]],
|
|
311
540
|
[constraint[np.newaxis, :], np.zeros((1, 1), dtype=float)],
|
|
312
541
|
]
|
|
313
542
|
)
|
|
@@ -337,17 +566,29 @@ def _solve_dense_scipy(
|
|
|
337
566
|
record_timing: Callable[[str, float], None] | None,
|
|
338
567
|
timing_prefix: str,
|
|
339
568
|
) -> np.ndarray:
|
|
569
|
+
import warnings
|
|
570
|
+
|
|
340
571
|
started = time.perf_counter()
|
|
341
572
|
linalg = _scipy_linalg()
|
|
342
573
|
if record_timing is not None:
|
|
343
574
|
record_timing(f"{timing_prefix}:scipy_import", time.perf_counter() - started)
|
|
344
575
|
started = time.perf_counter()
|
|
345
576
|
try:
|
|
346
|
-
|
|
577
|
+
with warnings.catch_warnings():
|
|
578
|
+
warnings.simplefilter("error", getattr(linalg, "LinAlgWarning", Warning))
|
|
579
|
+
result = np.asarray(
|
|
580
|
+
linalg.solve(matrix, rhs, assume_a="sym", check_finite=False),
|
|
581
|
+
dtype=float,
|
|
582
|
+
)
|
|
583
|
+
if not np.all(np.isfinite(result)) or (
|
|
584
|
+
_safe_relative_residual(matrix @ result - rhs, rhs)
|
|
585
|
+
> LEAST_SQUARES_RELATIVE_RESIDUAL_TOLERANCE
|
|
586
|
+
):
|
|
587
|
+
raise np.linalg.LinAlgError("Direct SciPy solve failed residual validation.")
|
|
347
588
|
if record_timing is not None:
|
|
348
589
|
record_timing(f"{timing_prefix}:scipy_solve", time.perf_counter() - started)
|
|
349
590
|
return result
|
|
350
|
-
except np.linalg.LinAlgError:
|
|
591
|
+
except (np.linalg.LinAlgError, Warning):
|
|
351
592
|
if record_timing is not None:
|
|
352
593
|
record_timing(
|
|
353
594
|
f"{timing_prefix}:scipy_solve_failed",
|
|
@@ -387,7 +628,12 @@ def _solve_dense_numpy(
|
|
|
387
628
|
) -> np.ndarray:
|
|
388
629
|
started = time.perf_counter()
|
|
389
630
|
try:
|
|
390
|
-
result = np.linalg.solve(matrix, rhs)
|
|
631
|
+
result = np.asarray(np.linalg.solve(matrix, rhs), dtype=float)
|
|
632
|
+
if not np.all(np.isfinite(result)) or (
|
|
633
|
+
_safe_relative_residual(matrix @ result - rhs, rhs)
|
|
634
|
+
> LEAST_SQUARES_RELATIVE_RESIDUAL_TOLERANCE
|
|
635
|
+
):
|
|
636
|
+
raise np.linalg.LinAlgError("Direct NumPy solve failed residual validation.")
|
|
391
637
|
if record_timing is not None:
|
|
392
638
|
record_timing(f"{timing_prefix}:numpy_solve", time.perf_counter() - started)
|
|
393
639
|
return result
|
|
@@ -470,7 +716,8 @@ def _demag_solve_diagnostics(
|
|
|
470
716
|
if _is_sparse_matrix(stiffness):
|
|
471
717
|
diagnostics["condition_numbers_skipped_for_sparse_storage"] = 1
|
|
472
718
|
else:
|
|
473
|
-
|
|
719
|
+
constraint_scale = _gauge_constraint_scale(np.asarray(stiffness, dtype=float))
|
|
720
|
+
constraint = np.full(len(divergence), constraint_scale, dtype=float)
|
|
474
721
|
augmented_stiffness = np.block(
|
|
475
722
|
[
|
|
476
723
|
[stiffness, constraint[:, np.newaxis]],
|
|
@@ -145,7 +145,11 @@ class Simulation(
|
|
|
145
145
|
self.last_probe_timings_seconds: dict[str, float] = {}
|
|
146
146
|
self.last_subfield_average_timings_seconds: dict[str, float] = {}
|
|
147
147
|
self.last_maxangle_timings_seconds: dict[str, float] = {}
|
|
148
|
-
self.
|
|
148
|
+
self._last_demag_solve_diagnostics: dict[str, float | int] | None = {}
|
|
149
|
+
self._last_demag_solve_diagnostics_inputs: (
|
|
150
|
+
tuple[Any, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]
|
|
151
|
+
| None
|
|
152
|
+
) = None
|
|
149
153
|
self._last_bem_operator_stats: BemOperatorStats | None = None
|
|
150
154
|
self.last_demag_internal_vectors: dict[str, np.ndarray] = {}
|
|
151
155
|
self._active_subfield_array_timings: dict[str, float] | None = None
|
|
@@ -159,45 +163,78 @@ class Simulation(
|
|
|
159
163
|
self.mat_of_mat_name: dict[str, Any] = {}
|
|
160
164
|
self.materials: list[Any] = []
|
|
161
165
|
self._fields: dict[str, Any] = {}
|
|
162
|
-
self._demag_cache_token: tuple[int,
|
|
166
|
+
self._demag_cache_token: tuple[int, ...] | None = None
|
|
163
167
|
self._demag_dipoles: tuple[np.ndarray, np.ndarray, float] | None = None
|
|
164
168
|
self._demag_nodal_cache: np.ndarray | None = None
|
|
165
|
-
self._demag_cell_cache_token: tuple[int,
|
|
169
|
+
self._demag_cell_cache_token: tuple[int, ...] | None = None
|
|
166
170
|
self._demag_cell_field_cache: tuple[np.ndarray, np.ndarray] | None = None
|
|
167
171
|
self._demag_geometry_cache_token: int | None = None
|
|
168
172
|
self._demag_boundary_faces_cache: list[tuple[int, tuple[int, int, int]]] | None = None
|
|
169
173
|
self._demag_bem_cache: tuple[str, np.ndarray, Any, BemOperatorStats] | None = None
|
|
170
|
-
self.
|
|
174
|
+
self._demag_bem_env_cache: tuple[Any, ...] | None = None
|
|
175
|
+
self._demag_fem_geometry_cache: (
|
|
176
|
+
tuple[str, np.ndarray, np.ndarray, Any, np.ndarray, np.ndarray] | None
|
|
177
|
+
) = None
|
|
178
|
+
self._demag_fem_env_cache: tuple[Any, ...] | None = None
|
|
171
179
|
self._demag_gauge_factorization_cache: ScipyLUFactorization | None = None
|
|
172
180
|
self._demag_dirichlet_factorization_cache: (
|
|
173
|
-
tuple[np.ndarray, np.ndarray, ScipyLUFactorization] | None
|
|
181
|
+
tuple[np.ndarray, np.ndarray, Any, ScipyLUFactorization] | None
|
|
182
|
+
) = None
|
|
183
|
+
self._demag_nodal_recovery_operator_cache: (
|
|
184
|
+
tuple[np.ndarray, np.ndarray, np.ndarray, int, Any] | None
|
|
185
|
+
) = None
|
|
186
|
+
self._demag_fem_divergence_operator_cache: (
|
|
187
|
+
tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, int, Any] | None
|
|
188
|
+
) = None
|
|
189
|
+
self._demag_cell_gradient_inputs_cache: (
|
|
190
|
+
tuple[
|
|
191
|
+
np.ndarray,
|
|
192
|
+
np.ndarray,
|
|
193
|
+
np.ndarray,
|
|
194
|
+
np.ndarray,
|
|
195
|
+
np.ndarray,
|
|
196
|
+
np.ndarray,
|
|
197
|
+
np.ndarray,
|
|
198
|
+
np.ndarray,
|
|
199
|
+
np.ndarray,
|
|
200
|
+
np.ndarray,
|
|
201
|
+
]
|
|
202
|
+
| None
|
|
203
|
+
) = None
|
|
204
|
+
self._demag_ms_values_cache: tuple[int, int, Any, np.ndarray, np.ndarray] | None = None
|
|
205
|
+
self._demag_volume_charge_scales_cache: (
|
|
206
|
+
tuple[int, int, Any, np.ndarray, np.ndarray] | None
|
|
174
207
|
) = None
|
|
175
|
-
self.
|
|
176
|
-
self.
|
|
177
|
-
self._nodal_ms_values_cache: tuple[int, np.ndarray] | None = None
|
|
178
|
-
self._demag_aux_cache_token: tuple[int, int] | None = None
|
|
208
|
+
self._nodal_ms_values_cache: tuple[tuple[int, int], np.ndarray] | None = None
|
|
209
|
+
self._demag_aux_cache_token: tuple[int, ...] | None = None
|
|
179
210
|
self._demag_phi_cache: np.ndarray | None = None
|
|
180
211
|
self._demag_rho_cache: np.ndarray | None = None
|
|
181
212
|
self._demag_volumes_cache: np.ndarray | None = None
|
|
182
|
-
self._mesh_points_cache: tuple[int, np.ndarray] | None = None
|
|
213
|
+
self._mesh_points_cache: tuple[int, Any, int, np.ndarray] | None = None
|
|
214
|
+
self._mesh_simplices_cache: tuple[int, Any, int, np.ndarray] | None = None
|
|
183
215
|
self._mesh_bounds_cache: tuple[int, np.ndarray, np.ndarray, np.ndarray] | None = None
|
|
184
216
|
self._mesh_edge_cache: tuple[int, np.ndarray] | None = None
|
|
185
217
|
self._simplex_volume_cache: tuple[int, np.ndarray, np.ndarray] | None = None
|
|
186
218
|
self._volume_average_node_weights_cache: tuple[int, int, np.ndarray] | None = None
|
|
187
219
|
self._incident_cell_volume_sums_cache: tuple[int, int, int, np.ndarray] | None = None
|
|
188
|
-
self._exchange_cache_token: tuple[int,
|
|
220
|
+
self._exchange_cache_token: tuple[int, ...] | None = None
|
|
189
221
|
self._exchange_nodal_cache: np.ndarray | None = None
|
|
222
|
+
self._exchange_operator_cache: (
|
|
223
|
+
tuple[int, Any, np.ndarray, Any, Any, np.ndarray, bool, np.ndarray] | None
|
|
224
|
+
) = None
|
|
190
225
|
self._exchange_spectral_bound_cache: tuple[int, float] | None = None
|
|
191
|
-
self._nodal_material_coefficients_cache:
|
|
226
|
+
self._nodal_material_coefficients_cache: (
|
|
227
|
+
tuple[tuple[int, int], NodalMaterialCoefficients] | None
|
|
228
|
+
) = None
|
|
192
229
|
self._nodal_anisotropy_groups_cache: (
|
|
193
|
-
tuple[int, tuple[NodalAnisotropyGroup, ...]] | None
|
|
230
|
+
tuple[tuple[int, int], tuple[NodalAnisotropyGroup, ...]] | None
|
|
194
231
|
) = None
|
|
195
232
|
self._anisotropy_fields_cache: tuple[
|
|
196
|
-
tuple[int, int],
|
|
233
|
+
tuple[int, int, int],
|
|
197
234
|
np.ndarray,
|
|
198
235
|
np.ndarray,
|
|
199
236
|
] | None = None
|
|
200
|
-
self._llg_affine_operator_cache: tuple[int, np.ndarray] | None = None
|
|
237
|
+
self._llg_affine_operator_cache: tuple[tuple[int, int, bool], np.ndarray] | None = None
|
|
201
238
|
self._probe_geometry_cache_token: int | None = None
|
|
202
239
|
self._probe_tetrahedral_cache: (
|
|
203
240
|
tuple[
|
|
@@ -16,13 +16,17 @@ class SimulationAnisotropyMixin(
|
|
|
16
16
|
SimulationAnisotropyMaterialMixin,
|
|
17
17
|
):
|
|
18
18
|
if TYPE_CHECKING:
|
|
19
|
-
_anisotropy_fields_cache: tuple[tuple[int, int], np.ndarray, np.ndarray] | None
|
|
19
|
+
_anisotropy_fields_cache: tuple[tuple[int, int, int], np.ndarray, np.ndarray] | None
|
|
20
20
|
|
|
21
21
|
def __getattr__(self, name: str) -> Any: ...
|
|
22
22
|
|
|
23
23
|
def _get_anisotropy_fields(self) -> tuple[np.ndarray, np.ndarray]:
|
|
24
24
|
m = np.asarray(self._fields["m"], dtype=np.float64)
|
|
25
|
-
token = (
|
|
25
|
+
token = (
|
|
26
|
+
self._mesh_geometry_token(),
|
|
27
|
+
self._material_state_token(),
|
|
28
|
+
id(self._fields["m"]),
|
|
29
|
+
)
|
|
26
30
|
cached = self._anisotropy_fields_cache
|
|
27
31
|
if cached is not None and cached[0] == token:
|
|
28
32
|
return cached[1], cached[2]
|
|
@@ -49,6 +53,8 @@ class SimulationAnisotropyMixin(
|
|
|
49
53
|
|
|
50
54
|
if not np.all(np.isfinite(field)) or not np.all(np.isfinite(energy)):
|
|
51
55
|
raise FloatingPointError("Anisotropy evaluation produced non-finite fields.")
|
|
56
|
+
field.setflags(write=False)
|
|
57
|
+
energy.setflags(write=False)
|
|
52
58
|
self._anisotropy_fields_cache = (token, field, energy)
|
|
53
59
|
return field, energy
|
|
54
60
|
|