Skip to content

Commit ea99cdb

Browse files
committed
Sav bound v0
1 parent 474a052 commit ea99cdb

3 files changed

Lines changed: 157 additions & 8 deletions

File tree

python/plot_drift_JAES.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,5 @@ def Fext(t):
141141
# Save figure
142142
fig.savefig(os.path.join(result_folder, f"test_drift_nl_force.pdf"),
143143
bbox_inches='tight')
144+
145+
plt.show(block=True)

python/sav_solver.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -421,26 +421,28 @@ def time_step(self, qlast, qnow, rn, unow, ConstantRmid=False, BoundG=False):
421421
else:
422422
multiplicative_bound = 0
423423
if (multiplicative_bound > 1):
424+
print("yo")
424425
g_mult = multiplicative_bound
425426
else:
426427
g_mult = 1
427428
else:
428429
g_mult = 1
429430

430-
den = (4 + g_mult**2 * self.gn.dot(self.A0_inv_n * self.gn)) # eq 19g
431+
self.gn *= g_mult
431432

432-
self.RHSn = self.B_op(qnow) + self.C_op(qlast, g_mult * self.gn,
433-
self.Rmidn) + self.Gn @ unow - g_mult * self.gn * rn
433+
den = (4 + self.gn.dot(self.A0_inv_n * self.gn)) # eq 19g
434+
435+
self.RHSn = self.B_op(qnow) + self.C_op(qlast, self.gn,
436+
self.Rmidn) + self.Gn @ unow - self.gn * rn
434437

435438
qnext = self.model.J0 * self.A0_inv_n * self.RHSn \
436-
- self.model.J0 * self.A0_inv_n * g_mult * self.gn / den * \
439+
- self.model.J0 * self.A0_inv_n * self.gn / den * \
437440
self.gn.dot(self.A0_inv_n * self.RHSn) # 19b+19g
438441

439442
# Update auxiliary variable
440-
rnext = rn + g_mult * \
441-
self.gn.dot((qnext - qlast) / (2 * self.model.J0))
442-
if (rnext < 0):
443-
print("wtf")
443+
rnext = rn + self.gn.dot((qnext - qlast) / (2 * self.model.J0))
444+
if ((rnext + rn)/2 < 0):
445+
print((rnext + rn)/2)
444446
return qnext, rnext, qn, pn, self.epsilon
445447

446448
def integrate(self, q0, u0, u_func, duration, ConstantRmid=False,

python/test_sav_bound.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import numpy as np
2+
import os
3+
import matplotlib.pyplot as plt
4+
from helper_plots import set_size
5+
import librosa
6+
from scipy.io.wavfile import write
7+
8+
from fd_string_model import FD_string_model, get_etas_from_decays, get_T_and_l0_from_f0_beta
9+
from sav_solver import SAVSolver
10+
from results_storage import STATE_STORAGE_CONFIG, DEFAULT_STORAGE_CONFIG
11+
from plotter import NO_PLOTTER_CONFIG
12+
13+
# Output folder
14+
result_folder = "results/SAV_bound"
15+
16+
d = os.path.dirname(os.path.abspath(result_folder))
17+
if d and not os.path.exists(d):
18+
os.makedirs(d, exist_ok=True)
19+
20+
"""
21+
Try to reproduce figure 3 from "Convergence analysis and relaxation techniques for modal scalar auxiliary variable
22+
methods applied to nonlinear transverse string vibration", Russo et al, 2025.
23+
"""
24+
25+
# %%
26+
# System description
27+
28+
# Physical parameters
29+
StringParams = {
30+
"Ra": np.sqrt(3.97e-7 / np.pi),
31+
"rho": 8050,
32+
"E": 174e9,
33+
"T": 75,
34+
"l0": 1
35+
}
36+
# Perceptive parameters
37+
f0 = 82.4
38+
beta = 5e-3
39+
T_60_0 = 4
40+
T_60_1000 = 3
41+
42+
# Deduce missing physical parameters
43+
# StringParams["T"], StringParams["l0"] = get_T_and_l0_from_f0_beta(
44+
# f0, beta, StringParams)
45+
StringParams["eta_0"], StringParams["eta_1"] = get_etas_from_decays(
46+
T_60_0, T_60_1000, StringParams)
47+
48+
print(StringParams)
49+
50+
51+
model = FD_string_model(44100, **StringParams)
52+
53+
# %%
54+
# Simulation parameters
55+
sr = 44100
56+
duration = 1
57+
kappa = 0.8
58+
lambda0s = [0, 1000]
59+
OF = 2 # Over-sampling factor for reference
60+
61+
# Deduce discretization from stability condition
62+
dt = 1 / sr
63+
model.recompute_stability(sr, kappa=kappa)
64+
65+
66+
# %%
67+
# Initial conditions and excitation
68+
69+
# External force (applied at the middle of the string)
70+
def Fext(t):
71+
Amp = 10
72+
width = 2e-3
73+
period = 500 * width
74+
out = np.zeros(1)
75+
out[0] = Amp * np.sin(np.pi * t / (2 * width))**2 * (t % period < width)
76+
return out
77+
78+
79+
q0 = np.zeros(model.N)
80+
u0 = np.zeros(model.N)
81+
82+
83+
# %% Run simulations and plot results
84+
fig, axs = plt.subplots(1 + 3*len(lambda0s), 1,
85+
figsize=set_size("JAES", height_ratio=0.6), sharex=True)
86+
linestyles = [":", "--", "-."]
87+
for i, lambda0 in enumerate(lambda0s):
88+
model.NL_type = "GE"
89+
# Compute SAV solution
90+
solver = SAVSolver(model, sr, lambda0)
91+
92+
storage = STATE_STORAGE_CONFIG
93+
storage["Drift"] = True
94+
storage["q_idx"] = np.array([model.N//2 + 1])
95+
storage["p_idx"] = None
96+
97+
solver.integrate(q0, u0, Fext, duration, ConstantRmid=True,
98+
plotter_config=NO_PLOTTER_CONFIG, storage_config=storage, BoundG=False)
99+
solver.storage.write(os.path.join(
100+
result_folder, f"sr{sr}_lambda{lambda0}.h5"))
101+
102+
f0, _, _ = librosa.pyin(
103+
solver.storage.q[:, 0], fmin=40, fmax=200, sr=44100, frame_length=2048 * 4)
104+
write(os.path.join(result_folder, f"sr{sr}_lambda{lambda0}.wav"),
105+
sr, solver.storage.q[:, 0] / np.max(np.abs(solver.storage.q[:, 0])))
106+
107+
axs[0].plot(solver.storage.t, [Fext(t)
108+
for t in solver.storage.t], color="black")
109+
axs[0].set_ylabel(r"$f_{in}$ [N]")
110+
axs[3 * i+1].plot(np.linspace(0, duration, len(f0)),
111+
f0)
112+
# Here, we divide espilon by the max observed nonlinear energy to get a relative measure
113+
print(solver.maxEnl)
114+
axs[3 * i+2].semilogy(solver.storage.t,
115+
np.abs(solver.storage.epsilon / solver.maxEnl))
116+
117+
axs[3*i+1].set_ylabel(r"$f_0$ [Hz]")
118+
axs[3*i+1].set_ylim(80, 110)
119+
axs[3*i+2].set_ylabel(r"$\vert\epsilon_{rel}\vert$")
120+
axs[3*i+2].set_ylim([1e-4, 1.5e3])
121+
axs[3*i+2].set_yticks([1e-4, 1e-1, 1e2])
122+
axs[3*i+2].set_yticklabels([1e-4, 1e-1, 1e2])
123+
axs[3*i+1].text(0.8, 0.6, fr"$\lambda_0 = {lambda0} s^{-1}$", transform=axs[2*i+1].transAxes,
124+
color="red", bbox=dict(facecolor='white', edgecolor='black', boxstyle='round'))
125+
axs[3 * i+2].text(0.8, 0.6, fr"$\lambda_0 = {lambda0} s^{-1}$", transform=axs[2*i+2].transAxes,
126+
color="red", bbox=dict(facecolor='white', edgecolor='black', boxstyle='round'))
127+
128+
# axs[3*i+3].plot(solver.storage.t, solver.storage.r)
129+
axs[3*i+3].plot(solver.storage.t[:-1], 0.5 *
130+
(solver.storage.r[:-1] + solver.storage.r[1:]))
131+
132+
axs[1].legend(loc="lower center", frameon=True, fancybox=True,
133+
bbox_to_anchor=(0.5, 2.1), ncol=3)
134+
axs[4].set_xlim(0, duration)
135+
axs[4].set_ylim(1e-8, 10)
136+
axs[4].set_xlabel(r"Time [s]")
137+
for ax in axs:
138+
ax.grid()
139+
# Squeeze
140+
fig.tight_layout()
141+
fig.align_ylabels(axs)
142+
fig.subplots_adjust(hspace=0.1, wspace=0.4)
143+
# Save figure
144+
fig.savefig(os.path.join(result_folder, f"test_drift_nl_force.pdf"),
145+
bbox_inches='tight')

0 commit comments

Comments
 (0)