Transient (time-history) analysis ================================== When to use ----------- Transient analysis recovers the **time-history response of a structure to time-varying loads**. Use it when: - Loading is shock / impact / ramp / arbitrary time-history. - The structure's response is governed by inertia (loading changes faster than natural-period scales). - You need displacement / stress at specific instants in time rather than steady-state amplitudes. - You're doing modal-superposition response-spectrum analysis (shock-spectrum / earthquake / ASCE 7 design spectrum). For periodic / sinusoidal forcing use :doc:`/user-guide/solving/harmonic` — the frequency-domain solve is much cheaper. For natural frequencies alone use :doc:`modal`. For a steady-state- under-fixed-load snapshot use :doc:`linear-static`. Boundary conditions and loads ----------------------------- - **Dirichlet constraints** — same as :doc:`linear-static`. - **Time-varying nodal forces** — supplied as a callable ``f(t)`` or a sampled ``(n_steps, n_dof)`` array. - **Initial conditions** — initial displacement and velocity passed to the solver. Default is zero / zero (the structure starts at rest at its undeformed configuration). - **Damping** — Rayleigh (:math:`\mathbf{C} = \alpha \mathbf{M} + \beta \mathbf{K}`) via :meth:`Model.rayleigh_damping ` is the supported path today; modal damping for mode-superposition is on the planned roadmap. The math (one paragraph) ------------------------ Transient analysis solves the **second-order ODE** .. math:: \mathbf{M}\, \ddot{\mathbf{u}}(t) + \mathbf{C}\, \dot{\mathbf{u}}(t) + \mathbf{K}\, \mathbf{u}(t) = \mathbf{f}(t) via the **Newmark-β** family of implicit time-integrators (Newmark 1959; Bathe §9.5). Default parameters are :math:`\beta = 1/4, \gamma = 1/2` (the unconditionally-stable average-acceleration scheme). Each time step solves :math:`(\mathbf{M} / (\beta \Delta t^{2}) + \mathbf{C} \gamma / (\beta \Delta t) + \mathbf{K})\, \mathbf{u}_{n+1} = \mathbf{f}_n`, re-using the same factorisation across all time steps. Running the solve ----------------- .. code-block:: python times = np.linspace(0.0, 0.1, 1001) # 100 ms, 0.1 ms steps forcing = np.zeros((len(times), n_dof)) forcing[:, tip_dof] = ramp_to_step(times) # user-defined input res = m.solve_transient(times=times, forcing=forcing) Returns a :class:`~femorph_solver.solvers.transient.TransientResult`: - ``res.times`` — ``(n_steps,)`` time samples [s]. - ``res.displacement`` — ``(n_dof, n_steps)`` displacement history. - ``res.velocity`` — ``(n_dof, n_steps)`` velocity history. - ``res.acceleration`` — ``(n_dof, n_steps)`` acceleration history (useful for inertial-stress checks). For a single-DOF / shock-input lookup, see the focused recipe at :ref:`sphx_glr_gallery_analyses_transient_example_sdof_transient.py`. Time-step selection ------------------- The Newmark-β scheme is unconditionally stable but accuracy depends on time-step: - For **modal-superposition** (when shipped), :math:`\Delta t \le 1 / (10\, f_\text{max-mode-included})` resolves the mode-set. - For **direct integration** with the default Newmark-β, :math:`\Delta t \le 1 / (20\, f_\text{highest-frequency-of-interest})` is the practical guidance. - For **shock / impact**, resolve the rise-time of the forcing input — typically :math:`\Delta t \le \tau_\text{rise} / 10`. Post-processing recipes ----------------------- - **Time-history plots** — index ``res.displacement`` by the DOF of interest, plot vs ``res.times``. - **Velocity / acceleration spectra** — ``res.velocity`` and ``res.acceleration`` give the corresponding histories without numerical differentiation. - **Snapshot stress** at a chosen instant — pass the column ``res.displacement[:, k]`` to ``compute_nodal_stress``. Common gotchas -------------- - **No damping → infinite ringing.** An undamped transient on a non-dissipative structure rings forever. Configure at least minimal Rayleigh damping (typically ζ ≈ 1-5 %) on the modes you care about. - **Time-step too large** smears high-frequency content into artificial damping. Verify by solving with halved time-step and checking if the response stabilises. - **Initial conditions inconsistent with the loads.** Starting from zero displacement under non-zero initial loading produces a transient ringing that has no physical meaning; pass ``u0 = static-equilibrium-solve(f(0))`` to start from the consistent static state. Verification + gallery ---------------------- - :ref:`sphx_glr_gallery_analyses_transient_example_sdof_transient.py` — single-DOF spring-mass-damper response to a step input vs textbook closed form. Solver mechanics + performance ------------------------------ For Newmark-β derivation, generalised-α / HHT alternatives, mode-superposition mechanics, and time-step batching, see :doc:`/user-guide/solving/transient` — that page is the solver-engineering deep-dive on the same ``Model.solve_transient()`` invocation.