Predeclare zero-damping pinball development cases

This commit is contained in:
Frank14f committed 2026-10-31 15:51:39 +08:00
1 parent d0bcefa60c
commit 7b81584458
3 files changed
+83 -12

No files matched your search

+20 -10
View File
@@ -1,6 +1,6 @@
"""Uncontrolled paper-geometry steady Re_D=50, one transverse assembly DOF.
One fixed protocol; --smoke performs 32 warmup + 32 observation steps only.
Three predeclared zero-damping structures; --smoke uses only 32 + 32 steps.
See docs/steady_pinball_fiv.md for historical solver differences and new structure.
"""
from __future__ import annotations
@@ -18,6 +18,7 @@ D, U, NU = 20., .01, .004
CENTRES = ((600., 255.5), (626., 270.5), (626., 240.5))
PROBES = ((800., 295.5), (800., 255.5), (800., 215.5))
WARMUP, STEPS, PERIOD = 512000, 600000, 10000
STRUCTURES = {'Z0': (10., 1.e-4), 'M2': (2., 1.e-4), 'R2': (2., 6.e-5)}
COLUMNS = ('step', 'y', 'v', 'cme_F', 'cme_U', 'cme_L', 'wen_F', 'wen_U', 'wen_L',
'node_F', 'node_U', 'node_L', 'eq_only', 'no_donor', 'crossings')
ROOT = Path(__file__).resolve().parents[2]
@@ -29,10 +30,15 @@ def config():
class Structure:
"""Exact damped oscillator update for a force held over dt=1; no force filter."""
def __init__(self):
self.mass = 10. * 3. * math.pi * D * D / 4.
self.omega = 2. * math.pi / PERIOD
self.zeta = .01
def __init__(self, mass_ratio=10., frequency=1.e-4, zeta=0.):
if not (math.isfinite(mass_ratio) and mass_ratio > 0. and
math.isfinite(frequency) and frequency > 0. and
math.isfinite(zeta) and 0. <= zeta < 1.):
raise ValueError('positive finite mass ratio/frequency and 0 <= zeta < 1 required')
self.mass_ratio, self.frequency = mass_ratio, frequency
self.mass = mass_ratio * 3. * math.pi * D * D / 4.
self.omega = 2. * math.pi * frequency
self.zeta = zeta
self.stiffness = self.mass * self.omega**2
self.damping = 2. * self.zeta * self.mass * self.omega
self.y = self.v = 0.
@@ -68,6 +74,8 @@ def main():
ap.add_argument('--out', required=True, type=Path)
ap.add_argument('--fixed', action='store_true', help='same modern moving-path geometry held fixed')
ap.add_argument('--smoke', action='store_true', help='32+32 steps, no physical result')
ap.add_argument('--case', choices=STRUCTURES, default='Z0',
help='predeclared structure; all cases have zero structural damping')
args = ap.parse_args()
out = args.out.resolve()
if out == ROOT or ROOT in out.parents:
@@ -76,7 +84,7 @@ def main():
cfg = config()
(out/'config.json').write_text(json.dumps(cfg, indent=2)+'\n')
source = subprocess.check_output(['git', '-C', str(ROOT), 'rev-parse', 'HEAD'], text=True).strip()
s = Structure()
s = Structure(*STRUCTURES[args.case], zeta=0.)
warmup, count = (32, 32) if args.smoke else (WARMUP, STEPS)
rows = np.lib.format.open_memmap(out/'steps.npy', mode='w+', dtype=np.float64,
shape=(count, len(COLUMNS)))
@@ -87,11 +95,13 @@ def main():
col = {k: names.index(k) for k in names}
status, completed, f_last = 'running', 0, 0.
started = time.monotonic()
report = dict(source=source, fixed=args.fixed, smoke=args.smoke, columns=COLUMNS,
report = dict(source=source, case=args.case, fixed=args.fixed, smoke=args.smoke, columns=COLUMNS,
structure=dict(mass=s.mass, stiffness=s.stiffness, damping=s.damping,
zeta=s.zeta, fn=1./PERIOD, y0=0., v0=0.),
mass_ratio_three_disks=s.mass_ratio,
zeta=s.zeta, fn=s.frequency, Tn=1./s.frequency, y0=0., v0=0.),
warmup_steps=warmup, requested_steps=count, dt=1., D=D, U=U, nu=NU,
duration_Tn=count/PERIOD, duration_tU_D=count*U/D,
duration_Tn=count*s.frequency, duration_tU_D=count*U/D,
summary_block_steps=10*PERIOD,
rhs='sum of three instantaneous CME_y + Wen_y, one-step lag, no filtering')
try:
with Simulation(str(out/'config.json')) as sim:
@@ -135,7 +145,7 @@ def main():
f_last = float(cme.sum(dtype=np.float64)+wen.sum(dtype=np.float64))
if completed % PERIOD == 0:
rows.flush()
print(json.dumps(dict(event='period', step=completed, t_Tn=completed/PERIOD,
print(json.dumps(dict(event='progress', step=completed, t_Tn=completed*s.frequency,
y_D=s.y/D, elapsed_s=time.monotonic()-started)), flush=True)
else:
status = 'smoke_completed' if args.smoke else 'duration_completed_not_qualified'