| docs | |
| examples | |
| src | |
| tests | |
| .gitignore | |
| prototype.py | |
| pyproject.toml | |
| README.md |
cymsim#
Explicit nonlinear finite-element simulation of struck cymbals. The cymbal is a Marguerre-von Karman shallow shell with a bell, a bow, a thickness taper and seeded imperfections, discretized on a triangulated disk with constant-strain membrane triangles and Discrete Kirchhoff bending triangles, integrated in time with an explicit leapfrog scheme, struck by one or more rigid exciters through power-law contact springs, and heard at a listener point through a Rayleigh-integral radiation model. The equations, the discretization and the time-stepping method are written out in docs/model.md.
Installation#
pip install -e ".[dev]" # numpy, scipy, jax (CPU), pyyaml, pytest, matplotlib
pip install "jax[cuda12]" # optional: run the time loop on a GPU
Runs at the default resolution (6 mm elements, about 4700 nodes and 40 sub-steps per audio sample) need a GPU to finish in minutes. On a CPU, use a coarser mesh and a shorter duration, as in examples/quick_cpu.yaml.
Usage#
cymsim example-config my_run.yaml --exciter mallet # write a default config
cymsim check my_run.yaml # mesh, time step, element self-tests, no time stepping
cymsim simulate my_run.yaml # run; writes <prefix>_rayleigh.wav, _unbaffled.wav, _result.npz, _cascade.png
cymsim simulate my_run.yaml --linear --duration 0.5 # linear reference run
The same from Python:
from cymsim import SimulationConfig, Hit, StickExciter, simulate
cfg = SimulationConfig.from_yaml("examples/crash_mallet.yaml")
cfg.hits.append(Hit(time=0.8, r=0.19, phi=2.0, velocity=7.0, exciter=StickExciter()))
result = simulate(cfg)
result.write_wav("crash")
print(result.hits) # peak force, contact time and rebound velocity of each hit
print(result.energy_log) # kinetic / membrane / bending / exciter energy and max|w|/h over time
A run is described by one SimulationConfig made of Material, Profile, Imperfections, MeshConfig, a list of Hits (each with a StickExciter or MalletExciter), Listener, Numerics and Output. The YAML file mirrors the dataclass fields; omitted fields take the defaults, which reproduce an 18 inch medium-thin B20 crash. Units are SI throughout. See examples/ for a mallet stroke, a stick crash, a train of four hits and a quick CPU run.
Layout#
| module | contents |
|---|---|
| config.py | configuration dataclasses, YAML load/save, validation |
| geometry.py | mid-surface height z0(x, y) and thickness h(x, y), seeded imperfections |
| mesh.py | numpy disk mesher (concentric rings, angular stitching, smoothing), CST operators |
| elements.py | DKT bending stiffness, Marguerre-von Karman membrane strains, element energies |
| shell.py | assembled shell: lumped mass, rigid modes, internal force by autodiff, stability bound |
| exciter.py | contact patches, power-law contact law, schedule of hits |
| radiation.py | Rayleigh integral and the unbaffled (rim-diffraction) approximation |
| simulate.py | leapfrog time loop, block compilation, result container |
| diagnostics.py | band envelopes, onset times, spectral centroid, cascade plot |
| cli.py | cymsim simulate, cymsim check, cymsim example-config |
prototype.py is the original single-file prototype that this package reorganizes; it is kept for reference and is not used by the package.
Tests#
pytest
The tests check the mesher (manifold, orientation, quality), the DKT element (rigid modes, constant curvature, twist), the membrane strains, the assembled shell (mass, rigid-mode annihilation, force = minus energy gradient, dissipative damping), the configuration round trip, and short coarse simulations with one and two hits.