# Droplet Bounce Parameter Estimation
## Import experimental data
- Each drop is an instance of the Experiment object class, with various attributes that are experimental metadata
- Why didn't I just use a dict/list of dicts? Dynamically generating these objects is very poor practice
- As of 10.19.2019 there are issues with the parameter estimation. I'm going to do some minor code clean up, and start using a virtual env once I'm able to replicate my previous results...

```python
import os, fnmatch, csv
import numpy as np

class Experiment(object):
    def __init__(self, data, name, start=0, end=-1, volume=1, surfaceV='[1000]', frame_rate=29.95,
                 outlier=False, time=1, debias=1, layers=4, guess=0.72*2.3):
        self.data = data
        self.name = name
        self.start = start
        self.end = end
        self.volume = volume
        self.surfaceV = surfaceV #sum(surfaceV)/len(surfaceV)
        self.frame_rate = frame_rate
        self.outlier = outlier
        #self.charge = charge
        #self.u0 = u0
        self.time = time
        self.debias = debias
        self.layers = layers
        self.guess = guess

def print_header(drop):
    """
    Prints the drop name and measured experimental parameters.
    """
    print('name', drop.name)
    print('volume', drop.volume, 'mL')
    print('surfaceV', drop.surfaceV, 'V')

def import_data(exp_class):
    """
    Takes an experiment type and returns an array of Experiment class objects, with attributes specified by
    a metadata csv. The 'data' attribute The column heads include:
    'R','Area','XM','YM','Major','Minor','Angle','Circ','Slice','AR','Round','Solidity'
    """
    meta_file_location = '../data/' + exp_class + '/meta.csv'
    imported_datatype = ('U9', int, int, float, object, float, bool, float, float, int, int, float)
    metadata = np.genfromtxt(meta_file_location, delimiter=';', dtype=imported_datatype, names=True)
    for keys, vals in np.ndenumerate(metadata['surfaceV']):
        metadata['surfaceV'][keys] = np.fromstring(vals, dtype=float, sep=',')
    globals()[exp_class + '_data_list'] = np.array([])
    for drop in metadata:
        name = 'drop' + str(drop['name'])
        path = '../data/' + exp_class + '/' + str(drop['name']) + '.csv'
        data = np.genfromtxt(path, dtype=float, delimiter=',', names=True)
        print(path[-8:])
        start = drop['start']
        end = drop['end']
        volume = drop['volume']
        surfaceV = drop['surfaceV']
        frame_rate = drop['frame_rate']
        outlier = drop['outlier']
        time = drop['time']
        debias = drop['debias']
        layers = drop['layers']
        guess = drop['guess']
        first_frame = drop['first_frame']
        data = data[first_frame:-1]
        data['Slice'] -= first_frame
        data['Minor'] = data['Minor']/2
        data['Major'] = data['Major']/2

        # check for gaps
        if (data.shape[0]) < data['Slice'][-1]:
            print('there is {} cells of gap'.format(int((data['Slice'][-1] - data.shape[0]))))
        globals()[str(name)] = Experiment(data, name, start, end, volume, surfaceV,
                                          frame_rate, outlier, time, debias, layers, guess)
        globals()[exp_class + '_data_list'] = np.append(globals()[exp_class + '_data_list'],
                                                        globals()[str(name)])
    print('done!')
```

```python
exp_class = 'dielectric_improved'
import_data(exp_class)

# use fancy indexing to make a list of outliers
mask = [datas.outlier==False for datas in globals()[exp_class + '_data_list']]
outliers = {datas.name:datas for datas in globals()[exp_class + '_data_list'][mask]}
alldrops = {datas.name:datas for datas in globals()[exp_class + '_data_list']}
#alldrops = outliers
```

    7294.csv
    there is 2 cells of gap
    7295.csv
    there is 1 cells of gap
    7296.csv
    7297.csv
    7298.csv
    7299.csv
    7300.csv
    7301.csv
    7319.csv
    7325.csv
    7326.csv
    7327.csv
    7328.csv
    7329.csv
    7334.csv
    7335.csv
    7336.csv
    done!

## Filtering
 - Purpose is to smooth trajectories as part of a preconditioning strategy in the parameter estimation

```python
from scipy import signal

def sg_filter(y, dt):
    """
    Takes raw data and returns a filtered array of the same length.
    The function avoids IndexErrors by a simple rule for setting the window size.
    """
    #try:
    if y.shape[0]>25:
        window = 25
    else:
        window = y.shape[0]
        if window % 2 ==0:
            window-=3
    return derivs(y, window, dt)
    #except ValueError:
    #    print('debug: Window size', window)

def derivs(y, window, dt):
    """
    Returns Savitsky-Golay filtered array of a variable and its derivatives by finite differences.
    """
    dtdt=dt*dt
    y_savgol = signal.savgol_filter(y, window, 3, deriv=0, axis=0)
    y_savgol1 = signal.savgol_filter(y_savgol, window, 3, deriv=0, axis=0)
    y_savgol2 = signal.savgol_filter(y_savgol1, window, 3, deriv=0, axis=0)
    y_savgol3 = signal.savgol_filter(y_savgol2, window, 3, deriv=0, axis=0)
    dy_savgol = signal.savgol_filter(y_savgol3, window, 3, deriv=1, axis=0)/dt
    ddy_savgol = signal.savgol_filter(y_savgol3, window, 3, deriv=2, axis=0)/dtdt
    return y_savgol3, dy_savgol, ddy_savgol
```

##  Trajectory Plots

```python
# set the look of the figures

import matplotlib
import matplotlib.pyplot as plt
from matplotlib import rc
import pylab
from mpl_toolkits.axes_grid1 import host_subplot
import mpl_toolkits.axisartist as AA
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap, BoundaryNorm
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.colors import Normalize

#matplotlib.use('pgf')
#%matplotlib inline
#matplotlib.rcParams['figure.figsize'] = (10,10)

pgf_with_latex = {                      # setup matplotlib to use latex for output
    "pgf.texsystem": "pdflatex",        # change this if using xetex or lautex
    "text.usetex": True,                # use LaTeX to write all text
    "font.family": "serif",
    "font.serif": ['Computer Modern Roman'], # blank entries should cause plots to inherit fonts from the document
    "font.sans-serif": ['Computer Modern Sans serif'],
    "font.monospace": ['Computer Modern Typewriter'],
    "axes.labelsize": 12,               # LaTeX default is 10pt font.
    "font.size": 12,
    "legend.fontsize": 10,               # Make the legend/label fonts a little smaller
    "xtick.labelsize": 12,
    "ytick.labelsize": 12,
    "pgf.preamble": [
        r"\usepackage[utf8x]{inputenc}",    # use utf8 fonts becasue your computer can handle it :)
        r"\usepackage[T1]{fontenc}",        # plots will be generated using this preamble
        ]
    }

#%config InlineBackend.figure_formats = ['svg']
#matplotlib.rcParams['text.latex.unicode'] = True
#matplotlib.rcParams.update(pgf_with_latex)
matplotlib.rcParams.update(
{
    'xtick.color': 'k',
    'ytick.color': 'k',
    'axes.labelcolor': 'k'
})

plt.rc('text', usetex=True)
plt.rc('font', family='serif')
#plt.rc('legend', frameon=False)

plt.rcParams['figure.dpi'] = 100
plt.rc('font', size=16)
%config InlineBackend.figure_format = 'retina'
pics = False
```

```python
def savefig(filename, pics):
    """When pics global variable is set to True this function saves figures."""
    if pics == True:
        plt.savefig('../doc/figures/{}.eps'.format(filename), bbox_inches='tight')
    else:
        pass
```

```python
def plot_single(drop, args_for_components=['YM'], kwargs_for_plot={'color':'k'}, **kwargs):
    """
    Plots a given variable over the trajectory of a single drop.
    """
    #plt.xlabel('t ($s$)')
    (ym, t, dt, contact_mask, y_parted,
        dy_parted, ddy_parted, t_parted, ind_parted) = get_data(drop, param_est=False)
    component = {'y_parted': y_parted, 'dy_parted':dy_parted, 'ddy_parted':ddy_parted}
    if 'parted' in kwargs.keys() and kwargs['parted']==True:
        if 'sub' in kwargs.keys():
            i = kwargs['i']
            fig = kwargs['fig']
            axs = kwargs['axs']
            axs[i].plot(t_parted, component[kwargs['variable']], 'ko',
                    markersize=6, markerfacecolor='white', markeredgecolor='black', label='data')
            axs[i].plot(t_parted[ind_parted.flatten()], component[kwargs['variable']][ind_parted.flatten()], 'o')
            #axs[i].ylabel(kwargs['variable'])
            #axs[i].legend(title=drop.name)
        else:
            plt.plot(t_parted, component[kwargs['variable']], 'ko',
                    markersize=6, markerfacecolor='white', markeredgecolor='black')
            plt.plot(t_parted[ind_parted.flatten()], component[kwargs['variable']][ind_parted.flatten()], 'o')
            #plt.ylabel(kwargs['variable'])
    else:
        try:
            a = drop.start + kwargs['start']
        except KeyError:
            a = drop.start
        b = -drop.end
        t = drop.data['Slice'][a:b]/drop.frame_rate
        for component in args_for_components:
            y = drop.data[component][a:b]
            if 'n' in kwargs.keys():
                minm = signal.argrelextrema(y, np.less, order=4)[0][::]
                minm = np.append([0],minm)
                minm = np.append(minm, [len(y)-1])
                maxm = signal.argrelextrema(y, np.greater, order=4)[0][::]
                n=kwargs['n']
                if n == 'all':
                    plt.plot(t, y, label=drop.name, **kwargs_for_plot)
                    plt.plot(t[minm],y[minm],'o')
                    plt.plot(t[maxm],y[maxm],'o')
                    plt.ylabel(component)
                else:
                    plt.plot(t[minm[n]:minm[n+1]+1], y[minm[n]:minm[n+1]+1], label=drop.name, **kwargs_for_plot)
                    plt.plot(t[minm][n:n+2],y[minm][n:n+2],'o')
                    plt.plot(t[maxm][n],y[maxm][n],'+')
                    plt.ylabel(component)
            elif 'c' in kwargs_for_plot.keys():
                plt.plot(t,y, **kwargs_for_plot)
            elif 'points' in kwargs.keys():
                plt.plot(t,y,'k-')
            elif 'qm' in kwargs.keys() and kwargs['qm']==True:
                eu, t0, y0, im = kwargs['qmvals']
                if 'dimless' in kwargs.keys() and kwargs['dimless']==True:
                    y, t = y_parted, t_parted
                    try:
                        y00 = y/y0
                        minm = signal.argrelextrema(y, np.less, order=2)[0][::]
                        minmm = minm[0]
                        #plt.plot(t/t0, y00-np.abs(y00[minmm]),#radius(volume=drop.result.x[1])/y0,
                        #     label=drop.name, **kwargs_for_plot)

                        #plt.plot(t/t0, (y-np.abs(y[minmm]) - radius(volume=drop.result.x[1]))/y0,#radius(volume=drop.result.x[1])/y0,
                        #     label=drop.name, **kwargs_for_plot)
                        plt.plot(t/t0, (y-radius(volume=drop.result.x[1]))/y0
                                 , label=drop.name, **kwargs_for_plot)
                        #plt.scatter(t[minm], y[minm]-radius(volume=drop.result.x[1]) - y[minmm], **kwargs_for_plot)
                    except IndexError:
                        print('uh oh')
                        try:
                            minm = signal.argrelextrema(y, np.less, order=2)[0][::]
                            minmm = minm[0]
                            plt.plot(t/t0, (y-radius(volume=drop.result.x[1]))/y0
                                     , label=drop.name, **kwargs_for_plot)
                            #plt.scatter(t[minm], y[minm]-radius(volume=drop.result.x[1]) - y[minmm], **kwargs_for_plot)
                        except IndexError:
                            pass
                else:
                    plt.plot(t, y,label=drop.name, **kwargs_for_plot)
            else:
                plt.plot(t, y,label=drop.name, **kwargs_for_plot)
        #return t[-1]
            if 'single' in kwargs.keys() and kwargs['single']==True:
                plt.show()
            else:
                pass

def plot_series(dataset, component='YM', **kwargs):
    """
    Plots a given variable over the trajectories of a series of drops.
    """
    if 'qm' in kwargs.keys() and kwargs['qm']==True:
        if 'reg' in kwargs.keys():
            if kwargs['reg'] == 'l':
                n=1
                m=1
            elif kwargs['reg'] == 'll':
                n=2
                m=1
            elif kwargs['reg'] == 'm':
                n=4
                m=2
            else:
                n=0
                m=0
        else:
            n = 0
            m = 0
        qs = np.array([])
        ts = np.array([])
        ys = np.array([])
        im = np.array([])
        #for drops in dataset.keys():
        for keys, vals in enumerate(dataset.items()):
            drop = vals[1]
            qs = np.append(qs, q_to_m(drop)[m])
            im = np.append(im, img(drop))
            ys = np.append(ys, yc(drop)[m])
            ts = np.append(ts, tc(drop)[n])
        norm = matplotlib.colors.LogNorm(vmin=(qs).min(), vmax=(qs).max())
        color = plt.get_cmap(kwargs['col'])
        my_map = cm.ScalarMappable(norm=norm, cmap=kwargs['col'])
        fig = plt.figure()
        for keys, vals in enumerate(dataset.items()):
            color = my_map.to_rgba(qs[keys])
            drop = vals[1]
            plot_single(drop, [component], kwargs_for_plot={'color':color},
                        qmvals=(qs[keys], ts[keys], ys[keys], im[keys]), **kwargs)
        my_map.set_array([])
        cb1 = plt.colorbar(my_map)
        cb1.set_label(r'${\mathbf{E} \mbox{u}}$', size=14, labelpad=8, rotation=0)
        plt.ylabel(kwargs['label'][1])
        plt.xlabel(kwargs['label'][0])
        if 'dimless' in kwargs.keys() and kwargs['dimless']==True:
            if 'reg' in kwargs.keys() and (kwargs['reg']=='s'):# or kwargs['reg']=='m'):
                tn = get_data(drop, param_est=True)[0][-1]
                plt.xlim(xmin=0, xmax = 4)
                #plt.ylim(ymin=0, ymax=2)
    else:
        total_time=[]
        vols = []
        for keys, vals in enumerate(dataset.items()):
            drop = vals[1]
            vols = np.append(vols, drop.volume)
        norm = matplotlib.colors.Normalize(vmin=0, vmax=len(vols))
        color = plt.get_cmap(kwargs['col'])
        my_map = cm.ScalarMappable(norm=norm, cmap=kwargs['col'])
        fig = plt.figure()
        for keys, vals in enumerate(dataset.items()):
            color = my_map.to_rgba(keys)
            drop = vals[1]
            tf = plot_single(drop, [component], kwargs_for_plot={'color':color}) #
            if drop.name in ['drop07298','drop07299','drop07297','drop07300']:
                total_time.append(tf)
            plt.legend(loc = 'upper right')
            plt.ylabel(component)
            plt.xlabel(r'$t$ (s)')

    #plt.xlim((0.1,2.2))
    if 'savefig' in kwargs.keys() and kwargs['savefig']==True:
        name = kwargs['name']
        savefig(name, pics)
    plt.show()
```

```python
#n = -4
#m = -1
#sorted_vals = [alldrops[x] for x in sorted_keys[:n]]
#sorted_drops = dict(zip(sorted_keys, sorted_vals))
#plot_series(sorted_drops, component='YM', qm=True, label=[r'$t^*$', r'$y^*$'],
#            savefig=True, reg = 's', dimless = True, name='series_s_ds', col=col)
```

```python
#"""
#Use n=1,2,3 etc (where n may be 0 or any positive integer)  to look at an individual bounce.
#Otherwise use n='all' to see all bounces.
#"""
#
#drop = drop07336
##drop.start=10
##print(len(get_data(drop, param_est=False)[1]), get_data(drop, param_est=False)[1][0], drop.start, drop.end)
#plot_series(alldrops, component='YM', qm=False, savefig=False, col='tab20')
#print_header(drop)
#plot_single(drop, parted=False, variable='y_parted', points=True, single=True)
```

```python
# #A bash program to write experiemental metadata.

#header = {'name':1,'start':2,'end':3,'volume':4,'surfaceV':5, \
#          'frame_rate':6,'outlier':7,'time':8,'debias':9,'first_frame':10}
#row = !grep -n "{drop.name[4:]}" ../data/dielectric_improved/meta.csv | cut -d , -f 1 | cut -d : -f 1
#row = int(row[0])
#col = header['start']
#value = drop.start
```

```bash
%%bash -s "$row" "$col" "$value"
#awk -F ";" -v r=$1 -v c=$2 -v val=$3 'BEGIN{OFS=";"}; NR != r; NR == r {$c = val; print}' \
#../data/dielectric_improved/meta.csv > ../data/dielectric_improved/meta2.csv
#cp ../data/dielectric_improved/meta2.csv ../data/dielectric_improved/meta.csv
#rm ../data/dielectric_improved/meta2.csv
cat ../data/dielectric_improved/meta.csv
```

##  Munging

```python
def radius(**kwargs):
    """
    Given experimental volume (in mL) the functon returns droplet radius (in m).
    """
    if 'mass' in kwargs.keys():
        pass
    if 'volume' in kwargs.keys():
        vol = kwargs['volume']
        return (3 * vol * 1E-6/(np.pi * 4))**(1/3.) # droplet radius [m]

def mass(volume):
    """
    Given experiemental volume (in mL) the function returns mass (in kg).
    """
    return 1000 * volume * 1E-6

def volume(radius_drop):
    """
    Given the radius (in m) returns the volume (in m^3).
    """
    return 4/3*np.pi*radius_drop**3

def weber(drop, U):
    """
    Returns the Weber number.
    """
    surface_tension = 72.86/1000
    density = 1000
    return density*2*radius(volume=drop.volume)*(U/100)**2/surface_tension

def tj(drop):
    """
    Returns the dimensionless contact time.
    """
    surface_tension = 72.86/1000
    density = 1000
    return np.sqrt(density*radius(volume=drop.volume)**3/surface_tension)

def reynolds(U, R_drop):
    """
    Returns the Reynolds number.
    """
    nu = 15.11E-6
    D = 2 * R_drop
    return D*np.abs(U)/nu

def ohnesorge(drop):
    R_drop = radius(volume=drop.result.x[1])
    viscosity = 1.0016
    density = 1000
    surface_tension = 72.86/1000
    return viscosity/np.sqrt(density * surface_tension * R_drop)

def bond(drop):
    #surfaceV, volume, q, dy0
    R_drop = radius(volume=drop.result.x[1])
    surface_tension = 72.86/1000
    #e_force = charge_density(drop.result[0])*eta_0*drop.result[2]
    #e_force = eta_0 * 90 * drop.result[0]**2 #Ree0U^2/y
    e_force = drop.Ef0**2 * 80 * eta_0
    return e_force * R_drop / surface_tension
```

```python
def ellipse(data):
    """
    Returns the droplet radius in the vertical direction.
    """
    YM = data['YM']
    a = data['Major']
    b = data['Minor']
    Angle = (data['Angle']-90)*np.pi/180
    return a*b/((b*np.cos(Angle))**2 + (a*np.sin(Angle))**2)**(1/2)

def ym_0(drop, n):
    """
    Returns drop minima and maxima.
    """
    ym = drop.data['YM'][drop.start:-drop.end]
    minm = signal.argrelextrema(ym, np.less, order=4)[0][::]
    maxm = signal.argrelextrema(ym, np.greater, order=4)[0][::]
    minm = np.append([0],minm)
    minm = np.append(minm, [len(ym)-1])
    intstart = minm[n]
    intend = minm[n+1]
    #if minm.shape[0] == 2:
    #    YM_0 = 0
    #else:
    #    YM_0 = ym[minm[1]] - ellipse(drop.data)[minm[1]]
    YM_0 = 0
    return YM_0, intstart, intend, minm

def contact(drop, ym, a, b):
    """
    Returns a mask with elements true where the droplet is not in contact with the surface.
    """
    contact = ym - ellipse(drop.data[a:b])*1 # distance from the bottom of the drop to YM_0
    atol = np.std(ellipse(drop.data[a:b]))*3
    above_mask = np.invert(np.isclose(contact, 0, atol=atol))
    below_zero_mask = np.invert(np.array([(contact <= 0)]))
    return np.logical_and(above_mask, below_zero_mask).flatten()

def get_data(drop, param_est=False, **kwargs):
    """
    Returns filtered droplet position, velocity and acceleration, up to the first trajectory apoapse when
    param_est==True, for all non-contacting time otherwise.
    """
    a = drop.start
    b = -drop.end
    t = drop.data['Slice'][a:b]/drop.frame_rate
    ym = drop.data['YM'][a:b]
    dt = t[1]-t[0]
    YM_0, intstart, intend, minm = ym_0(drop, n=0)
    ym = ym - YM_0
    contact_mask = contact(drop, ym, a, b)
    if param_est==True:
        intendmask = np.array([(t <= t[intend])])
        intstartmask = np.array([(t >= t[intstart])])
        parted_mask = np.logical_and.reduce((intstartmask.flatten(), intendmask.flatten(), contact_mask))
        try:
            maxm = signal.argrelextrema(sg_filter(ym[parted_mask],dt)[0], np.greater, order=4)[0][::][0]
        except IndexError:
            maxm = -1
        return (t[parted_mask][0:maxm], sg_filter(ym[parted_mask], dt)[0][0:maxm]/100,
                sg_filter(ym[parted_mask], dt)[1][0:maxm]/100, sg_filter(ym[parted_mask], dt)[2][0:maxm]/100)
    else: #partitioning
        stepsize = 1
        #split_array = np.split(contact_mask, np.where(contact_mask[1:] != contact_mask[:-1])[0] + 1)
        #contact_array = [subarray for subarray in split_array if np.any(subarray)==False]
        #for keys, vals in enumerate(contact_array):
        #    vals = len(vals)/120
        #    contact_array[keys]=vals
        #
        indices = np.where(contact_mask[1:] != contact_mask[:-1])[0] + 1
        indices[::2]-=1
        if contact_mask[0]==True:
            indices = np.append(0,indices)
        if contact_mask[-1]==True:
            indices = np.append(indices, len(contact_mask)-1)
        y_parted = np.array([])
        dy_parted = np.array([])
        ddy_parted = np.array([])
        t_parted = np.array([])
        ind_parted = np.array([])
        for keys, vals in np.ndenumerate(indices):
            if keys[0] % 2 == 0 and indices[keys[0]+1]+1-vals>=10:
                ind_parted_a = len(y_parted)
                y_parted = np.append(y_parted, sg_filter(ym[vals:indices[keys[0]+1]+1], dt)[0])
                dy_parted = np.append(dy_parted, sg_filter(ym[vals:indices[keys[0]+1]+1], dt)[1])
                ddy_parted = np.append(ddy_parted, sg_filter(ym[vals:indices[keys[0]+1]+1], dt)[2])
                t_parted = np.append(t_parted, t[vals:indices[keys[0]+1]+1])
                ind_parted = np.append(ind_parted, [ind_parted_a, len(y_parted)-1])
        return ym, t, dt, contact_mask, y_parted, dy_parted, ddy_parted, t_parted, ind_parted.astype(int)
```

# Parameter Estimation

```python
#inputs
d = 6.47/100

import numpy as np
#physical constants
eta_0 = 8.85E-12       # vacuum permitivity
eta_r = 3.4            # relative permitivity
k = 1/(4*np.pi*eta_0)  # Coulomb's constant
#global k, eta_0, d

eta_a=eta_r
susceptibility = eta_a - 1
k = susceptibility/(susceptibility + 2)
k
```

```python
def electric_field(end, sigma):
    """
    Returns the 1-D electric field, and the gradient of its square
    in the z-direction of a uniform finite square of charge. (units?)
    """

    #d = 6.5/100
    Ef = np.array([4*k*sigma*np.arctan(d**2/(2*z*np.sqrt(2*d**2 + 4*z**2))) \
                   for z in np.linspace(1E-6, end, 10000)])
    del_Ef2 = np.gradient(Ef**2)
    return Ef, del_Ef2

def ef2(z, sigma):
    return 4*k*sigma*np.arctan(d**2/(2*z*np.sqrt(2*d**2 + 4*z**2)))

def charge_density(surfaceV, layers):
    old = surfaceV * eta_0 * eta_r/((layers*2.8+5.9)/1000)
    return old
```

```python
#
#print(np.mean(list(drop.surfaceV)))
#print('Actual fieldmeter reading -{:.2f} kV'.format(V))
#print('Simulated fieldmeter reading -{:.2f} kV'.format(electric_field(2.54/100, sigma)[2]/1000))
#print('Simulated fieldmeter reading -{:.2f} kV/m'.format(electric_field(2.54/100, sigma)[0]/1000))
#
## Plot the results
##from matplotlib import rc
##import pylab
##%config InlineBackend.figure_formats=['svg']
##%matplotlib inline
##rc('font', **{'family': 'serif', 'serif': ['Computer Modern']})
##rc('text', usetex=True)
#
#zz = np.linspace(0.00001, 3./100, 1000)
##zz = np.linspace(0, 4, 1000)
#plt.semilogx(zz*1000, electric_field(zz, sigma)[2]/1000)
#plt.xlabel('z ($cm$)')
#plt.ylabel('Scalar Potential ($kV$)')
##plt.autoscale(enable=True, axis='x', tight=True)
#plt.yticks([2,3,4])
#plt.show()
#
#fig, ax1 = plt.subplots()
#ax1.plot(zz*100, electric_field(zz, sigma)[0]/1000, zz*100, electric_field(zz, sigma)[3]/1000)
#ax2 = ax1.twinx()
#ax2.plot(zz*100, np.multiply(electric_field(zz, sigma)[3],zz)/1000, 'r')
#ax1.set_xlabel('z ($cm$)')
#ax1.set_ylabel('Electric field ($kV/m$)')
#ax2.set_ylabel('Surface Voltage E*d ($kV$)')
#plt.show()
#
#fig, ax1 = plt.subplots()
#ax1.semilogy(zz*100, np.abs(electric_field(zz, sigma)[1]/1000), 'r', label=r'$\nabla E$')
#ax2 = ax1.twinx()
#ax2.semilogy(zz*100, np.abs(electric_field(zz, sigma)[4]/1000), 'k', label=r'$\nabla E^2$')
#ax1.set_xlabel('z ($cm$)')
#ax1.set_ylabel(r'$\nabla E$')
#ax2.set_ylabel(r'$\nabla E^2$')
#ax1.legend()
#plt.show()
```

```python
def force_ep(q, Ef):
    """
    Returns the electrophoretic force [N], including the contribution to the
    attraction of image charges reflected across the dielectric boundary.
    """
    return q * Ef

def force_image(z, q, R_drop):
    eta_a=eta_r
    susceptibility = eta_a - 1
    k = susceptibility/(susceptibility + 2)
    if z.any() <= 0:
        z[z <= 0] = R_drop
    return 1/(16*np.pi*eta_0)*k*q**2/z**2

def force_dep(z, R_drop, del_Ef):
    """
    Returns the dielectrophoretic force [N].
    """
    eta_air = 1.
    eta_water = 90.
    K = (eta_water - eta_air)/(eta_water + 2 * eta_air)
    return -2 * np.pi * R_drop**3. * eta_air * K * eta_0 * del_Ef

def cd(re):
    """
    Drag coefficent, Abraham correlation.
    """
    return 24/9.06**2 * (1 + 9.06/np.sqrt(re))**2

def drag(v, R_drop):
    """
    Returns the aerodynamic drag force [N].
    """
    rho = 1.225
    #Cd = 24/reynolds(v, R_drop) + 5/np.sqrt(reynolds(v, R_drop)) + 0.3
    return cd(reynolds(v, R_drop))*2*rho*np.pi*R_drop**2*v**2

```

```python
import scipy.integrate as integrate
from scipy import stats

def fun(y, t, params):
    """
    1-D Equation of motion for a droplet with initial velocity u0 subject to drag, and electrical forces.
    """

    z, u = y      # unpack current values of y
    m, R_drop, sigma, q, Ef, del_Ef, zf = params # unpack parameters
    ef_interp = np.interp(z, zf, Ef)
    del_ef_interp = np.interp(z,zf,del_Ef)
    #- force_dep(z, R_drop, del_ef_interp)
    derivs = [u, (- force_ep(q, ef_interp) \
                  - force_image(z, q, R_drop) - drag(u, R_drop))/m] # list of dy/dt=f functions
    return derivs

def get_model(x, model_params):
    """
    Solves the ODE for the droplet trajectory given the design vector, x.
    """
    t0, z0, volt0, vol0, tStop, layers, q0, Ef, del_Ef, zf = model_params
    surfaceV, volume, q, u0 = x
    m = mass(volume)                                # droplet mass [kg]
    R_drop = radius(volume=volume)                  # droplet radius
    sigma = charge_density(surfaceV, layers)

    # Initial values
    z0 = z0    # initial displacement
    u0 = u0    # initial velocity
    t0 = t0    # initial time

    # Bundle parameters for ODE solver
    params = (m, R_drop, sigma, q, Ef, del_Ef, zf)

    # Bundle initial conditions for ODE solver
    y0 = [z0, u0]

    # Make time array for solution
    tInc = 0.00001
    t_rk = np.arange(t0, tStop, tInc)

    # Call the ODE solver
    psoln = integrate.odeint(fun, y0, t_rk, args=(params,), mxords=5)
    return psoln, t_rk
```

```python
## error estimates from pyfssa
## http://pyfssa.readthedocs.io/en/stable/nelder-mead.html
#
#def _neldermead_errors(sim, fsim, func, X0, *args):
#    # fit quadratic coefficients
#    fun = func
#    args = args
#    n = len(sim) - 1
#    x = .5 * (sim[np.mgrid[0:n+1, 0:n+1]][1] + sim[np.mgrid[0:n+1, 0:n+1]][0])
#
#    for i in range(n + 1):
#        assert(np.array_equal(x[i,i], sim[i]))
#        for j in range(n + 1):
#            assert(np.array_equal(x[i,j], 0.5 * (sim[i] + sim[j])))
#
#    y = np.nan * np.ones(shape=(n + 1, n + 1))
#    for i in range(n + 1):
#        y[i, i] = fsim[i]
#        for j in range(i + 1, n + 1):
#            y[i, j] = y[j, i] = fun(np.multiply(X0, x[i, j]), *args)
#
#    y0i = y[np.mgrid[0:n+1, 0:n+1]][0][1:,1:, 0]
#    for i in range(n):
#        for j in range(n):
#            assert y0i[i, j] == y[0, i + 1], (i, j)
#
#    y0j = y[np.mgrid[0:n+1, 0:n+1]][0][0, 1:, 1:]
#    for i in range(n):
#        for j in range(n):
#            assert y0j[i, j] == y[0, j + 1], (i, j)
#
#    b = 2 * (y[1:, 1:] + y[0, 0] - y0i - y0j)
#    for i in range(n):
#        assert abs(b[i, i] - 2 * (fsim[i + 1] + fsim[0] - 2 * y[0, i + 1])) < 1e-12
#        for j in range(n):
#            if i == j:
#                continue
#            assert abs(b[i, j] - 2 * (y[i + 1, j + 1] + fsim[0] - y[0, i + 1] -
#                y[0, j + 1])) < 1e-12
#
#    q = (sim - sim[0])[1:].T
#    for i in range(n):
#        assert np.array_equal(q[:, i], sim[i + 1] - sim[0])
#
#    varco = -np.dot(q, np.dot(np.linalg.inv(b), q.T)) # variance-covariance matrix
#    #print(varco)
#    w = np.abs(np.linalg.eig(varco)[0])
#    cond = np.log10(np.nanmax(w)/np.nanmin(w))
#    #print('condition number', cond)
#    return np.sqrt(np.diag(varco))
```

```python
import scipy.optimize as opt
import scipy.interpolate as interp

def get_params(drop, a=3.5, **kwargs):
    """
    Gets experimental parameters from drop object.
    """
    layers = drop.layers
    surfaceV = np.mean(drop.surfaceV)              # superhydrophobic surface potential
    volume = drop.volume                           # droplet volume
    if hasattr(drop, 'guess'):
        q = drop.guess*1E-12*volume*surfaceV
    else:
        q = a*1E-12*volume*surfaceV                # wild-ass guess droplet net charge [C]
    if 'test' in kwargs.keys() and kwargs['test']==True:
        q = a*1E-12*volume*surfaceV
    sigma = charge_density(surfaceV, layers)
    t, y, dy, ddy = get_data(drop, param_est=True)
    t0 = t[0]                                       # droplet initial time
    y0 = y[0]                                       # droplet initial position
    dy0 = dy[0]                                     # droplet initial y-velocity
    return surfaceV, volume, q, sigma, t0, y0, dy0, layers  #remove sigma, m, R_drop

def get_constraints(drop):
    """
    Gets the minimization problem constraints from the experimental measurement error for each parameter.
    """

    constraints = {'volume': 0.02,      # [m^3]
                    'q': None,
                    't0': 1/120.,       # [s]
                    'y0': 0.02/100,     # [m]
                  }
    y, t, dy, ddy = get_data(drop, param_est=True)
    y = y
    dy = dy
    constraints.update({'dy0': dy[0]*np.sqrt(1 + (constraints['y0']/(y[1]-y[0]))**2)})
    if np.std(drop.surfaceV) > 0.:
        constraints.update({'surfaceV': np.std(drop.surfaceV) + np.mean(drop.surfaceV)*0.2}) # [V]
    else:
        constraints.update({'surfaceV':100. + np.mean(drop.surfaceV)*0.2})
    return constraints

def obj_func(x, *args):
    """
    Pseudo-objective function with box bound constraints handles by exterior penalty function. The function to be
    minimized is the chi2 goodness of fit between the experimental and model trajectories given the parameter
    estimates (e.g. the design vector), x.
    """

    exp_data, model_params, constraints = args
    surfaceV, volume, q, dy0 = x
    t, y, dy, ddy = exp_data
    yStop = y[-1]+10*(y[-1]-y[-2])
    layers = model_params[5]
    sigma = charge_density(surfaceV, layers)
    Ef, del_Ef = electric_field(yStop, sigma)
    zf = np.linspace(1E-6, yStop, 10000)
    model_params = model_params + (Ef, del_Ef, zf)
    psoln, t_rk = get_model(x, model_params)
    #psoln = psoln[:,0]*100
    #b = interp.interp1d(np.arange(psoln.size),psoln)
    #y_soln = b(np.linspace(0,psoln.size-1,len(t)))
    y_soln = np.array([])
    for times in t:
        y_soln = np.append(y_soln, np.interp(times, t_rk, psoln[:,0]))

    rp = 50
    penalty_function = rp * ( max(0,  volume/(model_params[3] + constraints['volume']) - 1)**2
                            + max(0, -volume/(model_params[3] - constraints['volume']) + 1)**2
                            + 0.80* max(0,  surfaceV/(model_params[2] + constraints['surfaceV']) - 1)**2
                            + 0.80* max(0, -surfaceV/(model_params[2] - constraints['surfaceV']) + 1)**2
                            + max(0, -q/(model_params[6]))**2)
    return np.log(stats.chisquare(y,f_exp=y_soln,axis=0)[0]) + penalty_function

def reporter(p):
    """Reporter function to capture intermediate states of optimization."""
    global sim
    sim.append(p)

def res(drop, a, **kwargs):
    """
    Returns the parameter estimates of the drop experiment.
    """
    global sim
    exp_data = get_data(drop, param_est=True)
    constraints = get_constraints(drop)
    surfaceV, volume, q, sigma, t0, y0, dy0, layers = get_params(drop, a)
    model_params = (t0, y0, surfaceV, volume, exp_data[0][-1], layers, q)
    X0 = np.asarray((surfaceV, volume, q, dy0))
    sim = [X0]
    args = (exp_data, model_params, constraints)
    result = opt.minimize(obj_func, X0, args=args,
                          method='nelder-mead', options={'maxiter':300}, callback=reporter)
    fsim = np.exp(np.array([obj_func(x, *args) for x in sim]))
    result.fun = np.exp(result.fun)
    #final_fsim = np.array([obj_func2(x, *args) for x in result.final_simplex[0]])
    #try:
    #    error = _neldermead_errors(np.divide(result.final_simplex[0],X0), \
    #                                         final_fsim, obj_func2, X0, *args)
    #except AssertionError:
    #    error = 'fail'
    error = False
    return result, sim, fsim, error
```

```python
def fmt(x):
    """ For pretty printing chi^2 in scientific notation"""
    x = float('%s' % float('%.1g' % x))
    a, b = '{:.1e}'.format(x).split('e')
    b = int(b)
    return r'${} \times 10^{{{}}}$'.format(a[0], b)
```

```python
def param_est_plot(drop, sub=False, method='nelder-mead', a=3.5, **kwargs):
    if 'test' in kwargs.keys() and kwargs['test']==True:
        exp_data = get_data(drop, param_est=True)
        surfaceV, volume, q, sigma, t0, y0, dy0, layers = get_params(drop, a, test=True)
        model_params = (t0, y0, surfaceV, volume, exp_data[0][-1], layers, q)
        x = np.asarray((surfaceV, volume, q, dy0))
    elif 'test' in kwargs.keys() and kwargs['test']==False and sub==False:
        result, sim, fsim, error = res(drop, a)
        x = result.x
        #print(result)
        #print("errors:", error)
        check_design_feasibility(drop, result)
    else:
        x = drop.result.x

    surfaceV, volume, q, sigma, t0, y0, dy0, layers = get_params(drop, a)
    t, y, dy, ddy = get_data(drop, param_est=True)
    model_params = (t0, y0, surfaceV, volume, t[-1], layers, q)
    yStop = y[-1] + 20 * (y[-1]-y[-2])
    sigma = charge_density(x[0], layers)
    Ef, del_Ef = electric_field(yStop, sigma)
    zf = np.linspace(1E-6, yStop, 10000)
    model_params = model_params + (Ef, del_Ef, zf)
    psoln, t_rk = get_model(x, model_params)
    y_soln = np.array([])
    dy_soln = np.array([])
    for times in t:
        y_soln = np.append(y_soln, np.interp(times, t_rk, psoln[:,0]*100))
        dy_soln = np.append(dy_soln, np.interp(times, t_rk, psoln[:,1]*100))

    dx = (t[1]-t[0])
    ddy_soln = np.gradient(dy_soln, dx, edge_order=2)
    drop.y_soln = y_soln
    drop.dy_soln = dy_soln
    drop.ddy_soln = ddy_soln
    y=y*100
    if 'adding' in kwargs.keys() and kwargs['adding']==True:
        return

    if 'forces' not in kwargs.keys() and sub==True:
        i = kwargs['i']
        fig = kwargs['fig']
        axs = kwargs['axs']
        axs[i].plot(t[::], y[::], 'ko',
                    markersize=6, markerfacecolor='white', markeredgecolor='black', label='experiment')
        axs[i].plot(t,y_soln, 'r', label='model')
        axs[i].set_title(r'$\chi^2 =$ ' + fmt(drop.result.fun), fontsize=12)#'{0:.1E}'.format(drop.result.fun))
        #axs[i].legend(title=drop.name)
        #axs.set_xlabel('time')
        #axs.set_ylabel('position')
        #plt.tight_layout()
        #plt.show()
    elif 'forces' in kwargs.keys() and kwargs['forces']==True:
        if 'sub' in kwargs.keys() and kwargs['sub']==True:
            i = kwargs['i']
            fig = kwargs['fig']
            axs = kwargs['axs']
            ep_force, drag_force, image_force, inertia = plot_forces(y_soln, dy_soln, ddy_soln, x, yStop, layers)
            #plt.semilogy(t, dep_force, label='Dielectrophoretic force')
            every = int(len(ep_force)/5)
            n = 3
            axs[i].semilogy(t[:-n], ep_force[:-n], 'rs--', label='Coulomb force', markevery=every)
            axs[i].semilogy(t[:-n], drag_force[:-n], 'bo-', label='Drag force', markevery=every)
            axs[i].semilogy(t[:-n], image_force[:-n], 'cv-.', label='Image force', markevery=every)
            axs[i].semilogy(t[:-n], inertia[:-n], 'k+:', label='Inertia', markevery=every)
        else:
            n=20
            ep_force, drag_force, image_force, inertia = plot_forces(y_soln, dy_soln, ddy_soln, x, yStop, layers)
            #plt.semilogy(t, dep_force, label='Dielectrophoretic force')
            plt.semilogy(y[:-n]/(d*100), ep_force[:-n]/inertia[:-n], 'r', alpha=0.75)
            plt.semilogy(y[:-n]/(d*100), drag_force[:-n]/inertia[:-n], 'b', alpha=0.75)
            plt.semilogy(y[:-n]/(d*100), image_force[:-n]/inertia[:-n], 'c', alpha=0.75)
    else:
        # Plot results

        if 'test' in kwargs.keys() and kwargs['test']==False and sub==False:
            #plt.semilogy(range(len(errors)-1), -np.diff(errors))
            fig = plt.figure()
            plt.semilogy(range(len(fsim)), fsim, 'k')
            plt.ylabel(r'$\chi^2$')
            plt.xlabel('Iteration number')
            savefig('convergence', pics)
            plt.show()

        if 'showall' in kwargs.keys() and kwargs['showall']==True and sub==False:
            #fig = plt.figure()
            plt.plot(t[::], y[::], 'ko',
                        markersize=6, markerfacecolor='None',
                     markeredgecolor='black', alpha=0.5)
            plt.plot(t,y_soln, 'r')
            #plt.xlabel('time (s)')
            #plt.ylabel('position (cm)')
            #plt.legend()
            #plt.show()
            return

        fig = plt.figure()
        plt.plot(t[::], y[::], 'ko',
                    markersize=6, markerfacecolor='white', markeredgecolor='black', label='experiment')
        plt.plot(t,y_soln, 'r', label='model')
        plt.xlabel('time (s)')
        plt.ylabel('position (cm)')
        plt.legend()
        plt.show()

        plt.plot(y_soln[1:], ddy_soln[1:], 'k', label='model')
        #plt.plot(y, ddy*100, 'b', label='data')
        plt.ylabel('acceleration ($\mbox{cm/}\mbox{s}^2$)')
        plt.xlabel('position (cm)')
        plt.legend()
        plt.show()

        plt.plot(t[1:], reynolds(dy_soln[1:], radius(volume=drop.volume)), 'k', label='model')
        #plt.plot(y, ddy*100, 'b', label='data')
        plt.ylabel('Re')
        plt.xlabel('t (s)')
        #plt.legend()
        plt.show()
        #print(cd(reynolds(dy_soln[1:], radius(volume=drop.volume)))[0])

        ep_force, drag_force, image_force, inertia = plot_forces(y_soln, dy_soln, ddy_soln, x, yStop, layers)
        #plt.semilogy(t, dep_force, label='Dielectrophoretic force')
        plt.semilogy(y/(d*100), ep_force/inertia, 'rs--', markevery=10)
        plt.semilogy(y/(d*100), drag_force/inertia, 'bo-', markevery=10)
        plt.semilogy(y/(d*100), image_force/inertia, 'cv-.', markevery=10)
        #plt.semilogy(t, inertia, 'k+:', label='Inertia', markevery=10)
        plt.ylabel('dimensionless force')
        plt.xlabel(r'$y/L$')
        plt.legend()
        plt.show()

def check_design_feasibility(drop, result):
    #(surfaceV, volume, q, dy0)
    print(result.fun)
    #print(result.errors)
    comp_params = get_params(drop)
    print('nit', result.nit)
    names = ['surfaceV', 'volume', 'q', 'dy0']
    constraints = get_constraints(drop)

    for n in range(len(result.x)):
        try:
            print('{},   {:.3} < {:.3}  < {:.3}'.format(names[n], \
                                                     comp_params[n]-constraints[names[n]],result.x[n], \
                                                     comp_params[n]+constraints[names[n]]))
        except (TypeError, KeyError):
            print('{},   {:.3} {:.3}'.format(names[n], result.x[n], comp_params[n]))
            continue
```

```python
def plot_forces(z, u, du, results, yStop, layers):
    z = z/100
    u = u/100
    du = du/100
    surfaceV, volume, q, dy0 = results
    sigma = charge_density(surfaceV, layers)
    Ef, del_Ef = electric_field(yStop, sigma)
    zf = np.linspace(1E-6, yStop, 10000)
    R_drop = radius(volume=volume)
    ef_interp = np.interp(z, zf, Ef)
    del_ef_interp = np.interp(z,zf,del_Ef)
    #force_dep(z, R_drop, del_ef_interp),
    inertia = mass(volume)*du
    return force_ep(q, ef_interp), drag(u, R_drop), force_image(z, q, R_drop), np.abs(inertia)
```

```python
"""
Use n=1,2,3 etc (where n may be 0 or any positive integer)  to look at an individual bounce.
Otherwise use n='all' to see all bounces.
"""

drop = drop07336
print_header(drop)
#print(radius(volume=drop.result[1]))
#print('Oh', ohnesorge(radius(volume=drop.volume)))
#print('Bo', bond(drop))
#plot_single(drop, parted=True, variable='y_parted');
```

```python
#%time
a=drop.guess*1 #= a #1.9*2.3
fig = plot_single(drop, parted=True, variable='y_parted', points=False, single=True)
name = 'bounce_series'
pics= True
plt.ylabel(r'$y$ (cm)')
plt.xlabel(r'$t$ (s)')
savefig(name, pics)
plt.show()
#param_est_plot(drop, sub=False, method='nelder-mead', a=a, test=True)
```

```python
#drop.guess = a
```

```python
## #A bash program to write experiemental metadata.
#header = {'name':1,'start':2,'end':3,'volume':4,'surfaceV':5, \
#          'frame_rate':6,'outlier':7,'time':8,'debias':9,'first_frame':10, 'layers':11, 'guess':12}
#row = !grep -n "{drop.name[4:]}" ../data/dielectric_improved/meta.csv | cut -d , -f 1 | cut -d : -f 1
#row = int(row[0])
#col = header['guess']
#value = drop.guess
```

```python
#%%bash -s "$row" "$col" "$value"
#awk -F ";" -v r=$1 -v c=$2 -v val=$3 'BEGIN{OFS=";"}; NR != r; NR == r {$c = val; print}' \
#../data/dielectric_improved/meta.csv > ../data/dielectric_improved/meta2.csv
#cp ../data/dielectric_improved/meta2.csv ../data/dielectric_improved/meta.csv
#rm ../data/dielectric_improved/meta2.csv
#cat ../data/dielectric_improved/meta.csv
```

### Series estimates

```python
#i = 0
#a = 0.72*2.3 #2.2
#for drop in alldrops:
#    print(drop)
#    (alldrops[drop].result, alldrops[drop].sim,
#    alldrops[drop].fsim, alldrops[drop].error)  = res(alldrops[drop], a=a)
#    param_est_plot(alldrops[drop], sub=True, adding=True)
#    i += 1
#
```

```python
import pickle
#with open('../data/pickles/sorted_data.pkl', 'wb') as f:
#    pickle.dump(alldrops, f)
#alldrops_ba = alldrops
```

```python
with open('../data/pickles/sorted_data.pkl', 'rb') as f:
    alldrops = pickle.load(f)
sorted_keys = [elements[1] for elements in \
                 sorted([[vals.y_soln.max(), keys] for keys, vals in alldrops.items()])]
sorted_vals = [alldrops[x] for x in sorted_keys]
sorted_drops = dict(zip(sorted_keys, sorted_vals))
```

```python
from math import ceil

fig, axs = plt.subplots(4,ceil(len(alldrops)/4), figsize=(12, 10), facecolor='w', edgecolor='k')
fig.subplots_adjust(hspace = .5, wspace=.3)
axs = axs.ravel()
i = 0
for drop in sorted_drops:
    plot_single(sorted_drops[drop], parted=True, variable='y_parted', sub=True, i=i, fig=fig, axs=axs)
    i += 1

fig.text(0.5, -0.02, r'$t$ (s)', ha='center', fontsize=16, color='k')
fig.text(-0.02, 0.5, r'$y$ (cm)', va='center', rotation='vertical', fontsize=16, color='k')
fig.tight_layout()
#plt.legend(loc=(1.2, 2.55), borderaxespad=0., fontsize=14)
name = 'jump_matrix'
savefig(name, pics)
plt.show()
```

```python
fig, axs = plt.subplots(4,ceil(len(alldrops)/4), figsize=(12, 10), facecolor='w', edgecolor='k')
fig.subplots_adjust(hspace = .5, wspace=.3)
axs = axs.ravel()
i = 0
for drop in sorted_drops:
    param_est_plot(sorted_drops[drop], sub=True, method='Nelder-Mead', a=a, i=i, fig=fig, axs=axs)
    i += 1

fig.text(0.5, -0.02, r'$t$ (s)', ha='center', fontsize=16)
fig.text(-0.02, 0.5, r'$y$ (cm)', va='center', rotation='vertical', fontsize=16)
#plt.legend(loc=(1.2, 2.55), borderaxespad=0., fontsize=14)
fig.tight_layout()
name = 'inverse_problem'
savefig(name, pics)
plt.show()
```

```python
#fig, axs = plt.subplots(4,ceil(len(alldrops)/4), figsize=(12, 10), facecolor='w', edgecolor='k')
#fig.subplots_adjust(hspace = .5, wspace=.3)
#axs = axs.ravel()
#i = 0
#for drop in sorted_drops:
#    param_est_plot(sorted_drops[drop], sub=True, method='Nelder-Mead', forces=True, a=a, i=i, fig=fig, axs=axs)
#    i += 1
#
#fig.text(0.5, 0.06, r'$t$ (s)', ha='center', fontsize=14)
#fig.text(0.06, 0.5, 'Force (N)', va='center', rotation='vertical', fontsize=14)
#plt.legend(loc=(1.2, 2.45), borderaxespad=0., fontsize=14);
#name = 'forces'
#savefig(name, pics)
#plt.show()
```

```python
plt.figure()
for drop in sorted_drops:
    param_est_plot(sorted_drops[drop], sub=False, method='Nelder-Mead', forces=True)
plt.ylim(ymin=5E-3)
plt.xlabel(r'$y/L$')
plt.ylabel('Dimensionless force')
plt.semilogy([],[],'r', label='Coulomb force', markevery=10)
plt.semilogy([],[],'b', label='Drag force', markevery=10)
plt.semilogy([],[],'c', label='Image force', markevery=10)
#leg = plt.legend(loc=4)
#leg.get_frame().set_linewidth(0.0)
name = 'forces_all'
savefig(name, pics)
plt.show();
```

```python
#likely outliers
#outliers = {#'drop07294':alldrops.pop('drop07294'),
#            'drop07334':alldrops.pop('drop07334')}
#alldrops['drop07334'] = outliers.pop('drop07334')
    #surfaceV, volume, q, dy0
```

```python
def param_est(alldrops):
    #surfaceV, volume, q, dy0
    Ef0 = []
    vols = []
    qs = []
    u0s = []
    surfaceVs = []
    for drop in alldrops.keys():
        qs.append(alldrops[drop].result.x[2])
        vols.append(alldrops[drop].result.x[1])
        y = get_data(alldrops[drop], param_est=True)[1]
        yStop = y[-1] + 20 * (y[-1]-y[-2])
        sigma = charge_density(alldrops[drop].result.x[0], alldrops[drop].layers)
        zf = np.linspace(1E-6, yStop, 10000)
        R_0 = radius(volume = alldrops[drop].result.x[1])
        Ef0.append(np.interp(radius(volume=alldrops[drop].result.x[1]), zf, electric_field(yStop, sigma)[0]))
        alldrops[drop].Ef0 = Ef0[-1]
        surfaceVs.append(alldrops[drop].result.x[0])
        u0s.append(alldrops[drop].result.x[3])
    return np.array(vols), np.array(qs), np.array(u0s), np.array(surfaceVs), np.array(Ef0)

vols, qs, u0s, surfaceVs, Ef0 = param_est(alldrops)
```

```python
def drop_planning_plots(surfaceVs, vol, qs, **kwargs):
    x = np.array(surfaceVs)/1000
    y = vol
    z = qs
    plt.plot(x,y,'.')
    plt.xlabel(r'$\varphi$ (kV)')
    plt.ylabel('$V_d$ (mL)')
    plt.show()

    xi = np.linspace(min(x), max(x), 50)
    yi = np.linspace(min(y),max(y), 50)
    Z = matplotlib.mlab.griddata(x, y, z, xi, yi, interp='linear')
    X, Y = np.meshgrid(xi, yi)

    fig = plt.figure()
    #ax = Axes3D(fig)
    #ax.scatter(x, vols, qs, c='k', marker='o', s=40)

    #norm = Normalize(vmin=np.min(Z), vmax=np.max(Z))
    #surf = ax.plot_surface(X,Y,Z, linewidth=1,
    #                       antialiased=True, cstride=2,
    #                       rstride=2, edgecolor='k',
    #                       cmap=cm.jet, alpha=0.3, norm=norm)
    #fig.colorbar(surf, label=r'$q$ (C)')
    #ax.view_init(90, 35-90)
    CS = plt.contourf(X,Y,Z,cmap=plt.cm.bone)
    plt.scatter(x, y, color='k', facecolors='white', edgecolors='k')
    #CS2 = plt.contour(CS, levels=CS.levels[::2], colors='white')
    #fmt = matplotlib.ticker.LogFormatterMathtext()
    #fmt.create_dummy_axis()
    #manual_locations = [(-1, -1.4), (-0.62, -0.7), (-2, 0.5)]
    #plt.clabel(CS2, inline=1, colors='w', fontsize=10, fmt=fmt, manual=manual_locations)
    cbar = plt.colorbar(CS)
    cbar.ax.set_ylabel('$q$ (C)')
    #cbar.add_lines(CS2)
    plt.xlabel(r'$\varphi_s$ (kV)')
    plt.ylabel(r'$V_d$ (mL)')
    #ax.set_xlabel(r'$E_0$ (kV/cm)')
    #ax.set_ylabel(r'Volume (mL)')
    #ax.set_zticks([])
    #ax.set_zlabel(r'$q$ (C)')
    if 'savefig' in kwargs.keys() and kwargs['savefig']==True:
        name = 'charge'
        savefig(name, pics)
    plt.show()

drop_planning_plots(surfaceVs, vols, qs, savefig=True)
#print(qs)
```

```python
#from sympy import *
#init_printing()
#
#y, z, nu, a = symbols('y z nu a')
#nu*integrate(z/((z**2 + y**2)*sqrt(z**2 + y**2 + a**2/4)), (y,0,a/2))
```

```python
def hysteresis_plot():
    # relating apparent contact angle hysteresis to
    # roll-off angle using the model of Furmidge, J. Colloid Sci. 1962, 17, 309.
    # 2 mL data for DDT surfaces

    #%config InlineBackend.figure_formats = ['svg']
    #matplotlib.rcParams['text.usetex'] = True
    #matplotlib.rcParams['text.latex.preamble'] = [r'\usepackage{amsmath}']
    #matplotlib.rcParams['text.latex.unicode'] = True
    #matplotlib.rcParams.update(pgf_with_latex)
    n = np.linspace(5,25,5)
    a = np.linspace(130,180,100)
    const = -10
    colors = plt.cm.cool_r(np.linspace(0,1,len(n)))

    for k,i in np.ndenumerate(n):
        b = a - i
        hyster = np.arcsin(np.cos(a*np.pi/180)-np.cos(b*np.pi/180))
        plt.plot(a, const*hyster, label=int(i), color=colors[k], zorder=1)

    exp_roll_off = 2.8
    exp_CA_appar = 148
    plt.scatter([exp_CA_appar,138],[exp_roll_off,2],100, marker="v", facecolors='r', edgecolors='m', zorder=2)
    plt.annotate('laser-etched PMMA ($R_q \sim 775 \mu m$)', xy=(exp_CA_appar,exp_roll_off), xycoords='data',
                xytext=(15, 30), textcoords='offset points', fontsize=10,
                arrowprops=dict(arrowstyle="->", connectionstyle="arc3, rad=.47"))
    plt.annotate('', xy=(138,2), xycoords='data',
                xytext=(85,74), textcoords='offset points', fontsize=10,
                arrowprops=dict(arrowstyle="->", connectionstyle="arc3, rad=.2"))

    plt.scatter([158,],[2,],100, marker="o", facecolors='b', edgecolors='c', zorder=2)
    plt.annotate('sandpaper ($R_q \sim 30 \mu m$)', xy=(158,2), xycoords='data',
                xytext=(-10, 40), textcoords='offset points', fontsize=10,
                arrowprops=dict(arrowstyle="->", connectionstyle="arc3, rad=.5"))

    plt.legend(title=r'$\theta_r - \theta_a$', loc='upper right')
    axes = plt.gca()
    axes.set_xlim(135,180)
    plt.xticks([140,150, 160,170, 180])
    plt.ylabel(r'$\alpha$')
    plt.xlabel(r'$\theta$')
    name = 'hysteresis'
    savefig(name, pics)
    plt.show()

hysteresis_plot()
```

```python
def impact_data(drop):
    """
    Returns Weber numbers, contact times, and coefficients of restituion for each bounce event in a drop.
    """
    ym, t, dt, contact_mask, y_parted, dy_parted, ddy_parted, t_parted, ind_parted = get_data(drop, param_est=False)
    ind_parted = ind_parted[1:-1]
    impact_weber = weber(drop, dy_parted[ind_parted])[::2]
    ind_parted = ind_parted.tolist()
    contact_time = np.array([])
    diameter_ratio = np.array([])
    restitution = np.array([])
    for keys, vals in enumerate(ind_parted[::2]):
        ind_parted.index(ind_parted[::2][keys])
        pair = [ind_parted[::2][keys], ind_parted[ind_parted.index(ind_parted[::2][keys])+1]]
        contact_time = np.append(contact_time, t_parted[pair[1]]-t_parted[pair[0]])
        restitution = np.append(restitution, abs(dy_parted[pair[1]]/dy_parted[pair[0]]))

        # Note: I scale the contact time by a parameter such that it
        # gives the same results as a manual count of frames.
    try:
        tb = t_parted[ind_parted[0]]
    except IndexError:
        tb = False
    return impact_weber, contact_time*.78, diameter_ratio, restitution*.78, tb #0.78

def q_to_m(drop):
    #surfaceV, volume, q, dy0
    density = 1000
    U = drop.dy_soln[0]/100 #drop.result.x[3]
    old_inertia = density * 2 * radius(volume=drop.result.x[1])**2 * U**2
    old_e_force = charge_density(drop.result.x[0], drop.layers)*eta_0*drop.result.x[2]
    #e_force = drop.result.x[0]/2.54*100*drop.result.x[2]
    e_force = drop.Ef0*drop.result.x[2]
    inertia = mass(drop.result.x[1]) * U**2
    eu_s = inertia/(e_force*radius(volume=drop.result.x[1]))
    phi = 4 * np.pi *radius(volume=drop.result.x[1])**2/d**2
    eu_m = inertia/(e_force*d)
    qm = drop.result.x[2]/mass(drop.result.x[1])
    return eu_s, 2*phi**2*eu_s, eu_m, qm, phi

def img(drop):
    q = drop.result.x[2]
    rad = radius(volume=drop.result.x[1])
    eta_a=eta_r
    susceptibility = eta_a - 1
    k = susceptibility/(susceptibility+2)
    return k * q /(16*np.pi*eta_0*rad**2*drop.Ef0)

def tc(drop):
    epss, epsl, epsm, qm, phi = q_to_m(drop)
    epsl = epss*phi
    tc_s = epss*radius(volume=drop.result.x[1])/(drop.result.x[3])
    #tc_l = epsl*radius(volume=drop.result.x[1])/(drop.result.x[3])
    tc_l = epss*phi*radius(volume=drop.result.x[1])/(drop.result.x[3])
    tfl = 0.456229037652571*epsl**3 + 0.799200144*epsl**2 + 1.33293333333333*epsl + 2
    tfs = (-4*epss**2*(0.0121*epss + 0.2121)*(2*(0.0121*epss + 0.2121)**4 +
        6*(0.0121*epss + 0.2121)**3 - 4*(0.0121*epss + 0.2121)**2 + 1)/
    (0.363*epss + 40*(0.0121*epss + 0.2121)**3 + 60*(0.0121*epss + 0.2121)**2 + 11.363)
    + 4*epss*(-0.0121*epss + 0.7879)*(0.0121*epss + 0.2121)/(3*(0.0242*epss + 1.4242)) + 2)
    tc_m = epsm*radius(volume=drop.result.x[1])/(drop.result.x[3])
    return tc_s, tc_l, tfl, tfs, tc_m

def yc(drop):
    epss, epsl, epsm, qm, phi = q_to_m(drop)
    epsl = epss*phi
    yc_s = epss*radius(volume=drop.result.x[1])*100
    yc_l = epsl*radius(volume=drop.result.x[1])*100
    yc_m = epsm*radius(volume=drop.result.x[1])*100
    return yc_s, yc_l, yc_m

def yf(drop):
    epsilon, phi = q_to_m(drop)[0], q_to_m(drop)[4]
    alpha = img(drop)
    epsilonl = epsilon*phi
    yfl = (0.00997118232863575*epsilonl**4 + 0.0180903441714801*epsilonl**3
    - 0.0972943974999999*epsilonl**2 + 0.2498375*epsilonl + 1/2)
    yfs = (epsilon**3*(alpha*(-17*alpha - 12)/60 + alpha*(73*alpha**2 + 117*alpha + 45)/630
                      + alpha*(-73*alpha**3 - 190*alpha**2 - 162*alpha - 45)/5040 + alpha/5) +
    epsilon**2*(alpha*(11*alpha + 9)/60 + alpha*(-11*alpha**2 - 20*alpha - 9)/360 - alpha/4) +
    epsilon*(alpha*(-alpha - 1)/12 + alpha/3) + 1/2)
    return yfl, yfs

def molacek(bo, web):
    st = 72.86/1000
    density = 1000
    #rad = radius(volume=drop.result.x[1])
    #density* rad**3/st
    return (2*(np.log(1/bo)/3 + 0.192/np.log(1/bo))**(1/2) *
            (np.pi - np.arccos((1 + 3.*web/(bo**2 * np.log(1./bo)))**(-1/2))))

def impact_plots(alldrops):
    #fig1, ax1 = plt.subplots()
    qm = np.array([])
    i_weber = np.array([])
    contact_r = np.array([])
    restn = np.array([])
    oh = np.array([])
    bo = np.array([])

    for keys in alldrops:
        impact_weber, contact_time, diameter_ratio, restitution, tb = impact_data(alldrops[keys])
        if impact_weber.size != 0:
                    qm = np.append(qm, impact_weber.size*[q_to_m(alldrops[keys])[0]])
                    R_drop = radius(volume=alldrops[keys].result.x[1])
                    oh = np.append(oh, impact_weber.size*[ohnesorge(alldrops[keys])])
                    bo = np.append(bo, impact_weber.size*[bond(alldrops[keys])])
        i_weber = np.append(i_weber, impact_weber)
        restn = np.append(restn, restitution)
        contact_r = np.append(contact_r, contact_time/tj(alldrops[keys]))
        alldrops[keys].tb = tb
        print(keys, restitution)
    print(np.average(i_weber), np.std(i_weber))

    fig = plt.figure()
    norm = matplotlib.colors.Normalize(vmin=bo.min(), vmax=bo.max())
    cmap = plt.cm.rainbow
    plt.scatter(i_weber, contact_r, c=bo, cmap=cmap, norm=norm)
    cb1 = plt.colorbar()
    cb1.set_label(r'$\mathrm{\mathit{Bo_e}} \equiv \frac{\epsilon E_0^2 R_d}{\gamma}$')

    plt.xscale('log')
    plt.xticks([10**(-2),10**(-1),10**0])
    #plt.grid(which='both', linestyle='--')
    webers = np.linspace(10**(-2),.99, 100)
    bos = [0.4,.6,.8]
    plt.plot(webers, 2.2*np.ones(len(webers)), 'k--', label='Richards, 2001')
    plt.plot(webers, np.log(1/webers) + 2.31, linestyle='-.', color='grey',
             label='Gopinath et al., 2002')
    plt.plot(webers, molacek(0.1, webers), c=cmap(norm(0.4)),
             label='Molacek et al., 2012, $\mathbf{B}\mbox{o} = $' + '{}'.format(0.2))
    for boo in bos:
        plt.plot(webers, molacek(boo, webers), c=cmap(norm(boo)),
                 label='..., $\mathbf{B}\mbox{o} = $' + '{}'.format(boo))

    plt.legend(loc=2)
    plt.xlabel('$\mathbf{W}\mbox{e}$')
    plt.ylabel(r'$t_j/ \tau$')
    plt.ylim(ymin=2)
    plt.xlim(xmin=5E-2)
    name = 'contact2'
    savefig(name, pics)
    plt.show()

    fig = plt.figure()
    plt.scatter(i_weber, restn, c=bo, cmap=plt.cm.rainbow, norm=norm)
    plt.xscale('log')
    plt.xticks([10**(-2),10**(-1),10**0])
    #plt.grid(which='both', linestyle='--')
    cb2 = plt.colorbar()
    cb2.set_label(r'$\mathrm{\mathit{Bo_e}} \equiv \frac{\epsilon E_0^2 R_d}{\gamma}$')
    plt.xlabel('$\mathbf{W}\mbox{e}$')
    plt.ylabel('$C_r$')
    name = 'restitution'
    bbox_props = dict(boxstyle="round", fc="w", ec="0.5", alpha=0.7)
    plt.text(1.5E-2,.75,r'$\mathbf{O}\mbox{h}_{\mu} = 2.2$', bbox=bbox_props)
    savefig(name, pics)
    plt.show()
```

```python
eus0s = np.array([])
bs = np.array([])
qss = np.array([])
for drop in alldrops:
    eus0, b, c, qm, phi = q_to_m(alldrops[drop])
    eus0s = np.append(eus0s, eus0)
    bs = np.append(bs, b)
    qss = np.append(qs, qm)
#escp, vols = (list(t) for t in zip(*sorted(zip(escp, vols))))
#plt.figure()
#plt.plot(vols, eus0,
#         'ko', markersize=6, markerfacecolor='white', markeredgecolor='black')
#plt.ylabel(r'$\mathbf{E}\mbox{u}$')
#plt.xlabel(r'$V_d$')
#name = 'eu_vs_vol'
#savefig(name, pics)
#plt.show()
def tof(dim):
    return 2+1.333*dim + 0.700*dim**2
print(eus0s)
print(bs*eus0s/(8*np.pi))
tof(bs*eus0s)
print(qs)
```

```python
impact_plots(alldrops)
```

```python
#figure_series = {'drop07296':drop07296, 'drop07327':drop07327, 'drop07295':drop07295,
#                 'drop07335':drop07335, 'drop07319':drop07319, 'drop07300':drop07300,
#                 'drop07325':drop07325, 'drop07326':drop07326}
#
figure_series = ['drop07296','drop07327','drop07295',
                 'drop07335','drop07319', 'drop07300', 'drop07325', 'drop07326']

keys = [elements[1] for elements in
                 sorted([[alldrops[vals].y_soln.max(), vals] for vals in figure_series], reverse=True)]
sorted_drops2 = dict(zip(keys, [alldrops[key] for key in keys]))
```

```python
#sorted_keys2 = [elements[1] for elements in
#                 sorted([[vals.y_soln.max(), keys] for keys, vals in figure_series.items()], reverse=True)]#[::-1]
#sorted_vals2 = [figure_series[x] for x in sorted_keys2]#[::-1]
#sorted_drops2 = dict(zip(sorted_keys2, sorted_vals2))

['y_soln' in dir(sorted_drops2[drop]) for drop in sorted_drops2]

i = 0
for drop in sorted_drops2:
    param_est_plot(sorted_drops2[drop], sub=False, method='Nelder-Mead', a=a, i=i, fig=fig, axs=axs, showall=True)
    i += 1

plt.xlabel('$t$ (s)')
plt.ylabel('$y$ (cm)')
plt.legend(loc='best')
plt.plot([],[],'ko', markersize=6, markerfacecolor='None',
                     markeredgecolor='black', label='experiment')
plt.plot([],[],'r-', label='model')

leg = plt.legend(loc='best')
leg.get_frame().set_linewidth(0.0)
for text in leg.get_texts():
    text.set_color('k')
name = 'forces_all'
name = 'inverse_problem2'
savefig(name, pics)
plt.show()
```

```python
sorted_keys = [elements[1] for elements in \
                 sorted([[q_to_m(vals), keys] for keys, vals in alldrops.items()])]
```

```python
col = 'viridis_r' #'rainbow_r'

#m=-5
#sorted_vals = [alldrops[x] for x in sorted_keys[:m]]
#sorted_drops = dict(zip(sorted_keys, sorted_vals))
#plot_series(alldrops, component='YM', qm=True, label=[r'$t$ (s)', r'$y$ (cm)'], col=col)
#plot_series(figure_series, component='YM', qm=True, label=[r'$t$ (s)', r'$y$ (cm)'],
#            savefig=True, reg = 's', name='series_s_eu', col=col)

n = -4
m = -1
sorted_vals = [alldrops[x] for x in sorted_keys[:n]]
sorted_drops = dict(zip(sorted_keys, sorted_vals))
plot_series(sorted_drops, component='YM', qm=True, label=[r'$t^*$', r'$y^*$'],
            savefig=False, reg = 's', dimless = True, name='series_s_ds', col=col)

#
#n = -5
#sorted_vals = [alldrops[x] for x in sorted_keys[n:]]
#sorted_drops = dict(zip(sorted_keys, sorted_vals))
#plot_series(sorted_drops, component='YM', qm=True, label=[r'$\bar{t}$',r'$\bar{y}$'],
#            savefig=True, reg = 'l', dimless = True, name='series_l_ds', col=col)
#
```

```python
n = -1
m = 0
sorted_vals = [alldrops[x] for x in sorted_keys[:n]]
sorted_drops = dict(zip(sorted_keys, sorted_vals))
plot_series(sorted_drops2, component='YM', qm=True, label=[r'$t$ (s)', r'$y$ (cm)'],
            savefig=True, reg = 's', dimless = False, name='series', col=col)
```

```python
#escape = []
#for drop in alldrops:
#    escape.append(8*np.pi()*radius(alldrops[drop])**2/d**2)
#print(np.average(os), np.std(os))
```

```python
def find_nearest(array,value):
    idx = (np.abs(array-value)).argmin()
    return idx

#print(650*eta_0/(2.54/100), 'field meter')
#print(650*eta_0/(2.54/100), 'non-contacting voltmeter')
#print(650*eta_0*3/(0.4*5/1000), 'nc-voltmeter w/ dielectric back by conductive groundplane')

drop = alldrops['drop07298']

ef = electric_field(40/100, charge_density(drop.result.x[0], drop.layers))[0]
z = np.linspace(1/1000, 40/100, 10000)

E0 = charge_density(drop.result.x[0], drop.layers)/(2*eta_0)
plt.loglog(z/(6.5/100), ef/E0, 'k')
plt.ylabel(r'$E/E_0$')
plt.xlabel(r'$y/L$')
print(E0/1000/100)

name = 'E0'
savefig(name, pics)
plt.show()

#print(np.interp(2.54/100,z,ef)/1000, 'electric field at 2.54 cm')
```

```python
#for drop in sorted_drops:
#    print(drop)
#    check_design_feasibility(sorted_drops[drop], sorted_drops[drop].result)
#    print('\n')
```

```python
Ef0
```

```python
import pandas
from statsmodels.formula.api import ols, rlm, WLS
from pandas import plotting
import seaborn

def scatter_matrix(x):
    area, qs, u0s, surfaceVs, Ef0 = x
    data = pandas.DataFrame({'area':area, 'charge':qs, 'u0': u0s, 'Ef0':Ef0/100/1000})
    plotting.scatter_matrix(data, diagonal='density', c='k');
    prod = area*Ef0
    data = pandas.DataFrame({'area':area, 'charge':qs, 'u0': u0s, 'Ef0':Ef0, 'prod': prod})
    model = ols('charge ~  prod - 1', data)
    #model = rlm('charge ~  area * Ef0', data)
    #r2_wls = WLS(model.endog, model.exog, weights=model.fit().weights).fit().rsquared
    print(model.fit().summary())
    #print(r'R^2 = {}'.format(r2_wls))

area = (vols/1E6)**(2/3)
variables = (area, qs, u0s, surfaceVs, Ef0)
scatter_matrix(variables)
name = 'scatter'
savefig(name, pics)
plt.show()

plt.scatter(qs, area*Ef0)
plt.xlim(0.25E-10, 0.6E-9)
plt.show()
```

```python
#plt.scatter(area*Ef0, qs, color='k')
#data = pandas.DataFrame({'charge':qs,'prod': area*Ef0})
#model = ols('charge ~  prod', data).fit()
#inter, coef = model._results.params
#f = lambda x: x*coef + inter
#x = np.linspace(0, (area*Ef0).max(), 100)
#plt.plot(x, f(x), 'r')
#plt.ylim(1E-11, 1E-9)
#plt.show()
#model.summary()
```

```python
parallel = np.abs(np.array([ -2.70158796e-12,  -4.20176010e-12,   7.78928795e-12,  -4.86568450e-12,
  -1.79171842e-12,  -1.74111697e-11,  -1.07898239e-11,  -5.68331113e-12,
  -2.89082418e-12,  -2.57343722e-12,  -1.30402067e-11,  -7.51379045e-13,
   1.11525974e-12,  -9.83808965e-13,  -6.00145897e-13,  -2.84923705e-12,
  -9.78463511e-12]))
stats.ttest_ind(qs, parallel, equal_var=False)
```

```python
from statsmodels.stats.anova import anova_lm
ims = np.array([])
eus = np.array([])
phis = np.array([])
plt.figure()
for drop in alldrops:
    eus = np.append(eus, q_to_m(alldrops[drop])[0])
    ims = np.append(ims, img(alldrops[drop]))
    phis = np.append(phis, q_to_m(alldrops[drop])[4])

# Convert the data into a Pandas DataFrame to use the formulas framework
# in statsmodels
data = pandas.DataFrame({'Eu': eus, 'Im': ims, 'Phi':phis})

# Fit the model
model = ols(formula="Im ~ Eu", data=data).fit()
offset, coef = model._results.params
plt.plot(eus, eus*coef + offset, 'k-', alpha=0.3,
         label=r'$\mathbf{I}\mbox{g} \approx 0.013 \mathbf{E}\mbox{u} + 0.230$, $R^2=0.57$')
plt.scatter(eus, ims, color='w', edgecolors='k')

plt.xlabel(r'$\mathbf{E}\mbox{u}$')
plt.ylabel(r'$\mathbf{I}\mbox{g}$')
plt.legend()
name = 'dnumbs'
savefig(name, pics)
plt.show()

# Fit the model
#model = ols(formula="Im ~ Phi", data=data).fit()
#offset, coef = model._results.params
plt.scatter(ims, 10*phis)
plt.show()
```

```python
# Print the summary
print(model.summary())

# Peform analysis of variance on fitted linear model
anova_results = anova_lm(model)

print('\nANOVA results')
print(anova_results)

```

```python
sorted_keys = [elements[1] for elements in \
                 sorted([[vals.y_soln.max(), keys] for keys, vals in alldrops.items()])]
m=-5
sorted_vals = [alldrops[x] for x in sorted_keys[:m]]
sorted_drops = dict(zip(sorted_keys, sorted_vals))

bo = np.array([alldrops[x].Ef0*alldrops[x].volume for x in sorted_keys[:m]])
norm = matplotlib.colors.Normalize(vmin=bo.min(), vmax=bo.max())
cmap = plt.cm.rainbow
tbl = np.array([])
tcl = np.array([])
eus = np.array([])
ybl = np.array([])
ycl = np.array([])
tk = np.array([])
yk = np.array([])
line = np.arange(0.2,1,100)
for drop in sorted_drops:
    #print(2*sorted_drops[drop].tb/tc(sorted_drops[drop])[3])
    tbl = np.append(tbl, sorted_drops[drop].tb)
    tcl = np.append(tcl, tc(sorted_drops[drop])[0]*tc(sorted_drops[drop])[2])
    ybl = np.append(ybl, sorted_drops[drop].data['YM'].max())
    ycl = np.append(ycl, yc(sorted_drops[drop])[0]*yf(sorted_drops[drop])[0])
    tk = np.append(tk,tc(sorted_drops[drop])[0])
    yk = np.append(yk, yc(sorted_drops[drop])[0])
    eus = np.append(eus, q_to_m(sorted_drops[drop])[0])
data = pandas.DataFrame({'tb': tbl, 'tof': tcl})

# Fit the model
model = ols(formula="tof ~ tb", data=data).fit()
offset, coef = model._results.params
print(1/coef)
#plt.plot(tbl, tbl*coef + offset, 'k-')
plt.scatter(tbl, tcl, color='None', edgecolors='k', label='$\mathcal{O}(\phi^3 \mathbf{E}\mbox{u}^3)$')
plt.scatter(tbl, tk*2, color='None', marker='s', edgecolors='r', label='$\mathcal{O}(1)$')
#cb1 = plt.colorbar()
#cb1.set_label(r'$\mathbf{E}\mbox{u}$')
plt.xlabel(r'${t_b}$ (s)')
plt.ylabel(r'$t_c t_f$ (s)')
leg = plt.legend()
leg.get_frame().set_linewidth(0.0)
for text in leg.get_texts():
    text.set_color('k')
name = 'times'
savefig(name, pics)
plt.show()
#model.summary()

#plt.scatter(tbl/tcl, eus, color='w', edgecolors='k')
#plt.scatter(tbl/(tk*2), eus, color='w', edgecolors='r')
#plt.show()

#print(tbl)

#sorted_keys = [elements[1] for elements in \
#                 sorted([[vals.y_soln.max(), keys] for keys, vals in alldrops.items()])]
#sorted_vals = [alldrops[x] for x in sorted_keys]
#sorted_drops = dict(zip(sorted_keys, sorted_vals))
#tbl = np.array([])
#tcl = np.array([])
#for drop in sorted_drops:
#    #print(2*sorted_drops[drop].tb/tc(sorted_drops[drop])[3])
#    tbl = np.append(tbl, sorted_drops[drop].tb)
#    tcl = np.append(tcl, tc(sorted_drops[drop])[0]*tc(sorted_drops[drop])[2])
#print(tcl)
```

```python
sorted_keys = [elements[1] for elements in \
                 sorted([[q_to_m(vals), keys] for keys, vals in alldrops.items()])]
n = -1
m = -1
sorted_vals = [alldrops[x] for x in sorted_keys[:n]]
#del sorted_vals[-2]
sorted_drops = dict(zip(sorted_keys, sorted_vals))

ybl = np.array([])
ycl = np.array([])
eus = np.array([])
yk = np.array([])
line = np.arange(0.2,1,100)
for drop in sorted_drops:
    #print(2*sorted_drops[drop].tb/tc(sorted_drops[drop])[3])
    ybl = np.append(ybl, sorted_drops[drop].data['YM'].max())
    ycl = np.append(ycl, yf(sorted_drops[drop])[0])
    yk = np.append(yk, yc(sorted_drops[drop])[0])
    eus = np.append(eus, q_to_m(sorted_drops[drop])[0])
data = pandas.DataFrame({'ymax': ybl, 'ycl': ycl*yk})

# Fit the model
model = ols(formula="ycl ~ ymax", data=data).fit()
offset, coef = model._results.params
print(1/coef)

plt.figure()
plt.scatter(eus, ybl/(yk), color='w', edgecolors='k')
plt.ylabel(r'${y_{max}}/y_c$')
plt.xlabel(r'$\mathbf{E}\mbox{u}$')
#plt.xlim(0.4,1.5)
#plt.ylim(0,2)
name = 'yscale_trend'
savefig(name, pics)
plt.show()

plt.scatter(ybl, yk*ycl, color='None', edgecolors='k', label='$\mathcal{O}(\phi^3 \mathbf{E}\mbox{u}^3)$')
plt.scatter(ybl, yk*0.5, color='None', marker='s', edgecolors='r', label='$\mathcal{O}(1)$')
plt.ylabel(r'$y_c y_f$ (cm)')
plt.xlabel(r'$y_{max}$ (cm)')
#plt.ylim(0,2.3)
leg = plt.legend()
leg.get_frame().set_linewidth(0.0)
for text in leg.get_texts():
    text.set_color('k')
name = 'ymaxes'
savefig(name, pics)
plt.show()
```

```python
eus
```

```python
#m = -1
#sorted_vals = [alldrops[x] for x in sorted_keys[:m]]
#sorted_drops = dict(zip(sorted_keys, sorted_vals))
#
#bo = np.array([q_to_m(alldrops[x])[0] for x in sorted_keys[:m]])
#norm = matplotlib.colors.Normalize(vmin=bo.min(), vmax=bo.max())
#cmap = plt.cm.rainbow
#
#tbs = np.array([])
#tcs = np.array([])
#for drop in sorted_drops:
#    tbs = np.append(tbs, sorted_drops[drop].tb/tc(sorted_drops[drop])[0])
#    tcs = np.append(tcs, tc(sorted_drops[drop])[3])
#
#plt.scatter(tbs, tcs, c=bo, cmap=cmap, norm=norm)
#cb1 = plt.colorbar()
#cb1.set_label(r'$\mathbf{E}\mbox{u}$')
#plt.xlabel(r'$\frac{t_b}{t_c}$')
#plt.ylabel(r'$t_f$')
#plt.ylim(ymin=1.9, ymax=2.2)
#plt.xlim(xmin = 2.5, xmax=3)
#name = 'times_short'
#savefig(name, pics)
#plt.show()
```

```python
tk = tc(alldrops['drop07296'])
print(tk[1]*113.75340697961346)
print(yc(alldrops['drop07296'])[1]/d)
```

```python
drop = 'drop07296'
y_soln = alldrops[drop].y_soln
t = get_data(alldrops[drop], param_est=True)[0]

plt.plot(t, y_soln)
plt.show()
```

```python

```