def get_tunes(recorded_particles, filename_output=None): print("NAFFlib spectral analysis...") qx_i = np.empty_like(recorded_particles.x_i[:, 0]) qy_i = np.empty_like(recorded_particles.x_i[:, 0]) for ii in range(len(qx_i)): qx_i[ii] = NAFFlib.get_tune(recorded_particles.x_i[ii] + 1j * recorded_particles.xp_i[ii]) qy_i[ii] = NAFFlib.get_tune(recorded_particles.y_i[ii] + 1j * recorded_particles.yp_i[ii]) print("NAFFlib spectral analysis done.") # Save dict_beam_status = { "x_init": np.squeeze(recorded_particles.x_i[:, 0]), "xp_init": np.squeeze(recorded_particles.xp_i[:, 0]), "y_init": np.squeeze(recorded_particles.y_i[:, 0]), "yp_init": np.squeeze(recorded_particles.yp_i[:, 0]), "z_init": np.squeeze(recorded_particles.z_i[:, 0]), "dp_init": np.squeeze(recorded_particles.dp_i[:, 0]), "qx_i": qx_i, "qy_i": qy_i, "x_centroid": np.mean(recorded_particles.x_i, axis=1), "y_centroid": np.mean(recorded_particles.y_i, axis=1), } if filename_output is not None: import h5py with h5py.File(filename_output, "w") as fid: for kk in list(dict_beam_status.keys()): fid[kk] = dict_beam_status[kk]
def finalize_simulation(self): if pp.footprint_mode: # Tunes import NAFFlib print 'NAFFlib spectral analysis...' qx_i = np.empty_like(self.recorded_particles.x_i[:, 0]) qy_i = np.empty_like(self.recorded_particles.x_i[:, 0]) for ii in range(len(qx_i)): qx_i[ii] = NAFFlib.get_tune(self.recorded_particles.x_i[ii] + 1j * self.recorded_particles.xp_i[ii]) qy_i[ii] = NAFFlib.get_tune(self.recorded_particles.y_i[ii] + 1j * self.recorded_particles.yp_i[ii]) print 'NAFFlib spectral analysis done.' # Save import h5py dict_beam_status = {\ 'x_init': np.squeeze(self.recorded_particles.x_i[:,0]), 'xp_init': np.squeeze(self.recorded_particles.xp_i[:,0]), 'y_init': np.squeeze(self.recorded_particles.y_i[:,0]), 'yp_init': np.squeeze(self.recorded_particles.yp_i[:,0]), 'z_init': np.squeeze(self.recorded_particles.z_i[:,0]), 'qx_i': qx_i, 'qy_i': qy_i, 'x_centroid': np.mean(self.recorded_particles.x_i, axis=1), 'y_centroid': np.mean(self.recorded_particles.y_i, axis=1)} with h5py.File('footprint.h5', 'w') as fid: for kk in dict_beam_status.keys(): fid[kk] = dict_beam_status[kk] else: #save data for multijob operation and launch new job import h5py with h5py.File( 'bunch_status_part%02d.h5' % (self.SimSt.present_simulation_part), 'w') as fid: fid['bunch'] = self.piece_to_buffer(self.bunch) if not self.SimSt.first_run: os.system('rm bunch_status_part%02d.h5' % (self.SimSt.present_simulation_part - 1)) self.SimSt.after_simulation()
def calculate_tunes(self, particles, n_turns, shift=0): n_particles = self.n_particles phys_coords = np.empty([n_turns, n_particles, 6]) phys_coords[:, :, 0] = np.roll(particles.x.reshape(n_turns, n_particles), shift, axis=0) - self.partCO.x phys_coords[:, :, 1] = np.roll(particles.px.reshape(n_turns, n_particles), shift, axis=0) - self.partCO.px phys_coords[:, :, 2] = np.roll(particles.y.reshape(n_turns, n_particles), shift, axis=0) - self.partCO.y phys_coords[:, :, 3] = np.roll(particles.py.reshape(n_turns, n_particles), shift, axis=0) - self.partCO.py phys_coords[:, :, 4] = np.roll(particles.zeta.reshape(n_turns, n_particles), shift, axis=0) - self.partCO.zeta phys_coords[:, :, 5] = np.roll(particles.delta.reshape(n_turns, n_particles), shift, axis=0) - self.partCO.delta norm_coords = np.tensordot(self.optics['invW'], phys_coords, [1, 2]).transpose(1, 2, 0) q1 = NAFFlib.multiparticle_tunes(norm_coords[:, :, 0].T - 1.j * norm_coords[:, :, 1].T).real q2 = NAFFlib.multiparticle_tunes(norm_coords[:, :, 2].T - 1.j * norm_coords[:, :, 3].T).real qx = NAFFlib.multiparticle_tunes(phys_coords[:, :, 0].T).real qy = NAFFlib.multiparticle_tunes(phys_coords[:, :, 2].T).real return q1, q2, qx, qy
def tune_analysis(x_i, xp_i, y_i, yp_i): n_turns = x_i.shape[1] macroparticlenumber = x_i.shape[0] qx_i = np.empty(macroparticlenumber) qy_i = np.empty(macroparticlenumber) print('analysing particle spectra ... this may take some time.') for p_idx in range(macroparticlenumber): qx_i[p_idx] = NAFFlib.get_tune(x_i[p_idx, :]) qy_i[p_idx] = NAFFlib.get_tune(y_i[p_idx, :]) sys.stdout.write('\rparticle %d' % p_idx) x_centroid = np.mean(x_i, axis=0) y_centroid = np.mean(y_i, axis=0) #print x_centroid.shape qx_centroid = NAFFlib.get_tune(x_centroid) qy_centroid = NAFFlib.get_tune(y_centroid) return qx_i, qy_i, qx_centroid, qy_centroid
import numpy as np np.random.seed(123456) N = 100000 noise_rms = 0.8 i = np.linspace(1, N, N) q_true = 0.24783351 q_exp = 0.44323 x = np.empty_like(i, dtype=np.complex128) print( ' True frequency is Q_true = {0:.10f}'.format(q_exp)) print( ' True frequency is Q_true = {0:.10f}'.format(q_true)) print(' True frequency is Q_true = {0:.10f}'.format( q_true * 1.1)) print(' True frequency is Q_true = {0:.10f}'.format( q_true * 1.1**2)) x = 1*np.cos(2*np.pi*q_true*i) \ + 0.5*np.cos(2*np.pi*1.1*q_true*i) \ + 0.3j*np.cos(2*np.pi*1.1**2*q_true*i) \ + np.exp(1j*2*np.pi*q_exp*i) # + 0j + np.random.normal(0,noise_rms,N) \ # + 1j*np.random.normal(0,noise_rms,N) #q = NAFF.get_tunes(x,5,3) q, A = NAFF.get_tunes_all(x, 10) print('Tune,\t\tAmplitude') for i in range(len(q)): print('{0:.6f}, {1:+.6f}{2:+.6f}i'.format(q[i], A[i].real, A[i].imag))
mask_lost = (K(max(t0_all[:, 0]) - 1, t0_all[:, 0]) == 0) mask_kept = (K(max(t0_all[:, 0]) - 1, t0_all[:, 0]) == 1) # q1_lost = NAFFlib.multiparticle_tunes(x_norm[mask_lost] + # 0.j*px_norm[mask_lost], 0) # q2_lost = NAFFlib.multiparticle_tunes(y_norm[mask_lost] + # 0.j*py_norm[mask_lost], 0) # # q1_kept = NAFFlib.multiparticle_tunes(x_norm[mask_kept] + # 0.j*px_norm[mask_kept], 0) # q2_kept = NAFFlib.multiparticle_tunes(y_norm[mask_kept] + # 0.j*py_norm[mask_kept], 0) q1_lost = np.array([ NAFFlib.multiparticle_tunes(x_norm[mask_lost][:, :200], 0), NAFFlib.multiparticle_tunes(x_norm[mask_lost][:, 200:400], 0), NAFFlib.multiparticle_tunes(x_norm[mask_lost][:, 400:600], 0), NAFFlib.multiparticle_tunes(x_norm[mask_lost][:, 600:800], 0), NAFFlib.multiparticle_tunes(x_norm[mask_lost][:, 800:1000], 0) ]) q2_lost = np.array([ NAFFlib.multiparticle_tunes(y_norm[mask_lost][:, :200], 0), NAFFlib.multiparticle_tunes(y_norm[mask_lost][:, 200:400], 0), NAFFlib.multiparticle_tunes(y_norm[mask_lost][:, 400:600], 0), NAFFlib.multiparticle_tunes(y_norm[mask_lost][:, 600:800], 0), NAFFlib.multiparticle_tunes(y_norm[mask_lost][:, 800:1000], 0) ]) q1_kept = np.array([
u_data[particle].append( tbt_data['{}'.format(plane_of_interest)][turn][particle]) pu_data[particle].append( tbt_data['p{}'.format(plane_of_interest)][turn][particle]) # Remove any lost particles as NAFF will crash lost_particles = [] Q_list = [] for particle in range(n_particles): if np.isnan(u_data[particle]).any() or np.isnan(pu_data[particle]).any(): lost_particles.append(particle) print('particle {} lost'.format(particle)) else: signal = u_data[particle] Q_list.append(pnf.get_tune(np.array(signal))) # Load data, type: pandas data = pd.Series(Q_list) # Plot for comparison plt.figure(figsize=(12, 8)) ax = data.plot(kind='hist', bins=50, color='C1', normed=True, alpha=0.5) # save plot limits #dataYLim = ax.get_ylim() # Find best fit distribution best_distribution, best_fit_name, best_fit_params = best_fit_distribution( data, 200, ax) best_dist = getattr(st, best_fit_name) print('The best fit is a {} distribution'.format(best_fit_name))
import NAFFlib as NAFF import numpy as np np.random.seed(123456) N = 100 noise_rms = 0.1 i = np.linspace(1, N, N) q_true = 6.24783351 x = np.empty_like(i, dtype=np.complex128) print( ' True frequency is Q_true = {0:.10f}'.format(q_true)) x = 1 * np.cos(2 * np.pi * q_true * i) + 0j * np.sin(2 * np.pi * q_true * i) q = NAFF.get_tune(x) print('(real ) Estimated frequency is Q_hat = {0:.10f}'.format(q)) x = 1 * np.cos(2 * np.pi * q_true * i) + 1j * np.sin(2 * np.pi * q_true * i) q = NAFF.get_tune(x) print('(complex ) Estimated frequency is Q_hat = {0:.10f}'.format(q)) x = 1 * np.cos(2 * np.pi * q_true * i) + np.random.normal( 0, noise_rms, N) + 0j * np.sin(2 * np.pi * q_true * i) q = NAFF.get_tune(x) print('(real + noise) Estimated frequency is Q_hat = {0:.10f}'.format(q)) x = 1 * np.cos(2 * np.pi * q_true * i) + np.random.normal( 0, noise_rms, N) + 1j * np.sin( 2 * np.pi * q_true * i) + 1j * np.random.normal(0, noise_rms, N) q = NAFF.get_tune(x)
import numpy as np np.random.seed(123456) N = 100 noise_rms = 0.1 i = np.linspace(1, N, N) q_true = 6.24783351 x = np.empty_like(i, dtype=np.complex128) print( ' True frequency is Q_true = {0:.10f}'.format(q_true)) x = 1 * np.cos(2 * np.pi * q_true * i) + 0j * np.sin(2 * np.pi * q_true * i) print('(real ) Estimated frequency is Q_hat = {0:.10f}'.format( NAFF.get_tune(x))) x = 1 * np.cos(2 * np.pi * q_true * i) + 1j * np.sin(2 * np.pi * q_true * i) print('(complex ) Estimated frequency is Q_hat = {0:.10f}'.format( NAFF.get_tune(x))) x = 1 * np.cos(2 * np.pi * q_true * i) + np.random.normal( 0, noise_rms, N) + 0j * np.sin(2 * np.pi * q_true * i) print('(real + noise) Estimated frequency is Q_hat = {0:.10f}'.format( NAFF.get_tune(x))) x = 1 * np.cos(2 * np.pi * q_true * i) + np.random.normal( 0, noise_rms, N) + 1j * np.sin( 2 * np.pi * q_true * i) + 1j * np.random.normal(0, noise_rms, N) print('(complex + noise) Estimated frequency is Q_hat = {0:.10f}'.format( NAFF.get_tune(x)))
fig1 = plt.figure(1, figsize=(20, 10)) ax1 = plt.subplot(1, 2, 1) for order in [1, 2, 3]: TD = tune_diagram.ResonanceLines(0, 0, order, 1) TD.plot_resonance(fig1) ax1.plot([0.29], [0.31], 'r.', markersize=20) ax2 = plt.subplot(1, 2, 2) Q_arr = np.empty_like(xy) k = 0 for q1 in Q_arr: for q2 in q1: B = tfs.open(trackdir + 'track.obs0001.p{0:04d}'.format(k + 1)) Bx = B['x'][np.isfinite(B['x'])] By = B['y'][np.isfinite(B['y'])] qx1 = NAFFlib.get_tune(Bx) qy1 = NAFFlib.get_tune(By) #print('{0:d} {1:.3f} {2:.3f} '.format(k, qx1, qy1)) q2[0] = qx1 q2[1] = qy1 k += 1 ax1.plot([0.29], [0.31], 'r.', markersize=20) ax2.plot(xy[:, :, 0], xy[:, :, 1], 'b.') footprint.draw_footprint(Q_arr, fig1, 0) footprint.draw_footprint(xy, fig1, 1) ax1.set_xlabel('$Q_x$') ax1.set_ylabel('$Q_y$') #ax1.set_xlim(0.25,0.30) #ax1.set_ylim(0.28,0.315) ax2.set_xlabel('$x$')
except TypeError: p_fit = np.polyfit(x_fit[:fit_cut], np.log(activity_mode)[:fit_cut], deg=1) y_fit = np.polyval(p_fit, x_fit) p_list.append(p_fit) tau = 1. / p_fit[0] ax13.plot(np.log(activity_mode), **kwargs) ax13.plot(y_fit, **kwargs) for ax in [axcentroid, ax1mode]: ax.set_ylim( np.array([-1, 1]) * np.max(np.abs(np.array(ax.get_ylim())))) tune_centroid = nl.get_tune(ob.mean_x[mask_zero]) if not flag_no_slice: tune_1mode_re = nl.get_tune(np.real(ffts[i_mode, :])) tune_1mode_im = nl.get_tune(np.imag(ffts[i_mode, :])) tune_1mode_re_list.append(tune_1mode_re) tune_1mode_im_list.append(tune_1mode_im) if flag_compact and not flag_no_slice: ax1mode.text(0.02, 0.02, (f'Tau: {tau:.0f} turns\n' f'Tunes: {tune_1mode_re:.4f}/{tune_1mode_im:.4f}'), transform=ax1mode.transAxes, ha='left', va='bottom', fontsize=11)
noise_rms = 0.8 i = np.linspace(1, N, N) q_true = 0.24783351 q_exp = 0.44323 x = np.empty_like(i, dtype=np.complex128) print( ' True frequency is Q_true = {0:.10f}'.format(q_exp)) print( ' True frequency is Q_true = {0:.10f}'.format(q_true)) print(' True frequency is Q_true = {0:.10f}'.format( q_true * 1.1)) print(' True frequency is Q_true = {0:.10f}'.format( q_true * 1.1**2)) x = 1*np.cos(2*np.pi*q_true*i) \ + 0.5*np.cos(2*np.pi*1.1*q_true*i) \ + 0.3j*np.cos(2*np.pi*1.1**2*q_true*i) \ + np.exp(1j*2*np.pi*q_exp*i) # + 0j + np.random.normal(0,noise_rms,N) \ # + 1j*np.random.normal(0,noise_rms,N) #q = NAFF.get_tunes(x,5,3) q, Ap, An = NAFF.get_tunes(x, 5) print( 'Tune, (Positive frequency\'s amplitude), (Negative frequency\'s amplitude)' ) for i in range(len(q)): print('{0:.6f}, {1:+.6f}{2:+.6f}i, {3:+.6f}{4:+.6f}i'.format( q[i], Ap[i].real, Ap[i].imag, An[i].real, An[i].imag))
n_wind = 50 N_lines = 10 freq_list = [] ampl_list = [] x_vect = ob.mean_x[mask_zero] N_samples = len(x_vect) for ii in range(N_samples): if ii < n_wind / 2: continue if ii > N_samples - n_wind / 2: continue freq, a1, a2 = nl.get_tunes( x_vect[ii - n_wind / 2:ii + n_wind / 2], N_lines) freq_list.append(freq) ampl_list.append(np.abs(a1)) fignaff = plt.figure(301) axnaff = fignaff.add_subplot(111) mpbl = axnaff.scatter(x=np.array(N_lines * [np.arange(len(freq_list))]).T, y=np.array(freq_list), c=(np.array(ampl_list)), vmax=1 * np.max(ampl_list), s=1) plt.colorbar(mpbl) # Details
print('--> Done.') print("Simulation time in seconds: " + str(time.clock() - t0)) # 7) Compute tunes Qx_list, Qy_list = [], [] # Add tunes for intensity 0, there should be no tune shift Qx_list.insert(0, 0.13) Qy_list.insert(0, 0.18) for intensity in intensity_list[1:]: # Load the file with all of the saved data from the run meanX, meanY = np.loadtxt(filename+f'_intensity{intensity/1e10:.2f}e10_ayy{app_y}_QpyQpx{Qp_x}.txt', delimiter = ",", unpack = True) Qx_list.append(pnf.get_tune(meanX)) Qy_list.append(pnf.get_tune(meanY)) Qy_coherent = {} Qx_coherent = {} for i, intensity in enumerate(intensity_list): Qy_coherent[f'intensity {intensity}'] = Qy_list[i] Qx_coherent[f'intensity {intensity}'] = Qx_list[i] save2pickle = True if save2pickle: with open(f'Qy_coherent_vs_Intensity_6D_ayy{app_y}_wakesON_QpyQpx{Qp_x}_{wakeContribution}.pkl', 'wb') as ff: pickle.dump(Qy_coherent, ff, pickle.HIGHEST_PROTOCOL) ff.close() if save2pickle:
# Dictionaries for Qx, Qy. Each entry corresponds to one window Qx_dict = {} Qy_dict = {} print('--> Start computing tunes') for particle in range(pp.macroparticlenumber): Qx_dict[particle] = [] Qy_dict[particle] = [] counter = 0 while True: window_signal_x = x_data[particle][counter:window_size + counter] window_signal_y = y_data[particle][counter:window_size + counter] counter = counter + step # Compute tune Qx_dict[particle].append(pnf.get_tune(np.array(window_signal_x), 0)) Qy_dict[particle].append(pnf.get_tune(np.array(window_signal_y), 0)) if x_data[particle][-1] in window_signal_x: break plt.plot(np.arange(0, n_turns, n_turns / len(Qx_dict[0])), Qy_dict[0], '.', label='Qy') plt.plot(np.arange(0, n_turns, n_turns / len(Qx_dict[0])), Qx_dict[0], '.', label='Qx') plt.ylabel('Qx,y') plt.xlabel('turns') plt.grid()
Dy_wrt_CO_m=0., Dpy_wrt_CO_rad=DpxDpy_wrt_CO[:, :, 1].flatten(), Dsigma_wrt_CO_m=0., Ddelta_wrt_CO=0., n_turns=n_turns, device_opencl=device_opencl) info = track_with if device_opencl is None: info += ' (CPU)' else: info += ' (GPU %s)'%device_opencl else: raise ValueError('What?!') n_part = x_tbt.shape[1] Qx = np.zeros(n_part) Qy = np.zeros(n_part) for i_part in range(n_part): Qx[i_part] = NAFFlib.get_tune(x_tbt[:, i_part]) Qy[i_part] = NAFFlib.get_tune(y_tbt[:, i_part]) Qxy_fp = np.zeros_like(xy_norm) Qxy_fp[:, :, 0] = np.reshape(Qx, Qxy_fp[:, :, 0].shape) Qxy_fp[:, :, 1] = np.reshape(Qy, Qxy_fp[:, :, 1].shape) plt.close('all') fig3 = plt.figure(3) axcoord = fig3.add_subplot(1, 1, 1) footprint.draw_footprint(xy_norm, axis_object=axcoord, linewidth = 1) axcoord.set_xlim(right=np.max(xy_norm[:, :, 0])) axcoord.set_ylim(top=np.max(xy_norm[:, :, 1]))
# py_tbt -= Dpy*delta_tbt Dx = np.mean(x_tbt * delta_tbt) / np.mean(delta_tbt * delta_tbt) Dpx = np.mean(px_tbt * delta_tbt) / np.mean(delta_tbt * delta_tbt) x_tbt -= Dx * delta_tbt px_tbt -= Dpx * delta_tbt # Dy = np.mean(y_tbt*delta_tbt)/np.mean(delta_tbt*delta_tbt) # Dpy = np.mean(py_tbt*delta_tbt)/np.mean(delta_tbt*delta_tbt) # y_tbt -= Dy*delta_tbt # py_tbt -= Dpy*delta_tbt n_part = x_tbt.shape[1] Qx = np.zeros(n_part) Qy = np.zeros(n_part) for i_part in range(n_part): Qx[i_part] = NAFFlib.get_tune(x_tbt[:, i_part]) Qy[i_part] = NAFFlib.get_tune(y_tbt[:, i_part]) Qxy_fp = np.zeros_like(xy_norm) Qxy_fp[:, :, 0] = np.reshape(Qx, Qxy_fp[:, :, 0].shape) Qxy_fp[:, :, 1] = np.reshape(Qy, Qxy_fp[:, :, 1].shape) plt.close('all') fig3 = plt.figure(3, figsize=(5, 5)) axcoord = fig3.add_subplot(1, 1, 1) footprint.draw_footprint(xy_norm, axis_object=axcoord, linewidth=1) axcoord.set_xlim(right=np.max(xy_norm[:, :, 0])) axcoord.set_ylim(top=np.max(xy_norm[:, :, 1])) axcoord.set_xlabel('px (sigma)')
momCorr = (phaseGain)*posCorr/beta_x[0] delayPhase[0:-1] = delayPhase[1:] delayPhase[numDelay] = momCorr #bunch.xp += delayPhase[0]*np.cos(2*np.pi*400e6/(bunch.beta*c)*bunch.z) ''' if i%decTurns is 0: j = int(i/decTurns) X[f'turn {j}'] = bunch.x Y[f'turn {j}'] = bunch.y meanX[j] = np.mean(bunch.x) meanY[j] = np.mean(bunch.y) # Compute coherent tune Qx_coherent = pnf.get_tune(meanX) Qy_coherent = pnf.get_tune(meanY) coherent_dict = {'Qy':Qy_coherent, 'Qx':Qx_coherent} with open('coherent_tunes.pkl', 'wb') as ff: pickle.dump(coherent_dict, ff, pickle.HIGHEST_PROTOCOL) ff.close() print(f'Qx coherent {Qx_coherent}, Qy coherent {Qy_coherent}') # Compute incoherent tune from tracking Qx_incoherent_tracking = [] Qy_incoherent_tracking = [] for particle in range(macroparticlenumber): x_signal = []
for particle in range(pp.macroparticlenumber): x_data[particle] = [] y_data[particle] = [] # maybe even 100 turns are enough for particle in range(pp.macroparticlenumber): for turn in range(n_turns): x_data[particle].append(X[turn][particle]) y_data[particle].append(Y[turn][particle]) Qx_list = [] Qy_list = [] for particle in range(pp.macroparticlenumber): signal_x = x_data[particle] Qx_list.append(pnf.get_tune(np.array(signal_x), 0)) signal_y = y_data[particle] Qy_list.append(pnf.get_tune(np.array(signal_y), 0)) print(np.std(Qy_list)) print(np.std(Qx_list)) print('--> Computing tunes Done.') dataExport = [Qx_list, Qy_list] save_tunes = False if save_tunes: f = open('mytunes_noTuneSpread_noAN_1e4.txt', 'w') with f: out = csv.writer(f, delimiter=',')
N_bunches = np.sum(mask_bunch) x_mat_m = x_mat[:, mask_bunch] y_mat_m = y_mat[:, mask_bunch] n_mat_m = n_mat[:, mask_bunch] plt.close('all') fig1 = plt.figure(1) axx = plt.subplot(3, 1, 1) axx.plot(x_mat_m) axy = plt.subplot(3, 1, 2, sharex=axx) axy.plot(y_mat_m) axn = plt.subplot(3, 1, 3, sharex=axx) axn.plot(n_mat_m) i_start = 0 i_stop = 50 plt.figure(2) axt = plt.subplot(1, 1, 1) for n_win in range(7): tune_list = [] for ii in range(N_bunches): i_0 = i_start + n_win * i_stop i_1 = (n_win + 1) * i_stop tune_list.append(NAFF.get_tune(x_mat_m[i_0:i_1, ii])) axt.plot(tune_list, label='%d-%d' % (i_0, i_1)) axt.legend() plt.show()