# Matplotlib Exercise 1¶

This is a demonstration of producing a particular complex graphic using matplotlib. The final image is shown below It shows the trajectory of a pair of drops in a confined shear flow.

The things that make up this image are as follows

1. The red drop is available as a png image.

2. The black curve showing the drop trajectory is from a data file

3. The blue curves are Bezier curves

In [1]:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
import matplotlib.patches as patches
from matplotlib.path import Path


The data is loaded and processed to get the trajectories. The data file contains the relative trajectory and is in units of radius of the undeformed drop. We divide it up among two drops to get their absolute trajectories and re-scale it to be in units of diameter.

In [2]:
data=np.loadtxt('./data.dat')

data[:,1:]*=(0.5*0.5)
data[:,1]-=0.4
p1= data[:,:3] #trajectory of drop1
p2=-data[:,:3] #trajectory of drop2
p2[:,0]=data[:,0]


In [3]:
fig,ax=plt.subplots(figsize=(5,3))

_=ax.plot( p1[:,1], p1[:,2],'k',zorder=10)
_=ax.plot( p2[:,1], p2[:,2],'k',zorder=10)
plt.show()


Remove the ticks and tick labels. Remove the left and right border lines. Set the axis limits and add axis labels.

In [4]:
ax.get_xaxis().set_ticks([])
ax.get_yaxis().set_ticks([])
ax.get_xaxis().set_ticklabels([])
ax.get_yaxis().set_ticklabels([])

ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)

ax.set_xlim([-0.9,0.9])
ax.set_ylim([-0.09,0.09])

fig

Out[4]:

The image of the drop in read-in at this point. It is loaded in as a numpy array shaped MxNx4, MxN is the pixel resolution of the image with four data values per pixel. The last place of the four is the alpha value which sets the transparency. We want to plot some drops looking transparent. The background of the drop is already transparent. We make a copy of the drop image and make a translucent one taking care to ensure that the background continues to be fully transparent. The OffsetImage gives us a object that we can plot in our figure

In [5]:
# read the drop image, make an additional transparent image
img2=img1.copy()

img2[:, :, -1] = 0.5 #sets alpha = 0.5 every where including the background where it was zero. This is fixed next

drop_marker1=OffsetImage(img1,zoom=0.06)
drop_marker2=OffsetImage(img2,zoom=0.06)


Now we identify the points where we want to have the drops and arrows along the black curve

In [6]:
l=len(data)

#points for drawing drop images
i1,i2,i3,i4=0,int(0.06*l),int(0.15*l),-1

#points for drawing arrows on the curve
a1=int(0.033*l)
a2=int(0.033*l)


Now we plot all the drops

In [7]:
ax.add_artist(AnnotationBbox(drop_marker2, (p1[i1,1], p1[i1,2]), frameon=False))

fig

Out[7]:

Add arrows to the black lines between the first and second drops

In [8]:
ax.annotate('', xy=(( p1[a1,1], p1[a1,2])), xycoords='data',
xytext=( p1[a1-1,1], p1[a1-1,2]),
arrowprops=dict(arrowstyle='->',color='k'))
ax.annotate('', xy=(( p2[a2,1], p2[a2,2])), xycoords='data',
xytext=( p2[a2-1,1], p2[a2-1,2]),
arrowprops=dict(arrowstyle='->',color='k'))
fig

Out[8]:

add the wall migration effect arrow and text.

In [9]:
_=ax.text(0.21,-0.005, 'wall-induced \n migration',color='g',fontsize=8)
ax.annotate('', xy=(p1[i2,1], p1[i2,2]-0.03), xycoords='data',
xytext=(p1[i2,1], p1[i2,2]),
arrowprops=dict(arrowstyle='-|>',fill=True,color='g'),
horizontalalignment='center',zorder=4)
ax.annotate('', xy=(p2[i2,1], p2[i2,2]+0.03), xycoords='data',
xytext=(p2[i2,1], p2[i2,2]),
arrowprops=dict(arrowstyle='-|>',fill=True,color='g'),
horizontalalignment='center',zorder=4)
fig

Out[9]:

Add the top and bottom wall, and arrows indication the direction of motion of the walls (and Gradient direction arrow). The walls are hatched rectangles

In [10]:
#flow gradient arrow
ax.arrow(-0.1, 0.1,0.2,0,fc='k',ec='k',
ax.arrow( 0.1,-0.1,-0.2,0,fc='k',ec='k',
ax.arrow(-0.9,-0.015,0.0,0.03,fc='k',ec='k',

## Rectangles for walls
ax.add_artist(plt.Rectangle((-0.9,0.09), 1.8, 0.006, linewidth=0, fill=None, hatch='///',clip_on=False))
ax.add_artist(plt.Rectangle((-0.9,-0.096), 1.8, 0.006, linewidth=0, fill=None, hatch='///',clip_on=False))
fig

Out[10]:

define and plot Bezier curves for the two other light colored curves. These are qualitative curves

In [11]:
#paths for swapping trajact

verts = [
(-0.9, 0.07),
( 0.1, 0.07),
( 0.1,-0.07),
(-0.9,-0.07),
]

codes = [Path.MOVETO,
Path.CURVE4,
Path.CURVE4,
Path.CURVE4,
]

path = Path(verts, codes)

verts = [
(  0.9,-0.07),
( -0.1,-0.07),
( -0.1, 0.07),
(  0.9, 0.07),
]

codes = [Path.MOVETO,
Path.CURVE4,
Path.CURVE4,
Path.CURVE4,
]

path = Path(verts, codes)

ax.text(-0.9,0.073, 'swapping (reversing) streamline',color='C',fontsize=8)
fig

Out[11]:

Finally draw some lines and arrows to mark the distance between the drops.

In [12]:
ax.add_patch(patches.FancyArrowPatch((p1[i4,1],-0.075),(p2[i4,1],-0.075),
color='k',linewidth=0.75))

ax.text(0,-0.07, '$l_{st}$',color='k',fontsize=12)