def main_operation(self, plot=True): """Run the ODE integrator for the system in question and save the plots.""" torque_index = 0 self._update_torque(self.y0[0], torque_index) f_baked = h.baker( c.f_full_torque, ["", "", self.i, self.b, self.k, self._get_recent_torque], pos_to_pass_through=(0, 1)) # Set initial conditions dy = c.rk4(f_baked) times, y, stepsize = *self.t0, self.y0, self.dt[0] / self.divider[0] results = [] update_counter = 0 while times <= self.t_fin: results.append( [times, *y, self.total_torque, self.theta_sim, self.omega_sim]) times, y = times + stepsize, y + dy(times, y, stepsize) update_counter += 1 # For a fixed step size, we can just repeat the torque # recalculation every self.divider steps, so there is no # overshooting. if update_counter == self.divider: update_counter = 0 torque_index += 1 if torque_index == self.period_divider: torque_index = 0 old_theta = n.get_old_theta(times, self.delay, results) self._update_torque(old_theta, torque_index) results = np.array(results).squeeze() # Calculate fourier series first 50 terms. ind_mag_phi = fourier.get_fourier_series(50, self.w_d, self.dt, self.g_0) # fourier theoretical results. torque_fourier = h.baker(t.calculate_sine_pi, [ "", "", "", "", ind_mag_phi[:, 1], self.w_d * ind_mag_phi[:, 0], ind_mag_phi[:, 2] ], pos_to_pass_through=(0, 3)) theory_fourier = t.calc_theory_soln(results[:, 0], self.t0, self.y0, self.b - self.b_prime, self.k - self.k_prime, self.i, torque_fourier) ss_bit_theory = theory_fourier[:, 1][np.where( theory_fourier[:, 0] > self.t_fin.squeeze() - 2)] # Find maximum error over amplitude. iterator_diffs = np.max( np.abs(m.calc_norm_errs([results[:, 1], theory_fourier[:, 1]])[1]) / np.max(ss_bit_theory)) exp_results = pd.DataFrame( np.array([ self.b, self.k, self.t_fin, self.period_divider, [iterator_diffs] ]).T, columns=['b', 'k', 'tfin', 'divider', 'max-frac-err']) return {'all-mmts': exp_results}
def main_operation(self, plot=True): """Read all data from a specific folder and produce real space and fourier space plots. Use for only one set of b, k, b', k', i, etc. parameters at a time.""" directory = self.prms['directory'][0] all_same = self.prms['all_same'][0] file_list = h.find_files(directory) filename_roots = r.check_matches(file_list, num_one='all-mmts', num_two=None) all_data = r.read_all_data(directory, filename_roots, disps_ext='all-mmts', torque_ext='all-mmts') sorted_by_params = r.sort_data(all_data, all_same=all_same) b = sorted_by_params[0][0]['b'] k = sorted_by_params[0][0]['k'] b_prime = sorted_by_params[0][0]['b\''] k_prime = sorted_by_params[0][0]['k\''] i = sorted_by_params[0][0]['i'] # Baked theoretical response function to require wd input only, # which depends on the range measured. need_wd = h.baker(t.theory_response, args=[b, k, i, b_prime, k_prime, ''], pos_to_pass_through=5) fft_data = r.match_torques(sorted_by_params, plot_real=self.prms['plot_real'][0], savepath=directory + 'plots/') print(fft_data) if plot: r.prepare_to_plot(fft_data, need_wd, savepath=directory + 'plots/')
def main_operation(self): """Run the ODE integrator for the system in question.""" # Set parameters. i = self.prms['i'] b = self.prms['b'] k = self.prms['k'] y0 = np.array([self.prms['theta_0'], self.prms['omega_0']]).squeeze() t0 = self.prms['t0'] t_fin = self.prms['tfin'] r = ode(c.f_full_torque) #.set_integrator('dop853') self._update_torque(y0[0]) r.set_initial_value(y0, t0).set_f_params( i, b, k, self._get_recent_torque).set_jac_params(i, b, k) results = [[*t0, *y0]] while r.successful() and r.t < t_fin: y = np.real(r.integrate(r.t + self.display_dt)) data_point = [*(r.t + self.display_dt), *y] results.append(data_point) print("Time-theta-omega", data_point) # Recalculate the reset the torque every dt seconds. # get the last set of consecutive points where the digitised # torque (-6th column) has the same value as the current one # every cycle. If the corresponding times have a range greater # than or equal to dt, re-measure the torque. matching_indices = h.find_consec_indices(self.torques[:, -6]) if self.torques[-1, 1] - min(self.torques[matching_indices, 1]) >= self.dt: self._update_torque(y[0]) print("triggered") r.set_initial_value(r.y, r.t).set_f_params(i, b, k, self._get_recent_torque) results = np.array(results).squeeze() sines_torque = h.baker(t.calculate_sine_pi, [ "", "", "", "", self.prms['g_0_mag'], self.prms['w_d'], np.array([0]) ], pos_to_pass_through=(0, 3)) theory = t.calc_theory_soln(np.linspace(0, 2, 1000), t0[0], y0, (b - self.prms['b\''])[0], (k - self.prms['k\''])[0], i[0], sines_torque) print( "Init parameters: dt: {}, display_dt: {}, b: {}, b': {}, k: {}, " "k': {}, I: {}, y0: {}, t0: {}, tfin: {}, g0: {}, w_d: {}".format( self.dt, self.display_dt, b, self.prms['b\''], k, self.prms['k\''], i, y0, t0, t_fin, self.prms['g_0_mag'], self.prms['w_d'])) print("Parameters from the C code: k': {}, b': {}, g0: {}".format( talk.get_k_prime(), talk.get_b_prime(), talk.get_amp())) plt.plot(theory[:, 0], theory[:, 1]) plt.plot(results[:, 0], results[:, 1]) plt.show()
def _single_operation(self, times, b, k, i, b_prime, k_prime, w_d, g_0_mag, phase, n_frq_peak, t0, y0): """One run for one value of b, k, i, b_prime, k_prime, torque_amplitude, torque_phase, torque_frequency and noise. t is a time array.""" times = m.check_nyquist(times, w_d, b, b_prime, k, k_prime, i) decimal_periods = np.abs(5.231 * 2 * np.pi / w_d) time_index = 0 num = 1 # The index for 5.231 periods of time being elapsed is used to # calculate the number per segment to use for the autocorrelation. while time_index < 10: time_index = np.abs(times - num * decimal_periods).argmin() num += 1 torque = g_0_mag * np.sin(w_d * times + phase) pi_contr = h.baker(t.calculate_sine_pi, ["", "", "", "", g_0_mag, w_d, phase], pos_to_pass_through=(0, 3)) theory = t.calc_theory_soln(times, t0, y0, b - b_prime, k - k_prime, i, pi_contr) w_res = t.w_res_gamma(b - b_prime, k - k_prime, i)[0] if b - b_prime >= 0: if b - b_prime == 0 and np.isreal(w_res): # filter out the transient frequency. theory[:, 1] = m.remove_one_frequency(times, theory[:, 1], w_res) theory[:, 2] = m.remove_one_frequency(times, theory[:, 2], w_res) # Will only reach steady state if this is the case, otherwise no # point making a response curve. Measure one point. b - b' = 0 # has two steady state frequencies, the transient and PI. ss_times = m.identify_ss(theory[:, 0], theory[:, 1], n_per_segment=time_index) if ss_times is not False: self._log('before fft') frq, fft_theta = m.calc_fft( times[(times >= ss_times[0]) * (times <= ss_times[1])], theory[:, 1][(times >= ss_times[0]) * (times <= ss_times[1])]) # For low frequencies, the length of time of the signal must # also be sufficiently wrong for the peak position to be # measured properly. freq = m.calc_freqs(np.absolute(fft_theta), frq, n_peaks=n_frq_peak) amp = m.calc_one_amplitude(theory[:, 1][(times >= ss_times[0]) * (times <= ss_times[1])]) phase = m.calc_phase(theory[:, 1], torque) return True, theory, np.array([freq, amp, phase]) else: return False, theory else: return False, theory
def ode_integrator(y0, t0, i, b_prime, k_prime, b, k, g_0_mags, w_ds, phases, t_fin, dt, torque_func): """Test function for ODE integration.""" r = ode(f_analytic).set_integrator('vode') baked_g_0 = h.baker(torque_func, args=['', w_ds, g_0_mags, phases]) r.set_initial_value(y0, t0).set_f_params(i, baked_g_0, b, b_prime, k, k_prime) results = [[t0, *y0]] while r.successful() and r.t < t_fin: data_point = [r.t + dt, *np.real(r.integrate(r.t + dt))] results.append(data_point) exp_results = np.array(results) return exp_results
def main_operation(self, plot=True): """Run the ODE integrator for the system in question and save the plots.""" torque_index = 0 self._update_torque(self.y0[0], torque_index) f_baked = h.baker( c.f_full_torque, ["", "", self.i, self.b, self.k, self._get_recent_torque], pos_to_pass_through=(0, 1)) # Set initial conditions dy = c.rk4(f_baked) times, y, stepsize = *self.t0, self.y0, self.dt[0] / self.divider[0] results = [] update_counter = 0 while times <= self.t_fin: results.append( [times, *y, self.total_torque, self.theta_sim, self.omega_sim]) times, y = times + stepsize, y + dy(times, y, stepsize) update_counter += 1 # For a fixed step size, we can just repeat the torque # recalculation every self.divider steps, so there is no # overshooting. if update_counter == self.divider: update_counter = 0 torque_index += 1 if torque_index == self.period_divider: torque_index = 0 old_theta = n.get_old_theta(times, self.delay, results) self._update_torque(old_theta, torque_index) results = np.array(results).squeeze() # Calculate fourier series first num_terms terms. ind_mag_phi = fourier.get_fourier_series(self.num_terms, self.w_d, self.dt, self.g_0) # fourier theoretical results. torque_fourier = h.baker(t.calculate_sine_pi, [ "", "", "", "", ind_mag_phi[:, 1], self.w_d * ind_mag_phi[:, 0], ind_mag_phi[:, 2] ], pos_to_pass_through=(0, 3)) theory_fourier = t.calc_theory_soln(results[:, 0], self.t0, self.y0, self.b - self.b_prime, self.k - self.k_prime, self.i, torque_fourier) f_torque = fourier.get_torque(results[:, 0], ind_mag_phi[:, 0], ind_mag_phi[:, 1], ind_mag_phi[:, 2], self.w_d) if plot: times = m.check_nyquist(results[:, 0], self.w_d, self.b, self.b_prime, self.k, self.k_prime, self.i) decimal_periods = np.abs(5.231 * 2 * np.pi / self.w_d) time_index = 0 num = 1 # The index for 5.231 periods of time being elapsed is used to # calculate the number per segment to use for the auto-correlation. while time_index < 10: time_index = np.abs(times - num * decimal_periods).argmin() num += 1 print( "Init parameters: dt: {}, b: {}, b': {}, k: {}, k': {}, I: {}, " "y0: {}, t0: {}, tfin: {}, g0: {}, w_d: {}".format( self.dt, self.b, self.b_prime, self.k, self.k_prime, self.i, self.y0, self.t0, self.t_fin, self.g_0, self.w_d)) # Find absolute errors and plot. iterator_diffs = m.calc_norm_errs( [results[:, 1], theory_fourier[:, 1]], [results[:, 2], theory_fourier[:, 2]])[1] simu_diffs = m.calc_norm_errs([results[:, 4], results[:, 1]], [results[:, 5], results[:, 2]])[1] real_space_data = \ [[[[theory_fourier[:, 0], theory_fourier[:, 1]], [results[:, 0], results[:, 1]], [results[:, 0], results[:, 4]]], [[results[:, 0], iterator_diffs[0]], [results[:, 0], simu_diffs[0]]]], [[[theory_fourier[:, 0], theory_fourier[:, 2]], [results[:, 0], results[:, 2]], [results[:, 0], results[:, 5]]], [[results[:, 0], iterator_diffs[1]], [results[:, 0], simu_diffs[1]]]]] p.two_by_n_plotter( real_space_data, self.filename, self.prms, tag='fourier-added', savepath=self.savepath, show=True, x_axes_labels=['t/s', 't/s'], y_top_labels=[r'$\theta$/rad', r'$\dot{\theta}$/rad/s'], y_bottom_labels=[ r'$\Delta\theta$/rad', r'$\Delta\dot{\theta}$/rad/s' ]) results = np.hstack((results, np.array([f_torque]).T)) exp_results = pd.DataFrame(results, columns=[ 't', 'theta', 'omega', 'total-torque', 'theta-sim', 'omega-sim', 'sine-torque' ]) return {'all-mmts': exp_results}
def _one_run(self, theta_0, omega_0, t0, i, b_prime, k_prime, b, k, g_0_mag, w_d, phase, t_fin, dt, num_terms, create_plot=False): """Compare experiment to theory for one set of parameters and return the difference between the two. Uses only Fourier torque expression.""" y0 = np.array([theta_0, omega_0]).squeeze() times = np.arange(t0, t_fin, dt / self.divider) try: w_d = t.w_res_gamma(b - b_prime, k - k_prime, i)[0] except ValueError: return [] # Standard sine calculation. # Calculate fourier series first num_terms terms. ind_mag_phi = fourier.get_fourier_series(num_terms, w_d, dt, g_0_mag) f_torque = fourier.get_torque(times, ind_mag_phi[:, 0], ind_mag_phi[:, 1], ind_mag_phi[:, 2], w_d) # Calculate theoretical results. torque_sine = h.baker( t.calculate_sine_pi, ["", "", "", "", g_0_mag, w_d, phase - dt * w_d / 2], pos_to_pass_through=(0, 3)) theory = t.calc_theory_soln(times, t0, y0, b - b_prime, k - k_prime, i, torque_sine) # fourier theoretical results. torque_fourier = h.baker(t.calculate_sine_pi, [ "", "", "", "", ind_mag_phi[:, 1], w_d * ind_mag_phi[:, 0], ind_mag_phi[:, 2] ], pos_to_pass_through=(0, 3)) theory_fourier = t.calc_theory_soln(times, t0, y0, b - b_prime, k - k_prime, i, torque_fourier) # Normalise error by amplitude max_theta_diff = np.max(np.abs(theory_fourier[:, 1] - theory[:, 1])) max_omega_diff = np.max(np.abs(theory_fourier[:, 2] - theory[:, 2])) norm_theta_diff = (theory_fourier[:, 1] - theory[:, 1]) / np.max( theory_fourier[:, 1]) norm_omega_diff = (theory_fourier[:, 2] - theory[:, 2]) / np.max( theory_fourier[:, 2]) max_theta_norm = np.max(np.abs(norm_theta_diff)) max_omega_norm = np.max(np.abs(norm_omega_diff)) # Plotting - for 4 subplots on 1 figure. if create_plot: self._update_filename() plotting_data = \ [ [ [[theory_fourier[:, 0], theory_fourier[:, 1], r'Digitised'], [theory[:, 0], theory[:, 1], r'Analogue']], [[theory_fourier[:, 0], norm_theta_diff]], ], [ [[theory_fourier[:, 0], theory_fourier[:, 2], r'Digitised'], [theory[:, 0], theory[:, 2], r'Analogue']], [[theory_fourier[:, 0], norm_omega_diff]], ] ] params = { 'theta_0': theta_0, 'omega_0': omega_0, 't0': t0, 'I': i, 'b\'': b_prime, 'k\'': k_prime, 'b': b, 'k': k, 'g_0_mag': g_0_mag, 'w_d': w_d, 'phi': phase, 't_fin': t_fin, 'dt': dt } p.two_by_n_plotter( plotting_data, self.filename, params, savepath=self.plotpath, show=True, x_axes_labels=['t/s', 't/s'], tag='with-uniform-noise-torque-over-100', y_top_labels=[r'$\theta$/rad', r'$\dot{\theta}$/rad/s'], y_bottom_labels=[ r'$(\theta_{sim}-\theta_{an})/|\theta_{max}|$', r'$(\dot{\theta}_{sim}-\dot{\theta}_{an})/' r'|\dot{\theta}_{max}|$' ], legend={ 'loc': 'upper center', 'bbox_to_anchor': (0.5, 1), 'ncol': 2 }) print(b, k, i, num_terms, max_theta_norm, max_omega_norm) return [ b, k, i, num_terms, max_theta_diff, max_omega_diff, max_theta_norm, max_omega_norm ]
def main_operation(self, plot=False): # feed in fixed b - b', k - k', torque, i, noise level and type, # but a range of driving frequencies to test. b_s = self.prms['b_s'] k_s = self.prms['k_s'] i_s = self.prms['i_s'] b_primes = self.prms['b\'s'] k_primes = self.prms['k\'s'] w_ds = np.arange(self.prms['w_d_start'], self.prms['w_d_final'], self.prms['w_d_step']) g_0_mags = self.prms['g0s'] phases = self.prms['phis'] t0 = self.prms['t0'].squeeze() y0 = np.array([self.prms['theta_0'], self.prms['omega_0']]).squeeze() times = np.arange(t0, self.prms['tfin'], self.prms['dt']) # Get the theoretical position of the peak and generate a finely # spaced set of frequency points around it to measure with greater # resolution here. w2_res = (k_s - k_primes) / i_s - (b_s - b_primes)**2 / (2 * i_s**2) gamma = (b_s - b_primes) / i_s # 5 * bandwidth on either side gamma = np.absolute(gamma) width = 5 * gamma if gamma != 0 else self.prms['w_d_step'] if w2_res >= 0: w_res = np.sqrt(w2_res) else: w_res = 0 w_range = np.linspace(w_res - width if w_res - width > 2 else 2, w_res + width, 100) single_run = h.baker( self._single_operation, [times, '', '', '', '', '', '', '', '', '', t0, y0], pos_to_pass_through=(1, 9)) ws = [w_ds, w_range] for j in range(len(ws)): all_times = h.all_combs(single_run, b_s, k_s, i_s, b_primes, k_primes, ws[j], g_0_mags, phases, 1) # final argument 1/2 for 1 frequency peak expected. fft_data = [] real_data = [] for i in range(len(all_times)): real_data.append(all_times[i][-1][1]) if all_times[i][-1][0]: fft_data.append(all_times[i][-1][-1]) if j == 0: fft_mmts = np.array(fft_data) else: fft_mmts = np.append(fft_mmts, np.array(fft_data), axis=0) fft_mmts = fft_mmts[fft_mmts[:, 0, 0].argsort()] ang_freqs = np.array([fft_mmts[:, 0, 0], fft_mmts[:, 0, 1] ]).T * 2 * np.pi amps = np.array([fft_mmts[:, 1, 0], fft_mmts[:, 1, 1]]).T / g_0_mags phases = np.array([fft_mmts[:, 2, 0], fft_mmts[:, 2, 1]]).T # For a single set of data, get the transfer function once only. This # allows error to be calculated as specifically the experimental # ang_freqs are used. fft_theory = np.array( h.all_combs(t.theory_response, b_s, k_s, i_s, b_primes, k_primes, ang_freqs[:, 0])) theory_amps = np.absolute(fft_theory[:, -1]) theory_phases = np.angle(fft_theory[:, -1]) amp_err, phase_err = m.calc_norm_errs([amps[:, 0], theory_amps], [phases[:, 0], theory_phases])[1] theory_n_mmt = \ [[[[ang_freqs, theory_amps], [ang_freqs, amps]], [[ang_freqs, amp_err / np.max(amps[:, 0])]]], [[[ang_freqs, theory_phases], [ang_freqs, phases]], [[ang_freqs, phase_err / np.max(np.absolute(phases[:, 0]))]]]] p.two_by_n_plotter(theory_n_mmt, self.filename, self.prms, savepath=self.plotpath, show=False, x_axes_labels=['$\omega$/rad/s', '$\omega$/rad/s'], y_top_labels=[ r'$\left|R(\omega)\right|$/rad/(Nm)', r'arg[$R(\omega)$]/rad' ], y_bottom_labels=[ r'Normalised error in $\left|R(\omega)\right|$', r'Normalised error in arg[$R(\omega)$]' ])