rcj(ax) tl(fig) fig.savefig('Figures/figure6d_bifurcation_sugar_glider.pdf', transparent=True) # %% Plot the equilibrium glide velocity on a polar plot fig, ax = plt.subplots() for ii, fp_kind in enumerate(possible_class): idx = np.where(nt_class_1 == fp_kind)[0] if len(idx) > 0: geq = nt_equil_1[idx, 1] veq = eqns.v_equil(geq, cl_fun_1, cd_fun_1) vxeq = veq * np.cos(geq) vzeq = -veq * np.sin(geq) ax.plot(vxeq, vzeq, 'o', c=bfbmap[ii], ms=2, label=fp_kind, mec=bfbmap[ii]) ax.set_xlim(0, 1.5) ax.set_ylim(-1.5, 0) ax.set_xlabel(r"$\hat{v}_x$", fontsize=18) ax.set_ylabel(r"$\hat{v}_z$", fontsize=18)
rcj(ax) tl(fig) fig.savefig('Figures/figure6c_bifurcation_chukar.pdf', transparent=True) # %% Plot the equilibrium glide velocity on a polar plot fig, ax = plt.subplots() for ii, fp_kind in enumerate(possible_class): idx = np.where(ch_class_spl == fp_kind)[0] if len(idx) > 0: geq = ch_equil_spl[idx, 1] veq = eqns.v_equil(geq, cl_fun, cd_fun) vxeq = veq * np.cos(geq) vzeq = -veq * np.sin(geq) ax.plot(vxeq, vzeq, 'o', c=bfbmap[ii], ms=2, label=fp_kind, mec=bfbmap[ii]) ax.axis('equal', adjustable='box') ax.set_xlim(0, 1.5) ax.set_ylim(-1.5, 0) ax.set_xlabel(r"$\hat{v}_x$", fontsize=18)
transparent=True) # %% Velocity bifurcation plot (could be for the paper...) fig, ax = plt.subplots() for ii, fp_kind in enumerate(possible_class): idx = np.where(sq_class_spl == fp_kind)[0] if len(idx) > 0: peq = sq_equil_spl[idx, 0] geq = sq_equil_spl[idx, 1] aleq = peq + geq veq = eqns.v_equil(aleq, cl_fun, cd_fun) vxeq = veq * np.cos(geq) vzeq = -veq * np.sin(geq) ax.plot(rd(peq), veq, 'o', c=bfbmap[ii], ms=2, mec=bfbmap[ii]) # nos = np.ones(len(veq)) # sk = 5 # ax.plot(rd(peq), veq, rd(geq), 'o', # c=bfbmap[ii], ms=2, label=fp_kind, mec=bfbmap[ii]) # ax.plot(rd(peq)[::sk], .755 * nos[::sk], rd(geq)[::sk], 'o', # c=bfbmap[ii], ms=2, mec=bfbmap[ii], alpha=.35) # ax.plot(rd(peq)[::sk], veq[::sk], 18 * nos[::sk], 'o', # c=bfbmap[ii], ms=2, mec=bfbmap[ii], alpha=.35) #ax.set_ylim(ymax=.755) ax.set_xlabel(r'$\theta$', fontsize=18)
lab = 'airfoil squirrel, ' + r'$\theta=$2' + u'\u00B0' ax.text(.05, -1, lab, fontsize=16) fig.savefig('Figures/figure5aii_vpd2_airfoil_squirrel.pdf', transparent=True) # %% Additional plots fig, ax = plt.subplots() for ii, fp_kind in enumerate(possible_class): idx = np.where(so_class_25 == fp_kind)[0] if len(idx) > 0: geq = so_equil_25[idx, 1] veq = eqns.v_equil(geq, cl_fun_25, cd_fun_25) vxeq = veq * np.cos(geq) vzeq = -veq * np.sin(geq) ax.plot(vxeq, vzeq, 'o', c=bfbmap[ii], ms=2, label=fp_kind, mec=bfbmap[ii]) ax.set_xlim(0, 1.5) ax.set_ylim(-1.5, 0) ax.set_xlabel(r"$\hat{v}_x$", fontsize=18) ax.set_ylabel(r"$\hat{v}_z$", fontsize=18)
def phase_plotter(afdict, pitch, lims, arng, tvec, ngrid=201, nseed=41, nseed_skip=1, quiver=False, skip=10, seed=False, timer=False, gamtest=None, extrap=None, traj=None, seedloc=None, fig=None, ax=None, acc_contour=True, nullcline_x=False, nullcline_z=False): # unpack the data cli, cdi, = afdict['cli'], afdict['cdi'] clip, cdip = afdict['clip'], afdict['cdip'] # upack axis limits vxlim, vzlim = lims from eqns import cart_eqns, cart_model if traj is None: traj = ps_traj_dp5 VXorig, VZorig, VX, VZ, ALPHA = setup_grid(ngrid, vxlim, vzlim, pitch, arng) CL = cli(ALPHA.flatten()).reshape(ALPHA.shape) CD = cdi(ALPHA.flatten()).reshape(ALPHA.shape) dVX, dVZ = cart_eqns(VX, VZ, CL, CD) AMAG = np.hypot(dVX, dVZ) AX, AZ = dVX / AMAG, dVZ / AMAG # calculation the integral curves now = time.time() # vxs, vzs, VXs, VZs, als = setup_grid(nseed, vxlim, vzlim, pitch, arng) vxseed, vzseed = seed_locations(nseed, vxlim, vzlim, pitch, arng) vxseed, vzseed = vxseed[::nseed_skip], vzseed[::nseed_skip] if seedloc is not None: vxseed = np.r_[vxseed, seedloc[:, 0]] vzseed = np.r_[vzseed, seedloc[:, 1]] odeargs = (pitch, cli, cdi) solns = [] for i in range(len(vxseed)): x0 = (0, 0, vxseed[i], vzseed[i]) soln = traj(x0, tvec, odeargs, cart_model, arng, vxlim, vzlim) solns.append(soln) if timer: print('Elapsed time: {:.3f}'.format(time.time() - now)) # equilibrium points if gamtest is not None: from eqns import pitch_bifurcation as pb from eqns import v_equil, vxvz_equil, tau_delta, classify_fp equil = pb([pitch], gamtest, cli, cdi, angle_rng=arng) thbar, gambar = equil.T vbar = v_equil(pitch + gambar, cli, cdi) vxbar, vzbar = vxvz_equil(vbar, gambar) td, ev = tau_delta(equil, cli, cdi, clip, cdip, arng) _, _, fp_class = classify_fp(td) possible_class = [ 'saddle point', 'unstable focus', 'unstable node', 'stable focus', 'stable node' ] bfbmap = [bmap[0], bmap[4], bmap[2], bmap[3], bmap[1]] if fig is None or ax is None: fig, ax = plt.subplots(figsize=(5.5, 5.125)) if quiver: ax.quiver(VX[::skip, ::skip], VZ[::skip, ::skip], AX[::skip, ::skip], AZ[::skip, ::skip], color='gray', pivot='middle', alpha=.8, edgecolor='gray', linewidths=0 * np.ones(VX[::skip, ::skip].size)) for i in range(len(solns)): ax.plot(solns[i][:, 2], solns[i][:, 3], '-', ms=2.25, c='gray', alpha=.95, lw=.5) # color=bmap[1]) if seed: ax.plot(vxseed, vzseed, 'o', ms=2.5) if gamtest is not None: for ii, fp_kind in enumerate(possible_class): idx = np.where(fp_class == fp_kind)[0] if len(idx) == 0: continue # now globalize the phase space if fp_kind == 'saddle point': # or fp_kind == 'stable node' or fp_kind == 'unstable node': tvec = np.linspace(tvec[0], 3 * tvec[-1], 6 * len(tvec)) vxpt, vzpt = vxbar[idx], vzbar[idx] solns = [] for i in range(len(vxpt)): vxs, vzs = saddle_filler(vxpt[i], vzpt[i]) for vx0, vz0 in zip(vxs, vzs): ic = (0, 0, vx0, vz0) sn1 = traj(ic, tvec, odeargs, cart_model, arng, vxlim, vzlim) sn2 = traj(ic, -tvec, odeargs, cart_model, arng, vxlim, vzlim) solns.append(sn1) solns.append(sn2) # plot the globalized saddles for i in range(len(solns)): ax.plot(solns[i][:, 2], solns[i][:, 3], '-', c=bfbmap[ii]) # plot the equilibrium point ax.plot(vxbar[idx], vzbar[idx], 'o', ms=7, c=bfbmap[ii], zorder=200) if acc_contour: ax.contourf(VX, VZ, AMAG, [0, .1], colors=[bmap[3]], alpha=.2) # ax.contourf(VX, VZ, dVZ, [0, .1], colors=[bmap[4]], alpha=.2) if extrap is not None and extrap[0] is not None: ax.contourf(VX, VZ, np.array(ALPHA < extrap[0]).astype(np.int), [.5, 1.5], colors='gray', alpha=.1) if extrap is not None and extrap[1] is not None: ax.contourf(VX, VZ, np.array(ALPHA > extrap[1]).astype(np.int), [.5, 1.5], colors='gray', alpha=.1) # plot the nullclines if nullcline_x: ax.contour(VX, VZ, dVX, [0], colors=[bmap[3]], alpha=1, zorder=100) if nullcline_z: ax.contour(VX, VZ, dVZ, [0], colors=[bmap[3]], alpha=1, zorder=100) ax.set_xlim(vxlim[0], vxlim[1]) ax.set_ylim(vzlim[1], vzlim[0]) ax.xaxis.set_label_position('top') ax.xaxis.tick_top() ax.set_xlabel(r"$\hat{v}_x$", fontsize=20) ax.set_ylabel(r"$\hat{v}_z $", fontsize=20, rotation=0) ax.set_aspect('equal', adjustable='box') # these need to be square ax.set_xticks([0, .25, .5, .75, 1, 1.25]) ax.set_yticks([0, -.25, -.5, -.75, -1, -1.25]) ax.set_xticklabels(['0', '', '', '', '', '1.25']) ax.set_yticklabels(['0', '', '', '', '', '-1.25']) [ttl.set_size(18) for ttl in ax.get_xticklabels()] [ttl.set_size(18) for ttl in ax.get_yticklabels()] rcj(ax, ['bottom', 'right']) tl(fig) return fig, ax