easyfea 3.2.0__tar.gz → 3.2.2__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.
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/Operators/NonLinear.py +67 -63
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Simulations/_hyperelastic.py +90 -48
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Simulations/_phasefield.py +11 -5
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Simulations/_simu.py +166 -95
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Utilities/PyVista.py +20 -5
- {easyfea-3.2.0 → easyfea-3.2.2}/PKG-INFO +1 -1
- {easyfea-3.2.0 → easyfea-3.2.2}/easyfea.egg-info/PKG-INFO +1 -1
- {easyfea-3.2.0 → easyfea-3.2.2}/pyproject.toml +1 -1
- {easyfea-3.2.0 → easyfea-3.2.2}/AUTHORS.md +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/Elems/__init__.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/Elems/_beam.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/Elems/_hexa.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/Elems/_point.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/Elems/_prism.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/Elems/_quad.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/Elems/_seg.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/Elems/_tetra.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/Elems/_tri.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/Operators/Bilinear.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/Operators/Linear.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/Operators/__init__.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/__init__.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/_boundary_conditions.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/_field.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/_forms.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/_gauss.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/_group_elem.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/_linalg.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/_mesh.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/_mesher.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/FEM/_utils.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Geoms/__init__.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Geoms/_circle.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Geoms/_contour.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Geoms/_domain.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Geoms/_geom.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Geoms/_line.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Geoms/_points.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Geoms/_utils.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Models/Beam/__init__.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Models/Beam/_beam.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Models/Elastic/__init__.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Models/Elastic/_laws.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Models/HyperElastic/__init__.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Models/HyperElastic/_laws.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Models/HyperElastic/_state.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Models/__init__.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Models/_phasefield.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Models/_thermal.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Models/_utils.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Models/_weakforms.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Simulations/Solvers.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Simulations/__init__.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Simulations/_beam.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Simulations/_dic.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Simulations/_elastic.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Simulations/_thermal.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Simulations/_utils.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Simulations/_weakforms.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Utilities/Folder.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Utilities/GLTF.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Utilities/Matplotlib.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Utilities/MeshIO.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Utilities/Paraview.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Utilities/Terminal.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Utilities/USD.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Utilities/Vizir.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Utilities/__init__.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Utilities/_cache.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Utilities/_mpi.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Utilities/_observers.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Utilities/_params.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Utilities/_requires.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Utilities/_tic.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/Utilities/_types.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/__about__.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/EasyFEA/__init__.py +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/LICENSE.txt +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/README.md +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/easyfea.egg-info/SOURCES.txt +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/easyfea.egg-info/dependency_links.txt +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/easyfea.egg-info/requires.txt +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/easyfea.egg-info/top_level.txt +0 -0
- {easyfea-3.2.0 → easyfea-3.2.2}/setup.cfg +0 -0
|
@@ -397,14 +397,13 @@ def __AdaptiveTimeQuadratureStressTensor(
|
|
|
397
397
|
material: "_HyperElastic",
|
|
398
398
|
state_n: "HyperElasticState",
|
|
399
399
|
state_np1: "HyperElasticState",
|
|
400
|
+
coefK: float,
|
|
400
401
|
tol: float,
|
|
401
402
|
maxPoints: int,
|
|
402
403
|
) -> tuple["FeArray", "FeArray", int]:
|
|
403
404
|
r"""Per-element adaptive strain-path quadrature — the ``tol``-driven path of :func:`TimeQuadratureStressTensor`.
|
|
404
405
|
|
|
405
|
-
Each element refines along the nested chain ``1, 3, 5, 9, …`` until *its own* energy defect is within ``tol
|
|
406
|
-
|
|
407
|
-
Only the still-active elements are evaluated at each level — the rule is applied to that subset via :meth:`_StrainPathState._sliced` — so the constitutive cost tracks the hard elements, not the mesh. (Nodes shared with a coarser level are re-evaluated rather than cached; the active set shrinks fast, so that stays cheap and keeps the loop plain.) Returns ``(dWde_quad, d2Wde_quad)`` — each row carrying its element's accepted rule — and the largest point count any element reached.
|
|
406
|
+
Each element refines along the nested chain ``1, 3, 5, 9, …`` (capped at ``maxPoints``) until *its own* integrated energy defect is within ``tol`` — ``Σ_p V_(ep) |S:Δe − ΔW| ≤ tol · Σ_p V_(ep) |ΔW|``, ``V_(ep)`` the Gauss-point volume — then freezes, so points are spent only where the step is nonlinear. The defect is the quadrature error of the exact identity ``S:Δe = ΔW`` (``ΔW`` known from the endpoints), so the test is absolute; taking ``|·|`` before summing bounds the element's real energy drift and is safe against Gauss-point sign cancellation. Only still-active elements are evaluated at each level (via :meth:`_StrainPathState._sliced`), so cost tracks the hard elements. Returns ``(dWde_quad, d2Wde_quad, nPts_e)``, each row carrying its element's accepted rule and point count.
|
|
408
407
|
"""
|
|
409
408
|
groupElem = state_n.groupElem # state_n and state_np1 share the group
|
|
410
409
|
dim = groupElem.dim
|
|
@@ -417,7 +416,7 @@ def __AdaptiveTimeQuadratureStressTensor(
|
|
|
417
416
|
dW = material.Compute_W(state_np1) - material.Compute_W(state_n) # ΔW per point
|
|
418
417
|
dE = state_np1.Compute_GreenLagrange() - state_n.Compute_GreenLagrange()
|
|
419
418
|
dE_vec = Project_matrix_to_vector(dE[..., :dim, :dim]) # Δe (Kelvin-Mandel)
|
|
420
|
-
refW = einsum("ep,ep
|
|
419
|
+
refW = einsum("ep,ep->e", wJ_e_pg, np.abs(dW)) # Σ_p V |ΔW| (Ne,)
|
|
421
420
|
Ne, nPg, ncomp = *wJ_e_pg.shape[:2], dE_vec.shape[-1]
|
|
422
421
|
|
|
423
422
|
def at(
|
|
@@ -428,7 +427,8 @@ def __AdaptiveTimeQuadratureStressTensor(
|
|
|
428
427
|
dWde_quad = FeArray.zeros(Ne, nPg, ncomp)
|
|
429
428
|
d2Wde_quad = FeArray.zeros(Ne, nPg, ncomp, ncomp)
|
|
430
429
|
activeElements = np.arange(Ne) # elements still refining
|
|
431
|
-
|
|
430
|
+
nPts_e = np.zeros(Ne, dtype=int) # accepted point count per element (diagnostic)
|
|
431
|
+
nPts = 1
|
|
432
432
|
while activeElements.size:
|
|
433
433
|
nodes, weights = __clenshaw_curtis(nPts)
|
|
434
434
|
S = sum(
|
|
@@ -441,72 +441,72 @@ def __AdaptiveTimeQuadratureStressTensor(
|
|
|
441
441
|
defect = einsum("epi,epi->ep", S, dE_vec[activeElements]) - dW[activeElements]
|
|
442
442
|
next_nPts = 3 if nPts == 1 else 2 * nPts - 1 # next level in the chain
|
|
443
443
|
# accept an element once its own energy defect is within tol (all of them at the last level)
|
|
444
|
-
|
|
445
|
-
einsum("ep,ep
|
|
446
|
-
<= tol *
|
|
444
|
+
isAccepted = (next_nPts > max(maxPoints, 1)) | (
|
|
445
|
+
einsum("ep,ep->e", wJ_e_pg[activeElements], np.abs(defect))
|
|
446
|
+
<= tol * refW[activeElements]
|
|
447
447
|
)
|
|
448
|
-
if
|
|
449
|
-
|
|
450
|
-
dWde_quad[
|
|
451
|
-
# their tangent only: Σ_k
|
|
452
|
-
d2Wde_quad[
|
|
448
|
+
if isAccepted.any():
|
|
449
|
+
acceptedElems = activeElements[isAccepted] # elements accepting this rule
|
|
450
|
+
dWde_quad[acceptedElems] = S[isAccepted]
|
|
451
|
+
# their tangent only: Σ_k (w_k s_k / coefK) d2Wde, s=0 drops out (∂e/∂u = s B)
|
|
452
|
+
d2Wde_quad[acceptedElems] = sum(
|
|
453
453
|
(
|
|
454
|
-
(
|
|
454
|
+
(w * s / coefK) * material.Compute_d2Wde(at(s, acceptedElems))
|
|
455
455
|
for s, w in zip(nodes, weights)
|
|
456
456
|
if s
|
|
457
457
|
),
|
|
458
458
|
0.0,
|
|
459
459
|
)
|
|
460
|
-
|
|
461
|
-
activeElements = activeElements[~
|
|
460
|
+
nPts_e[acceptedElems] = nPts # record each frozen element's accepted rule
|
|
461
|
+
activeElements = activeElements[~isAccepted]
|
|
462
462
|
nPts = next_nPts
|
|
463
463
|
|
|
464
|
-
return dWde_quad, d2Wde_quad,
|
|
464
|
+
return dWde_quad, d2Wde_quad, nPts_e
|
|
465
465
|
|
|
466
466
|
|
|
467
467
|
def TimeQuadratureStressTensor(
|
|
468
468
|
material: "_HyperElastic",
|
|
469
469
|
state_n: "HyperElasticState",
|
|
470
|
-
|
|
470
|
+
state_t: "HyperElasticState",
|
|
471
471
|
state_np1: "HyperElasticState",
|
|
472
|
+
coefK: float,
|
|
472
473
|
nPoints: int,
|
|
473
474
|
tol: Optional[float] = None,
|
|
474
475
|
maxPoints: int = 33,
|
|
475
476
|
) -> tuple[np.ndarray, np.ndarray, int]:
|
|
476
477
|
r"""Tangent and residual for the PK2 stress **averaged along the strain path** of the step.
|
|
477
478
|
|
|
478
|
-
Returns ``(K_e, R_e)`` in ``(xi,yi,zi,...,xn,yn,zn)``. Where :func:`SecondPiolaKirchhoffStressTensor` samples the stress at one configuration, this
|
|
479
|
+
Returns ``(K_e, R_e)`` in ``(xi,yi,zi,...,xn,yn,zn)``. Where :func:`SecondPiolaKirchhoffStressTensor` samples the stress at one configuration, this averages it along the segment between the two end strains and tests it against ``B(u_t)``::
|
|
479
480
|
|
|
480
481
|
e(s) = e_n + s Δe , Δe = e_{n+1} - e_n , s ∈ [0, 1]
|
|
481
|
-
|
|
482
482
|
S_quad = ∫₀¹ ∂W/∂e(e(s)) ds ≈ Σ_k w_k ∂W/∂e(e(s_k))
|
|
483
|
-
R_e = ∫ B(
|
|
484
|
-
|
|
485
|
-
``(s_k, w_k)`` is the Clenshaw-Curtis rule on ``nPoints`` points (:func:`__clenshaw_curtis`); ``1, 2, 3`` are the midpoint, trapezoid and Simpson rules. Intermediate nodes are :class:`_StrainPathState`; ``s = 0, 1`` reuse the end states.
|
|
483
|
+
R_e = ∫ B(u_t)ᵀ · S_quad dΩ
|
|
486
484
|
|
|
487
|
-
|
|
485
|
+
``(s_k, w_k)`` is the Clenshaw-Curtis rule on ``nPoints`` points; ``1, 2, 3`` are the midpoint, trapezoid and Simpson rules (``nPoints = 1`` is the *average-strain* stress, not the midpoint-displacement one). At midpoint ``Δe = B(ū)·Δu`` exactly, so ``S_quad:Δe = ΔW`` once the ``s``-integral is exact — a **discrete gradient** whose energy defect is just the quadrature error, driven down spectrally (a quadratic ``W`` is exact at every rule). With ``tol`` set the rule is refined *per element* by :func:`__AdaptiveTimeQuadratureStressTensor`.
|
|
488
486
|
|
|
489
|
-
|
|
487
|
+
Tangent — only ``e_{n+1}`` depends on ``u_{n+1}`` (``∂e(s_k)/∂u_{n+1} = s_k B_{n+1}``), so::
|
|
490
488
|
|
|
491
|
-
|
|
489
|
+
coefK · K_e = coefK · A_geo(state_t, S_quad) + ∫ B(u_t)ᵀ [ Σ_k w_k s_k ℂ(e(s_k)) ] B_{n+1} dΩ
|
|
492
490
|
|
|
493
|
-
|
|
494
|
-
+ ∫ B(ū)ᵀ [ Σ_k w_k s_k ℂ(e(s_k)) ] B_{n+1} dΩ # pre-doubled
|
|
495
|
-
|
|
496
|
-
Built for :attr:`~EasyFEA.AlgoType.midpoint`'s ``coefK = 0.5`` as in :func:`GonzalezStressTensor`; the doubled weights sum to 1 whatever ``nPoints``. Pairing ``B(ū)`` with ``B_{n+1}`` makes ``K_e`` non-symmetric.
|
|
491
|
+
The geometric block is raw so it rides the caller's ``coefK`` chain factor ``∂u_t/∂u_{n+1}``; the material term is pre-scaled by ``1/coefK`` to survive it. Pairing ``B(u_t)`` with ``B_{n+1}`` makes ``K_e`` non-symmetric.
|
|
497
492
|
|
|
498
493
|
Parameters
|
|
499
494
|
----------
|
|
500
495
|
material
|
|
501
496
|
Hyperelastic constitutive law — supplies ``Compute_dWde(state)`` and ``Compute_d2Wde(state)``.
|
|
502
|
-
state_n,
|
|
503
|
-
Hyperelastic states at ``u_n``,
|
|
497
|
+
state_n, state_t, state_np1
|
|
498
|
+
Hyperelastic states at ``u_n``, the time-scheme base displacement ``u_t``, and ``u_{n+1}`` (same group / matrix type).
|
|
499
|
+
coefK
|
|
500
|
+
Time-scheme K-coefficient ``= ∂u_t/∂u_{n+1}`` for the base displacement ``u_t`` passed as
|
|
501
|
+
``state_t`` — ``0.5`` for :attr:`~EasyFEA.AlgoType.midpoint` (the only value that conserves energy),
|
|
502
|
+
``1`` for :attr:`~EasyFEA.AlgoType.newmark`, ``1−α`` for :attr:`~EasyFEA.AlgoType.hht`. Other schemes
|
|
503
|
+
give a consistent but non-conserving stress.
|
|
504
504
|
nPoints
|
|
505
505
|
Number of Clenshaw-Curtis points for the fixed rule (``tol is None``). Ignored when
|
|
506
506
|
adaptive, which always starts from 1 (the midpoint).
|
|
507
507
|
tol
|
|
508
508
|
If set, refine adaptively (see above) until the relative energy defect
|
|
509
|
-
|
|
509
|
+
``|S_quad:Δe − ΔW| / |ΔW|`` falls below ``tol``. ``None`` (default) keeps the fixed
|
|
510
510
|
``nPoints`` rule.
|
|
511
511
|
maxPoints
|
|
512
512
|
Adaptive only: cap on the number of points, by default 33. Refinement stops here even
|
|
@@ -515,20 +515,20 @@ def TimeQuadratureStressTensor(
|
|
|
515
515
|
Returns
|
|
516
516
|
-------
|
|
517
517
|
K_e : ndarray of shape ``(Ne, nPe·dim, nPe·dim)``
|
|
518
|
-
Consistent tangent, built
|
|
518
|
+
Consistent tangent, built so ``coefK · K_e = ∂R_e/∂u_{n+1}``.
|
|
519
519
|
R_e : ndarray of shape ``(Ne, nPe·dim)``
|
|
520
520
|
Internal residual force.
|
|
521
|
-
|
|
522
|
-
Clenshaw-Curtis points used — ``nPoints`` when fixed
|
|
523
|
-
|
|
521
|
+
nPts_e : ndarray of shape ``(Ne,)``
|
|
522
|
+
Clenshaw-Curtis points each element used — constant ``nPoints`` when fixed, per-element
|
|
523
|
+
when adaptive.
|
|
524
524
|
"""
|
|
525
525
|
|
|
526
|
-
groupElem =
|
|
527
|
-
wJ_e_pg = groupElem.Get_weightedJacobian_e_pg(
|
|
526
|
+
groupElem = state_t.groupElem
|
|
527
|
+
wJ_e_pg = groupElem.Get_weightedJacobian_e_pg(state_t.matrixType)
|
|
528
528
|
nPe = groupElem.nPe
|
|
529
529
|
dim = groupElem.dim
|
|
530
530
|
|
|
531
|
-
_,
|
|
531
|
+
_, B_t = __block_grad_B(state_t)
|
|
532
532
|
_, B_np1 = __block_grad_B(state_np1)
|
|
533
533
|
|
|
534
534
|
if tol is None:
|
|
@@ -546,8 +546,8 @@ def TimeQuadratureStressTensor(
|
|
|
546
546
|
state = _StrainPathState(state_n, state_np1, s)
|
|
547
547
|
dWde_quad += w * material.Compute_dWde(state)
|
|
548
548
|
if s != 0.0:
|
|
549
|
-
d2Wde_quad += (
|
|
550
|
-
|
|
549
|
+
d2Wde_quad += (w * s / coefK) * material.Compute_d2Wde(state)
|
|
550
|
+
nPts_e = np.full(groupElem.Ne, int(nPoints)) # every element uses the same rule
|
|
551
551
|
else:
|
|
552
552
|
# Adaptive: refine per element on the *energy defect*. This stress exists so that
|
|
553
553
|
# S_quad : Δe = ΔW exactly (a discrete gradient, for energy conservation); the only error
|
|
@@ -555,16 +555,16 @@ def TimeQuadratureStressTensor(
|
|
|
555
555
|
# is *known* from the endpoints — so the test is absolute (each level scored on its own).
|
|
556
556
|
# Refining element-by-element rather than the whole block spends points only where the
|
|
557
557
|
# material is nonlinear over the step (see __AdaptiveTimeQuadratureStressTensor).
|
|
558
|
-
dWde_quad, d2Wde_quad,
|
|
559
|
-
material, state_n, state_np1, tol, int(maxPoints)
|
|
558
|
+
dWde_quad, d2Wde_quad, nPts_e = __AdaptiveTimeQuadratureStressTensor(
|
|
559
|
+
material, state_n, state_np1, coefK, tol, int(maxPoints)
|
|
560
560
|
)
|
|
561
561
|
|
|
562
562
|
# not __second_piola_block: that pairs one B with itself, while here the tangent is
|
|
563
|
-
# differentiated at u_{n+1} but tested against B(
|
|
564
|
-
residual_e = einsum("ep,epi,epij->ej", wJ_e_pg, dWde_quad,
|
|
563
|
+
# differentiated at u_{n+1} but tested against B(u_t) — hence the non-symmetry.
|
|
564
|
+
residual_e = einsum("ep,epi,epij->ej", wJ_e_pg, dWde_quad, B_t)
|
|
565
565
|
tangent_e = einsum(
|
|
566
|
-
"ep,epji,epjk,epkl->eil", wJ_e_pg,
|
|
567
|
-
) + __geometric_tangent(wJ_e_pg,
|
|
566
|
+
"ep,epji,epjk,epkl->eil", wJ_e_pg, B_t, d2Wde_quad, B_np1
|
|
567
|
+
) + __geometric_tangent(wJ_e_pg, state_t, dWde_quad)
|
|
568
568
|
|
|
569
569
|
if dim == 2:
|
|
570
570
|
thickness = material.thickness
|
|
@@ -572,7 +572,7 @@ def TimeQuadratureStressTensor(
|
|
|
572
572
|
residual_e *= thickness
|
|
573
573
|
|
|
574
574
|
K_e, R_e = __reorder_dofs(dim, nPe, tangent_e, residual_e)
|
|
575
|
-
return K_e, R_e,
|
|
575
|
+
return K_e, R_e, nPts_e
|
|
576
576
|
|
|
577
577
|
|
|
578
578
|
def ActiveStressTensor(
|
|
@@ -628,17 +628,20 @@ def KelvinVoigtDamping(
|
|
|
628
628
|
material: "_HyperElastic",
|
|
629
629
|
state: "HyperElasticState",
|
|
630
630
|
velocity: np.ndarray,
|
|
631
|
-
) -> tuple[np.ndarray, np.ndarray]:
|
|
632
|
-
r"""Kelvin–Voigt viscous element contributions (
|
|
631
|
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
632
|
+
r"""Kelvin–Voigt viscous element contributions (Kgeo_e, R_e, C_e) for the
|
|
633
633
|
large-strain viscous force F_visco(u) = C(u)·v, with Σ_visco = η·Ė
|
|
634
634
|
(Green-Lagrange strain rate of velocity) and B = De(u)·grad.
|
|
635
635
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
and the coefC·C tangent).
|
|
636
|
+
Ordered ``(tangent, residual, extra)`` like the rest of this module.
|
|
637
|
+
|
|
639
638
|
- Kgeo_e — the configuration tangent ∂(C·v)/∂u at fixed velocity (geometric
|
|
640
639
|
stiffening from Σ_visco plus the ∂Ė/∂u term); the simulation adds it to
|
|
641
640
|
K_e so it rides coefK.
|
|
641
|
+
- R_e = thickness · η · ∫ Bᵀ Ė dΩ — the viscous residual, which the simulation
|
|
642
|
+
subtracts from F_e.
|
|
643
|
+
- C_e = thickness · η · ∫ Bᵀ B dΩ — the damping matrix; the simulation puts it
|
|
644
|
+
in slot 2 of (K, C, M, F), where it rides the coefC·C tangent.
|
|
642
645
|
|
|
643
646
|
Parameters
|
|
644
647
|
----------
|
|
@@ -653,11 +656,12 @@ def KelvinVoigtDamping(
|
|
|
653
656
|
Returns
|
|
654
657
|
-------
|
|
655
658
|
tuple
|
|
656
|
-
(None, None) when material.eta == 0 or velocity is None.
|
|
657
|
-
are (Ne, nPe·dim, nPe·dim)
|
|
659
|
+
(None, None, None) when material.eta == 0 or velocity is None. Kgeo_e and C_e
|
|
660
|
+
are (Ne, nPe·dim, nPe·dim) and R_e is (Ne, nPe·dim), all reordered to
|
|
661
|
+
(xi, yi, zi, ..., xn, yn, zn).
|
|
658
662
|
"""
|
|
659
663
|
if material.eta == 0.0 or velocity is None:
|
|
660
|
-
return None, None # type: ignore [return-value]
|
|
664
|
+
return None, None, None # type: ignore [return-value]
|
|
661
665
|
|
|
662
666
|
groupElem = state.groupElem
|
|
663
667
|
matrixType = state.matrixType
|
|
@@ -668,22 +672,22 @@ def KelvinVoigtDamping(
|
|
|
668
672
|
|
|
669
673
|
grad_e_pg, B_e_pg = __block_grad_B(state)
|
|
670
674
|
Beta_e_pg = state.Compute_Deta(velocity) @ grad_e_pg
|
|
675
|
+
sig_e_pg = material.eta * state.Compute_Edot_vec(velocity) # Σ_visco = η·Ė
|
|
671
676
|
|
|
672
677
|
# damping matrix C = thickness · η · ∫ Bᵀ B (fused einsum, see SPK above)
|
|
673
678
|
subscripts = "ep,epji,epjl->eil"
|
|
674
679
|
C_e = thickness * material.eta * einsum(subscripts, wJ_e_pg, B_e_pg, B_e_pg)
|
|
675
680
|
|
|
681
|
+
# viscous residual ∫ Bᵀ Σ_visco — same contraction as the active stress
|
|
682
|
+
residual_e = thickness * einsum("ep,epji,epj->ei", wJ_e_pg, B_e_pg, sig_e_pg)
|
|
683
|
+
|
|
676
684
|
# configuration tangent ∂(C·v)/∂u = geometric (∫ gradᵀ Sig grad) + material-like
|
|
677
685
|
# (η ∫ Bᵀ (∂Ė/∂u)) pieces
|
|
678
686
|
A_mat = material.eta * einsum(subscripts, wJ_e_pg, B_e_pg, Beta_e_pg)
|
|
679
|
-
A_geo = __geometric_tangent(
|
|
680
|
-
wJ_e_pg,
|
|
681
|
-
state,
|
|
682
|
-
material.eta * state.Compute_Edot_vec(velocity),
|
|
683
|
-
)
|
|
687
|
+
A_geo = __geometric_tangent(wJ_e_pg, state, sig_e_pg)
|
|
684
688
|
Kgeo_e = thickness * (A_mat + A_geo)
|
|
685
689
|
|
|
686
|
-
return __reorder_dofs(dim, nPe,
|
|
690
|
+
return __reorder_dofs(dim, nPe, Kgeo_e, residual_e, C_e)
|
|
687
691
|
|
|
688
692
|
|
|
689
693
|
def __skew(v: np.ndarray) -> np.ndarray:
|
|
@@ -15,6 +15,7 @@ from ..Utilities import Terminal, _types
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
16
16
|
from ..FEM import Mesh
|
|
17
17
|
from ..FEM import MatrixType, Operators
|
|
18
|
+
from ..Utilities._cache import cache_computed_values
|
|
18
19
|
|
|
19
20
|
# models
|
|
20
21
|
from ..Models import ModelType, Project_Kelvin, Result_strain_or_stress_field_e
|
|
@@ -169,7 +170,7 @@ class HyperElastic(_Simu):
|
|
|
169
170
|
stressType: "HyperElastic.StressType" = StressType.pointwise,
|
|
170
171
|
nPoints: int = 3,
|
|
171
172
|
useConsistentTangent: bool = True,
|
|
172
|
-
|
|
173
|
+
energyTol: Optional[float] = None,
|
|
173
174
|
) -> None:
|
|
174
175
|
r"""Selects the stress used by the internal force. Call **after** :py:meth:`~EasyFEA.Simulations._Simu.Solver_Set_Hyperbolic_Algorithm`.
|
|
175
176
|
|
|
@@ -180,9 +181,11 @@ class HyperElastic(_Simu):
|
|
|
180
181
|
total energy :math:`\mathrm{KE} + W` is conserved. See :class:`StressType` for the three
|
|
181
182
|
options and the operators for their construction.
|
|
182
183
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
184
|
+
Energy conservation for both non-default stresses rests on the midpoint identity
|
|
185
|
+
:math:`\Delta \eb = \Brm(\bar{\ub}) \cdot \Delta \ub`, so it holds only under
|
|
186
|
+
:attr:`~EasyFEA.AlgoType.midpoint`. ``gonzalez`` is intrinsically a midpoint discrete gradient and is
|
|
187
|
+
rejected off midpoint. ``quadrature`` also runs under any dynamic scheme — it scales its tangent by
|
|
188
|
+
that scheme's ``coefK`` and stays Newton-consistent, but conserves energy only at midpoint.
|
|
186
189
|
|
|
187
190
|
Parameters
|
|
188
191
|
----------
|
|
@@ -192,37 +195,41 @@ class HyperElastic(_Simu):
|
|
|
192
195
|
nPoints : int, optional
|
|
193
196
|
``quadrature`` only: number of Clenshaw-Curtis points, by default 3 (Simpson).
|
|
194
197
|
``1`` and ``2`` are the midpoint and trapezoid rules; more converges spectrally.
|
|
195
|
-
When ``
|
|
198
|
+
When ``energyTol`` is set this is the starting (minimum) level instead of a fixed count.
|
|
196
199
|
useConsistentTangent : bool, optional
|
|
197
200
|
``gonzalez`` only: if False, drop the discrete-gradient corrections from the
|
|
198
201
|
tangent. Same residual and same exact conservation, but Newton converges linearly
|
|
199
202
|
— a diagnostic, to measure what the consistent tangent is worth.
|
|
200
|
-
|
|
203
|
+
energyTol : float, optional
|
|
201
204
|
``quadrature`` only: if set, the rule is refined adaptively *element by element*
|
|
202
205
|
along the nested Clenshaw-Curtis chain ``1, 3, 5, 9, ...`` (capped at 33) — each
|
|
203
206
|
element stops once its own **integrated** relative energy defect
|
|
204
|
-
|
|
207
|
+
``∫|S_quad:Δe − ΔW| dΩ ≤ energyTol · ∫|ΔW| dΩ`` over that element is met (a volume
|
|
205
208
|
integral over its Gauss points, not a pointwise density). So energy is conserved to
|
|
206
|
-
``
|
|
209
|
+
``energyTol`` while points are spent only where the step is nonlinear. ``None``
|
|
207
210
|
(default) keeps the fixed ``nPoints`` rule.
|
|
208
211
|
"""
|
|
209
212
|
stressType = HyperElastic.StressType(stressType)
|
|
210
213
|
|
|
211
|
-
if stressType
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
"
|
|
214
|
+
if stressType == HyperElastic.StressType.gonzalez:
|
|
215
|
+
# gonzalez is the midpoint energy-momentum stress: its discrete gradient is built on ū and
|
|
216
|
+
# conserves energy only there — intrinsically midpoint-only.
|
|
217
|
+
assert self.algo == AlgoType.midpoint, (
|
|
218
|
+
f"the 'gonzalez' stress requires AlgoType.midpoint (got {self.algo}). "
|
|
219
|
+
"Call Solver_Set_Hyperbolic_Algorithm(dt, algo=AlgoType.midpoint) first."
|
|
216
220
|
)
|
|
221
|
+
# quadrature works with any dynamic scheme (its tangent is scaled by coefK); the dynamic-scheme
|
|
222
|
+
# requirement is checked at assembly, so the algo need not be set before this call.
|
|
217
223
|
assert nPoints >= 1, f"nPoints must be >= 1 (got {nPoints})."
|
|
218
224
|
|
|
219
|
-
self.__stressParams = (stressType, nPoints, useConsistentTangent,
|
|
220
|
-
|
|
225
|
+
self.__stressParams = (stressType, nPoints, useConsistentTangent, energyTol)
|
|
226
|
+
# diagnostic: per-element quadrature-point counts, last assembly
|
|
227
|
+
self.__nPts_e = None
|
|
221
228
|
|
|
222
229
|
def __Solver_Get_Stress_Params(
|
|
223
230
|
self,
|
|
224
231
|
) -> tuple["HyperElastic.StressType", int, bool, Optional[float]]:
|
|
225
|
-
"""Returns (stressType, nPoints, useConsistentTangent,
|
|
232
|
+
"""Returns (stressType, nPoints, useConsistentTangent, energyTol) internal-force props."""
|
|
226
233
|
return self.__stressParams
|
|
227
234
|
|
|
228
235
|
@property
|
|
@@ -230,6 +237,11 @@ class HyperElastic(_Simu):
|
|
|
230
237
|
"""Stress used by the internal force — see :py:meth:`Solver_Set_Stress`."""
|
|
231
238
|
return self.__Solver_Get_Stress_Params()[0]
|
|
232
239
|
|
|
240
|
+
@cache_computed_values
|
|
241
|
+
def __Mass_e(self, groupElem, rho, thickness, dim):
|
|
242
|
+
"""Constant element mass matrix ``thickness · ∫ρ N·N``."""
|
|
243
|
+
return thickness * Operators.Bilinear.UV(groupElem, rho, dof_n=dim)
|
|
244
|
+
|
|
233
245
|
def Construct_local_matrix_system(
|
|
234
246
|
self,
|
|
235
247
|
problemType,
|
|
@@ -237,31 +249,39 @@ class HyperElastic(_Simu):
|
|
|
237
249
|
):
|
|
238
250
|
"""Returns ``{groupElem: (K_e, C_e, M_e, F_e)}`` for the current Newton iterate.
|
|
239
251
|
|
|
240
|
-
Newton solves ``A(u)·Δu = -R(u)
|
|
252
|
+
Newton solves ``A(u)·Δu = -R(u)`` with ``A = coefK·K + coefC·C + coefM·M``. Per group of elements: ``K_e`` is the elastic tangent (plus the viscous configuration tangent ``Kgeo_e``), ``C_e`` the Kelvin–Voigt damping matrix, ``M_e`` the mass matrix (dynamic schemes only) and ``F_e = -R_e`` the complete residual — internal force together with the ``C_e·v_t + M_e·a_t`` inertia and damping terms.
|
|
241
253
|
"""
|
|
242
254
|
dim = self.dim
|
|
243
255
|
thickness = self.material.thickness if dim == 2 else 1
|
|
244
256
|
isDynamic = self.algo in AlgoType.Get_Hyperbolic_Types()
|
|
245
257
|
|
|
246
|
-
# current Newton-Raphson iterate; for dynamic schemes also capture the
|
|
247
|
-
#
|
|
258
|
+
# current Newton-Raphson iterate; for dynamic schemes also capture the velocity
|
|
259
|
+
# (Kelvin–Voigt viscosity) and the acceleration (inertia), both needed by F_e.
|
|
248
260
|
displacement = self._Solver_Get_Newton_Raphson_current_solution()
|
|
249
|
-
velocity = None
|
|
250
|
-
# Both non-default stresses are built from the step endpoints (u_n, u_{n+1}) on top
|
|
251
|
-
#
|
|
252
|
-
#
|
|
253
|
-
#
|
|
254
|
-
|
|
255
|
-
stressType, nPoints, useConsistentTangent, quadTol = (
|
|
261
|
+
velocity = accel = None
|
|
262
|
+
# Both non-default stresses are built from the step endpoints (u_n, u_{n+1}) on top of the scheme's
|
|
263
|
+
# base point u_t (ū at midpoint), so both need u_{n+1} kept before the scheme evaluation overwrites
|
|
264
|
+
# `displacement` with u_t. Re-checked here (not only in the setter) so that re-calling
|
|
265
|
+
# Solver_Set_Hyperbolic_Algorithm with another algo can't leave a stale selection.
|
|
266
|
+
stressType, nPoints, useConsistentTangent, energyTol = (
|
|
256
267
|
self.__Solver_Get_Stress_Params()
|
|
257
268
|
)
|
|
258
269
|
isPointwise = stressType == HyperElastic.StressType.pointwise
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
270
|
+
if stressType == HyperElastic.StressType.gonzalez:
|
|
271
|
+
# gonzalez is the midpoint energy-momentum stress: its discrete gradient Ŝ = S̄ + α Δe is built
|
|
272
|
+
# on ū and conserves energy only there — it is intrinsically midpoint-only.
|
|
273
|
+
assert (
|
|
274
|
+
self.algo == AlgoType.midpoint
|
|
275
|
+
), f"the 'gonzalez' stress requires AlgoType.midpoint (got {self.algo})."
|
|
276
|
+
elif stressType == HyperElastic.StressType.quadrature:
|
|
277
|
+
# quadrature builds a consistent tangent for any dynamic scheme via its `coefK = ∂u_t/∂u_{n+1}`;
|
|
278
|
+
# energy is conserved only at midpoint (coefK = 0.5) — see TimeQuadratureStressTensor.
|
|
279
|
+
assert (
|
|
280
|
+
isDynamic
|
|
281
|
+
), f"the 'quadrature' stress requires a dynamic (hyperbolic) time scheme (got {self.algo})."
|
|
262
282
|
u_np1 = displacement
|
|
263
283
|
if isDynamic:
|
|
264
|
-
displacement, velocity,
|
|
284
|
+
displacement, velocity, accel = self._Solver_Evaluate_u_v_a_for_time_scheme(
|
|
265
285
|
problemType, displacement
|
|
266
286
|
)
|
|
267
287
|
if not isPointwise:
|
|
@@ -270,7 +290,7 @@ class HyperElastic(_Simu):
|
|
|
270
290
|
errDetF = "det(F) < 0 - reduce load steps"
|
|
271
291
|
|
|
272
292
|
out = {}
|
|
273
|
-
|
|
293
|
+
list_nPts_e = [] # per-element point counts across groups (quadrature only)
|
|
274
294
|
for groupElem in self.mesh.Get_list_groupElem():
|
|
275
295
|
state = HyperElasticState(groupElem, displacement, matrixType)
|
|
276
296
|
|
|
@@ -298,15 +318,18 @@ class HyperElastic(_Simu):
|
|
|
298
318
|
useConsistentTangent,
|
|
299
319
|
)
|
|
300
320
|
elif stressType == HyperElastic.StressType.quadrature:
|
|
301
|
-
|
|
321
|
+
# coefK = ∂u_t/∂u_{n+1} scales the tangent for the active scheme (0.5 at midpoint).
|
|
322
|
+
coefK = self._Solver_Get_K_C_M_coefs_for_time_scheme()[0]
|
|
323
|
+
K_e, residual_e, nPts_e = (
|
|
302
324
|
Operators.NonLinear.TimeQuadratureStressTensor(
|
|
303
325
|
self.material,
|
|
304
326
|
*hyperElasticStates,
|
|
327
|
+
coefK,
|
|
305
328
|
nPoints,
|
|
306
|
-
|
|
329
|
+
energyTol,
|
|
307
330
|
)
|
|
308
331
|
)
|
|
309
|
-
|
|
332
|
+
list_nPts_e.append(nPts_e)
|
|
310
333
|
else:
|
|
311
334
|
raise NotImplementedError
|
|
312
335
|
|
|
@@ -324,24 +347,41 @@ class HyperElastic(_Simu):
|
|
|
324
347
|
|
|
325
348
|
F_e = -residual_e
|
|
326
349
|
|
|
327
|
-
# Kelvin–Voigt viscosity: C_e is the damping matrix (slot 2, rides
|
|
328
|
-
#
|
|
329
|
-
#
|
|
350
|
+
# Kelvin–Voigt viscosity: C_e is the damping matrix (slot 2, so it rides coefC
|
|
351
|
+
# in A), R_visco_e its residual, and Kgeo_e the configuration tangent
|
|
352
|
+
# ∂(C·v)/∂u, added to K_e so it rides coefK.
|
|
330
353
|
C_e = None
|
|
331
354
|
if self.material.eta != 0 and velocity is not None:
|
|
332
|
-
|
|
355
|
+
Kgeo_e, R_visco_e, C_e = Operators.NonLinear.KelvinVoigtDamping(
|
|
333
356
|
self.material, state, velocity
|
|
334
357
|
)
|
|
335
358
|
K_e += Kgeo_e
|
|
359
|
+
F_e -= R_visco_e
|
|
336
360
|
|
|
337
|
-
# mass matrix
|
|
361
|
+
# mass matrix and the inertia residual — dynamic schemes only
|
|
338
362
|
M_e = None
|
|
339
363
|
if isDynamic:
|
|
340
|
-
M_e = thickness
|
|
364
|
+
# M_e = thickness · ∫ρ N·N is constant across the solve, so it is cached (see __Mass_e)
|
|
365
|
+
# instead of rebuilt every Newton iteration. An array ρ is unhashable, so it cannot key the
|
|
366
|
+
# cache and is recomputed directly.
|
|
367
|
+
if isinstance(self.rho, np.ndarray):
|
|
368
|
+
M_e = thickness * Operators.Bilinear.UV(
|
|
369
|
+
groupElem, self.rho, dof_n=dim
|
|
370
|
+
)
|
|
371
|
+
else:
|
|
372
|
+
M_e = self.__Mass_e(groupElem, self.rho, thickness, dim)
|
|
373
|
+
F_e -= np.einsum(
|
|
374
|
+
"eij,ej->ei",
|
|
375
|
+
M_e,
|
|
376
|
+
groupElem.Locates_sol_e(accel, dim),
|
|
377
|
+
optimize=True,
|
|
378
|
+
)
|
|
341
379
|
|
|
342
380
|
out[groupElem] = (K_e, C_e, M_e, F_e)
|
|
343
381
|
|
|
344
|
-
|
|
382
|
+
# per-element Clenshaw-Curtis point counts this assembly, or None if the quadrature
|
|
383
|
+
# stress did not run (pointwise / gonzalez)
|
|
384
|
+
self.__nPts_e = np.concatenate(list_nPts_e) if list_nPts_e else None
|
|
345
385
|
|
|
346
386
|
return out
|
|
347
387
|
|
|
@@ -358,10 +398,8 @@ class HyperElastic(_Simu):
|
|
|
358
398
|
if self.algo in AlgoType.Get_Hyperbolic_Types():
|
|
359
399
|
iter["speed"] = self._Get_v_n(self.problemType)
|
|
360
400
|
iter["accel"] = self._Get_a_n(self.problemType)
|
|
361
|
-
if
|
|
362
|
-
self.
|
|
363
|
-
): # points the converged step used (quadrature stress only)
|
|
364
|
-
iter["quadNPoints"] = self.__quadNPoints
|
|
401
|
+
if self.__nPts_e is not None: # per-element point counts, quadrature only
|
|
402
|
+
iter["nPts_e"] = self.__nPts_e
|
|
365
403
|
|
|
366
404
|
return super().Save_Iter(iter)
|
|
367
405
|
|
|
@@ -491,10 +529,14 @@ class HyperElastic(_Simu):
|
|
|
491
529
|
elif result == "W_e":
|
|
492
530
|
values = self._Calc_W(False)
|
|
493
531
|
|
|
494
|
-
elif
|
|
495
|
-
|
|
532
|
+
elif result in ["Green-Lagrange", "Piola-Kirchhoff"] or (
|
|
533
|
+
("S" in result or "E" in result) and ("_norm" not in result)
|
|
534
|
+
):
|
|
535
|
+
# Green-Lagrange (E) and second Piola-Kirchhoff (S), group by group.
|
|
536
|
+
# "Green-Lagrange" and "Piola-Kirchhoff" carry no upper-case E or S, so they have to
|
|
537
|
+
# be matched by name: they are what Results_nodeFields_elementFields hands to Paraview.
|
|
496
538
|
|
|
497
|
-
isStress = "S" in result
|
|
539
|
+
isStress = result == "Piola-Kirchhoff" or "S" in result
|
|
498
540
|
res = (
|
|
499
541
|
result
|
|
500
542
|
if result in ["Green-Lagrange", "Piola-Kirchhoff"]
|
|
@@ -687,16 +687,22 @@ class PhaseField(_Simu):
|
|
|
687
687
|
return self._Calc_Psi_Elas()
|
|
688
688
|
|
|
689
689
|
elif result == "Wdef_e":
|
|
690
|
-
values = self._Calc_Psi_Elas()
|
|
690
|
+
values = self._Calc_Psi_Elas(returnScalar=False)
|
|
691
691
|
|
|
692
692
|
elif result == "Psi_Crack":
|
|
693
693
|
return self._Calc_Psi_Crack()
|
|
694
694
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
695
|
+
elif result == "psiP":
|
|
696
|
+
# group by group, in Get_list_groupElem order, so the (Ne,) result lines up with
|
|
697
|
+
# Get_Node_Values on meshes carrying several element groups
|
|
698
|
+
values = np.concatenate(
|
|
699
|
+
[
|
|
700
|
+
np.asarray(self.__Calc_psiPlus_e_pg(groupElem)).mean(1)
|
|
701
|
+
for groupElem in self.mesh.Get_list_groupElem()
|
|
702
|
+
]
|
|
703
|
+
)
|
|
698
704
|
|
|
699
|
-
|
|
705
|
+
elif result == "damage":
|
|
700
706
|
values = self.damage # type: ignore [assignment]
|
|
701
707
|
|
|
702
708
|
elif result in ["ux", "uy", "uz"]:
|