# -*- coding: utf-8 -*-
#
# This file is subject to the terms and conditions defined in
# file 'LICENSE.txt', which is part of this source code package.
#
from typing import Optional, List, Tuple
import numpy as np
import os
import tempfile
import platform
import getpass
import sympy
from sympy import Symbol, DiracDelta, Matrix, Expr
from sympy.utilities.lambdify import lambdify
from Muscat.Types import NDArray
from Muscat.Helpers.Cache import CachedResultDecorator
from Muscat.FE.Spaces.SpaceBase import SpaceBase, SpaceAtIntegrationPoints
[docs]def DiracDeltaNumeric(data, der=None):
if abs(data) > 1e-15:
return 0
else:
return 1
cachePath = tempfile.gettempdir() + os.sep + f"{getpass.getuser()}_Muscat_SymSpaceBase_Create_{str(platform.python_version())}_{str(sympy.__version__)}safe_to_delete" + os.sep
@CachedResultDecorator(name="Create", path=cachePath, needDill=True)
def ProcessShapeFunctions(nbDim: int, symN: Matrix, NExceptions: List[Tuple[Expr, NDArray]], dNExceptions: List[Tuple[Expr, NDArray]]):
"""Function to process shape function and to compute derivatives and lambdas for the evaluation
Parameters
----------
nbDim : int
number of local coordinates of the shape functions
symN : Matrix
A sympy matrix containing the shape function for every dof
NExceptions : :List[Tuple[Expr, NDArray]]
the Value exceptions for the shape function (see PyrSpaces.py for a use of it)
dNExceptions : :List[Tuple[Expr, NDArray]]
the Value exceptions for the first derivative of shape function (see PyrSpaces.py for a use of it)
Returns
-------
lcoords : List[Symbols]
List of the coordinate of the elements
symN : Matrix
return of the argument symN
fct_N_Matrix : Callable
Function to evaluate the shape functions on a local position
symdNdxi : Matrix
Matrix of the derivatives of the shape functions
fct_dNdxi_Matrix : Callable
Function to evaluate the shape functions on a local position
symdNdxidxi : Matrix
Matrix of the second derivatives of the shape functions
fct_dNdxidxi_Matrix : Callable
Function to evaluate the shape functions on a local position
"""
allcoords = (Symbol("xi"), Symbol("eta"), Symbol("phi"))
lcoords = tuple(allcoords[x] for x in range(nbDim))
subsList = [(DiracDelta(0), 1.0), (DiracDelta(0, 1), 1.0), (DiracDelta(0, 2), 1.0)]
lambdifyList = [{"DiracDelta": DiracDeltaNumeric}, "numpy"]
nbSF = len(symN)
############# shape function treatment ########################
clean_N = symN.subs(subsList)
fct_N_Matrix = lambdify(allcoords, [clean_N[i] for i in range(nbSF)], lambdifyList)
if NExceptions is not None:
def GetModifiedShapeFunctions(_fct_N_Matrix, NExceptions):
def ShapeFunctions(xi_n, eta_n, phi_n):
for condition, values in NExceptions:
if abs(condition.subs({allcoords[0]: xi_n, allcoords[1]: eta_n, allcoords[2]: phi_n})) < 1.0e-6:
return values
else:
return _fct_N_Matrix(xi_n, eta_n, phi_n)
return ShapeFunctions
fct_N_Matrix = GetModifiedShapeFunctions(fct_N_Matrix, NExceptions)
############# shape functions first derivative #################
symdNdxi = [[None] * nbSF for i in range(nbDim)]
for i in range(nbDim):
for j in range(nbSF):
symdNdxi[i][j] = symN[j].diff(lcoords[i])
symdNdxi = Matrix(symdNdxi)
if symdNdxi.shape == (0, 0):
fct_dNdxi_Matrix = lambda xi, chi, phi: np.empty((0, 0))
else:
fct_dNdxi_Matrix = lambdify(allcoords, symdNdxi.subs(subsList), lambdifyList)
if dNExceptions is not None:
def GetModifiedShapeFunctionsDef(fct_dNdxi_Matrix, dNExceptions):
def ShapeFunctionsDer(xi_n, eta_n, phi_n):
for condition, values in dNExceptions:
if abs(condition.subs({allcoords[0]: xi_n, allcoords[1]: eta_n, allcoords[2]: phi_n})) < 1.0e-6:
return values
else:
return fct_dNdxi_Matrix(xi_n, eta_n, phi_n)
return ShapeFunctionsDer
fct_dNdxi_Matrix = GetModifiedShapeFunctionsDef(fct_dNdxi_Matrix, dNExceptions)
############ shape functions second derivative ################
symdNdxidxi = [None] * nbSF
fct_dNdxidxi_Matrix = [None] * nbSF
for i in range(nbSF):
symdNdxidxi[i] = [[0] * nbDim for j in range(nbDim)]
for j in range(nbDim):
for k in range(nbDim):
func = symN[i].diff(lcoords[j]).diff(lcoords[k])
symdNdxidxi[i][j][k] = func
symdNdxidxi[i] = Matrix(symdNdxidxi[i])
fct_dNdxidxi_Matrix[i] = lambdify(allcoords, symdNdxidxi[i].subs(subsList), lambdifyList)
return lcoords, symN, fct_N_Matrix, symdNdxi, fct_dNdxi_Matrix, symdNdxidxi, fct_dNdxidxi_Matrix
[docs]class SymSpaceBase(SpaceBase):
def __init__(self):
super().__init__()
# ----symbolic part use Create() to pass to the next step ------------
self.xi = Symbol("xi")
self.eta = Symbol("eta")
self.phi = Symbol("phi")
self.symN = None
self.symdNdxi = None
self.__created__ = False
def __getstate__(self):
return None
def __setstat__(self, state):
self.__init__()
[docs] def GetNumberOfShapeFunctions(self):
return len(self.symN)
[docs] def Created(self):
return self.__created__
[docs] def Create(self, force=False):
if self.__created__ and not force:
return
self.__created__ = True
nbDim = self.GetDimensionality()
self.lcoords, self.symN, self.fct_N_Matrix, self.symdNdxi, self.fct_dNdxi_Matrix, self.symdNdxidxi, self.fct_dNdxidxi_Matrix = ProcessShapeFunctions(
nbDim, self.symN, self.__dict__.get("NExceptions", None), self.__dict__.get("dNExceptions", None)
)
# This is not perfect because for P2 spaces we check only the number of shape functions
if np.all([dof[0] == "P" and nb == dof[1] for nb, dof in enumerate(self.dofAttachments)]):
self.fromConnectivityCompatible = True
[docs] def GetSpaceEvaluatedAt(self, points, weights):
self.Create()
res = SpaceAtIntegrationPoints()
res.SetIntegrationRule(self, points, weights)
return res
[docs] def GetPosOfShapeFunction(self, i, Xi):
valN = self.GetShapeFunc(self.posN[i, :])
return np.dot(valN, Xi).T
[docs] def GetShapeFunc_default(self, xi=0, chi=0, phi=0):
return self.fct_N_Matrix(xi, chi, phi)
[docs] def GetShapeFunc(self, qcoor):
return np.asarray(self.GetShapeFunc_default(*qcoor), dtype=float)
[docs] def GetShapeFuncDer_default(self, xi=0, chi=0, phi=0):
return self.fct_dNdxi_Matrix(xi, chi, phi)
[docs] def GetShapeFuncDer(self, qcoor):
return np.asarray(self.GetShapeFuncDer_default(*qcoor), dtype=float)
[docs] def GetShapeFuncDerDer_default(self, xi=0, chi=0, phi=0):
return [np.asarray(x(xi, chi, phi), dtype=float) for x in self.fct_dNdxidxi_Matrix]
[docs] def GetShapeFuncDerDer(self, qcoor):
return self.GetShapeFuncDerDer_default(*qcoor)
[docs]def CheckIntegrity():
np.testing.assert_equal(DiracDeltaNumeric(0), 1.0)
np.testing.assert_equal(DiracDeltaNumeric(1), 0.0)
a = SymSpaceBase()
a.Created()
return "ok"