#!/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.
# ---------------------------------------------------------------------
"""The base of the arc length controllers of the indirect displacement control, whose controlled quantity is a linear
combination of degrees of freedom."""
from abc import abstractmethod
import numpy as np
from edelweissfe.numerics.dofmanager import DofManager, DofVector
from edelweissfe.stepactions.base.arclengthcontrollerbase import ArcLengthControllerBase
from edelweissfe.timesteppers.timestep import TimeStep
[docs]
class IndirectDisplacementControlBase(ArcLengthControllerBase):
"""The base of the arc length controllers of the indirect displacement control (Jirásek and Bažant): the controlled
quantity is a linear combination of degrees of freedom,
.. math::
q = \\boldsymbol{c} \\cdot \\boldsymbol{U},
with the weights :math:`\\boldsymbol{c}` of the controlled degrees of freedom (:attr:`cVector`), e.g. the opening of
a crack between two nodes, or the mean contraction of a ring of nodes. Over its step, :math:`q` changes by
:math:`\\Delta L`, and every increment by its share, :math:`\\Delta p \\, \\Delta L` for an increment of the step
progress :math:`\\Delta p`. The controller's equation for the corrected increment of the solution,
.. math::
\\boldsymbol{c} \\cdot (\\Delta \\boldsymbol{U} + \\Delta\\Delta \\boldsymbol{U}_0
+ \\Delta\\Delta\\lambda \\, \\Delta\\Delta \\boldsymbol{U}_f) = \\Delta p \\, \\Delta L,
is linear in the correction of the load parameter :math:`\\Delta\\Delta\\lambda`, which :meth:`computeDDLambda`
solves it for.
The target ``L`` of a declaration -- the construction of the controller, or its re-declaration in a later step,
which updates it -- is, with ``absolute``, the value of :math:`q` at the end of the step: the change of the step is
:math:`\\Delta L = L - q`, with :math:`q` of the model when the controller is declared, i.e. of the state the step
starts from (:meth:`declareTarget`). A controller first declared in a later step aims at ``L`` as well, and so does
one whose quantity changed over steps it did not control. Without ``absolute``, ``L`` is the change
:math:`\\Delta L` itself. A step that does not re-declare the controller changes :math:`q` by the :math:`\\Delta L`
of its last declaration once more.
A controller of this kind names its controlled degrees of freedom and their weights: :attr:`cVector`,
:meth:`controlledDofIndices` and :meth:`controlledValuesOfModel`.
Parameters
----------
name
The name of this step action.
absolute
Whether the target ``L`` of a declaration is the value of the controlled quantity at the end of the step, or its
change over the step. Fixed at construction: a re-declaration cannot change it.
"""
#: The change of the controlled quantity over the step, :math:`\\Delta L`, which a declaration derives from its
#: target and the model; a resumed run declares the controller anew on the restored model, so a checkpoint holds it.
checkpointedState = {"changeOverStep": float}
#: The weights :math:`\\boldsymbol{c}` of the controlled degrees of freedom, in the order of
#: :meth:`controlledDofIndices` and :meth:`controlledValuesOfModel`.
cVector: np.ndarray
def __init__(self, name: str, absolute: bool):
self.name = name
#: Whether the target ``L`` of a declaration is the value of the controlled quantity at the end of the step.
self.absolute = absolute
#: The change of the controlled quantity over the step, :math:`\\Delta L`; see :meth:`declareTarget`.
self.changeOverStep = 0.0
[docs]
@abstractmethod
def controlledDofIndices(self, dofManager: DofManager) -> np.ndarray:
"""The indices of the controlled degrees of freedom in the dof vector of an equation system.
Parameters
----------
dofManager
The dof manager of the equation system.
Returns
-------
np.ndarray
The indices, in the order of :attr:`cVector`.
"""
[docs]
@abstractmethod
def controlledValuesOfModel(self) -> np.ndarray:
"""The values of the controlled degrees of freedom in the model, as its node fields hold them.
Returns
-------
np.ndarray
The values, in the order of :attr:`cVector`.
"""
[docs]
def controlledQuantity(self) -> float:
"""The controlled quantity of the model, :math:`q = \\boldsymbol{c} \\cdot \\boldsymbol{U}`.
Returns
-------
float
The controlled quantity.
"""
return self.cVector.dot(self.controlledValuesOfModel())
[docs]
def declareTarget(self, L: float):
"""Declare the target of the step, from which the change of the controlled quantity over the step,
:attr:`changeOverStep`, follows: :math:`\\Delta L = L - q`, with the controlled quantity :math:`q` of the model
now, if ``absolute`` is set, else :math:`\\Delta L = L`. Called with the controlled degrees of freedom and their
weights in place.
Parameters
----------
L
The target: the value of the controlled quantity at the end of the step with ``absolute``, else its change
over the step.
"""
self.changeOverStep = L - self.controlledQuantity() if self.absolute else L
[docs]
def computeDDLambda(
self, dU: DofVector, ddU_0: DofVector, ddU_f: DofVector, timeStep: TimeStep, dofManager: DofManager
) -> float:
"""The correction of the load parameter that makes the corrected increment change the controlled quantity by the
share of the increment, :math:`\\Delta p \\, \\Delta L`; see
:meth:`~edelweissfe.stepactions.base.arclengthcontrollerbase.ArcLengthControllerBase.computeDDLambda`.
Parameters
----------
dU
The current increment of the solution.
ddU_0
The correction of the solution caused by the out-of-balance force.
ddU_f
The correction of the solution caused by the reference load alone.
timeStep
The increment being solved, whose size :math:`\\Delta p` is ``timeStep.stepProgressIncrement``.
dofManager
The dof manager of the current equation system.
Returns
-------
float
The correction of the load parameter.
"""
idcs = self.controlledDofIndices(dofManager)
dL = timeStep.stepProgressIncrement * self.changeOverStep
return (dL - self.cVector.dot(dU[idcs] + ddU_0[idcs])) / self.cVector.dot(ddU_f[idcs])