Coverage for pyxfem/solidmechanics/largestrain.py: 15%
235 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-24 07:17 +0000
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-24 07:17 +0000
1"""
2Continuum Mechanics : Tensor Transformations and Acoustic Analysis Module.
4This module provides a suite of functions to perform pull-back and push-forward
5operations between different stress measures (Cauchy, PK1, PK2) and their
6associated elasticity tangents. It also includes tools for computing acoustic
7tensors and wave speeds in finite strain frameworks.
9Supported measures:
10 - Deformation gradients (F) and Cauchy-Green tensors (C, b).
11 - Stress tensors: Cauchy (s), First Piola-Kirchhoff (P), Second Piola-Kirchhoff (S).
12 - Elasticity tensors: Material (C), Spatial (c), and Mixed (dPdF).
14Compatibility:
15 All functions (but the acoustic tensors) are vectorized.
16 They support single tensors of shape (dim, dim)
17 or batches of tensors of shape (..., dim, dim) using NumPy broadcasting
18 via the `smallsize_tensor` backend.
19"""
20# pylint: disable = C0103
21import numpy as np
22from typing import Optional
23from numba import guvectorize, float64, float32, objmode
24import numba as nb
26from pyxfem.core.config import FASTMATH, FloatArray
27from pyxfem.core.smallsize_tensor import metric, sqrt22, trans, inv, det, \
28 det_invt, cofac, cross_ikjl, Id2, eigval22sym, AAT22, sqrt22_kernel
31def rightCauchyGreen(F):
32 """
33 Compute the Right Cauchy-Green deformation tensor C = F^T @ F.
35 Args:
36 F (ndarray): Deformation gradient tensor(s) of shape (..., dim, dim).
37 Returns:
38 ndarray: The material deformation tensor(s) C.
39 """
40 return metric(F)
42def leftCauchyGreen(F:FloatArray, out:FloatArray|None = None) -> FloatArray:
43 """
44 Compute the Left Cauchy-Green deformation tensor b = F @ F^T.
46 Args:
47 F (ndarray): Deformation gradient tensor(s) of shape (..., dim, dim).
48 Returns:
49 ndarray: The spatial deformation tensor(s) b.
50 """
51 if out is None :
52 out = np.empty_like(F)
53 if F.shape[-2:] == (2,2):
54 return AAT22(F, out)
55 out[:] = F@trans(F)
56 return out
58def PKII_to_PKI(PKII, F):
59 """
60 Transform Second Piola-Kirchhoff stress (S) to First Piola-Kirchhoff stress (P).
61 Formula: P = F @ S
63 Args:
64 PKII (ndarray): PK2 stress tensor(s) of shape (..., dim, dim).
65 F (ndarray): Deformation gradient tensor(s) of shape (..., dim, dim).
66 Returns:
67 ndarray: First Piola-Kirchhoff stress tensor(s).
68 """
69 return F@PKII
71def PKI_to_PKII(PKI, F):
72 """
73 Transform First Piola-Kirchhoff stress (P) to Second Piola-Kirchhoff stress (S).
74 Formula: S = inv(F) @ P
76 Args:
77 PKI (ndarray): PK1 stress tensor(s) of shape (..., dim, dim).
78 F (ndarray): Deformation gradient tensor(s) of shape (..., dim, dim).
79 Returns:
80 ndarray: Second Piola-Kirchhoff stress tensor(s).
81 """
82 return inv(F)@PKI
84def PKI_to_Cauchy(PKI, F):
85 """
86 Push-forward First Piola-Kirchhoff stress (P) to Cauchy stress (sigma).
87 Formula: sigma = (1/J) * P @ F^T
89 Args:
90 PKI (ndarray): PK1 stress tensor(s) of shape (..., dim, dim).
91 F (ndarray): Deformation gradient tensor(s) of shape (..., dim, dim).
92 Returns:
93 ndarray: Cauchy stress tensor(s).
94 """
95 J = det(F)[..., np.newaxis, np.newaxis]
96 return PKI@trans(F)/J
98def Cauchy_to_PKI(Cauchy, F):
99 """
100 Pull-back Cauchy stress (sigma) to First Piola-Kirchhoff stress (P).
101 Formula: P = J * sigma @ F^-T = sigma @ cofac(F)
103 Args:
104 Cauchy (ndarray): Cauchy stress tensor(s) of shape (..., dim, dim).
105 F (ndarray): Deformation gradient tensor(s) of shape (..., dim, dim).
106 Returns:
107 ndarray: First Piola-Kirchhoff stress tensor(s).
108 """
109 #return det(F)*Cauchy@invt(F)
110 print("## ", Cauchy.shape, F.shape, cofac(F).shape)
111 return Cauchy@cofac(F)
113def PKII_to_Cauchy(PKII, F):
114 """
115 Push-forward Second Piola-Kirchhoff stress (S) to Cauchy stress (sigma).
116 Formula: sigma = (1/J) * F @ S @ F^T
118 Args:
119 PKII (ndarray): PK2 stress tensor(s) of shape (..., dim, dim).
120 F (ndarray): Deformation gradient tensor(s) of shape (..., dim, dim).
121 Returns:
122 ndarray: Cauchy stress tensor(s).
123 """
124 J = det(F)[..., np.newaxis, np.newaxis]
125 return F@PKII@trans(F)/J
127def Cauchy_to_PKII(Cauchy, F):
128 """
129 Pull-back Cauchy stress (sigma) to Second Piola-Kirchhoff stress (S).
130 Formula: S = J * F^-1 @ sigma @ F^-T
132 Args:
133 Cauchy (ndarray): Cauchy stress tensor(s) of shape (..., dim, dim).
134 F (ndarray): Deformation gradient tensor(s) of shape (..., dim, dim).
135 Returns:
136 ndarray: Second Piola-Kirchhoff stress tensor(s).
137 """
138 J, FmT = det_invt(F)
139 J_view = np.reshape(J, (-1, 1, 1)) if np.ndim(J) > 0 else J
140 return J_view*trans(FmT)@Cauchy@FmT
142def dPKIIdE_to_dPKIdF(dPKIIdE, PKII, F):
143 """
144 Transform the material tangent (dS/dE) to the mixed tangent (dP/dF).
146 Args:
147 dPKIIdE (ndarray): Fourth-order material elasticity tensor(s).
148 PKII (ndarray): PK2 stress tensor(s).
149 F (ndarray): Deformation gradient tensor(s).
150 Returns:
151 ndarray: Mixed elasticity tensor(s) dP/dF.
152 """
153 dPKIdF = np.einsum("...iI, ...IJKL, ...kK->...iJkL", F, dPKIIdE, F)
154 dim = F.shape[-1]
155 Id = Id2[dim].reshape( (1,)*(F.ndim-2) + (dim, dim))
156 dPKIdF += cross_ikjl(Id, PKII)
157 return dPKIdF
159def dPKIdF_to_dPKIIdE(dPKIdF, PKI, F):
160 """
161 Transform the mixed tangent (dP/dF) to the material tangent (dS/dE).
163 Args:
164 dPKIdF (ndarray): Mixed elasticity tensor(s) dP/dF.
165 PKI (ndarray): PK1 stress tensor(s).
166 F (ndarray): Deformation gradient tensor(s).
167 Returns:
168 ndarray: Material elasticity tensor(s) dS/dE.
169 """
170 iF = inv(F)
171 PkII = iF@PKI
172 dim = F.shape[-1]
173 Id = Id2[dim].reshape( (1,)*(F.ndim-2) + (dim, dim))
174 tmp = dPKIdF - cross_ikjl(Id, PkII)
175 dPKIIdE = np.einsum("...Ii, ...iJkL, ...Kk->...IJKL", iF, tmp, iF)
176 return dPKIIdE
178def dPKIIdE_to_c(dPKIIdE, F):
179 """
180 Push-forward the material tangent (dS/dE) to the spatial elasticity tensor (c).
182 Args:
183 dPKIIdE (ndarray): Material elasticity tensor(s) (dPKII/dE).
184 F (ndarray): Deformation gradient tensor(s).
185 Returns:
186 ndarray: Spatial elasticity tensor(s) c.
187 """
188 J = det(F).reshape((-1,1,1,1,1))
189 c = np.einsum("...iI, ...jJ, ...kK, ...lL, ...IJKL -> ...ijkl",
190 F,F,F,F,dPKIIdE)/J
191 return c
193def c_to_dPKIIdE(c, F):
194 """
195 Pull-back the spatial elasticity tensor (c) to the material tangent (dS/dE).
197 Args:
198 c (ndarray): Spatial elasticity tensor(s).
199 F (ndarray): Deformation gradient tensor(s).
200 Returns:
201 ndarray: Material elasticity tensor(s) dS/dE.
202 """
203 J = det(F).reshape((-1,1,1,1,1))
204 iF = inv(F)
205 C = J*np.einsum("...Ii, ...Jj, ...Kk, ...Ll, ...ijkl -> ...IJKL",
206 iF,iF,iF,iF,c)
207 return C
209def dPKIdF_to_c(dPKIdF, PKI, F):
210 """
211 Convert the mixed tangent (dP/dF) to the spatial elasticity tensor (c).
213 Args:
214 dPKIdF (ndarray): Mixed elasticity tensor(s).
215 PKI (ndarray): PK1 stress tensor(s).
216 F (ndarray): Deformation gradient tensor(s).
217 Returns:
218 ndarray: Spatial elasticity tensor(s) c.
219 """
220 J = det(F)
221 dim = F.shape[-1]
222 s = PKI@trans(F)/J.reshape((-1,1,1))
223 Id = Id2[dim].reshape( (1,)*(F.ndim-2) + (dim, dim))
224 c = np.einsum("...jJ, ...iJkL, ...lL->...ijkl", F, dPKIdF , F)/J.reshape((-1,1,1,1,1))
225 c -= cross_ikjl(Id, s)
226 return c
228def c_dPKIdF(c, s, F):
229 """
230 Convert the spatial elasticity tensor (c) to the mixed tangent (dP/dF).
232 Args:
233 c (ndarray): Spatial elasticity tensor(s).
234 s (ndarray): Cauchy stress tensor(s).
235 F (ndarray): Deformation gradient tensor(s).
236 Returns:
237 ndarray: Mixed elasticity tensor(s) dP/dF.
238 """
239 J = det(F)
240 dim = F.shape[-1]
241 Id = Id2[dim].reshape( (1,)*(F.ndim-2) + (dim, dim))
242 tmp = J.reshape((-1,1,1,1,1))*(c + cross_ikjl(Id, s))
243 iF = inv(F)
244 dPdF = np.einsum("...Jj, ...Ll, ...ijkl -> ...iJkL", iF,iF, tmp)
245 return dPdF
247def acoustic_tensor_mixte(dPdF, N, J):
248 """
249 Compute the acoustic tensor using the mixed tangent dP/dF.
251 Args:
252 dPdF (ndarray): Mixed elasticity tensor(s).
253 N (ndarray): normal vector(s) in reference config (..., dim).
254 N is the piola pull back of a unit normal vector in current configuration
255 N = (1/J) F^T n with |n| = 1
256 J (float/ndarray): Determinant of F.
257 Returns:
258 ndarray: The acoustic tensor(s) of shape (..., dim, dim).
259 Qik = J AiJkL NJ N L
260 """
261 return np.einsum( "I,iIjJ,J->ij", N, dPdF, N)*J
263def acoustic_tensor_space_v0(c, s, n):
264 """
265 Compute the acoustic tensor using spatial variables (c, sigma).
267 Args:
268 c (ndarray): Spatial elasticity tensor(s).
269 s (ndarray): Cauchy stress tensor(s).
270 n (ndarray): Direction vector(s) in current config (..., dim).
271 Returns:
272 ndarray: The spatial acoustic tensor(s).
273 """
274 l = len(n.shape)-1
275 dim = n.shape[-1]
276 return np.einsum( "...j,...ijkl,...l-> ...ik", n, c, n) + np.einsum("...j,...jl,...l,...ik->...ik", n,s,n, Id2[dim].reshape(l*(1,)+(2,2) )) #n@(s@n)*Id2[dim]
280def acoustic_tensor_space_v1(c, s, n, out=None):
281 """ Version déroulée spécifiquement pour la 2D """
282 if n.shape[-1] != 2:
283 raise NotImplementedError()
284 if out is None:
285 out = np.empty(n.shape+(2,))
286 #out = np.empty_like(s)
288 n0, n1 = n[..., 0], n[..., 1]
290 # --- Calcul de la contrainte normale (scalaire) ---
291 # sigma_n = n0*(s00*n0 + s01*n1) + n1*(s10*n0 + s11*n1)
292 # Note : On utilise des vues temporaires, Python gère ça très vite
293 sigma_n = n0 * (s[..., 0, 0]*n0 + s[..., 0, 1]*n1) + \
294 n1 * (s[..., 1, 0]*n0 + s[..., 1, 1]*n1)
296 # --- Calcul de A00 ---
297 out[..., 0, 0] = (c[..., 0, 0, 0, 0] * n0 * n0 +
298 c[..., 0, 1, 0, 0] * n1 * n0 +
299 c[..., 0, 0, 0, 1] * n0 * n1 +
300 c[..., 0, 1, 0, 1] * n1 * n1) + sigma_n
302 # --- Calcul de A01 ---
303 out[..., 0, 1] = (c[..., 0, 0, 1, 0] * n0 * n0 +
304 c[..., 0, 1, 1, 0] * n1 * n0 +
305 c[..., 0, 0, 1, 1] * n0 * n1 +
306 c[..., 0, 1, 1, 1] * n1 * n1)
308 # --- Calcul de A10 ---
309 out[..., 1, 0] = (c[..., 1, 0, 0, 0] * n0 * n0 +
310 c[..., 1, 1, 0, 0] * n1 * n0 +
311 c[..., 1, 0, 0, 1] * n0 * n1 +
312 c[..., 1, 1, 0, 1] * n1 * n1)
314 # --- Calcul de A11 ---
315 out[..., 1, 1] = (c[..., 1, 0, 1, 0] * n0 * n0 +
316 c[..., 1, 1, 1, 0] * n1 * n0 +
317 c[..., 1, 0, 1, 1] * n0 * n1 +
318 c[..., 1, 1, 1, 1] * n1 * n1) + sigma_n
320 return out
322def acoustic_tensor_space(c: np.ndarray, s: np.ndarray, n: np.ndarray, out=None) -> np.ndarray:
323 # ...
324 if out is None:
325 batch_out = np.broadcast_shapes(c.shape[:-4], s.shape[:-2], n.shape[:-1])
326 out_dtype = n.dtype
327 out = np.empty(batch_out + (2, 2), dtype=out_dtype)
328 _acoustic_tensor_2d_guvec(c, s, n, out=out)
330 return out
332#acoustic_tensor_space = acoustic_tensor_space
334@nb.njit(cache=True, fastmath = FASTMATH)
335def acoustic_tensor_2d_kernel_njit(c, s, n, out):
336 n0 = n[0]
337 n1 = n[1]
338 sigma_n = n0 * (s[0, 0]*n0 + s[0, 1]*n1) + \
339 n1 * (s[1, 0]*n0 + s[1, 1]*n1)
341 out[0, 0] = (c[0, 0, 0, 0] * n0 * n0 + c[0, 1, 0, 0] * n1 * n0 +
342 c[0, 0, 0, 1] * n0 * n1 + c[0, 1, 0, 1] * n1 * n1) + sigma_n
343 out[0, 1] = (c[0, 0, 1, 0] * n0 * n0 + c[0, 1, 1, 0] * n1 * n0 +
344 c[0, 0, 1, 1] * n0 * n1 + c[0, 1, 1, 1] * n1 * n1)
345 out[1, 0] = (c[1, 0, 0, 0] * n0 * n0 + c[1, 1, 0, 0] * n1 * n0 +
346 c[1, 0, 0, 1] * n0 * n1 + c[1, 1, 0, 1] * n1 * n1)
347 out[1, 1] = (c[1, 0, 1, 0] * n0 * n0 + c[1, 1, 1, 0] * n1 * n0 +
348 c[1, 0, 1, 1] * n0 * n1 + c[1, 1, 1, 1] * n1 * n1) + sigma_n
350@guvectorize([
351 (float64[:,:,:,:], float64[:,:], float64[:], float64[:,:]),
352 (float32[:,:,:,:], float32[:,:], float32[:], float32[:,:])
353], '(m,m,m,m),(m,m),(m)->(m,m)', nopython=True, cache=True)
354def _acoustic_tensor_2d_guvec(c, s, n, out):
355 acoustic_tensor_2d_kernel_njit(c, s, n, out)
361def acoustic_impedance_tensor(rho, c, s, n, outZ = None):
362 """ compute the acoustic tensor impedance in direction n
363 c is the 4 order tangent spacial material tensor
364 s is the cauchy stress
365 n is a direction (norm 1 ..)
366 rho is the density
367 return OutZ : acoustic tensor in direction n
368 """
369 #print(rho.shape, c.shape, s.shape ,n.shape, outZ.shape)
371 #print(c.shape, s.shape, n.shape)
372 if outZ is None :
373 batch_out = np.broadcast_shapes(c.shape[:-4], s.shape[:-2], n.shape[:-1])
374 out_dtype = n.dtype
375 outZ = np.empty(batch_out + (2, 2), dtype=out_dtype)
377 acoustic_tensor_space(c,s,n, outZ)
378 #print("G", G.shape)
379 outZ *= rho[..., None, None]
380 return sqrt22(outZ, outZ)
383 #Z = sqrt22(rho.reshape(rho.shape+(1,1))*A)
384 #return outZ
386@nb.njit(parallel = True, cache = True, fastmath = FASTMATH)
387def acoustic_impedance_tensor_pnjit(rho, c, s, n, out):
388 #print(rho.shape, c.shape, s.shape ,n.shape, out.shape)
389 N = rho.shape[0]
390 assert(N == c.shape[0] and s.shape[0] == N and n.shape[0] == N and out.shape[0] == N)
391 assert( len(n.shape) == 3) # typically we have more than one normal for each
392 for j in nb.prange(N):
393 jrho = rho[j, 0]
394 jc = c[j, 0]
395 js = s[j, 0]
396 jvn = n[j]
397 jvout = out[j]
398 for r in range(n.shape[1]) :
399 jrout = jvout[r]
400 acoustic_tensor_2d_kernel_njit(jc, js, jvn[r], jrout )
401 jrout[0,0]*=jrho
402 jrout[0,1]*=jrho
403 jrout[1,0]*=jrho
404 jrout[1,1]*=jrho
405 sqrt22_kernel(jrout, jrout)
406 return out
408def acoustic_tensor_material(C, S, N, F):
409 """
410 Compute the acoustic tensor using material variables (C, S).
411 Args:
412 C (ndarray): Material elasticity tensor(s).
413 S (ndarray): PK2 stress tensor(s).
414 N (ndarray): normal vector(s) in reference config (..., dim).
415 N is the piola pull back of a unit normal vector in current configuration
416 N = (1/J) F^T n with |n| = 1
417 F (ndarray): Deformation gradient tensor(s).
418 Returns:
419 ndarray: The material acoustic tensor(s).
420 """
422 dim = len(N)
423 J =det(F)
425 A = J*(np.einsum( "iI,J,IJKL,kK,L -> ik", F, N, C, F, N ) + N.dot(S@N)*Id2[dim])
426 return A
428def wavespeed(A, rho):
429 """
430 Calculate wave speeds from the acoustic tensor and density.
431 For now only work for 2x2 acoustic tensor ....
432 Args:
433 A (ndarray): Acoustic tensor(s).
434 rho (float/ndarray): Material density.
435 Returns:
436 ndarray: Wave speeds (sqrt of eigenvalues of A/rho).
437 """
438 if A.shape[-1] != 2 :
439 raise NotImplementedError("Wavespeed only works for acoustic tensor in 2d space")
440 eigs = eigval22sym(A)
441 np.sqrt(eigs/rho)
442 return eigs
445def test_transport_formula():
446 """ checking the transformation formulas """
447 # pylint: disable=import-outside-toplevel
448 from pyxfem.solidmechanics.hyperelasticity import CompressibleNeoHookeen
449 from pyxfem.solidmechanics.elasticity import MaterialHypothesis
450 law_ps = CompressibleNeoHookeen(lamb = 1., mu=0.5, hyp=MaterialHypothesis.PLANESTRAIN)
451 law_3D = CompressibleNeoHookeen(lamb = 1., mu=0.5, hyp=MaterialHypothesis.DIM3)
453 F_2D = np.array([[2.,0.1], [0.2, 1.]])
454 array_of_F_2D = np.array([[[2.,0.3], [0.22, 1.]],
455 [[1.2,0.3], [0.21, 0.8]],
456 [[2.,0.3], [0.22, 1.]]] )
457 F_3D = np.array([[2.,0.1, -0.1], [0.05, 1.1, 0.2], [0.15, -0.13, 0.8] ])
458 array_of_F_3D = np.array([[[2.,0.1, -0.1], [0.05, 1.1, 0.2], [0.15, -0.13, 0.8] ],
459 [[3.,0.5, -0.1], [0.05, 1.1, 0.2], [-0.15, -0.13, 0.8] ] ])
462 for F, name, law in zip([F_2D, array_of_F_2D, F_3D, array_of_F_3D],
463 ["F 2D", "array of F 2D", "F3D", "array of F 3D"],
464 [law_ps, law_ps, law_3D, law_3D]):
465 print("### Start test for ", name )
466 C = rightCauchyGreen(F)
467 b = leftCauchyGreen(F)
468 P = law.PKI(F)
469 S = law.PKII(C)
470 s = law.Cauchy(b)
471 print(P, S, s)
472 print(s.shape)
473 P2 = PKII_to_PKI(S, F)
474 print('S-> P error ', np.linalg.norm(P-P2))
475 P3 = Cauchy_to_PKI(s, F)
476 print('s-> P error ', np.linalg.norm(P-P3))
477 S2 = PKI_to_PKII(P, F)
478 print('P-> S error ', np.linalg.norm(S-S2))
479 S3 = Cauchy_to_PKII(s, F)
480 print('s-> S error ', np.linalg.norm(S-S3))
481 s2 = PKI_to_Cauchy(P,F)
482 s3 = PKII_to_Cauchy(S,F)
483 print('P-> s error ', np.linalg.norm(s-s2))
484 print('S-> s error ', np.linalg.norm(s-s3))
486 dPdF = law.dPKIdF(F)
487 dSdE = 2.*law.dPKIIdC(C)
488 c = law.spacial_elasticty_tensor(b)
490 dPdF2 = dPKIIdE_to_dPKIdF(dSdE, S, F)
491 print("dSdE-> dPdF error", np.linalg.norm(dPdF- dPdF2) )
492 dPdF3 = c_dPKIdF(c, s, F)
493 print("c-> dPdF error", np.linalg.norm(dPdF- dPdF3) )
494 dSdE2 = dPKIdF_to_dPKIIdE(dPdF, P, F)
495 print("dPdF-> dSdE error", np.linalg.norm(dSdE- dSdE2) )
496 dSdE3 = c_to_dPKIIdE(c, F)
497 print("c-> dSdE error", np.linalg.norm(dSdE- dSdE3) )
498 c2 = dPKIIdE_to_c(dSdE, F)
499 print("dSdE-> c error", np.linalg.norm(c- c2) )
500 c3 = dPKIdF_to_c(dPdF, P, F)
501 print("dPdF-> c error", np.linalg.norm(c- c3) )
503def test_acoustic_tensor():
504 """ Testing Acoustic tensor and wave seed computation """
505 # pylint: disable=import-outside-toplevel
506 from pyxfem.solidmechanics.hyperelasticity import CompressibleNeoHookeen
507 from pyxfem.solidmechanics.elasticity import MaterialHypothesis
508 law = CompressibleNeoHookeen(lamb = 3., mu= 7., hyp=MaterialHypothesis.PLANESTRAIN)
509 rho0 = 1.
510 #F = np.array([[1.,0.], [0., 1.]])
511 F = np.array([[3.,1.], [0.8, 2]])
512 n = np.array([1.,0.8])
513 n /= np.linalg.norm(n)
514 dPdF = law.dPKIdF(F)
515 J = det(F)
516 N = trans(F)@n/J
517 A1 = acoustic_tensor_mixte(dPdF, N, J)
518 #print("A1", A1)
520 b = leftCauchyGreen(F)
521 s = law.Cauchy(b)
524 c = law.spacial_elasticty_tensor(b)
525 A2 = acoustic_tensor_space(c, s, n)
527 S = law.PKII(rightCauchyGreen(F))
528 C = law.dPKIIdE(rightCauchyGreen(F))
529 A3 = acoustic_tensor_material(C, S, N, F)
530 print("A1", A1)
531 print("A2", A2)
532 print("A3", A3)
534 J = det(F)
535 rho = rho0/J
537 print('wavespeed', wavespeed(A1, rho))
538 print('wavespeed', wavespeed(A2, rho))
539 print('wavespeed', wavespeed(A3, rho))
541 print("test Acoustic tensor for multiple direction")
542 ns = np.array([[1.,0.],[0,1], [np.sqrt(2)/2, np.sqrt(2)/2], [n[0], n[1]]])
543 As = acoustic_tensor_space(c[np.newaxis], s[np.newaxis], ns)
547 print(As.shape)
548 print(As)
549 c = np.stack((c, 2*c), axis=0)
550 s = np.stack((s, 2*s), axis=0)
553 As = acoustic_tensor_space(c[:,np.newaxis], s[:,np.newaxis], ns[np.newaxis])
554 print("2 states, 4 vectors ")
555 print(As.shape)
556 print(As)
557 rho = np.array([rho, rho])
558 print('wavespeed', wavespeed(As, rho[np.newaxis,:]))
561if __name__ == "__main__":
562 test_transport_formula()
563 test_acoustic_tensor()