Source code for edelweissfe.stepactions.base.stepactionbase

#!/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 ABC

import numpy as np

from edelweissfe.journal.journal import Journal
from edelweissfe.models.femodel import FEModel
from edelweissfe.timesteppers.timestep import TimeStep
from edelweissfe.utils.checkpointedstate import packState, unpackState
from edelweissfe.utils.fieldoutput import FieldOutputController
from edelweissfe.utils.schema import OptionSchemaProvider


[docs] class StepActionBase(OptionSchemaProvider, ABC): """This is the base class for all step actions. User defined step actions must implement the methods. Two construction paths ----------------------- A step action is reached either from Python or from an ``.inp`` file, and the input file is a *serialization* of the Python path, not a second way of building the object. A step action with a typed constructor -- ``nSet`` is a node set, ``f_t`` is a callable, prescribed values are a ``dict`` -- overrides :meth:`fromStepActionDefinition` / :meth:`updateStepActionFromDefinition` to translate the parser's option mapping into a call to it. Everything string-shaped stays on that translation, which is the only thing the ``.inp`` front-end adds. A step action with an untyped constructor needs no such override: the two hooks below default to handing the raw ``definition`` dict straight to ``__init__``/``updateStepAction``. Which path a module takes is decided by whether it overrides the hooks -- ordinary polymorphism, not attribute probing, and no separate list of which modules use which convention to keep in sync. Parameters ---------- name The name of this step action. definition A dictionary containing the options for this step action. jobInfo A dictionary containing the information about the job. model The model tree. fieldOutputController The field output controlling object. journal The journal object for logging. """ #: The state this step action carries from one increment (and step) to the next, by attribute name and type; #: see :mod:`~edelweissfe.utils.checkpointedstate`. Undeclared, :meth:`getRestartData` and #: :meth:`setRestartData` must be overridden for checkpointing. checkpointedState: dict | None = None
[docs] def getRestartData(self) -> dict[str, np.ndarray]: """The state this step action carries; by default the attributes declared in :attr:`checkpointedState`. Returns ------- dict[str, numpy.ndarray] A flat mapping of array name to array. """ return packState(self)
[docs] def setRestartData(self, data: dict[str, np.ndarray]): """Restore the state :meth:`getRestartData` returned. Parameters ---------- data The mapping of arrays. """ unpackState(self, data)
def __init__( self, name: str, definition: dict, jobInfo: dict, model: FEModel, fieldOutputController: FieldOutputController, journal: Journal, ): pass
[docs] @classmethod def fromStepActionDefinition( cls, name: str, definition: dict, jobInfo: dict, model: FEModel, fieldOutputController: FieldOutputController, journal: Journal, ) -> "StepActionBase": """Create this step action from a parsed ``.inp`` step action definition. This is the one place a module's input-file shape (numbered component options, a ``f(t)`` expression string, a node *set name*) is turned into the typed arguments its real constructor takes. Override it together with a typed ``__init__``; leave it alone and the default dict-consuming constructor is used unchanged. Parameters ---------- name The name of the step action. definition The parsed option mapping for this step action. jobInfo A dictionary containing the information about the job. model The model tree. fieldOutputController The field output controlling object. journal The journal object for logging. Returns ------- StepActionBase The constructed step action. """ return cls(name, definition, jobInfo, model, fieldOutputController, journal)
[docs] def updateStepActionFromDefinition( self, definition: dict, jobInfo: dict, model: FEModel, fieldOutputController: FieldOutputController, journal: Journal, ): """Update this step action from a parsed ``.inp`` step action definition. The update counterpart of :meth:`fromStepActionDefinition`, needed as its own hook because a later step re-declaring an action reaches the *instance*, not the class. Override both or neither: a module whose constructor is typed but whose update path still consumed a dict would work only until the first multi-step input. Parameters ---------- definition The parsed option mapping for this step action. jobInfo A dictionary containing the information about the job. model The model tree. fieldOutputController The field output controlling object. journal The journal object for logging. """ self.updateStepAction(definition, jobInfo, model, fieldOutputController, journal)
[docs] def updateStepAction( self, definition: dict, jobInfo: dict, model: FEModel, fieldOutputController: FieldOutputController, journal: Journal, ): """Is called when an updated definition is present for a new step. Parameters ---------- definition A dictionary containing the options for this step action. jobInfo A dictionary containing the information about the job. model The model tree. fieldOutputController The field output controlling object. journal The journal object for logging. """
[docs] def applyAtStepStart(self, model: FEModel): """Is called when a step starts. Parameters ---------- model The current state of the model. """
[docs] def applyAtStepEnd(self, model: FEModel): """Is called when a step successfully finished. Parameters ---------- model The current state of the model. """
[docs] def applyAtIncrementStart(self, model: FEModel, timeStep: TimeStep): """Is called when a step increment starts. Parameters ---------- model The current state of the model. timeStep The definition of the time increment. """
def _checkSetChanged(self, theSet) -> bool: """Lazily detect whether ``theSet`` (a stable-identity :class:`~edelweissfe.sets.nodeset.NodeSet` or :class:`~edelweissfe.sets.elementset.ElementSet`) was mutated in-place (e.g. by AMR) since this step action last checked it. A step action that pre-sizes a derived array to ``len(theSet)`` (e.g. Dirichlet's ``delta``) calls this at its own per-increment entry point to recompute that array lazily, without registering as a :class:`~edelweissfe.models.meshdependent.MeshDependent`. A step action that merely iterates ``theSet`` needs no such check -- it sees new members automatically. Parameters ---------- theSet The set whose version is being tracked. Returns ------- bool True if ``theSet`` changed since its version was last recorded or checked. """ setVersions = self.__dict__.setdefault("_setVersions", {}) if theSet.name not in setVersions: raise RuntimeError( f"{type(self).__name__}: a set is checked for changes, but the state derived " "from it never recorded its version (call _recordSetVersion where that state is built)." ) changed = setVersions[theSet.name] != theSet._version setVersions[theSet.name] = theSet._version return changed def _recordSetVersion(self, theSet): """Record the version of ``theSet`` that derived state was just built from, so the next :meth:`_checkSetChanged` reports any change after this point -- including the first one. Parameters ---------- theSet The set the derived state was built from. """ self.__dict__.setdefault("_setVersions", {})[theSet.name] = theSet._version