# -*- 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 Union
import numpy as np
from Muscat.Types import MuscatFloat, MuscatIndex
from Muscat.Helpers.TextFormatHelper import TFormat
from Muscat.FE.Fields.FieldBase import FieldBase
from Muscat.FE.Spaces.FESpaces import LagrangeSpaceGeo
[docs]
class FEField(FieldBase):
def __init__(self,name=None,mesh=None,space=None,numbering=None,data=None):
super().__init__(name=name,mesh = mesh)
self.data = data
if space is None:
space = LagrangeSpaceGeo
self.space = space
if (numbering is None) and (mesh is not None) and (space is not None):
from Muscat.FE.DofNumbering import ComputeDofNumbering
if space is LagrangeSpaceGeo:
numbering = ComputeDofNumbering(mesh, space, fromConnectivity=True)
else:
numbering = ComputeDofNumbering(mesh, space)
self.numbering = numbering
[docs]
def Allocate(self,val=0):
if val == 0:
self.data = np.zeros(self.numbering.size,dtype=MuscatFloat)
else:
self.data = np.full(self.numbering.size,val,dtype=MuscatFloat)
[docs]
def copy(self):
return FEField(name=self.name,mesh=self.mesh,space=self.space,numbering=self.numbering, data=self.data.copy())
[docs]
def GetPointRepresentation(self,fillValue=0):
"""Generates a vector representation of the field data corresponding to the mesh.
This function transfers the field's data into a vector that is compatible in size
with the mesh, which can be useful for visualization purposes. If an entity in the
mesh does not have degrees of freedom (dofs), it is filled with a specified fill value,
which defaults to 0 if not provided.
Parameters
----------
fillValue : float, optional
The value used to fill entities with no degrees of freedom. Default is 0.
Returns
-------
numpy.ndarray
A numpy array representing the field data across the entire mesh. Entities
that originally had no dofs are filled with the specified fill value.
"""
from Muscat.FE.Spaces.FESpaces import LagrangeSpaceGeo
if self.space is LagrangeSpaceGeo and self.numbering.fromConnectivity:
return self.data
res = np.zeros(self.mesh.GetNumberOfNodes(), dtype=MuscatFloat)
restemp = np.zeros(self.mesh.GetNumberOfNodes(), dtype=MuscatFloat)
from Muscat.FE.Spaces.FESpaces import LagrangeSpaceGeo
from Muscat.MeshContainers.Filters.FilterObjects import ElementFilter
## loop over the elements in descending dimensionality order to fill the data and
#mask to track updated points
mask = np.zeros(self.mesh.GetNumberOfNodes(), dtype=bool)
#counter to tack the number of contribution to a point
cpt = np.zeros(self.mesh.GetNumberOfNodes(), dtype=MuscatIndex)
cpttmp = np.zeros(self.mesh.GetNumberOfNodes(), dtype=MuscatIndex)
for dim in [3,2,1,0]:
localMask = np.zeros(self.mesh.GetNumberOfNodes(), dtype=bool)
restemp.fill(0.)
cpttmp.fill(0)
for selection in ElementFilter(dimensionality=dim).IterOnTypesOnly(self.mesh, force=True):
elementType = selection.elements.elementType
selfNumbering = self.numbering[elementType]
if selfNumbering is None:
continue
geoElSpace = LagrangeSpaceGeo[elementType]
selfElSpace = self.space[elementType]
#evaluation of the shape function of this space at the LagrangeSpaceGeo pos
ir = (geoElSpace.posN, np.ones(geoElSpace.GetNumberOfShapeFunctions(), dtype=MuscatFloat))
saip = selfElSpace.GetSpaceEvaluatedAt(ir[0], ir[1])
# mask des element with a numbering
emask = np.all(selfNumbering >= 0,axis=1)
dataOnElementShapeFunctions = self.data[selfNumbering[emask,:]]
localConnectivity = selection.elements.connectivity[emask,:]
for sfn in range(geoElSpace.GetNumberOfShapeFunctions()):
# evaluation of the field at the geo space points
restemp[localConnectivity[:,sfn]] += saip.valN[sfn].dot(dataOnElementShapeFunctions.T)
cpttmp[localConnectivity[:,sfn]] += 1
localMask[localConnectivity[:,sfn]] = True
notmask = np.logical_not(mask)
res[notmask] += restemp[notmask]
cpt[notmask] += cpttmp[notmask]
np.logical_or(mask, localMask, out=mask)
res[mask] = res[mask]/cpt[mask]
if fillValue != 0.:
# last time we use mask, so we do an inplace operation
np.logical_not(mask,out=mask)
res[mask] = fillValue
return res
[docs]
def SetDataFromPointRepresentation(self, userdata, fillValue=0.):
if fillValue == 0.:
self.data = np.zeros(self.numbering.size)
else:
self.data = np.full(self.numbering.size,fillValue)
self.data[self.numbering.doftopointRight] = userdata[self.numbering.doftopointLeft]
[docs]
def GetCellRepresentation(self, fillValue: MuscatFloat = 0, method: Union[str, int] = 'mean') -> np.ndarray:
"""
Function to push the data from the field into a vector homogeneous to
the mesh cell (for visualisation for example). Entities with no dofs are filled
with the fillValue (default 0)
the method controls to transfer function
"""
if fillValue == 0.:
res = np.zeros(self.mesh.GetNumberOfElements(),dtype=MuscatFloat)
else:
res = np.full(self.mesh.GetNumberOfElements(),fillValue,dtype=MuscatFloat)
cpt = 0
for name, data in self.mesh.elements.items():
nbelems = data.GetNumberOfElements()
numbering = self.numbering[name]
if name is None:
cpt += nbelems
continue
if method == 'mean':
data = np.mean(self.data[numbering],axis=1)
elif method == 'max':
data = np.max(self.data[numbering],axis=1)
elif method == 'min':
data = np.min(self.data[numbering],axis=1)
elif method == 'maxdiff' or method == "maxdifffraction":
cols = self.data[numbering].shape[1]
op = np.zeros( (cols,(cols*(cols-1))//2) )
icpt = 0
for i in range(0,cols-1):
for j in range(i+1,cols):
op[i,icpt] = 1
op[j,icpt] = -1
icpt += 1
data = np.max(abs(self.data[numbering].dot(op)),axis=1)
if method == "maxdifffraction":
data /= np.mean(self.data[numbering],axis=1)
else:
col = min(int(method),self.data[numbering].shape[1])
data = self.data[numbering][:,col]
res[cpt:cpt+nbelems] = data
cpt += nbelems
return res
[docs]
def CheckCompatibility(self,B):
if isinstance(B,type(self)):
if id(self.mesh) != id(B.mesh):
raise (Exception("The support of the fields are not the same"))
if id(self.space) != id(B.space):
raise (Exception("The space of the fields are not the same"))
if self.numbering != B.numbering:
raise (Exception("The numbering of the fields are not the same"))
[docs]
def unaryOp(self,op):
res = type(self)(name = None,mesh=self.mesh,space=self.space, numbering = self.numbering )
res.data = op(self.data)
return res
[docs]
def binaryOp(self,other,op):
self.CheckCompatibility(other)
res = type(self)(name = None,mesh=self.mesh,space=self.space, numbering = self.numbering )
if isinstance(other,type(self)):
res.data = op(self.data,other.data)
return res
elif type(other).__module__ == np.__name__ and np.ndim(other) != 0 :
res = np.empty(other.shape,dtype=object)
for res_data,other_data in np.nditer([res,other],flags=["refs_ok"],op_flags=["readwrite"]):
res_data[...] = op(self,other_data)
return res
elif np.isscalar(other):
res.data = op(self.data,other)
return res
else:
raise Exception(f"operator {op} not valid for types :{type(self)} and {type(other)} ")
[docs]
def GetTestField(self):
from Muscat.FE.SymWeakForm import GetTestSufixChar
tc = GetTestSufixChar()
if len(self.name) == 0:
raise Exception("FEField must have a name")
elif self.name[-1] == tc:
raise Exception("this FEField is already a test field")
else:
return FEField(name=self.name+tc,mesh=self.mesh,space=self.space,numbering=self.numbering)
def __str__(self):
return f"<FEField object '{self.name}' ({id(self)})>"
def __repr__(self):
return str(self)
[docs]
def CheckIntegrity(GUI:bool=False):
from Muscat.MeshTools.MeshCreationTools import CreateCube
mesh = CreateCube([2.,3.,4.],[-1.0,-1.0,-1.0],[2./10, 2./10,2./10])
sig11 = FEField(name = "temp",mesh=mesh)
from Muscat.FE.Spaces.FESpaces import LagrangeSpaceGeo
sig11 = FEField(name = "temp",mesh=mesh, space= LagrangeSpaceGeo)
sig11 = FEField(name = "temp",mesh=mesh)
print(sig11.GetTestField())
sig11.Allocate()
print(sig11)
sig11 = sig11.GetPointRepresentation()
sig22 = sig11+0.707107
sig12 = 2*(-sig22)*5
A = sig11**2
B = sig11*sig22
C = sig22**2
D = 1.5*sig12*2
E = A-B+C+(D)**2
vonMises = np.sqrt(E)
print(vonMises.data)
print(np.linalg.norm([sig22, sig22 ] ).data )
print(A/C)
dummyField = FEField()
dummyField.data = np.arange(3)+1
print("dummyField")
print(dummyField*dummyField-dummyField**2/dummyField)
return "ok"
if __name__ == '__main__':
print(CheckIntegrity(True))# pragma: no cover