import type { EquationGroup } from "./types"; /** * The theory, as the equations tab shows it: the model, then how each mount * enters, then the discretization and its limits. Every group names what it * idealizes. Groups with a `mounts` list are shown only for those mounts. */ export const GOVERNING: EquationGroup = { title: "The plate under gravity", equations: [ { tex: String.raw`\mu\,\ddot w + 2\mu\sigma_0\,\dot w - 2\mu\sigma_1\,\nabla^2\dot w + D\,\nabla^4 w - \nabla\!\cdot\!\left(\mathbf N\,\nabla w\right) = f(x,y,t) + \mu\, g_n + f_{\mathrm{mount}}`, caption: "Damped Kirchhoff-Love plate in its own coordinates: w the transverse displacement, mu = rho h the mass per area, D the bending stiffness. Gravity enters twice: its normal component g_n loads the plate transversely, and its in-plane component puts the plate under its own weight, whose stress resultants N (tension positive) act on the tilted plate through the membrane term. The mount supplies f_mount: springs, contacts, or nothing where it holds the plate still. The two loss terms are a model choice, not a measurement.", }, { tex: String.raw`D = \frac{E h^3}{12(1-\nu^2)}, \qquad \kappa = \sqrt{D/\mu}, \qquad \mathbf g = g\,(g_x, g_y, g_z), \quad g_x^2 + g_y^2 + g_z^2 = 1`, caption: "Bending stiffness, the stiffness parameter kappa that sets the dispersion of bending waves, and the direction of gravity in plate coordinates, which the mount decides: a hung plate turns in its plane until its centre of mass is below the hole, a plate on pads is horizontal, a vise or a hand holds it at a chosen tilt.", }, { tex: String.raw`\nabla\!\cdot\!\left(\mathbf C\, \boldsymbol\varepsilon(\mathbf u)\right) h + \rho h\, g\,(g_x, g_y) = 0, \qquad \mathbf N = h\,\mathbf C\,\boldsymbol\varepsilon(\mathbf u), \qquad \oint_{\text{edge}} \mathbf N\mathbf n\, ds = 0 \text{ except at the mount}`, caption: "Plane stress of the self-weight: the in-plane displacement u under the in-plane body force, free edges, and the mount's constraints taking the reactions (the thread tension, the jaws, the fingers). Solved once at setup by a sparse direct solve; it is the only place a linear system is solved, everything else is explicit.", }, { tex: String.raw`U_N = \frac12\iint \left( N_{xx} w_x^2 + N_{yy} w_y^2 + 2 N_{xy} w_x w_y \right) dA, \qquad \mathbf r^{\!\top}\mathbf K_N\,\mathbf r = m\,g\,d`, caption: "The membrane energy of the prestress. For a rigid rotation r of a hung plate about the horizontal axis through its hole, it equals the compound-pendulum stiffness m g d, d the distance from the hole to the centre of mass; the lab assembles U_N with the same difference operators as the plane-stress energy, and then this identity holds to rounding. So the swing of a hanging plate and the small stiffening of its lowest flexural modes come from one operator, and a thin plate hung from a low point (its centre of mass above the hole) is correctly unstable.", }, { tex: String.raw`f(x,y,t) = F(t)\,s(x,y), \qquad \iint s\,dA = 1`, caption: "A strike is a force history F(t), a half-sine pulse, spread over a raised-cosine footprint s so that F is the total force. A release has no force: the plate starts from the static deflection under a pull at a point, scaled to the asked displacement there, and is let go.", }, ], }; export const THREAD_GROUP: EquationGroup = { title: "Threads", mounts: ["thread", "threads"], equations: [ { tex: String.raw`T = m g \ (\text{one thread}), \qquad T_1 = m g\,(1 - s_c),\ T_2 = m g\, s_c \ (\text{two}), \qquad k_T = \frac{T}{L}`, caption: "A taut thread of length L and tension T pulls its hole back with force T w/L when the hole moves w out of plane: the linearized pendulum, a transverse spring of stiffness T/L at the hole. With two threads of one length from hooks at one height the line of the holes is horizontal and statics shares the weight by the position s_c of the centre of mass between them; a centre of mass outside the holes would slacken a thread, and the lab refuses it.", }, { tex: String.raw`k_\theta = \frac{G J}{L}, \qquad J = \frac{\pi d^4}{32}`, caption: "One thread leaves the plate free to spin about the vertical axis through its hole, since the self-weight tension has no moment about it (the identity above gives zero for that rotation). The only restoring torque is the thread's torsion, which for any thread is so small that the spin has a period of minutes: after an off-centre strike the plate turns slowly, and only the sigma0 loss stops it. Two threads restrain the spin as a yaw on their two springs.", }, ], }; export const CONTACT_GROUP: EquationGroup = { title: "Pads and felts", mounts: ["pads", "cymbal"], equations: [ { tex: String.raw`f_c = k\,\max(0,\, g - w) - c\,\dot w\,[g - w > 0], \qquad g = \text{compression at rest}`, caption: "A pad is a one-sided spring and dashpot per unit area: it pushes while compressed and does nothing once the plate has lifted off it. The static solve finds which pads carry the plate at rest by an active-set iteration (a pad found in tension is released and the solve repeated); the dynamics then track the departure from that state, so a hard strike can lift the plate off a pad and let it land again. A cymbal stand has a felt below and one above the plate, each preloaded by the wing nut, so the centre is held two-sided until a strike separates one of them.", }, { tex: String.raw`\Delta t \le \frac{2 M_i}{c_i}`, caption: "The dashpots are treated explicitly, on the backward-difference velocity, which is stable while the step stays below this bound; the plan keeps it under half of it. Stiff pads (a bare bench) also raise the stiffness bound below, so the hardest supports cost a shorter step.", }, ], }; export const VISE_GROUP: EquationGroup = { title: "The vise", mounts: ["vise"], equations: [ { tex: String.raw`w = 0 \ \text{in the jaws}, \qquad U = \frac{D}{2}\sum_q \omega_q\left(\frac{w_{q+1} - 2 w_q + w_{q-1}}{\Delta x^2}\right)^2 + \dots \quad\Rightarrow\quad w_n = 0 \ \text{at the clamp}`, caption: "The nodes within the jaw depth are held at zero and dropped from the unknowns after the bending energy is assembled. A second difference centred on a held node that reaches the first free node then penalizes the slope at the clamp, which is the clamped condition w = w_n = 0 (the 7/dx^4 coefficient of the ghost-node scheme); it needs a held node behind every held edge node, so the jaw depth is at least two rows. The self-weight then hangs from, sags from or presses on the jaws according to the tilt.", }, ], }; export const PINCH_GROUP: EquationGroup = { title: "Fingers", mounts: ["pinched"], equations: [ { tex: String.raw`f_{\mathrm{hand}} = -\frac{k\, a_i}{\sum a}\, w_i - \frac{c\, a_i}{\sum a}\, \dot w_i \quad \text{over the patch}, \qquad \mathbf u = 0 \ \text{on the patch}`, caption: "A grip between finger and thumb is a two-sided spring and dashpot, spread over the patch by node area, with the patch also fixed in the plane so it takes the weight. The stiffness and the damping of a hand are guesses of the right order (a few kN/m, tens of N s/m); the loss is what makes a hand-held plate die away faster than one on a thread.", }, ], }; export const SCHEME_GROUP: EquationGroup = { title: "The explicit scheme", equations: [ { tex: String.raw`\mathbf M\ddot{\mathbf w} + 2\sigma_0 \mathbf M\dot{\mathbf w} - 2\sigma_1\mu\,\mathbf L\dot{\mathbf w} + \left(\mathbf K_b + \mathbf K_N + \mathbf K_s\right)\mathbf w + \mathbf f_c(\mathbf w, \dot{\mathbf w}) = F(t)\,\mathbf s + \mathbf M g_n`, caption: "The semi-discrete system on the grid. K_b is the bending stiffness assembled from the discrete bending energy (finite-difference energy method): the 13-point biharmonic stencil inside, the free-edge conditions as natural conditions on the edges; K_N the prestress operator; K_s the linear springs of the mount; L the Laplacian energy operator with its natural zero-slope condition; M the trapezoidal node masses.", }, { tex: String.raw`\mathbf w = \mathbf w_0 + \mathbf R\,\mathbf c + \boldsymbol\varphi, \qquad \mathbf R^{\!\top}\mathbf M\,\boldsymbol\varphi = 0, \qquad \mathbf R = \left[\,1,\ (\mathbf P - \mathbf A)\cdot\mathbf e_d,\ (\mathbf P - \mathbf A)\cdot\mathbf e_h\,\right]`, caption: "The state is the departure from the static equilibrium w0, split into three rigid-body coordinates c (a translation along the normal at the pivot A, and the rotations about the horizontal and the vertical in-plane axes through it) and a flexural remainder mass-orthogonal to them. The split is exact, and it is what keeps a millimetre swing and a micrometre vibration apart in single precision: bending annihilates R, so the bending operator is never applied to the swing.", }, { tex: String.raw`\mathbf G\,(\ddot{\mathbf c} + 2\sigma_0\dot{\mathbf c}) = \mathbf R^{\!\top}\mathbf f - \mathbf K_{rr}\,\mathbf c - (\mathbf A\mathbf R)^{\!\top}\boldsymbol\varphi, \qquad \ddot{\boldsymbol\varphi} + 2\sigma_0\dot{\boldsymbol\varphi} - 2\sigma_1\mathbf L_m\dot{\boldsymbol\varphi} = \mathbf M^{-1}\left(\mathbf f - \mathbf A\boldsymbol\varphi - \mathbf A\mathbf R\,\mathbf c\right) - \mathbf R\,(\ddot{\mathbf c} + 2\sigma_0\dot{\mathbf c})`, caption: "The two parts, with A = K_b + K_N + K_s, G = R'MR the rigid mass matrix, K_rr = R'AR the reduced stiffness (pendulum, thread springs, thread torsion) and f the applied and contact forces about rest. The last term of the flexural equation removes from it exactly the rigid acceleration, so phi stays flexural. The sigma1 loss, a model of losses that grow with spatial frequency, acts on the flexural part only.", }, { tex: String.raw`(1+\sigma_0\Delta t)\,\boldsymbol\varphi^{n+1} = 2\boldsymbol\varphi^n - (1-\sigma_0\Delta t)\,\boldsymbol\varphi^{n-1} + \Delta t^2\,\mathbf a^n + 2\sigma_1\Delta t\,\mathbf L_m\!\left(\boldsymbol\varphi^n - \boldsymbol\varphi^{n-1}\right)`, caption: "Second-order explicit stepping of both parts, the same form for c; a^n is the flexural acceleration above at step n, and the contact forces and dashpots are evaluated at step n. The GPU port carries (phi, phi - phi_prev) instead of (phi, phi_prev), the same arithmetic rearranged so that the rounding of the field does not feed back into the velocity (Bilbao, Numerical Sound Synthesis, 2009, ch. 12, for the scheme).", }, { tex: String.raw`\Delta t \le \frac{-2\sigma_1\lambda + \sqrt{4\sigma_1^2\lambda^2 + 4\left(\kappa^2\lambda^2 + \Omega^2\right)}}{\kappa^2\lambda^2 + \Omega^2}, \qquad \lambda = \frac{4}{\Delta x^2} + \frac{4}{\Delta y^2}, \qquad \Omega^2 = \max_i \frac{1}{M_i}\sum_j \left|\left(\mathbf K_N + \mathbf K_s + \mathbf K_c\right)_{ij}\right|`, caption: "Stability bound from von Neumann analysis of the bending scheme, lambda the largest eigenvalue of the five-point Laplacian, with the extra stiffness of the prestress, the springs and the engaged contacts bounded by Gershgorin's theorem and added as a rate Omega. Without the extras it is the familiar dt <= dx^2 / (4 kappa) of a square grid. Because dt scales with dx^2, halving the spacing costs eight times the work; and because a swing takes a second to show while the step is microseconds, a run is hundreds of thousands of steps, which is why the loop lives on the GPU.", }, ], }; export const CURVED_EDGE_GROUP: EquationGroup = { title: "Elliptical plates", equations: [ { tex: String.raw`\Omega_h = \left\{ (x_j, y_i) : \frac{(x_j - L_x/2)^2}{(L_x/2)^2} + \frac{(y_i - L_y/2)^2}{(L_y/2)^2} \le 1 + O(\Delta x) \right\}`, caption: "An ellipse is the set of grid nodes within half a spacing of the inscribed ellipse, so its edge is a staircase that straddles the true curve. The energy method assembles the free-edge conditions on that footprint as it does on a rectangle, and the geometry and the stress along the edge are first order in the spacing (second order inside); refining the grid is the check. Every edge in this lab is free unless a mount holds it, so the plate paradox of simply supported curved edges does not arise.", }, ], }; export const STRESS_GROUP: EquationGroup = { title: "Stress", equations: [ { tex: String.raw`\sigma_{xx} = -\frac{E z}{1-\nu^2}\left(w_{xx} + \nu w_{yy}\right),\quad \sigma_{yy} = -\frac{E z}{1-\nu^2}\left(w_{yy} + \nu w_{xx}\right),\quad \tau_{xy} = -\frac{E z}{1+\nu}\,w_{xy}`, caption: "Surface stresses at z = h/2 from the curvatures of the flexural part (the rigid-body motion has none). Stress is a second derivative of the primary unknown, so it inherits and amplifies the solution error. The in-plane self-weight stress N/h is a separate, static field, shown with the statics; for a hanging steel sheet it is tens of kilopascals against the megapascals of a strike.", }, { tex: String.raw`\sigma_{vM} = \sqrt{\sigma_{xx}^2 - \sigma_{xx}\sigma_{yy} + \sigma_{yy}^2 + 3\tau_{xy}^2}, \qquad \sigma_{1,2} = \frac{\sigma_{xx}+\sigma_{yy}}{2} \pm \sqrt{\left(\frac{\sigma_{xx}-\sigma_{yy}}{2}\right)^2 + \tau_{xy}^2}`, caption: "Von Mises equivalent stress and principal stresses; the lab reports the principal stress of largest magnitude with its sign.", }, ], }; export const RADIATION_GROUP: EquationGroup = { title: "Sound", equations: [ { tex: String.raw`p(\mathbf r, t) = \frac{\rho_{\mathrm{air}}}{2\pi} \iint \frac{\ddot w\!\left(\mathbf r_s,\, t - |\mathbf r - \mathbf r_s|/c\right)}{|\mathbf r - \mathbf r_s|}\, dA`, caption: "Baffled: Rayleigh's first integral for a flat radiator in an infinite rigid baffle. Right for a plate set in a large table or wall, an upper estimate for one on a bench.", }, { tex: String.raw`p \approx \frac{\rho_{\mathrm{air}}}{2\pi} \iint \left[ \frac{\ddot w(\mathbf r_s, t - R_f/c)}{R_f} - \frac{\ddot w(\mathbf r_s, t - R_b/c)}{R_b} \right] dA, \qquad R_b = |\mathbf r_s - \mathbf r_e| + |\mathbf r - \mathbf r_e|`, caption: "Unbaffled: a plate hanging in free air radiates from both faces with opposite signs, and at wavelengths longer than the plate the two cancel. The lab estimates this with the back face's contribution taken around the nearest point of the edge r_e. The cancellation is complete only towards zero frequency, and at high frequency, where the real diffracted field is weak, the two add incoherently and the estimate is a little loud: a rough model of the front-back cancellation, not a solution of the diffraction problem. Neither model knows about the room.", }, ], }; export const ALL_GROUPS: EquationGroup[] = [ GOVERNING, THREAD_GROUP, CONTACT_GROUP, VISE_GROUP, PINCH_GROUP, SCHEME_GROUP, CURVED_EDGE_GROUP, STRESS_GROUP, RADIATION_GROUP, ];