Aufgaben

Lösung zu Aufgabe 13.6

Das unten angegebene Programm definiert eine entsprechende Klasse. In der Methode __init__ wird im Wesentlichen die Methode __init__ der Elternklasse aufgerufen. Außerdem werden die Reibungskoeffizienten festgelegt.

Eine private Methode _dgl definiert die rechte Seite der Differentialgleichung. In der Methode solve werden die Anfangsbedingungen entsprechend aufbereitet und die Funktion scipy.integrate.solve_ivp aufgerufen. Die Schlüsselwortargumente werden direkt an scipy.integrate.solve_ivp weitergegeben, sodass man auf die volle Funktionalität der Integrationsroutine zugreifen kann. Es wird ein Tupel zurückgegeben, das die Zeitpunkte, sowie die Orts- und Geschwindigkeitsvektoren enthält. Die Vektoren werden jeweils so aufbereitet, dass sie die Dimension n_zeitpunkte × n_punkte × n_dim besitzen. Auf diese Weise kann man ein vorhandenes Stabwerk sehr einfach auf den Zustand zu einem bestimmten Zeitzustand setzen, wie wir in Aufgabe 13.7 sehen werden.

Download    OOsim/Loesungen/stabwerksdynamik.py

"""Dynamik eines elastischen Stabwerks."""

import numpy as np
import scipy.integrate
import stabwerke


class StabwerkElastischDynamisch(stabwerke.StabwerkElastisch):
    """Dynamik eines Stabwerks mit elastischen Stäben.

    Die Stäbe können gedehnt und gestaucht werden. Sie verbiegen
    sich allerdings nicht. Die Dehnung der Stäbe wird mit dem
    hookeschen Gesetz berechnet und es wird eine Reibungskraft
    angesetzt, die proportional zur Geschwindigkeit der Punkte
    des Stabwerks ist. Das Anfangswertproblem des Stabwerks
    lässt sich mit der Methode `solve` lösen.

    Args:
        punkte (np.ndarray):
            Ortsvektoren der Punkte (n_punkte × n_dim).
        stuetz (list[int]):
            Indizes der Punkte, bei denen es sich um Stützpunkte
            handelt.
        staebe (np.ndarray):
            Jede Zeile enthält die Indizes der miteinander
            verbundenen Punkte (n_staebe × 2).
        reibung (np.ndarray):
            Reibungskoeffizienten der Punkte [kg/s] (n_punkte).
        **kwargs:
            Weitere Schlüsselwortargumente für `StabwerkElastisch`.
    """

    def __init__(self, punkte, stuetz, staebe, reibung=None,
                 **kwargs):
        super().__init__(punkte, stuetz, staebe, **kwargs)

        self.reibung = reibung
        """np.ndarray: Reibungskoeffizienten [kg / s] (n_punkte)."""

        # Setze die Reibungskoeffizienten auf null, wenn keine
        # Reibung angegeben wurde.
        if self.reibung is None:
            self.reibung = np.zeros(self.n_punkte)

        # Setze die Reibungskoeffizienten für alle Punkte gleich,
        # falls eine skalare Größe angegeben wurde.
        if np.isscalar(reibung):
            self.reibung = reibung * np.ones(self.n_punkte)

    def _dgl(self, t, u):
        """Berechne die rechte Seite der Differentialgleichung."""
        r, v = np.split(u, 2)
        r = r.reshape(self.n_knoten, self.n_dim)
        v = v.reshape(self.n_knoten, self.n_dim)

        # Verschiebe den mittleren Punkt an die angegebene Position.
        self.punkte[self.indizes_knoten] = r

        # Berechne die Kräfte.
        kraefte = self.gesamtkraefte()[self.indizes_knoten]
        kraefte -= (self.reibung[self.indizes_knoten].
                    reshape(-1, 1) * v)

        # Berechne die Beschleunigung des mittleren Punkts.
        m = self.punktmassen[self.indizes_knoten].reshape(-1, 1)
        a = kraefte / m

        return np.concatenate([v.reshape(-1), a.reshape(-1)])

    def solve(self, t_max, r0=None, v0=None, **kwargs):
        """Bestimme die zeitabhängige Dynamik des Stabwerks.

        Die Dynamik des Stabwerks wird in Form eines
        Anfangswertproblems mithilfe der Funktion
        `scipy.integrate.solve_ivp` bestimmt.

        Args:
            t_max (float):
                Simulationsdauer [s].
            r0 (np.ndarray):
                Ortsvektoren der Punkte [m] (n_punkte × n_dim).
                Der Vorgabewert entspricht der aktuellen
                Konfiguration des Stabwerks.
            v0 (np.ndarray):
                Anfangsgeschwindigkeiten [m/s] (n_punkte × n_dim).
                Der Vorgabewert entspricht einer
                Anfangsgeschwindigkeit von null.
            **kwargs:
                Schlüsselwortargumente für die Funktion
                `scipy.integrate.solve_ivp`.

        Returns:
            tuple[np.ndarray, np.ndarray, np.ndarray]:
                - Zeitpunkte, an denen die Lösung berechnet wurde.
                - Ortsvektoren der Punkte des Stabwerks
                  (n_zeitpunkte × n_punkte × n_dim)
                - Geschwindigkeitsvektoren der Punkte des Stabwerks
                  (n_zeitpunkte × n_punkte × n_dim)
        """
        # Setzte die aktuelle Konfiguration des Stabwerks für die
        # Anfangspositionen ein, falls diese nicht angegeben sind.
        if r0 is None:
            r0 = self.punkte.copy()

        # Setze die Anfangsgeschwindigkeiten auf null, wenn diese
        # nicht angegeben sind.
        if v0 is None:
            v0 = np.zeros((self.n_punkte, self.n_dim))

        # Lege den Anfangszustand fest.
        u0 = np.concatenate((r0[self.indizes_knoten].reshape(-1),
                             v0[self.indizes_knoten].reshape(-1)))

        # Sichere die aktuelle Konfiguration.
        punkte = self.punkte
        self.punkte = self.punkte.copy()

        # Löse die Differentialgleichungen numerisch.
        result = scipy.integrate.solve_ivp(self._dgl,
                                           [0, t_max], u0,
                                           **kwargs)
        t = result.t
        r, v = np.split(result.y, 2)

        # Schreibe die ursprüngliche Konfiguration zurück.
        self.punkte = punkte

        # Mache den ersten Index in den Lösungsarrays zum Zeitindex.
        # und wandle dies in ein n_zeitpunkte × n_knoten × n_dim -
        # Array um.
        r = r.T.reshape(-1, self.n_knoten, self.n_dim)
        v = v.T.reshape(-1, self.n_knoten, self.n_dim)

        # Erzeuge Arrays für die Positionen und Geschwindigkeiten
        # aller Punkte (Knoten und Stützpunkte)
        punkte = np.empty((t.size, self.n_punkte, self.n_dim))
        punkte[:] = self.punkte
        punkte[:, self.indizes_knoten, :] = r
        geschw = np.zeros((t.size, self.n_punkte, self.n_dim))
        geschw[:, self.indizes_knoten, :] = v

        return t, punkte, geschw