Source code for edelweissfe.stepactions.base.loadbase

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#  ---------------------------------------------------------------------
#
#  _____    _      _              _         _____ _____
# | ____|__| | ___| |_      _____(_)___ ___|  ___| ____|
# |  _| / _` |/ _ \ \ \ /\ / / _ \ / __/ __| |_  |  _|
# | |__| (_| |  __/ |\ V  V /  __/ \__ \__ \  _| | |___
# |_____\__,_|\___|_| \_/\_/ \___|_|___/___/_|   |_____|
#
#
#  Unit of Strength of Materials and Structural Analysis
#  University of Innsbruck,
#  2017 - today
#
#  Matthias Neuner matthias.neuner@uibk.ac.at
#
#  This file is part of EdelweissFE.
#
#  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 EdelweissFE.
#  ---------------------------------------------------------------------

from abc import abstractmethod
from collections.abc import Callable

from edelweissfe.numerics.dofmanager import DofManager, DofVector
from edelweissfe.numerics.systemmatrixassembly import SystemMatrixBlocks
from edelweissfe.stepactions.base.stepactionbase import StepActionBase
from edelweissfe.timesteppers.timestep import TimeStep


[docs] class LoadBase(StepActionBase): """A load: a step action that adds the residual contribution of the external load it applies -- the *negative* of that load, see :ref:`residual-and-tangent-convention` -- and the tangent of that contribution, which is nonzero only for a load that follows the deformation. Every load assembles its own contribution in :meth:`assembleLoad`, wherever it acts: a node force goes to the degrees of freedom of its nodes, a distributed load or a body force asks each of its elements for the integral over the loaded face or volume. A solver therefore only loops over the loads, see :meth:`~edelweissfe.solvers.base.nonlinearsolverbase.NonlinearSolverBase.assembleLoads`, which it finds among the step's actions by this base class, whatever step action module holds them (see :meth:`~edelweissfe.solvers.base.nonlinearsolverbase.NonlinearSolverBase.collectLoads`): a new kind of load -- one acting on the cells or particles of a meshfree model, say -- needs no change to any solver. Over a step, a load changes along its amplitude :math:`a(t)`, from its value at the start of the step, :math:`f(0)`, to :math:`f(1)` at the end; at the end of the step it keeps the value it reached, from which the next step starts (see :meth:`applyAtStepEnd`). """
[docs] @abstractmethod def assembleLoad( self, P: DofVector, K: SystemMatrixBlocks | None, U_np: DofVector, dU: DofVector, timeStep: TimeStep, dofManager: DofManager, ): """Add this load's residual contribution to ``P``, and its tangent to ``K``. Both are augmented, never overwritten. Parameters ---------- P The global residual contribution vector to augment. K The blocks of the global system matrix to augment: ``K[entity]`` hands out the block of an entity (an element, a cell, a particle) to add to, see :class:`~edelweissfe.numerics.systemmatrixassembly.SystemMatrixBlocks`; the solver commits the last one. None for an explicit solver, which needs no tangent. U_np The current global solution. dU The global solution increment since the last converged state. timeStep The current time step. dofManager The DOF manager of the equation system, which locates the degrees of freedom the load acts on. """
[docs] @abstractmethod def applyAtStepEnd(self, model, loadParameter: float | None = None): """Keep the value the load reached at the end of the step, from which the next step starts, and go idle until a later step declares a change again. A step that follows the amplitudes ends at :math:`f(1)`. An arc length step (see :ref:`arc-length-control`) applies :math:`f(0) + \\lambda \\, (f(1) - f(0))`, the change over the step scaled by its load parameter :math:`\\lambda`, and the load keeps exactly that: the factor of the change is :meth:`amplitudeAtStepEnd`. Parameters ---------- model The current state of the model. loadParameter The load parameter an arc length step ended at; None for a step that follows the amplitudes. """
[docs] @staticmethod def amplitudeAtStepEnd(amplitude: Callable[[float], float], loadParameter: float | None) -> float: """The value of a load's amplitude at the end of a step, by which the load keeps the change the step declared. A step that follows the amplitudes ends at the end of the amplitude, :math:`a(1)`. An arc length step scales the change of every load over the step, :math:`f(1) - f(0)`, by its load parameter :math:`\\lambda`, and so it ends on the straight line from the start to the end of the amplitude: .. math:: a(0) + \\lambda \\, \\left( a(1) - a(0) \\right). With the default linear amplitude, this is the load parameter itself. A load the step holds at a constant amplitude, e.g. a self-weight declared with ``f(t)='1'``, is part of the dead load of the arc length step, and keeps its full value, whatever the load parameter. Parameters ---------- amplitude The amplitude of the load over the step progress interval ``[0...1]``. loadParameter The load parameter an arc length step ended at; None for a step that follows the amplitudes. Returns ------- float The amplitude at the end of the step. """ if loadParameter is None: return amplitude(1.0) return amplitude(0.0) + loadParameter * (amplitude(1.0) - amplitude(0.0))