def prepare_to_plot(grouped_mmts, theory_resp, savepath=None, show=True): """For this particular arrangement of measurements, reformat the grouped data to be plotted. :param grouped_mmts: The measurements of frequency, amplitude and phase returned by measure_all_groups. :param theory_resp: Theory's response function baked to require just the driving angular frequency. :param savepath: The path to save the graphs to. :param show: Whether to show each graph or not.""" mmts = [[[], []]] for parameter_set in grouped_mmts: to_plot = np.array(parameter_set[-1]) # Convert to angular frequencies. simulated_ang_freqs = to_plot[:, 0, 0, :] * 2 * np.pi # Sort by frequencies. sort_args = simulated_ang_freqs[:, 0].argsort() simulated_ang_freqs = simulated_ang_freqs[sort_args].squeeze() simulated_amps = to_plot[:, 0, 1, :][sort_args].squeeze() simulated_phase = to_plot[:, 0, 2, :][sort_args].squeeze() mmts[0][0].append([ simulated_ang_freqs, simulated_amps, r"k={:.1e}, k'={:.1e}, b={:.1e}, b'={:.1e}".format( parameter_set[2][0], parameter_set[3][0], parameter_set[0][0], parameter_set[1][0]) ]) mmts[0][1].append([ simulated_ang_freqs, simulated_phase, r"k={:.1e}, k'={:.1e}, b={:.1e}, b'={:.1e}".format( parameter_set[2][0], parameter_set[3][0], parameter_set[0][0], parameter_set[1][0]) ]) # Generate evenly spaced angular frequencies. even_spaced_wd = np.linspace(10, 140, 5000) mmts[0][0].append([ even_spaced_wd, np.absolute(theory_resp(even_spaced_wd)), r'Theoretical' ]) mmts[0][1].append([ even_spaced_wd, np.angle(theory_resp(even_spaced_wd)), r'Theoretical' ]) p.two_by_n_plotter(mmts, '', { 'i': grouped_mmts[0][4], 'g_0_mag': grouped_mmts[0][5], 'phi': grouped_mmts[0][6], 't0': grouped_mmts[0][7], 'tfin': grouped_mmts[0][8], 'theta_0': grouped_mmts[0][9], 'omega_0': grouped_mmts[0][10] }, savepath=savepath, show=show, x_axes_labels=['$\omega$/rad/s'], tag='response-curve-{}'.format(h.time_for_name()), y_top_labels=[r'$\left|R(\omega)\right|$/rad/(Nm)'], y_bottom_labels=[r'$\phi(R(\omega))$/rad'])
def read_plot(filepaths, savepath=None, show=True): """Read a CSV file's columns and immediately plot its displacement as a function of time. Point to multiple files to plot them on a single plot.""" if type(filepaths) is not str and type(filepaths) is not list: raise Exception('Invalid type.') if not h.check_iterable(filepaths): filepaths = [filepaths] to_plot = [[[], []]] for filepath in filepaths: data = pd.read_csv(filepath, index_col=0) to_plot[0][0].append([data['t'], data['theta']]) to_plot[0][1].append([data['t'], data['omega']]) p.two_by_n_plotter(to_plot, start='real-comparison', params_dict={}, savepath=savepath, show=show, x_axes_labels=['t/s'], y_bottom_labels=[r'$\dot{\theta}$/rad/s'], y_top_labels=[r'$\theta$/rad'])
def match_torques(grouped_sets, plot_real=False, savepath=None): """Match the times to the torques and displacements and return an array of those values for each of the parameter combinations. :param grouped_sets: All grouped data. :param plot_real: Plot the real space data and save with logs. :param savepath: Save path to send to the n_plotter function.""" fft_mmts = [] for group in grouped_sets: # a list of dictionaries with the same parameter values except for w_d. b = group[0]['b'] b_prime = group[0]['b\''] k = group[0]['k'] k_prime = group[0]['k\''] i = group[0]['i'] g_0_mag = group[0]['g_0_mag'] phi = group[0]['phi'] t0 = group[0]['t0'].squeeze() tfin = group[0]['tfin'] w_d = group[0]['w_d'] theta_0 = group[0]['theta_0'] omega_0 = group[0]['omega_0'] delay = group[0]['delay'] one_group = [ b, b_prime, k, k_prime, i, g_0_mag, phi, t0, tfin, theta_0, omega_0, delay ] one_dataset = [] for dataset in group: # one dictionary with real space data and torque values. disps = dataset['disps'] torques = dataset['torques'] analytic_torque = torques['total-torque'] - k_prime * torques[ 'theta-sim'] - b_prime * torques['omega-sim'] analytic, theta_sim, omega_sim = [], [], [] for t in disps['t']: # match up the real and torque data times. idx = (np.abs(torques['t'] - t)).argmin() analytic.append(analytic_torque[idx]) theta_sim.append(torques['theta-sim'][idx]) omega_sim.append(torques['omega-sim'][idx]) output = np.vstack( (disps.as_matrix().T, analytic, theta_sim, omega_sim)).T if plot_real: expected_torque = g_0_mag * np.sin(w_d * torques['t'] + phi) # Get the number of segments to use per correlation # calculation - equal to one period of the expected torque. period = 2 * np.pi / w_d match_one_period = np.abs(torques['t'] - period) index_of_period = match_one_period.argmin() if match_one_period[index_of_period] < 0: index_of_period += 1 real_space = \ [ [ [ [output[:, 0], output[:, 1]] ], [ [torques['t'], expected_torque], [torques['t'], analytic_torque] ] ] ] p.two_by_n_plotter(real_space, 't-torque', { 'b': b, 'b\'': b_prime, 'k': k, 'k\'': k_prime, 'i': i, 'g_0_mag': g_0_mag, 'phi': phi, 't0': t0, 'tfin': tfin, 'theta_0': theta_0, 'omega_0': omega_0, 'delay': delay }, show=True, savepath=savepath, x_axes_labels=['t/s'], tag='{}'.format(time()), y_top_labels=[r'$\theta$/rad'], y_bottom_labels=[r'$G_{s}(t)$/Nm']) # Times have now been matched and we are ready to obtain frequency, # amplitude and phase values from the output data. # Do once for simulated values and compare to measured values. measure_for = [[output[:, 1], output[:, 2]]] mmts = [] for measure in measure_for: # Normalise the theta by the analytic torque amplitude to get # the response function. mmts.append( m.one_mmt_set(output[:, 0], measure[0] / g_0_mag, output[:, 3], b, b_prime, k, k_prime, i)) one_dataset.append(mmts) one_group.append(one_dataset) fft_mmts.append(one_group) return fft_mmts
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)$]' ])