/ proof-of-concept / cymsim_old
Sign in
proof-of-concept / cymsim_old
cymsim_old / src / cymsim / presets.py
186 lines · 7.2 KBBlameHistoryRaw
1"""Predefined designs: flat and bowed sheets, discs, tapered cymbals with and
2without a bell, an ellipse, a thunder sheet. Each is a function returning a
3``Design``, so that it can be modified before evaluation::
5 d = presets.crash_16()
6 d.thickness = 1.0e-3
7 plate = d.evaluate(0.008)
8"""
10from __future__ import annotations
12import math
14import numpy as np
16from .design import Design
17from .material import ALUMINUM, BRONZE, MILD_STEEL, STAINLESS_STEEL
19inch = 0.0254
20mm = 1e-3
23def ramp(a: float, b: float, x):
24 """0 at x = a, 1 at x = b, clamped to that range outside."""
25 return np.clip((x - a) / (b - a), 0, 1)
28def bow(s, span: float, sag: float, offset: float = 0.0):
29 """A parabola of the given sag across a span centered on 0; ``s`` runs along
30 the span, and ``offset`` in -1..1 moves the crest toward one end."""
31 t = 2 * s / span
32 return sag * (1 - t**2) * (1 + offset * t)
35def dome(d, radius: float, height: float):
36 """A cosine dome of the given height and base radius, with zero slope both
37 at its rim and at its apex; ``d`` is the distance from its center."""
38 return 0.5 * height * (1 + np.cos(math.pi * d / radius)) * (d < radius)
41def dents(x, y, r0: float, r1: float, n: int, rd: float, depth: float, random: bool = True, seed: int = 1):
42 """``n`` hammer marks between the radii ``r0`` and ``r1``: Gaussian dents of
43 1/e radius ``rd``, placed on a sunflower spiral (equal area per mark,
44 golden-angle spacing) so that the pattern has no symmetry, or at random with
45 uniform density over the annulus, from a small congruential generator so
46 that a seed always gives the same pattern."""
47 v = np.zeros_like(x)
48 ga = math.pi * (3 - math.sqrt(5))
49 s = seed
50 for k in range(1, n + 1):
51 if random:
52 s = (s * 1664525 + 1013904223) % 4294967296
53 u = s / 4294967296
54 s = (s * 1664525 + 1013904223) % 4294967296
55 w = s / 4294967296
56 rk = math.sqrt(r0**2 + (r1**2 - r0**2) * u)
57 tk = 2 * math.pi * w
58 else:
59 rk = math.sqrt(r0**2 + (r1**2 - r0**2) * (k - 0.5) / n)
60 tk = k * ga
61 dx = x - rk * math.cos(tk)
62 dy = y - rk * math.sin(tk)
63 v = v - depth * np.exp(-(dx**2 + dy**2) / rd**2)
64 return v
67def flat_rectangle() -> Design:
68 """A flat 12 by 18 inch sheet of stainless steel, 0.79 mm thick, hanging freely."""
69 return Design("Flat steel rectangle", STAINLESS_STEEL, 12 * inch, 18 * inch, 0.79 * mm)
72def aluminum_square() -> Design:
73 """A flat 12 inch square of 6061-T6 aluminum, 1.02 mm thick. Aluminum is a
74 third the density of steel and a third as stiff, so the bending frequencies
75 land near those of a steel plate of the same size and thickness; its high
76 thermal expansion and conductivity give it the strongest thermoelastic damping."""
77 return Design("Aluminum 6061-T6 square", ALUMINUM, 12 * inch, 12 * inch, 1.02 * mm)
80def bowed_rectangle() -> Design:
81 """The steel sheet with a 3 mm parabolic bow across its width, the kind of
82 bow that rolling and handling leave in sheet metal. The bow makes the sheet
83 a shallow shell: the linear modes couple bending to stretching, and the full
84 model oil-cans and wobbles when struck."""
85 W = 12 * inch
86 return Design("Bowed steel rectangle", STAINLESS_STEEL, W, 18 * inch, 0.79 * mm, profile=lambda x, y, r, t: bow(x, W, 3 * mm, 0.3))
89def flat_disc() -> Design:
90 """A flat 16 inch bronze disc, 1 mm thick."""
91 D = 16 * inch
92 return Design("Flat bronze disc", BRONZE, D, D, 1.0 * mm, inside=lambda x, y, r, t: r <= D / 2)
95def crash_16() -> Design:
96 """A 16 inch flat crash: bronze, lathed to a taper from 1.2 mm at the center
97 to 0.7 mm at the edge. Flat, so the linear modes are pure bending modes."""
98 D = 16 * inch
99 R = D / 2
100 tc, te = 1.2 * mm, 0.7 * mm
101 return Design("16 inch flat crash", BRONZE, D, D, lambda x, y, r, t: tc + (te - tc) * ramp(40 * mm, R, r), inside=lambda x, y, r, t: r <= R)
104def ride_20() -> Design:
105 """A 20 inch flat ride: thicker bronze, 2.2 mm at the center to 1.4 mm at the edge."""
106 D = 20 * inch
107 R = D / 2
108 return Design("20 inch flat ride", BRONZE, D, D, lambda x, y, r, t: 2.2 * mm + (1.4 * mm - 2.2 * mm) * ramp(50 * mm, R, r), inside=lambda x, y, r, t: r <= R)
111def crash_bell_16() -> Design:
112 """A 16 inch crash with a shallow bow and a cosine bell. The profile makes
113 the cymbal a shallow shell (a three-field linear system, so the modes take
114 longer), and the full model shows the asymmetric nonlinearity of a curved
115 plate. The slopes are kept small; ``Plate.profile_stats`` reports the largest."""
116 D = 16 * inch
117 R = D / 2
118 bell_r, bell_h, bow_h = 55 * mm, 8 * mm, 10 * mm
119 tc, te = 1.2 * mm, 0.7 * mm
120 return Design(
121 "16 inch crash with bell",
122 BRONZE,
123 D,
124 D,
125 lambda x, y, r, t: tc + (te - tc) * ramp(bell_r, R, r),
126 inside=lambda x, y, r, t: r <= R,
127 profile=lambda x, y, r, t: bow_h * (1 - (r / R) ** 2) + dome(r, bell_r, bell_h),
128 )
131def crash_hammered_16(random_dents: bool = True, seed: int = 1, n_dents: int = 160, dent_depth: float = 0.4 * mm) -> Design:
132 """The 16 inch crash with bell, hammered: shallow dents on the bow, at random
133 places (``seed`` picks the pattern) or on a sunflower spiral. A hammered
134 cymbal is no longer axisymmetric, so its degenerate mode pairs split and its
135 modes mix. The dents are a change of the rest shape; the residual stresses
136 of real hammering are not modeled."""
137 D = 16 * inch
138 R = D / 2
139 bell_r, bell_h, bow_h = 55 * mm, 8 * mm, 10 * mm
140 tc, te = 1.2 * mm, 0.7 * mm
141 dent_r = 12 * mm
142 return Design(
143 "16 inch hammered crash",
144 BRONZE,
145 D,
146 D,
147 lambda x, y, r, t: tc + (te - tc) * ramp(bell_r, R, r),
148 inside=lambda x, y, r, t: r <= R,
149 profile=lambda x, y, r, t: bow_h * (1 - (r / R) ** 2) + dome(r, bell_r, bell_h) + dents(x, y, bell_r + dent_r, R - dent_r, n_dents, dent_r, dent_depth, random_dents, seed),
150 )
153def ellipse() -> Design:
154 """A 14 by 10 inch elliptical steel plate, 1 mm thick. The degenerate mode pairs of a disc split on an ellipse."""
155 A, B = 14 * inch, 10 * inch
156 return Design("Steel ellipse", STAINLESS_STEEL, A, B, 1.0 * mm, inside=lambda x, y, r, t: (x / (A / 2)) ** 2 + (y / (B / 2)) ** 2 <= 1)
159def thunder_sheet() -> Design:
160 """A thunder sheet: a 24 by 36 inch steel sheet, 0.5 mm thick. Its modes are
161 dense and low, and the full model turns a hard hit into a roar. Long at the
162 default spacing; a coarser grid keeps it interactive."""
163 return Design("Thunder sheet", MILD_STEEL, 24 * inch, 36 * inch, 0.5 * mm)
166PRESETS = {
167 "flat-rectangle": flat_rectangle,
168 "aluminum-square": aluminum_square,
169 "bowed-rectangle": bowed_rectangle,
170 "flat-disc": flat_disc,
171 "crash-16": crash_16,
172 "ride-20": ride_20,
173 "crash-bell-16": crash_bell_16,
174 "crash-hammered-16": crash_hammered_16,
175 "ellipse": ellipse,
176 "thunder-sheet": thunder_sheet,
178"""The presets by name."""
181def preset(name: str) -> Design:
182 """A preset design by name (see ``PRESETS``)."""
183 try:
184 return PRESETS[name]()
185 except KeyError:
186 raise KeyError(f"unknown preset {name!r}; choose from {', '.join(PRESETS)}") from None
↑↓ move↵openescclose