Source code for edelweissmeshfree.numerics.dofmanager

# -*- 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 _gatherCellsInformation(self, entities: list) -> tuple[int, int, int, int]: """Generates some auxiliary information, which may be required by some modules of EdelweissFE. Parameters ---------- entities The list of entities, for which the information is gathered. Returns ------- tuple[int,int] The tuple of - number of accumulated elemental degrees of freedom. - number of accumulated system matrix sizes. - the number of acummulated fluxes Σ_entities Σ_nodes ( nDof (field) ) for Abaqus-like convergence tests. - largest occuring number of dofs on any element. """ return self._gatherElementsInformation(entities)
[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)