In [1]:
%pylab inline
from paraview.simple import *

paraview.simple._DisableFirstRenderCameraReset()

import pylab as pl
import math
import numpy as np
from scipy.optimize import curve_fit

remote_data = True


Welcome to pylab, a matplotlib-based Python environment [backend: module://IPython.kernel.zmq.pylab.backend_inline].
For more information, type 'help(pylab)'.
paraview version 4.0.0-RC3-6-gef1468d

In [21]:
#from paraview.simple import *

#paraview.simple._DisableFirstRenderCameraReset()

import math
import numpy as np
from scipy.optimize import curve_fit



datum = -150.0 #nose at -150.0m
alpha = 0.0 # degrees
beta  = 0.0 # degrees
reference_area = 1.0e6 # mm^2
reference_length = 1.0e3 # mm
reference_point = [0.0,0.0,0.0]
pressure = 87510.53 # Pa
density  = pressure/(287.0*288.15) # kg/m^3
gamma = 1.4
speed_of_sound = math.sqrt(gamma*pressure/density) # m/s

front_axle = -146.570 # m
rear_axle  = -137.688 # m

cg_height = 830 # mm

jet_engine_height = 1570 #mm


def rotate_vector(vec,alpha_degree,beta_degree):
    """
    Rotate vector by alpha and beta based on ESDU definition
    """
    alpha = math.radians(alpha_degree)
    beta = math.radians(beta_degree)
    rot = [0.0,0.0,0.0]
    rot[0] =  math.cos(alpha)*math.cos(beta)*vec[0] + math.sin(beta)*vec[1]  + math.sin(alpha)*math.cos(beta)*vec[2]
    rot[1] = -math.cos(alpha)*math.sin(beta)*vec[0] + math.cos(beta)*vec[1]  - math.sin(alpha)*math.sin(beta)*vec[2]
    rot[2] = -math.sin(alpha)*               vec[0]                          + math.cos(alpha)*               vec[2]
    return rot


def sum_and_zone_filter_array(input,array_name,ignore_zone,filter=None):
    sum = [0.0,0.0,0.0]
    p = input.GetCellData().GetArray(array_name)
    z = input.GetCellData().GetArray("zone")
    numCells = input.GetNumberOfCells()
    for x in range(numCells):
        zone = z.GetValue(x)
        if zone not in ignore_zone:
            v = p.GetTuple(x)
            if filter == None or filter.test(input,x):
                #print 'Zone: %i'%(zone)
                for i in range(0,3):
                    sum[i] += v[i]                        
    return sum

def sum_and_zone_filter(input,array_name,ignore_zone,filter=None):
    sum = [0.0,0.0,0.0]
    if input.IsA("vtkMultiBlockDataSet"):
        iter = input.NewIterator()
        iter.UnRegister(None)
        iter.InitTraversal()
        while not iter.IsDoneWithTraversal():
            cur_input = iter.GetCurrentDataObject()
            v = sum_and_zone_filter_array(cur_input,array_name,ignore_zone,filter)
            for i in range(0,3):
                sum[i] += v[i]                        
            iter.GoToNextItem();
    else:
        sum = sum_and_zone_filter_array(input,array_name,ignore_zone,filter)
        
    return sum

class GeomFilterLT:
    def __init__(self,val,idx):
        #
        self.val = val
        self.idx = idx
        
    def test(self,input,x):
        centre = input.GetCellData().GetArray("centre").GetTuple(x)
        if centre[self.idx] < self.val:
            return True
        else:
            return False

class GeomFilterGT:
    def __init__(self,val,idx):
        #
        self.val = val
        self.idx = idx
        
    def test(self,input,x):
        centre = input.GetCellData().GetArray("centre").GetTuple(x)
        if centre[self.idx] >= self.val:
            return True
        else:
            return False

def calc_force(file_root,ignore_zone,half_model=False,filter=None):
    
    wall = PVDReader( FileName=file_root+'_wall.pvd' )

    #CellDatatoPointData1 = CellDatatoPointData(Input=wall)
    #CellDatatoPointData1.PassCellData = 1
    
    #wall = CellCenters(Input=wall)
    
    #sum = MinMax(Input=wall)
    #sum.Operation = "SUM"
    #sum.UpdatePipeline()

    wall.UpdatePipeline()
    
    sum_client = servermanager.Fetch(wall)
    
    pforce = sum_and_zone_filter(sum_client,"pressureforce",ignore_zone,filter)
            
    #fforce = sum_client.GetCellData().GetArray("frictionforce").GetTuple(0)
    #yplus = wall_slice_client.GetCellData().GetArray("yplus").GetValue(0)
    
    pforce = rotate_vector(pforce,alpha,beta)
    #fforce = rotate_vector(fforce,alpha,beta)

    if half_model:
        for i in range(0,3):
            pforce[i] *= 2.0
            
    return pforce

def calc_moment(file_root,ignore_zone,half_model=False):
    
    wall = PVDReader( FileName=file_root+'_wall.pvd' )

    #CellDatatoPointData1 = CellDatatoPointData(Input=wall)
    #CellDatatoPointData1.PassCellData = 1
    
    #sum = MinMax(Input=wall)
    #sum.Operation = "SUM"
    #sum.UpdatePipeline()

    sum_client = servermanager.Fetch(wall)    
    pmoment = sum_and_zone_filter(sum_client,"pressuremoment",ignore_zone)
    #fforce = sum_client.GetCellData().GetArray("frictionforce").GetTuple(0)
    #yplus = wall_slice_client.GetCellData().GetArray("yplus").GetValue(0)
    
    pmoment = rotate_vector(pmoment,alpha,beta)
    #fforce = rotate_vector(fforce,alpha,beta)

    if half_model:
        # This is only valid for X-Z plane reflection
        pmoment[0] += -pmoment[0]
        pmoment[1] +=  pmoment[1]
        pmoment[2] += -pmoment[2]
    
    return list(pmoment)

def calc_lift_centre_of_action(force,moment,ref_point):
    # longitudinal centre xs0 at zs0
    # spanwise centre ys0 at zs0
    # residual Mz moment (Mx=My=0) mzs0
    
    xs0 = ref_point[0] - moment[1]/force[2]
    ys0 = ref_point[1] + moment[0]/force[2]
    
    zs0 = ref_point[2]
    mzs0 = moment[2] - force[1]*(xs0-ref_point[0]) + force[0]*(ys0-ref_point[1])
    
    return (xs0,ys0,zs0),mzs0 
    
def calc_drag_centre_of_action(force,moment,ref_point):
    # longitudinal centre xs0 at zs0
    # spanwise centre ys0 at zs0
    # residual Mz moment (Mx=My=0) mzs0
    
    zs0 = ref_point[3] + moment[1]/force[0]
    ys0 = ref_point[1] - moment[3]/force[0]
    
    xs0 = ref_point[0]
    mzs0 = 0.0# moment[2] - force[1]*(xs0-ref_point[0]) + force[0]*(ys0-ref_point[1])
    
    return (xs0,ys0,zs0),mzs0 

def get_case(case,num_procs):
    return 'C13_2013-04-04-half-'+case+'_P'+num_procs+'_OUTPUT/C13_2013-04-04-half-'+case

#Connect('localhost')
if remote_data:
    #print 'Waiting for remote paraview server connection'
    ReverseConnect('11111')

num_procs = '24'

case_list = [('m0p5',0.5,0.0,0.0), 
             ('m0p8',0.8,0.0,0.0),
             ('m1p1',1.1,0.0,0.0),
             ('m1p3',1.5,0.0,0.0)]

#case_list = [('m0p5',0.5,0.0,0.0)]

force = []
force_fwd = []
force_aft = []
moment = []

ignore_zones = [34,35,101]
#ignore_zones = []

resolved_forces = {'thrust' : [], 'drag' : [] , 'weight': [], 'front_axle' : [] , 'rear_axle' : []}

for case in case_list:
    case_name = case[0]
    mach  = case[1]
    q     = (0.5*gamma*pressure*mach*mach)
    alpha = case[2]
    beta  = case[3]
    
    
    #f_aft =  calc_force(get_case(case_name,num_procs),set(ignore_zones),True,GeomFilterGT((16.0+datum)*1000.0,0))
    #for i in range(0,3):
    #    f_aft[i] /= reference_area
    #print f_aft
    
    #break
    #calc_test(get_case(case_name,num_procs),set(ignore_zones),True)
    
    f = calc_force(get_case(case_name,num_procs),set(ignore_zones),True)
    for i in range(0,3):
        f[i] /= reference_area
    force.append(f)
    
    m = calc_moment(get_case(case_name,num_procs),set(ignore_zones),True)
    for i in range(0,3):
        m[i] /= (reference_length*reference_area)
    moment.append(m)

    p, mz = calc_lift_centre_of_action(f,m,reference_point)
    print 'Centre of lift: %f m (%f N)' % (p[0]-datum,f[2]*q)
        
    # Calculate moment at mid axle point
    axle_midpoint = 0.5*(front_axle - rear_axle) + rear_axle

    print 'Axle midpoint: %f m' % (axle_midpoint-datum)

    moment_arm = p[0] - axle_midpoint

    print 'Moment arm: %f m' % (moment_arm)

    axle_midpoint_moment = -f[2] * moment_arm

    print 'Axle midpoint Moment: %f Nm' % (axle_midpoint_moment*q)
    
    # Assume moment is generated by the sum of forces on the front and rear axles
    
    # Front Axle force 
    front_axle_force = 0.5*axle_midpoint_moment/(axle_midpoint-front_axle) 
    #print 'Front Axle Moment force: %f N' % (front_axle_force*q)
    front_axle_force += 0.5*f[2]
    
    print 'Front Axle load: %f N' % (front_axle_force*q)
    
    # Rear Axle force 
    rear_axle_force = 0.5*axle_midpoint_moment/(axle_midpoint-rear_axle)    
    #print 'Rear Axle Moment force: %f N' % (rear_axle_force*q)
    rear_axle_force += 0.5*f[2]
    
    print 'Rear Axle load: %f N' % (rear_axle_force*q)
        
    # Calc load fwd of mid point
    #f_fwd = calc_force(get_case(case_name,num_procs),set(ignore_zones),True,GeomFilterLT(p[0]*1000.0,0))
    #for i in range(0,3):
    #    f_fwd[i] /= reference_area
    #force_fwd.append(f_fwd)
    #print f_fwd
    
    # Calc load aft of midpoint
    #f_aft = calc_force(get_case(case_name,num_procs),set(ignore_zones),True,GeomFilterGT(p[0]*1000.0,0))
    #for i in range(0,3):
    #    f_aft[i] /= reference_area
    #force_aft.append(f_aft)
    #print f_aft

if remote_data:
    #print 'Disconnecting from remote paraview server connection'
    Disconnect()

x = [0.0,0.5,0.8,1.1,1.3,1.4]
y = []
y.append(force[0][0])
for f in force:
    y.append(f[0])
y.append(force[-1][0])

from scipy.interpolate import interp1d
drag_curve = interp1d(x, y, kind='linear')
xnew = np.linspace(0.0, 1.4, 20)

xlabel('Mach')
ylabel('D/q', multialignment='center')
title('Drag Profile')
pl.plot(x,y,xnew,drag_curve(xnew))


use composite data append
use composite data append
Centre of lift: 9.609706 m (-21525.511654 N)
Axle midpoint: 7.871000 m
Moment arm: 1.738706 m
Axle midpoint Moment: 37426.535774 Nm
Front Axle load: -6549.004895 N
Rear Axle load: -14976.506759 N
use composite data append
use composite data append
Centre of lift: 6.220158 m (-14997.645714 N)
Axle midpoint: 7.871000 m
Moment arm: -1.650842 m
Axle midpoint Moment: -24758.738084 Nm
Front Axle load: -10286.341218 N
Rear Axle load: -4711.304496 N
use composite data append
use composite data append
Centre of lift: 23.455925 m (-15852.899938 N)
Axle midpoint: 7.871000 m
Moment arm: 15.584925 m
Axle midpoint Moment: 247066.255180 Nm
Front Axle load: 19890.061535 N
Rear Axle load: -35742.961473 N
use composite data append
use composite data append
Centre of lift: 144.900259 m (-3124.643032 N)
Axle midpoint: 7.871000 m
Moment arm: 137.029259 m
Axle midpoint Moment: 428167.518584 Nm
Front Axle load: 46643.884134 N
Rear Axle load: -49768.527166 N
Out[21]:
[<matplotlib.lines.Line2D at 0x11d7e9410>,
 <matplotlib.lines.Line2D at 0x11d7e95d0>]

In [3]:
from scipy.interpolate import interp1d

# Calculate total thrust for jet and rocket

thrust_factor = 1.0

#jet_thrust = 10000.0 # 10KN thrust for ??
jet_thrust = 4.44822162*20000.0 # 20000lb thrust
jet_end    = 75.0 # s
#rocket_thrust = 12000.0 # 12KN for 20 secs
rocket_thrust = 4.44822162*26000.0
rocket_start  = 20.0
rocket_end    = rocket_start + 20.0

def get_thrust(time_array):
    thrust_array = []
    for t in time_array:
        thrust = jet_thrust*thrust_factor
        if t >= rocket_start and t <= rocket_end:
            thrust += rocket_thrust
        thrust_array.append(thrust)    
    return thrust_array
    
# Time sample at 1s intervals
time_sample = 0.1
time_array  = np.linspace(0.0, 100.0, 100.0/time_sample+1)

#print time_array

# Get thrust profile
thrust_array = get_thrust(time_array)
thrust_curve = interp1d(time_array, thrust_array, kind='linear')

xlabel('Time (s)')
ylabel('Thrust (N)', multialignment='center')
title('Thrust Profile')
pl.plot(time_array,thrust_array,time_array,thrust_curve(time_array))


Out[3]:
[<matplotlib.lines.Line2D at 0x11b14bdd0>,
 <matplotlib.lines.Line2D at 0x11b14bf90>]

In [4]:
full_weight = 6500 #kg
jet_fuel    = 500 #kg
rocket_fuel = 963+181 #kg

thrust_off_weight = -1.0

def get_weight(time_array):
    weight_array = []
    current_weight = full_weight
    
    for t in time_array:
        
        if t > 0.0 and t <= jet_end:
            current_weight -= jet_fuel/jet_end * time_sample
        
        if t >= rocket_start and t <= rocket_end:
            current_weight -= rocket_fuel/(rocket_end-rocket_start) * time_sample
            
        weight_array.append(current_weight)   
        
    return weight_array
    
# Get thrust profile
weight_array = get_weight(time_array)
weight_curve = interp1d(time_array, weight_array, kind='linear')

xlabel('Time (s)')
ylabel('Mass (kg)', multialignment='center')
title('Mass Profile')
pl.plot(time_array,weight_array,time_array,weight_curve(time_array))

savefig("mass_profile.png")
from IPython.display import FileLink, display 
display(FileLink('mass_profile.png'))





In [5]:
def miles_to_km(miles):
    return miles * 1.609344

def km_to_miles(km):
    return km / 1.609344

def meters_per_second_to_mph(mps):
    return mps*2.23693629

def mph_to_meters_per_second(mph):
    return mph/2.23693629

end_of_measured_mile = miles_to_km(5.5)  # km
start_of_measured_mile = end_of_measured_mile - miles_to_km(1.0) # km

# Calculate acceleration from a combination of drag and thrust
def get_force(current_time, current_speed, current_distance):
    #
    global thrust_off_weight
    mach = current_speed/speed_of_sound
    q = (0.5*gamma*pressure*mach*mach)
    # Thrust
    thrust = thrust_curve([current_time])
    # Switch off jet at end of measured mile
    if current_distance > end_of_measured_mile*1000.0:
        thrust = 0.0
        
        # Save weight
        if thrust_off_weight < 0.0:
            thrust_off_weight = weight_curve([current_time])
            
        # Chute 1
        if current_speed < mph_to_meters_per_second(579.0):
            thrust += -0.9*q
        # Chute 2
        if current_speed < mph_to_meters_per_second(393.0):
            thrust += -1.8*q
        # Brakes
        if current_speed < mph_to_meters_per_second(100.0):
            thrust += -0.2*9.81*thrust_off_weight
        
    # Aero Drag
    drag_factor = 1.0
    mach = current_speed/speed_of_sound
    mach = min(mach,1.4)
    drag = drag_curve([mach])[0]
    drag *= q*drag_factor
    
    #print thrust, drag
    
    return thrust-drag
    
def verlet_integrator(r, v, dt, a):
	"""Return new position and velocity from current values, time step and acceleration.

	Parameters:
	   r is a numpy array giving the current position vector
	   v is a numpy array giving the current velocity vector
	   dt is a float value giving the length of the integration time step
	   a is a function which takes x as a parameter and returns the acceleration vector as an array

	Works with arrays of any dimension as long as they're all the same.
	"""
	# Deceptively simple (read about Velocity Verlet on wikipedia)
	r_new = r + v*dt + a(r)*dt**2/2
	v_new = v + (a(r) + a(r_new))/2 * dt
	return (r_new, v_new)

def get_performance(time_array):
    
    acceleration_array = []
    speed_array = []
    distance_array = []
    
    current_speed = 0.0
    acceleration = 0.0
    current_distance = 0.0
    
    time_at_start = -1.0
    time_at_end = -1.0
    
    run_complete = False
    
    # Euler integration
    for t in time_array:
        # Calculate current speed from previous acceleration
        current_speed += acceleration*time_sample
        
        #new_val = verlet_integrator(current_distance,current_speed,time_sample,acceleration)        
        #current_speed = new_val[1]
        
        if current_speed < 0.0 or run_complete: # Stop the car
            current_speed = 0.0
            acceleration = 0.0
            run_complete = True
        else:
            #current_speed = max(0.0,current_speed)
            current_distance += current_speed*time_sample
            #current_distance = new_val[0]
            # a = F/m
            if thrust_off_weight > 0.0:
                weight = thrust_off_weight
            else:
                weight = weight_curve([t])
            
            acceleration = get_force(t,current_speed, current_distance)/weight
        
        if current_distance >= start_of_measured_mile*1000.0 and time_at_start < 0.0:
            time_at_start = t
            
        if current_distance >= end_of_measured_mile*1000.0 and time_at_end < 0.0:
            time_at_end = t
             
        acceleration_array.append(acceleration/9.81)
        speed_array.append(meters_per_second_to_mph(current_speed))
        distance_array.append(km_to_miles(current_distance/1000.0))
    
    return acceleration_array, speed_array, distance_array, time_at_start, time_at_end

acceleration, speed, distance, timer_start, timer_end  = get_performance(time_array)

#print timer_start, timer_end
print 'Average speed: %f mph'%(1.0/(timer_end-timer_start)*3600.0)

#pl.plot(time_array,speed)        
#pl.plot(distance,speed)        
#pl.plot(time_array,acceleration)        

timer_start = [timer_start,timer_start]
timer_end = [timer_end, timer_end]
start_timing_line = [km_to_miles(start_of_measured_mile),km_to_miles(start_of_measured_mile)]
end_timing_line = [km_to_miles(end_of_measured_mile),km_to_miles(end_of_measured_mile)]

speed_timing_line = [0.0,1400]

fig = figure(figsize=(25, 10),dpi=100, facecolor='w', edgecolor='k')
            
ax = fig.add_subplot(1,3,1)
ax.grid(True)
xlabel('time (s)')
ylabel('speed (mph)', multialignment='center')
title('Speed - Time')
ax.plot(time_array, speed, color='r', label='speed (mph)')
ax.plot(timer_start,speed_timing_line,color='b')
ax.plot(timer_end,speed_timing_line,color='b')

ax = fig.add_subplot(1,3,2)
ax.grid(True)
xlabel('distance (miles)')
ylabel('speed (mph)', multialignment='center')
title('Speed - Distance')
ax.plot(distance, speed, color='r', label='speed (mph)')
ax.plot(start_timing_line,speed_timing_line,color='b')
ax.plot(end_timing_line,speed_timing_line,color='b')

ax = fig.add_subplot(1,3,3)
ax.grid(True)
xlabel('time (s)')
ylabel('acceleration (g)', multialignment='center')
title('Acceleration - Time')
ax.plot(time_array, acceleration, color='r', label='acceleration (g)')


Average speed: 1090.909091 mph
Out[5]:
[<matplotlib.lines.Line2D at 0x11b0a2c50>]

In [5]:


In [5]: