/ proof-of-concept / cymsim
Sign in
proof-of-concept / cymsim
cymsim
Go to fileHistoryFork
docsContinue a run from a .cymsim file (cymsim run --resume, simulate(cfg, resume=...)), identical to the uninterrupted run; keep the outputs past the duration in the state and store probe signals in the file
examplescymsim: nonlinear FEM cymbal simulation package
srcCache keys for runs: hash of the non-default config fields without duration, energy-report cadence and output naming, with MODEL_VERSION; cymsim cache-key
testsCache keys for runs: hash of the non-default config fields without duration, energy-report cadence and output naming, with MODEL_VERSION; cymsim cache-key
toolsTime step from the global stability bound (power iteration), with numerics.stability_bound to keep the element bound
.gitignoresave .cymsim file
prototype.pycymsim: nonlinear FEM cymbal simulation package
pyproject.tomlsave .cymsim file
README.mdCache keys for runs: hash of the non-default config fields without duration, energy-report cadence and output naming, with MODEL_VERSION; cymsim cache-key

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, h5py, 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 38 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
cymsim run my_run.yaml -o run.cymsim --save-every 60 # also write a .cymsim file, updated every minute of wall time
cymsim run my_run.yaml -o run.cymsim --record-acceleration   # include the audio-rate normal acceleration of every node
cymsim run my_run.yaml --resume run.cymsim --duration 4 -o run4.cymsim   # continue a saved run to 4 s
cymsim info run.cymsim                               # summarise a .cymsim file
cymsim cache-key my_run.yaml --show                  # the run's cache key (independent of the duration) and what it hashes

A .cymsim file is an HDF5 file holding the configuration, the mesh, the listener signals, displacement frames at a low rate, optionally the normal acceleration of every node at the audio rate, and the integrator state; the layout is specified in docs/format.md. It is meant for taking the shell's motion into other programs (other listeners, rooms, acoustic solvers) and is the file the web companion cymsimweb saves and loads. A partial file written during a run is a consistent prefix of the complete one.

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()))
cfg.output.record_acceleration = True      # off by default: about 0.4 GB per simulated second at the default mesh
result = simulate(cfg)
result.write_wav("crash")
result.save("crash.cymsim")
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 = result.acceleration.as_float(0, 4410)   # normal acceleration [m/s^2], first 0.1 s, shape (4410, n_nodes)
w = result.displacement.w                   # transverse displacement at 60 Hz, shape (n_frames, n_nodes)

from cymsim import load_cymsim
back = load_cymsim("crash.cymsim")          # the same SimulationResult, from the file
cfg.numerics.duration = 4.0
longer = simulate(cfg, resume=back)         # continue it to 4 s; identical to a 4 s run on the same device

A run is described by one SimulationConfig made of Material, Profile, Imperfections, MeshConfig, a list of Hits (each with a StickExciter, a MalletExciter, or a ForceExciter for a prescribed force pulse), 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
decimation.py the anti-aliasing kernel that takes accelerations and pressure from the sub-step rate to the audio rate
simulate.py leapfrog time loop, block compilation, acceleration and displacement records, result container
cymsim_file.py the .cymsim HDF5 file: streaming writer, loader, summary
cache.py cache keys: hash of the non-default config fields (without duration and output naming) and MODEL_VERSION
diagnostics.py band envelopes, onset times, spectral centroid, cascade plot
cli.py cymsim simulate (run), cymsim check, cymsim info, cymsim cache-key, 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, the decimation kernel and streaming decimator, short coarse simulations with one and two hits, and the .cymsim file (round trip, partial files, and that the baffled listener signal is recovered from the stored accelerations alone).

↑↓ move↵openescclose