# -*- 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.
# ---------------------------------------------------------------------
import numpy as np
from edelweissfe.fields.nodefield import NodeField
from edelweissfe.numerics.dofmanager import DofManager
from edelweissfe.variables.scalarvariable import ScalarVariable
[docs]
class MPMDofManager(DofManager):
"""A DofManager class for the Material Point Method.
Derived from DofManager, it is used to manage the degrees of freedom of the model
and to provide the necessary information for the assembly of the global system matrix.
Parameters
----------
nodeFields : list
A list of node fields.
scalarVariables : list
A list of scalar variables.
elements : list
A list of elements.
constraints : list
A list of constraints.
nodeSets : list
A list of node sets.
cells : list
A list of cells.
cellElements : list
A list of cell elements.
particles : list
The list of particles (RKPM).
initializeVIJPattern : bool
A flag indicating whether the VIJ pattern should be initialized.
"""
def __init__(
self,
nodeFields: list[NodeField],
scalarVariables: list[ScalarVariable] = [],
elements: list = [],
constraints: list = [],
nodeSets: list = [],
cells: list = [],
cellElements: list = [],
particles: list = [],
initializeVIJPattern: bool = True,
initializeAccumulatedNodalFluxesFieldwise: bool = True,
determiningIndexToHostObjectMappping: bool = True,
):
super().__init__(
nodeFields,
scalarVariables,
elements,
constraints,
nodeSets,
initializeVIJPattern=False,
initializeAccumulatedNodalFluxesFieldwise=initializeAccumulatedNodalFluxesFieldwise,
determiningIndexToHostObjectMappping=determiningIndexToHostObjectMappping,
)
(
self.accumulatedCellNDof,
self._accumulatedCellVIJSize,
self._nAccumulatedNodalFluxesFieldwiseFromCells,
self.largestNumberOfCellNDof,
) = self._gatherCellsInformation(cells)
(
self.accumulatedCellElementNDof,
self._accumulatedCellElementVIJSize,
self._nAccumulatedNodalFluxesFieldwiseFromCellElements,
self.largestNumberOfCellElementNDof,
) = self._gatherCellsInformation(cellElements)
(
self.accumulatedParticleNDof,
self._accumulatedParticleVIJSize,
self._nAccumulatedNodalFluxesFieldwiseFromParticles,
self.largestNumberOfParticleNDof,
) = self._gatherCellsInformation(particles)
self.idcsOfCellsInDofVector = self._locateCellsInDofVector(cells)
self.idcsOfCellElementsInDofVector = self._locateCellsInDofVector(cellElements)
self.idcsOfParticlesInDofVector = self._locateParticlesInDofVector(particles)
if initializeAccumulatedNodalFluxesFieldwise:
for field in self.nAccumulatedNodalFluxesFieldwise.keys():
self.nAccumulatedNodalFluxesFieldwise[field] += self._nAccumulatedNodalFluxesFieldwiseFromCells[field]
self.nAccumulatedNodalFluxesFieldwise[field] += self._nAccumulatedNodalFluxesFieldwiseFromCellElements[
field
]
self.nAccumulatedNodalFluxesFieldwise[field] += self._nAccumulatedNodalFluxesFieldwiseFromParticles[
field
]
self.idcsOfHigherOrderEntitiesInDofVector |= self.idcsOfCellsInDofVector
self.idcsOfHigherOrderEntitiesInDofVector |= self.idcsOfCellElementsInDofVector
self.idcsOfHigherOrderEntitiesInDofVector |= self.idcsOfParticlesInDofVector
if initializeVIJPattern:
self._sizeVIJ = (
self._accumulatedElementVIJSize
+ self._accumulatedConstraintVIJSize
+ self._accumulatedCellVIJSize
+ self._accumulatedCellElementVIJSize
+ self._accumulatedParticleVIJSize
)
self.I, self.J, self.idcsOfHigherOrderEntitiesInVIJ = self._initializeVIJPattern()
[docs]
def _locateCellsInDofVector(self, cells: list) -> dict:
"""Creates a dictionary containing the location (indices) of each cell
within the DofVector structure.
Returns
-------
dict
A dictionary containing the location mapping.
"""
idcsOfCellsInDofVector = {}
for cl in cells:
destList = np.hstack(
[
self.idcsOfFieldVariablesInDofVector[node.fields[nodeField]]
for iNode, node in enumerate(cl.nodes) # for each node of the cell ..
for nodeField in cl.fields[iNode] # for each field of this node
]
) # the index in the global system
if cl.dofIndicesPermutation is not None:
idcsOfCellsInDofVector[cl] = destList[cl.dofIndicesPermutation]
else:
idcsOfCellsInDofVector[cl] = destList
return idcsOfCellsInDofVector
[docs]
def _locateParticlesInDofVector(self, particles: list) -> dict:
"""Creates a dictionary containing the location (indices) of each particle
within the DofVector structure.
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, ...]
Returns
-------
dict
A dictionary containing the location mapping.
"""
return self._locateNodeCouplingEntitiesInDofVector(particles)
[docs]
def updateParticles(self, particles: list):
"""
Updates the connectivity mapping for particles without rebuilding
the entire DofManager structure.
Parameters
----------
particles : list
The list of particles to update.
"""
if not particles:
return
particles = list(particles) # Ensure we have a list for iteration
self.idcsOfParticlesInDofVector = self._locateParticlesInDofVector(particles)
self.idcsOfHigherOrderEntitiesInDofVector.update(self.idcsOfParticlesInDofVector)
[docs]
def updateConstraints(self, constraints: list):
"""
Updates the connectivity mapping for constraints in a serial loop.
Reuses global index maps to avoid re-instancing the manager.
Parameters
----------
constraints : list
The list of constraints to be considered.
"""
if not constraints:
return
constraints = list(constraints) # Ensure we have a list for iteration
self.idcsOfConstraintsInDofVector = self._locateConstraintsInDofVector(constraints)
self.idcsOfHigherOrderEntitiesInDofVector.update(self.idcsOfConstraintsInDofVector)