6
6
"""
7
7
8
8
from __future__ import annotations
9
+ from typing import Any , Callable
9
10
import itertools
10
11
from logging import getLogger
11
12
import dataclasses
12
13
import numpy as np
13
14
import numpy .typing as npt
14
15
16
+ try :
17
+ from numba import jit
18
+ except ImportError :
19
+
20
+ def jit (** kwargs : Any ) -> Callable [[Callable ], Callable ]:
21
+ return lambda f : f
22
+
15
23
16
24
mu_0 = 1.2566370614359173e-6 # Vacuum permeability [henry/meter]
17
25
@@ -125,6 +133,7 @@ def solve(
125
133
126
134
Returns sample points for the H, M, and B fields in the order of their appearance.
127
135
"""
136
+ c_r , M_s , a , k_p , alpha = coef .c_r , coef .M_s , coef .a , coef .k_p , coef .alpha
128
137
hmb_fragments : list [list [tuple [float , float , float ]]] = []
129
138
130
139
# noinspection PyPep8Naming
@@ -136,9 +145,9 @@ def sweep(H: float, M: float, sign: int) -> list[tuple[float, float, float]]:
136
145
# Integrate using Heun's method (instead of Euler's) for better stability.
137
146
dH = sign * dH_abs
138
147
try :
139
- dM_dH_1 = _dM_dH (coef , H = H , M = M , direction = sign )
148
+ dM_dH_1 = _dM_dH (c_r = c_r , M_s = M_s , a = a , k_p = k_p , alpha = alpha , H = H , M = M , direction = sign )
140
149
M_1 = M + dM_dH_1 * dH
141
- dM_dH_2 = _dM_dH (coef , H = H + dH , M = M_1 , direction = sign )
150
+ dM_dH_2 = _dM_dH (c_r = c_r , M_s = M_s , a = a , k_p = k_p , alpha = alpha , H = H + dH , M = M_1 , direction = sign )
142
151
M_2 = M + 0.5 * (dM_dH_1 + dM_dH_2 ) * dH
143
152
except (FloatingPointError , ZeroDivisionError ) as ex :
144
153
if idx < 10 :
@@ -193,40 +202,42 @@ def sweep(H: float, M: float, sign: int) -> list[tuple[float, float, float]]:
193
202
194
203
195
204
# noinspection PyPep8Naming
196
- def _dM_dH (coef : Coef , * , H : float , M : float , direction : int ) -> float :
205
+ @jit (nogil = True , nopython = True )
206
+ def _dM_dH (* , c_r : float , M_s : float , a : float , k_p : float , alpha : float , H : float , M : float , direction : int ) -> float :
207
+ # noinspection PyTypeChecker
197
208
"""
198
209
Evaluates the magnetic susceptibility derivative at the given point of the M(H) curve.
199
210
The result is sensitive to the sign of the H change; the direction is defined as sign(dH).
200
211
This implements the model described in "Jiles–Atherton Magnetic Hysteresis Parameters Identification", Pop et al.
201
212
202
- >>> fun = lambda H, M, d: _dM_dH(COEF_COMSOL_JA_MATERIAL, H=H, M=M, direction=d)
213
+ >>> fun = lambda H, M, d: _dM_dH(*dataclasses.asdict( COEF_COMSOL_JA_MATERIAL) , H=H, M=M, direction=d)
203
214
>>> assert np.isclose(fun(0, 0, +1), fun(0, 0, -1))
204
215
>>> assert np.isclose(fun(+1, 0, +1), fun(-1, 0, -1))
205
216
>>> assert np.isclose(fun(-1, 0, +1), fun(+1, 0, -1))
206
217
>>> assert np.isclose(fun(-1, 0.8e6, +1), fun(+1, -0.8e6, -1))
207
218
>>> assert np.isclose(fun(+1, 0.8e6, +1), fun(-1, -0.8e6, -1))
208
219
"""
209
- if direction not in (- 1 , + 1 ):
210
- raise ValueError (f"Invalid direction: { direction } " )
211
-
212
- H_e = H + coef .alpha * M
213
- M_an = coef .M_s * _langevin (H_e / coef .a )
214
- dM_an_dH_e = coef .M_s / coef .a * _dL_dx (H_e / coef .a )
215
- dM_irr_dH = (M_an - M ) / (coef .k_p * direction * (1 - coef .c_r ) - coef .alpha * (M_an - M ))
216
- return (coef .c_r * dM_an_dH_e + (1 - coef .c_r ) * dM_irr_dH ) / (1 - coef .alpha * coef .c_r )
220
+ assert direction in (- 1 , + 1 )
221
+ H_e = H + alpha * M
222
+ M_an = M_s * _langevin (H_e / a )
223
+ dM_an_dH_e = M_s / a * _dL_dx (H_e / a )
224
+ dM_irr_dH = (M_an - M ) / (k_p * direction * (1 - c_r ) - alpha * (M_an - M ))
225
+ return (c_r * dM_an_dH_e + (1 - c_r ) * dM_irr_dH ) / (1 - alpha * c_r ) # type: ignore
217
226
218
227
228
+ @jit (nogil = True , nopython = True )
219
229
def _langevin (x : float ) -> float :
220
230
"""
221
231
L(x) = coth(x) - 1/x
222
232
For tensors, the function is applied element-wise.
223
233
"""
224
234
if np .abs (x ) < _EPSILON : # For very small |x|, use the series expansion ~ x/3
225
235
return x / 3.0
226
- return float ( 1.0 / np .tanh (x ) - 1.0 / x )
236
+ return 1.0 / np .tanh (x ) - 1.0 / x # type: ignore
227
237
228
238
229
239
# noinspection PyPep8Naming
240
+ @jit (nogil = True , nopython = True )
230
241
def _dL_dx (x : float ) -> float :
231
242
"""
232
243
Derivative of Langevin L(x) = coth(x) - 1/x.
@@ -236,7 +247,7 @@ def _dL_dx(x: float) -> float:
236
247
return 1.0 / 3.0
237
248
# exact expression: -csch^2(x) + 1/x^2
238
249
# csch^2(x) = 1 / sinh^2(x)
239
- return float ( - 1.0 / (np .sinh (x ) ** 2 ) + 1.0 / (x ** 2 ))
250
+ return - 1.0 / (np .sinh (x ) ** 2 ) + 1.0 / (x ** 2 ) # type: ignore
240
251
241
252
242
253
_logger = getLogger (__name__ )
0 commit comments