11from functools import partial
22from math import atan2 , cos , exp , pi , sin , sqrt
3+ from pathlib import Path
34
45import matplotlib .pyplot as plt
56import numpy as np
6- from pathlib import Path
77
88
99def arrays_equal (res : tuple [float , ...] | list [float ], expected : tuple [float , ...] | list [float ], eps = 1e-7 ):
@@ -29,9 +29,9 @@ def do_show(time: list, z: list, v: list, compare1: list | None = None, compare2
2929
3030def force (t : float , ampl : float = 1.0 , omega : float = 0.1 , d_omega : float = 0.0 ):
3131 if d_omega == 0.0 :
32- return np .array ((0 , 0 , ampl * sin (omega * t )), float ) # fixed frequency
32+ return np .array ((0 , 0 , ampl * sin (omega * t )), float ) # fixed frequency
3333 else :
34- return np .array ((0 , 0 , ampl * sin ((omega + d_omega * t ) * t )), float ) # frequency sweep
34+ return np .array ((0 , 0 , ampl * sin ((omega + d_omega * t ) * t )), float ) # frequency sweep
3535
3636
3737def forced_oscillator (
@@ -118,6 +118,7 @@ def run_oscillation_z(
118118
119119 return (osc , times , z , v )
120120
121+
121122def sweep_oscillation_z (
122123 k : float ,
123124 c : float ,
@@ -136,13 +137,9 @@ def sweep_oscillation_z(
136137 and return the oscillator object and the time series for z-position and z-velocity."""
137138
138139 from examples .oscillator import Oscillator
139- f_func = f_func = partial (force , ampl = ampl , omega = 0.0 , d_omega = d_omega )
140- osc = Oscillator (k = (1.0 , 1.0 , k ),
141- c = (0.0 , 0.0 , c ),
142- m = m ,
143- tolerance = tol ,
144- f_func = f_func
145- )
140+
141+ f_func = f_func = partial (force , ampl = ampl , omega = 0.0 , d_omega = d_omega )
142+ osc = Oscillator (k = (1.0 , 1.0 , k ), c = (0.0 , 0.0 , c ), m = m , tolerance = tol , f_func = f_func )
146143 osc .x [2 ] = x0 # set initial z value
147144 osc .v [2 ] = v0 # set initial z-speed
148145 times , z , v , f = [], [], [], []
@@ -264,70 +261,75 @@ def area(x: list[float], y: list[float]):
264261 assert abs (y [- 1 ] - 4.0 ) < 1e-12 , f"Found { y [- 1 ]} "
265262 osc , x , y = run_2d (x0 = (1.0 , 0.0 , 0.0 ), v0 = (0.0 , 1.0 , 0.0 ), k = (1.0 , 1.0 / 15.8 , 0 ), end = 20 * np .pi )
266263 if show :
267- show_2d (x ,y )
264+ show_2d (x , y )
265+
268266
269267def test_sweep_oscillator (show : bool = True ):
270268 """A forced oscillator where the force frequency is changed linearly as d_omega*time.
271269 The test demonstrates that a monolithic simulation provides accurate results in all ranges of the force frequency.
272270 Co-simulating the oscillator and the force, this does not work.
273271 """
274- osc , times0 , z0 , v0 , f0 = sweep_oscillation_z ( k = 1.0 ,
275- c = 0.1 ,
276- m = 1.0 ,
277- ampl = 1.0 ,
278- d_omega = 0.1 ,
279- x0 = 0.0 ,
280- v0 = 0.0 ,
281- dt = 0.1 , # 'ground truth', small dt
282- end = 100.0 ,
283- tol = 1e-3
284- )
285- with open (Path .cwd () / "oscillator_sweep0.dat" , 'w' ) as fp :
272+ osc , times0 , z0 , v0 , f0 = sweep_oscillation_z (
273+ k = 1.0 ,
274+ c = 0.1 ,
275+ m = 1.0 ,
276+ ampl = 1.0 ,
277+ d_omega = 0.1 ,
278+ x0 = 0.0 ,
279+ v0 = 0.0 ,
280+ dt = 0.1 , # 'ground truth', small dt
281+ end = 100.0 ,
282+ tol = 1e-3 ,
283+ )
284+ with open (Path .cwd () / "oscillator_sweep0.dat" , "w" ) as fp :
286285 for i in range (len (times0 )):
287- fp .write ( f"{ times0 [i ]} \t { z0 [i ]} \t { v0 [i ]} \t { f0 [i ]} \n " )
288-
286+ fp .write (f"{ times0 [i ]} \t { z0 [i ]} \t { v0 [i ]} \t { f0 [i ]} \n " )
287+
289288 if show :
290- freq = [0.1 * t / 2 / np .pi for t in times0 ]
289+ freq = [0.1 * t / 2 / np .pi for t in times0 ]
291290 fig , ax = plt .subplots ()
292291 ax .plot (freq , z0 , label = "z0(t)" )
293292 ax .plot (freq , v0 , label = "v0(t)" )
294293 ax .plot (freq , f0 , label = "F0(t)" )
295294 ax .legend ()
296295 plt .show ()
297-
298- osc , times , z , v , f = sweep_oscillation_z ( k = 1.0 ,
299- c = 0.1 ,
300- m = 1.0 ,
301- ampl = 1.0 ,
302- d_omega = 0.1 ,
303- x0 = 0.0 ,
304- v0 = 0.0 ,
305- dt = 1 , # dt similar to resonance frequency
306- end = 100.0 ,
307- tol = 1e-3
308- )
296+
297+ osc , times , z , v , f = sweep_oscillation_z (
298+ k = 1.0 ,
299+ c = 0.1 ,
300+ m = 1.0 ,
301+ ampl = 1.0 ,
302+ d_omega = 0.1 ,
303+ x0 = 0.0 ,
304+ v0 = 0.0 ,
305+ dt = 1 , # dt similar to resonance frequency
306+ end = 100.0 ,
307+ tol = 1e-3 ,
308+ )
309309 i0 = 0
310- for i in range (len (times )): # demonstrate that the results are accurate, even if dt is large
310+ for i in range (len (times )): # demonstrate that the results are accurate, even if dt is large
311311 t = times [i ]
312- while abs (times0 [i0 ]- t ) > 1e-10 :
312+ while abs (times0 [i0 ] - t ) > 1e-10 :
313313 i0 += 1
314314 assert times0 [i0 ] - t < 0.1 , f"Time entry for time { t } not found in times0"
315-
315+
316316 assert abs (z0 [i0 ] - z [i ]) < 2e-2 , f"Time { t } . Found { z0 [i0 ]} != { z [i ]} "
317317 assert abs (v0 [i0 ] - v [i ]) < 2e-2 , f"Time { t } . Found { v0 [i0 ]} != { v [i ]} "
318-
318+
319319 if show :
320320 fig , ax = plt .subplots ()
321321 ax .plot (times0 , z0 , label = "z0(t)" )
322322 ax .plot (times , z , label = "z(t)" )
323323 ax .legend ()
324324 plt .show ()
325325
326+
326327if __name__ == "__main__" :
327- retcode = 0 # pytest.main(["-rA", "-v", "--rootdir", "../", "--show", "False", __file__])
328+ retcode = pytest .main (["-rA" , "-v" , "--rootdir" , "../" , "--show" , "False" , __file__ ])
328329 assert retcode == 0 , f"Non-zero return code { retcode } "
329330 import os
331+
330332 os .chdir (Path (__file__ ).parent .absolute () / "test_working_directory" )
331333 # test_oscillator_class(show=True)
332334 # test_2d(show=True)
333- test_sweep_oscillator ()
335+ # test_sweep_oscillator()
0 commit comments