Source code for BasicTools.Containers.SymExpr

# -*- 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 List, Dict
import numpy as np

from BasicTools.Helpers.BaseOutputObject import BaseOutputObject

[docs]class SymExprBase(BaseOutputObject): """Store read from string and store a symbolic expression. the expression read using sympy. the first and the second derivative are automatically computed. """ def __init__(self, string=None, symbols=None): """create and set a symbolic expression. A list of symbols (list[str]) can be provided to determine the free variable of the expression. if no symbols are provided the symbol 't' (for time) with value 0.0 is defined by default. """ super(SymExprBase,self).__init__() self._expression = "" """The string representation of the expression. """ self.constants = {} """Constants used to evaluate the expression. (free variables). """ if symbols is None: self.SetConstant("t",0.0) else: for s in symbols: self.SetConstant(s,0.0) if string is not None: self.SetExpression(string)
[docs] def SetExpression(self, string:str, _symbols: List[str]=None): """Set the expression to be used. A list of symbols (list[str]) can be provided to determine the free variable of the expression. if no symbols are provided the symbol 't' (for time) with value 0.0 is defined by default. Parameters ---------- string : str the string representation of the expression to be parsed by sympy _symbols : list[str], optional List of symbol to be used to parse the expression, by default None """ from sympy import Symbol from sympy import symbols from sympy import lambdify from sympy.parsing.sympy_parser import parse_expr if _symbols is None: self.stringSymbols = list(self.constants.keys()) _symbols = self.stringSymbols self._expression = string _expr = parse_expr(self._expression) self.func = lambdify(symbols(self.stringSymbols),_expr) self.dfuncd = dict() self.d2funcd2 = dict() for s in self.stringSymbols: self.dfuncd[s] = lambdify(symbols(self.stringSymbols),_expr.diff(Symbol(s))) for s2 in self.stringSymbols: self.d2funcd2[(s,s2)] = lambdify(symbols(self.stringSymbols),_expr.diff(Symbol(s)).diff(Symbol(s)))
[docs] def SetConstant(self, name:str, value:np.number): """Add/Set the value of the free variable of the expression Parameters ---------- name : str name of the variable value : np.number value """ self.constants[name]= value
[docs] def GetValue(self, pos=None)-> np.number: """Return the evaluated expression Parameters ---------- pos : _type_, optional Not Used, by default None Returns ------- np.number the evaluated expression """ return self.func(**self.constants)
[docs] def GetValueDerivative(self, coor:str, pos=None) -> np.number: """Return the first derivative of the expression with respect to coor Parameters ---------- coor : str the name of the variable to be used by the derivative pos : _type_, optional Not Used, by default None Returns ------- np.number the evaluation of the derivative of the expression with respect to coor. """ return self.dfuncd[coor](**self.constants)
[docs] def GetValueSecondDerivative(self, coor1: str, coor2: str, pos=None) -> np.number: """Return the second derivative of the expression with respect to coor1 and coor2 Parameters ---------- coor1 : str the name of the variable to be used by the derivative coor2 : str the name of the variable to be used by the derivative pos : _type_, optional Not Used, by default None Returns ------- np.number the evaluation of the derivative of the expression with respect to coor1 and coord2. d/dcoord1 * d/dcoord2 * expr """ return self.d2funcd2[(coor1,coor2)](**self.constants)
def __call__(self,pos=None) -> np.number: """Wrapper for the GetValue. Return the evaluated expression Parameters ---------- pos : _type_, optional Not used, by default None Returns ------- np.number the evaluated expression """ return self.GetValue(pos)
[docs]class SymExprWithPos(SymExprBase): """Store read from string and store a symbolic expression depending implicitly on (x,y,z). the expression read using sympy. the first and the second derivative are automatically computed. """ def __init__(self, string=None, symbols=None): super(SymExprWithPos,self).__init__(string=string, symbols=symbols)
[docs] def SetExpression(self,string): self.stringSymbols = list(self.constants.keys()) self.stringSymbols.extend("xyz") super().SetExpression(string,self.stringSymbols)
[docs] def GetValue(self,pos): res = self.func(x=pos[:,0],y=pos[:,1],z=pos[:,2], **self.constants) if res.size == pos.shape[0]: return res else: return np.full((pos.shape[0],),fill_value=res)
[docs] def GetValueDerivative(self,coor,pos): res =self.dfuncd[coor](x=pos[:,0],y=pos[:,1],z=pos[:,2], **self.constants) if res.size == pos.shape[0]: return res else: return np.full((pos.shape[0],),fill_value=res)
[docs] def GetValueSecondDerivative(self,coor1,coor2,pos): res = np.asarray(self.d2funcd2[(coor1,coor2)](x=pos[:,0],y=pos[:,1],z=pos[:,2], **self.constants)) if res.size == pos.shape[0]: return res else: return np.full((pos.shape[0],),fill_value=res)
def __str__(self): res = f"SymExprWithPos('{self._expression}') " return res def __repr__(self): return self.__str__()
[docs]def CreateSymExprWithPos(ops:Dict)->SymExprWithPos: """Simple wrapper to create a SymExprWithPos from a dict. ["val"] is used to extract the expression to be used for the contruction of the SymExprWithPos Parameters ---------- ops : Dict _description_ Returns ------- SymExprWithPos the Symbolic expression dependent of the position """ sym = SymExprWithPos() sym.SetExpression(ops["val"]) return sym
[docs]def CheckIntegrity(GUI=False): #minthreshold="0.00000" string = """<Pressure eTag="ET2" val="sin(3*t)+x**2" />""" import xml.etree.ElementTree as ET root = ET.fromstring(string) data = root.attrib data.pop("id",None) obj = CreateSymExprWithPos(data) obj.SetConstant("t",3.14159/6.) print(obj) print("data : ") data = np.array([[100.0,0.1,0.2 ],[0,0.1,0.2 ] ]) print(data) print("f = ", obj._expression) print(obj.GetValue(data)) print("dfdx :") print(obj.GetValueDerivative("x",data)) print("dfdt :") c = obj.GetValueDerivative("t",data) print(c) print("d2fdx2 :") print(obj.GetValueSecondDerivative("x","x",data)) import BasicTools.Containers.ElementNames as EN from BasicTools.Containers.UnstructuredMeshCreationTools import CreateCube mesh = CreateCube(dimensions=[10,11,12],spacing=[1.,1.,1.],ofTetras=False) if np.any(mesh.nodes[:,0]**2+np.sin(3*3.14159/6.) - obj.GetValue(mesh.nodes)): raise (ValueError("vectors does not match")) for name,data in mesh.elements.items(): if EN.dimension[name] == 3: data.tags.CreateTag("Inside3D",False).SetIds(np.arange(data.GetNumberOfElements())) data.tags.CreateTag("Outside3D",False) if EN.dimension[name] == 2: data.tags.CreateTag("InterSurf",False).SetIds(np.arange(data.GetNumberOfElements())) return "ok"
if __name__ == '__main__': print(CheckIntegrity(GUI=True))