# -*- coding: utf-8 -*-
# ---------------------------------------------------------------------
#
# _____ _ _ _
# | ____|__| | ___| |_ _____(_)___ ___
# | _| / _` |/ _ \ \ \ /\ / / _ \ / __/ __|
# | |__| (_| | __/ |\ V V / __/ \__ \__ \
# |_____\__,_|\___|_| \_/\_/_\___|_|___/___/
# | \/ | ___ ___| |__ / _|_ __ ___ ___
# | |\/| |/ _ \/ __| '_ \| |_| '__/ _ \/ _ \
# | | | | __/\__ \ | | | _| | | __/ __/
# |_| |_|\___||___/_| |_|_| |_| \___|\___|
#
#
# Unit of Strength of Materials and Structural Analysis
# University of Innsbruck,
#
# Research Group for Computational Mechanics of Materials
# Institute of Structural Engineering, BOKU University, Vienna
#
# 2023 - today
#
# Matthias Neuner | matthias.neuner@boku.ac.at
# Thomas Mader | thomas.mader@bokut.ac.at
#
# This file is part of EdelweissMeshfree.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# The full text of the license can be found in the file LICENSE.md at
# the top level directory of EdelweissMeshfree.
# ---------------------------------------------------------------------
"""
Together with meshfree kernel functions (:class:`~BaseMeshfreeKernelFunction`), particles represent the basic building blocks of a particle based method. They
are used for computing the quadrature.
Implementing your own particles can be done easily by subclassing from
the abstract base class :class:`~BaseParticle`.
"""
from abc import abstractmethod
import numpy as np
from edelweissfe.nodecouplingentity.base.nodecouplingentity import (
BaseNodeCouplingEntity,
)
from edelweissmeshfree.meshfree.kernelfunctions.base.basemeshfreekernelfunction import (
BaseMeshfreeKernelFunction,
)
[docs]
class BaseParticle(BaseNodeCouplingEntity):
"""The BaseParticle class is an abstract base class for all particles.
If you want to implement a new particle, you have to inherit from this class.
Particles communicate with field variables (:class:`~FieldVariable`) through meshfree kernel functions (:class:`~BaseMeshfreeKernelFunction`),
which are attached to nodes.
During a simulation, particles are used to compute the quadrature of the weak form of the governing equations.
Accordingly, like elements and cells, they are responsible for computing the residual and stiffness matrix.
Naturally, and in contrast to elements, cells and cell elements, particles have an identical set of fields on each attached node.
Furthermore, due to the varying number attached nodes, no permutation is allowed.
For particles generally a node-wise layout is assumed.
For instance: [node_1_displacement, node_1_temperature, node_2_displacement, node_2_temperature, ...]
"""
@property
@abstractmethod
def dimension(self) -> int:
"""Defines the dimension of the particle, e.g., 1, 2 or 3."""
@property
@abstractmethod
def baseFields(self) -> list[str]:
"""Defines which fields are attached to the particle."""
@property
@abstractmethod
def propertyNames(self) -> list[str]:
"""Defines which properties are valid for the particle."""
[docs]
@abstractmethod
def setProperty(self, propertyName: str, propertyValue: np.ndarray):
"""Set a single property of the particle.
Parameters
----------
propertyName
The name of the property to be set.
propertyValue
The value of the property to be set.
"""
[docs]
@abstractmethod
def setProperties(self, properties: np.ndarray):
"""Set all properties of the particle.
The order of the properties is defined by the order of the
:attr:`propertyNames` property.
Parameters
----------
properties
The properties to be set.
"""
@property
@abstractmethod
def kernelFunctions(self) -> list[BaseMeshfreeKernelFunction]:
"""The kernel functions assigned to the particle."""
[docs]
@abstractmethod
def getBoundingBox(
self,
) -> np.ndarray:
"""Get the bounding box of the particle.
Returns
-------
np.ndarray
All coordinates for all bounding vertices."""
[docs]
@abstractmethod
def getVertexCoordinates(
self,
) -> np.ndarray:
"""The vertices defining the shape of the particle.
Returns
-------
np.ndarray
All coordinates for all bounding vertices."""
[docs]
@abstractmethod
def getFaceCoordinates(
self,
faceID: int,
) -> np.ndarray:
"""The vertices defining the shape of the particle.
Parameters
----------
faceID
The ID of the face to be returned.
Returns
-------
np.ndarray
All coordinates for all bounding vertices."""
[docs]
@abstractmethod
def getEvaluationCoordinates(
self,
) -> np.ndarray:
"""Get the evaluation coordinates of the particle.
Returns
-------
np.ndarray
All coordinates for all bounding vertices."""
[docs]
@abstractmethod
def getCenterCoordinates(
self,
) -> np.ndarray:
"""Get the center coordinates of the particle.
Returns
-------
np.ndarray
The center coordinates."""
[docs]
@abstractmethod
def getVolumeUndeformed(
self,
) -> float:
"""Get the undeformed volume of the particle.
Returns
-------
float
The undeformed volume."""
[docs]
@abstractmethod
def acceptStateAndPosition(
self,
):
"""Accept the computed state (in nonlinear iteration schemes) and the position."""
[docs]
@abstractmethod
def getResultArray(self, result: str, getPersistentView: bool = True, qp: int = 0) -> np.ndarray:
"""Get the array of a result, possibly as a persistent view which is continiously
updated by a MaterialPoint.
Parameters
----------
result
The name of the result.
getPersistentView
If true, the returned array should be continiously updated by the element.
qp
The quadrature point index. This is only relevant for higher order particles.
Returns
-------
np.ndarray
The result.
"""
[docs]
@abstractmethod
def initializeYourself(
self,
):
"""Initalize the particle to be ready for computing."""
[docs]
@abstractmethod
def prepareYourself(self, timeTotal: float, dTime: float):
"""Prepare the particle to be ready for computing."""
[docs]
@abstractmethod
def setInitialCondition(self, stateType: str, values: np.ndarray):
"""Assign initial conditions.
Parameters
----------
stateType
The type of initial state.
values
The numpy array describing the initial state.
"""
[docs]
@abstractmethod
def computePhysicsKernels(
self,
dU: np.ndarray,
P: np.ndarray,
K: np.ndarray,
timeTotal: float,
dTime: float,
):
"""Evaluate the kernel residual and stiffness for given time based for all assigned MaterialPoints.
Parameters
----------
dU
The current solution increment.
P
The external load vector to be defined.
K
The stiffness matrix to be defined.
timeTotal
The current total time.
dTime
The time increment.
"""
[docs]
@abstractmethod
def computeBodyLoad(
self,
loadType: str,
load: np.ndarray,
P: np.ndarray,
K: np.ndarray,
timeTotal: float,
dTime: float,
):
"""Compute bulk loads (body loads) for given time based for all assigned MaterialPoints.
Parameters
----------
loadType
The type of load to be computed (e.g., 'bodyforce')
load
The float (vector) describing the load.
P
The external load vector to be defined.
K
The stiffness matrix to be defined.
timeTotal
The current total time.
dTime
The time increment.
"""
[docs]
@abstractmethod
def computeDistributedLoad(
self,
loadType: str,
surfaceID: int,
load: np.ndarray,
P: np.ndarray,
K: np.ndarray,
timeTotal: float,
dTime: float,
):
"""Compute distributed (surface) a for given time based for a specific assigned material point.
Parameters
----------
loadType
The type of load to be computed (e.g., 'pressure')
surfaceID
The ID describing the surface of the material point.
load
The float (vector) describing the load.
P
The external load vector to be defined.
K
The stiffness matrix to be defined.
timeTotal
The current total time.
dTime
The time increment.
"""
[docs]
@abstractmethod
def computePhysicsKernelsExplicit(
self,
dU: np.ndarray,
P: np.ndarray,
timeTotal: float,
dTime: float,
):
"""Evaluate the kernel residual and stiffness for given time based for all assigned MaterialPoints.
Parameters
----------
dU
The current solution increment.
P
The external load vector to be defined.
timeTotal
The current total time.
dTime
The time increment.
"""
[docs]
@abstractmethod
def computeBodyLoadExplicit(
self,
loadType: str,
load: np.ndarray,
P: np.ndarray,
timeTotal: float,
dTime: float,
):
"""Compute bulk loads (body loads) for given time based for all assigned MaterialPoints.
Parameters
----------
loadType
The type of load to be computed (e.g., 'bodyforce')
load
The float (vector) describing the load.
P
The external load vector to be defined.
timeTotal
The current total time.
dTime
The time increment.
"""
[docs]
@abstractmethod
def computeDistributedLoadExplicit(
self,
loadType: str,
surfaceID: int,
load: np.ndarray,
P: np.ndarray,
timeTotal: float,
dTime: float,
):
"""Compute distributed (surface) a for given time based for a specific assigned material point.
Parameters
----------
loadType
The type of load to be computed (e.g., 'pressure')
surfaceID
The ID describing the surface of the material point.
load
The float (vector) describing the load.
P
The external load vector to be defined.
timeTotal
The current total time.
dTime
The time increment.
"""
[docs]
@abstractmethod
def computeLumpedInertia(
self,
M: np.ndarray,
):
"""Compute the lumped mass matrix for explicit time integration.
Parameters
----------
M
The mass matrix to be defined.
"""
[docs]
@abstractmethod
def assignMeshfreeKernelFunctions(self, kernelFunctions: list[BaseMeshfreeKernelFunction]):
"""Assign the meshfree kernel functions to the particle.
Parameters
----------
shapeFunctions
The meshfree shape functions.
"""
[docs]
@abstractmethod
def getInterpolationVector(self, coordinate: np.ndarray) -> np.ndarray:
"""Get the interpolation vector for a given global coordinate.
Returns
-------
np.ndarray
The interpolation vector for all nodes.
"""
[docs]
@abstractmethod
def vci_getNumberOfConstraints(
self,
) -> int:
"""Get the number of constraints if a variationally consistent integration is used.
Returns
-------
int
The number of VCI constraints.
"""
[docs]
@abstractmethod
def vci_compute_Test_P_BoundaryIntegral(
self, R_AiC: np.ndarray, boundarySurfaceVector: np.ndarray, boundaryFaceID: int
):
"""Compute the boundary integral for the test function.
Parameters
----------
R_AiC
The integral.
boundarySurfaceVector
The surface vector defining the area and orientation of the boundary.
For higher order (non point) particles, the surface vector might be omitted and computed internally from the boundary face ID.
boundaryFaceID
The ID of the boundary face (for point shaped particles, this one is ignored).
"""
[docs]
@abstractmethod
def vci_compute_TestGradient_P_Integral(self, R_AiC: np.ndarray):
"""Compute the volume integral for the test function gradient.
Parameters
----------
R_AiC
The integral.
"""
[docs]
@abstractmethod
def vci_compute_Test_PGradient_Integral(self, R_AiC: np.ndarray):
"""Compute the volume integral for the test function gradient.
Parameters
----------
R_AiC
The integral.
"""
[docs]
@abstractmethod
def vci_compute_MMatrix(self, M_ACD: np.ndarray):
"""Compute the symmetric M-matrix.
Parameters
----------
M_ACD
The M-matrix
"""
[docs]
@abstractmethod
def vci_assignTestFunctionCorrectionTerms(self, eta_AjC: np.ndarray):
"""Assign the shape function correction terms in
for of a 3D numpy array.
Parameters
----------
corrections
The correction terms.
"""
[docs]
@abstractmethod
def getRestartData(self) -> np.ndarray:
"""Get the restart data in form of a numpy array.
Returns
-------
np.ndarray
The restart data.
"""
[docs]
@abstractmethod
def readRestartData(self, restartData: np.ndarray):
"""Set the restart data.
Parameters
----------
restartData
The restart data.
"""