February 2, 2016

Offshore crane

When it comes to developing minimum offshore crane requirements, there are two standards, viz., API Spec. 2C and EN13852-1 that guide an engineer. These represent two geographically distinct locations, viz., Gulf of Mexico and the North Sea respectively, while rest of the world employs one of these two standards to size a crane up.

Within the industry, API is known for wider applicability within benign environments, including the Gulf of Mexico, which the standard itself considers to be of very mild sea and wind conditions (cf. §5.4.4 Legacy Dynamic Method), excluding hurricanes, of course. Notwithstanding this self-assessment, there have been projects and locations that continue to use API, when it may be more sensible to use the EN13852-1 instead in harsher environments — tropical or otherwise.

I’ve been poring over these two standards all last week to see how comparable they may be for a general application, and I thought, the quickest way to test the two would be to look at their off-board lift prescriptions, and in particular, their recipes for minimum hoisting velocity calculations. I needed to understand differences, so over the weekend, I wrote a little code (see below), which helps in producing the two following plots.

At minimum, these plots reveal differences in Vhmin. API plots corresponds to hoisting load from a supply vessel, while EN offers two additional options, viz., barge, and sea surface.

I am curious to know how crane vendors cater to North Sea-like or worse environments with API’s prescriptions. Do they simply go for a faster motor, or do they also review crane’s spring factor in combination to ensure optimum crane performance? And what else?

Here’s that code:

#!/usr/bin/env python
# encoding: utf-8
"""
crane.py: Offshore crane: min. hoisting velocity
2016 ckunte 
"""
import numpy as np
import matplotlib.pyplot as plt

# -- OFF-BOARD LIFTS --
# x corresponds to significant wave height (Hsig)
x = np.arange(0.0, 3.0, 0.001)

def api2c():
    # -- API Spec. 2C 7th Ed --
    # Min. (required) hoisting velocity, m/s (§5.4.5.2)
    cf = 0.3048 # ft to m conversion factor
    if x.any() <= 1.8288:
        Vhmin_api = (0.033 * cf) + 0.098 * x
    elif x.any() > 1.8288:
        Vhmin_api = 0.067 * (x + (3.3 * cf))

    plt.plot(x, Vhmin_api)
    plt.xlabel('Hsig (m)')
    plt.ylabel('Vhmin (m/s)')
    plt.grid(True)
    plt.title("API Spec 2C, 7th Edition")
    plt.savefig("Vhmin_api.png")
    plt.show()

def en13852():
    # -- EN 13852-1:2013 --
    # Radial displacement (m) of load on vessel deck relative
    # to the boom tip shall be taken as follows (§ B.3.2.3):
    # Offlead = 2.5 + 1.5 * x

    # Radial displacement (m) of load on supply vessel deck relative
    # to the boom tip shall be taken as follows (§ B.3.3.3):
    # Sidelead = 0.5 * (2.5 + 1.5 * x)

    # Table B.2: Load supporting deck velocity, Vd (m/s)
    Vd = [0, (3.2 * x / (x + 13.5)), (4.0 * x / (x + 7.0)), \
    (6.0 * x / (x + 8.0)), (5.3 * x / (x + 5.5))]

    # Table B.3: Crane boom tip velocity, Vc (m/s)
    Vc = [0, (0.25 * x), (0.5 * x)]

    # Table B.4: Velocity factor K_H
    # K_H structure: [NL & SFR, RC & SFR, NL & MFR, RC & MFR]
    K_H = [0.65, 0.50, 0.40, 0.28]

    # Hook velocity 
    # Crane on bottom supported structure
    ## Lifting to/from: Barge (No load, Single fall reeving)
    VH_f_b_nl_sfr = K_H[0] * (Vd[2]**2 + Vc[0]**2)**0.5
    ## Lifting to/from: Barge (At rated capacity, Single fall reeving)
    VH_f_b_rc_sfr = K_H[1] * (Vd[2]**2 + Vc[0]**2)**0.5
    ## Lifting to/from: Barge (No load, Multiple fall reeving)
    VH_f_b_nl_mfr = K_H[2] * (Vd[2]**2 + Vc[0]**2)**0.5
    ## Lifting to/from: Barge (At rated capacity, Multiple fall reeving)
    VH_f_b_rc_mfr = K_H[3] * (Vd[2]**2 + Vc[0]**2)**0.5

    ## Lifting to/from: Supply vessel (No load, Single fall reeving)
    VH_f_sv_nl_sfr = K_H[0] * (Vd[3]**2 + Vc[0]**2)**0.5
    ## Lifting to/from: Supply vessel (At rated capacity, Single fall reeving)
    VH_f_sv_rc_sfr = K_H[1] * (Vd[3]**2 + Vc[0]**2)**0.5
    ## Lifting to/from: Supply vessel (No load, Multiple fall reeving)
    VH_f_sv_nl_mfr = K_H[2] * (Vd[3]**2 + Vc[0]**2)**0.5
    ## Lifting to/from: Supply vessel (At rated capacity, Multiple fall reeving)
    VH_f_sv_rc_mfr = K_H[3] * (Vd[3]**2 + Vc[0]**2)**0.5

    ## Lifting to/from: Sea surface (No load, Single fall reeving)
    VH_f_ss_nl_sfr = K_H[0] * (Vd[4]**2 + Vc[0]**2)**0.5
    ## Lifting to/from: Sea surface (At rated capacity, Single fall reeving)
    VH_f_ss_rc_sfr = K_H[1] * (Vd[4]**2 + Vc[0]**2)**0.5
    ## Lifting to/from: Sea surface (No load, Multiple fall reeving)
    VH_f_ss_nl_mfr = K_H[2] * (Vd[4]**2 + Vc[0]**2)**0.5
    ## Lifting to/from: Sea surface (At rated capacity, Multiple fall reeving)
    VH_f_ss_rc_mfr = K_H[3] * (Vd[4]**2 + Vc[0]**2)**0.5

    # Crane on semi-submersible

    ## Lifting to/from: Barge (No load, Single fall reeving)
    VH_semi_b_nl_sfr = K_H[0] * (Vd[2]**2 + Vc[1]**2)**0.5
    ## Lifting to/from: Barge (At rated capacity, Single fall reeving)
    VH_semi_b_rc_sfr = K_H[1] * (Vd[2]**2 + Vc[1]**2)**0.5
    ## Lifting to/from: Barge (No load, Multiple fall reeving)
    VH_semi_b_nl_mfr = K_H[2] * (Vd[2]**2 + Vc[1]**2)**0.5
    ## Lifting to/from: Barge (At rated capacity, Multiple fall reeving)
    VH_semi_b_rc_mfr = K_H[3] * (Vd[2]**2 + Vc[1]**2)**0.5

    ## Lifting to/from: Supply vessel (No load, Single fall reeving)
    VH_semi_sv_nl_sfr = K_H[0] * (Vd[3]**2 + Vc[1]**2)**0.5
    ## Lifting to/from: Supply vessel (At rated capacity, Single fall reeving)
    VH_semi_sv_rc_sfr = K_H[1] * (Vd[3]**2 + Vc[1]**2)**0.5
    ## Lifting to/from: Supply vessel (No load, Multiple fall reeving)
    VH_semi_sv_nl_mfr = K_H[2] * (Vd[3]**2 + Vc[1]**2)**0.5
    ## Lifting to/from: Supply vessel (At rated capacity, Multiple fall reeving)
    VH_semi_sv_rc_mfr = K_H[3] * (Vd[3]**2 + Vc[1]**2)**0.5

    ## Lifting to/from: Sea surface (No load, Single fall reeving)
    VH_semi_ss_nl_sfr = K_H[0] * (Vd[4]**2 + Vc[1]**2)**0.5
    ## Lifting to/from: Sea surface (At rated capacity, Single fall reeving)
    VH_semi_ss_rc_sfr = K_H[1] * (Vd[4]**2 + Vc[1]**2)**0.5
    ## Lifting to/from: Sea surface (No load, Multiple fall reeving)
    VH_semi_ss_nl_mfr = K_H[2] * (Vd[4]**2 + Vc[1]**2)**0.5
    ## Lifting to/from: Sea surface (At rated capacity, Multiple fall reeving)
    VH_semi_ss_rc_mfr = K_H[3] * (Vd[4]**2 + Vc[1]**2)**0.5

    # Crane on FPSO or Drill ship

    ## Lifting to/from: Barge (No load, Single fall reeving)
    VH_ds_b_nl_sfr = K_H[0] * (Vd[2]**2 + Vc[2]**2)**0.5
    ## Lifting to/from: Barge (At rated capacity, Single fall reeving)
    VH_ds_b_rc_sfr = K_H[1] * (Vd[2]**2 + Vc[2]**2)**0.5
    ## Lifting to/from: Barge (No load, Multiple fall reeving)
    VH_ds_b_nl_mfr = K_H[2] * (Vd[2]**2 + Vc[2]**2)**0.5
    ## Lifting to/from: Barge (At rated capacity, Multiple fall reeving)
    VH_ds_b_rc_mfr = K_H[3] * (Vd[2]**2 + Vc[2]**2)**0.5

    ## Lifting to/from: Supply vessel (No load, Single fall reeving)
    VH_ds_sv_nl_sfr = K_H[0] * (Vd[3]**2 + Vc[2]**2)**0.5
    ## Lifting to/from: Supply vessel (At rated capacity, Single fall reeving)
    VH_ds_sv_rc_sfr = K_H[1] * (Vd[3]**2 + Vc[2]**2)**0.5
    ## Lifting to/from: Supply vessel (No load, Multiple fall reeving)
    VH_ds_sv_nl_mfr = K_H[2] * (Vd[3]**2 + Vc[2]**2)**0.5
    ## Lifting to/from: Supply vessel (At rated capacity, Multiple fall reeving)
    VH_ds_sv_rc_mfr = K_H[3] * (Vd[3]**2 + Vc[2]**2)**0.5

    ## Lifting to/from: Sea surface (No load, Single fall reeving)
    VH_ds_ss_nl_sfr = K_H[0] * (Vd[4]**2 + Vc[2]**2)**0.5
    ## Lifting to/from: Sea surface (At rated capacity, Single fall reeving)
    VH_ds_ss_rc_sfr = K_H[1] * (Vd[4]**2 + Vc[2]**2)**0.5
    ## Lifting to/from: Sea surface (No load, Multiple fall reeving)
    VH_ds_ss_nl_mfr = K_H[2] * (Vd[4]**2 + Vc[2]**2)**0.5
    ## Lifting to/from: Sea surface (At rated capacity, Multiple fall reeving)
    VH_ds_ss_rc_mfr = K_H[3] * (Vd[4]**2 + Vc[2]**2)**0.5

    fig, ax = plt.subplots()
    # ---
    # Crane on bottom supported structure
    # Legend: NL: No load; [S,M]FR: [Single,Multiple] fall reeving 
    plt.title('Crane on bottom supported structure')
    #ax.plot(x, VH_f_b_nl_sfr, label="From Barge (NL, SFR)", linewidth=2)
    ax.plot(x, VH_f_b_rc_sfr, label="From Barge (RC, SFR)", linewidth=2)
    #ax.plot(x, VH_f_b_nl_mfr, label="From Barge (NL, MFR)", linewidth=2)
    ax.plot(x, VH_f_b_rc_mfr, label="From Barge (RC, MFR)", linewidth=2)
    #
    #ax.plot(x, VH_f_sv_nl_sfr, label="From Supply vessel (NL, SFR)", linewidth=2)
    ax.plot(x, VH_f_sv_rc_sfr, label="From Supply vessel (RC, SFR)", linewidth=2)
    #ax.plot(x, VH_f_sv_nl_mfr, label="From Supply vessel (NL, MFR)", linewidth=2)
    ax.plot(x, VH_f_sv_rc_mfr, label="From Supply vessel (RC, MFR)", linewidth=2)
    #
    #ax.plot(x, VH_f_ss_nl_sfr, label="From Sea surface (NL, SFR)", linewidth=2)
    ax.plot(x, VH_f_ss_rc_sfr, label="From Sea surface (RC, SFR)", linewidth=2)
    #ax.plot(x, VH_f_ss_nl_mfr, label="From Sea surface (NL, MFR)", linewidth=2)
    ax.plot(x, VH_f_ss_rc_mfr, label="From Sea surface (RC, MFR)", linewidth=2)
    # ---
    # Crane on semi-submersible
    # plt.title('Crane on semi-submersible')
    # ax.plot(x, VH_semi_b_nl_sfr, label="From Barge (NL, SFR)", linewidth=2)
    # ax.plot(x, VH_semi_b_rc_sfr, label="From Barge (RC, SFR)", linewidth=2)
    # ax.plot(x, VH_semi_b_nl_mfr, label="From Barge (NL, MFR)", linewidth=2)
    # ax.plot(x, VH_semi_b_rc_mfr, label="From Barge (RC, MFR)", linewidth=2)
    # #
    # ax.plot(x, VH_semi_sv_nl_sfr, label="From Supply vessel (NL, SFR)", linewidth=2)
    # ax.plot(x, VH_semi_sv_rc_sfr, label="From Supply vessel (RC, SFR)", linewidth=2)
    # ax.plot(x, VH_semi_sv_nl_mfr, label="From Supply vessel (NL, MFR)", linewidth=2)
    # ax.plot(x, VH_semi_sv_rc_mfr, label="From Supply vessel (RC, MFR)", linewidth=2)
    # #
    # ax.plot(x, VH_semi_ss_nl_sfr, label="From Sea surface (NL, SFR)", linewidth=2)
    # ax.plot(x, VH_semi_ss_rc_sfr, label="From Sea surface (RC, SFR)", linewidth=2)
    # ax.plot(x, VH_semi_ss_nl_mfr, label="From Sea surface (NL, MFR)", linewidth=2)
    # ax.plot(x, VH_semi_ss_rc_mfr, label="From Sea surface (RC, MFR)", linewidth=2)
    # ---
    # Crane on FPSO / drill ship
    # plt.title('Crane on FPSO / drill ship')
    # ax.plot(x, VH_ds_b_nl_sfr, label="From Barge (NL, SFR)", linewidth=2)
    # ax.plot(x, VH_ds_b_rc_sfr, label="From Barge (RC, SFR)", linewidth=2)
    # ax.plot(x, VH_ds_b_nl_mfr, label="From Barge (NL, MFR)", linewidth=2)
    # ax.plot(x, VH_ds_b_rc_mfr, label="From Barge (RC, MFR)", linewidth=2)
    # #
    # ax.plot(x, VH_ds_sv_nl_sfr, label="From Supply vessel (NL, SFR)", linewidth=2)
    # ax.plot(x, VH_ds_sv_rc_sfr, label="From Supply vessel (RC, SFR)", linewidth=2)
    # ax.plot(x, VH_ds_sv_nl_mfr, label="From Supply vessel (NL, MFR)", linewidth=2)
    # ax.plot(x, VH_ds_sv_rc_mfr, label="From Supply vessel (RC, MFR)", linewidth=2)
    # #
    # ax.plot(x, VH_ds_ss_nl_sfr, label="From Sea surface (NL, SFR)", linewidth=2)
    # ax.plot(x, VH_ds_ss_rc_sfr, label="From Sea surface (RC, SFR)", linewidth=2)
    # ax.plot(x, VH_ds_ss_nl_mfr, label="From Sea surface (NL, MFR)", linewidth=2)
    # ax.plot(x, VH_ds_ss_rc_mfr, label="From Sea surface (RC, MFR)", linewidth=2)
    # ---
    ax.legend(loc=0)
    ax.set_xlabel('Hsig (m)')
    ax.set_ylabel('Vhmin (m/s)')
    plt.grid(True)
    plt.title("EN13852-1:2013")    
    plt.savefig("Vhmin_en_ds_nl_rc_sfr_mfr.png")
    plt.show()

if __name__ == '__main__':
    print "Plot min.required hoisting velocity:"
    print "Choose a Standard:"
    print " 1 - API Spec 2C, 7th Ed."
    print " 2 - EN13852-1:2013"
    print " 0 - Exit"
    standard = input("Standard to use: ")
    if standard == 1:
        api2c()
    if standard == 2:
        en13852()

November 18, 2015

Unconditional

While fixing an UnboundLocalError in my code today, I realized, I tint my scripts unconsciously with cyclomatic complexity — an avoidable habit I picked up as a young engineer. Back then, time-sharing was still very much a necessity, and so I’d spend much of my time buckling down and performing hand-calculations instead. Conditional problems worsened this, like for instance, referring to a table to pick an option, and then use its data to perform specific calculations. Also, results for ten options, when I needed for one or two, at the time felt both repetitive as well as unnecessary.

Scripts produced from thinking linearly tend to grow verbose is what I’ve come to realize. Whereas, knowing results of cases — I may not readily be interested-in — offers new insights, all sans extra labor. And so, running with an entire dataset, instead of one value contained within, gives me a high now.

Here is an example, in which I am trying to calculate energy absorbing capacity of a non-dented member for a number of end-conditions:

#!/usr/bin/env python
# encoding: utf-8
"""
impactcheck.py -- tube impact check
2013 ckunte
"""
from math import *

# Mechanical properties E and G (both in MPa)
E = 2.1E+5
G = 8000.0

def impactcheck():
    print "Impact check"
    print "D - Tube diameter (mm)"
    print "t - Tube thickness (mm)"
    print "L - Tube span (mm)"
    print "Fy - Yield strength of steel (MPa)"

    D, t, L, Fy = input("Enter D, t, L, Fy: ")
    D, t, L, Fy = float(D), float(t), float(L), float(Fy)

    # Section properties (A, I):
    A_tube = (pi / 4) * (D**2 - (D - 2 * t)**2)
    I_tube = (pi / 64) * (D**4 - (D - 2 * t)**4)

    print "From Design of Welded Structures O. Blodgett, Table 2: Impact formulas"
    print "Case 1: Simply supported concentrated load uniform section."
    print "Case 2: Fixed-ends concentrated load uniform section."
    print "Case 3: Cantilever concentrated load uniform section."
    print "Case 4: Axial tension uniform section."
    print "Case 5: Torsion: Rounded shaft."
    print "Case 6: Simply supported uniform load uniform section."
    print "Case 7: Fixed ends uniform load uniform section."
    print "Case 8: Cantilever uniform load uniform section."
    print "Case 9: Simply supported concentrated load variable section."

    case = input("Enter applicable case number: ")

    # All c values below are in mm (In Blodgett they are in inch)
    c = [4.2342, 6.7742, 2.54, 8.4582]

    if case == 1:
        U = (Fy**2 * I_tube * L) / (6 * E * c[0]**2)
    elif case == 2:
        U = (Fy**2 * I_tube * L) / (6 * E * c[0]**2)
    elif case == 3:
        U = (Fy**2 * I_tube * L) / (6 * E * c[0]**2)
    elif case == 4:
        U = (Fy**2 * A_tube * L) / (2 * E)
    elif case == 5:
        U = (Fy**2 * (D**2 + (D - 2 * t)**2) * A_tube * L) / (4 * G * D**2)
    elif case == 6:
        U = (4 * Fy**2 * I_tube * L) / (15 * E * c[1]**2)
    elif case == 7:
        U = (Fy**2 * I_tube * L) / (10 * E * c[2]**2)
    elif case == 8:
        U = (Fy**2 * I_tube * L) / (10 * E * c[2]**2)
    elif case == 9:
        U = (Fy**2 * I_tube * L) / (3 * E * c[3]**2)
    else:
        print "No available option was selected."

    print "Energy absorbing capacity -- nondented tube: ", U

if __name__ == '__main__':
    impactcheck()

As you can see, most visible parts of this code are all those waffling conditional checks, all for just one user-selected case. Here’s a simpler way, minimum overhead notwithstanding:

#!/usr/bin/env python
# encoding: utf-8
"""
impactcheck.py -- tube impact check
2015 ckunte
"""
from math import *

# Mechanical properties E and G (both in MPa)
E = 2.1E+5
G = 8000.0

def impactcheck():
    print "Impact check"
    print "D - Tube diameter (mm)"
    print "t - Tube thickness (mm)"
    print "L - Tube span (mm)"
    print "Fy - Yield strength of steel (MPa)"
    D, t, L, Fy = input("Enter D, t, L, Fy: ")
    D, t, L, Fy = float(D), float(t), float(L), float(Fy)

    # Section properties (A, I):
    A_tube = (pi / 4) * (D**2 - (D - 2 * t)**2)
    I_tube = (pi / 64) * (D**4 - (D - 2 * t)**4)

    print "From Design of Welded Structures O. Blodgett, Table 2: Impact formulas"
    print "Case 1: Simply supported concentrated load uniform section."
    print "Case 2: Fixed-ends concentrated load uniform section."
    print "Case 3: Cantilever concentrated load uniform section."
    print "Case 4: Axial tension uniform section."
    print "Case 5: Torsion: Rounded shaft."
    print "Case 6: Simply supported uniform load uniform section."
    print "Case 7: Fixed ends uniform load uniform section."
    print "Case 8: Cantilever uniform load uniform section."
    print "Case 9: Simply supported concentrated load variable section."

    # All c values below are in mm (In Blodgett they are in inch)
    c = [4.2342, 6.7742, 2.54, 8.4582]

    U1 = (Fy**2 * I_tube * L) / (6 * E * c[0]**2)
    U4 = (Fy**2 * A_tube * L) / (2 * E)
    U5 = (Fy**2 * (D**2 + (D - 2 * t)**2) * A_tube * L) / (4 * G * D**2)
    U6 = (4 * Fy**2 * I_tube * L) / (15 * E * c[1]**2)
    U7 = (Fy**2 * I_tube * L) / (10 * E * c[2]**2)
    U9 = (Fy**2 * I_tube * L) / (3 * E * c[3]**2)
    U = [U1, U1, U1, U4, U5, U6, U7, U7, U9]

    case = input("Enter applicable case number: ")

    # Data structure index starts with 0 in python, and so:
    i = case - 1

    # Energy for the user-selected case:
    print "Energy absorbing capacity -- nondented tube: ", U[i]

    print "Results for all cases:"
    print U
    pass

if __name__ == '__main__':
    impactcheck()

In the above, instead of if-then-else, I put all cases in to one basket, U, and then produce both user-selected case result, U[i], as well as for the entire list with print U. The fact that this also results in fewer lines of code is the icing. Here are the results:

$ python impactcheck.py                                                 
Impact check
D - Tube diameter (mm)
t - Tube thickness (mm)
L - Tube span (mm)
Fy - Yield strength of steel (MPa)
Enter D, t, L, Fy: 406.4, 15.9, 5000, 248
From Design of Welded Structures O. Blodgett, Table 2: Impact formulas
Case 1: Simply supported concentrated load uniform section.
Case 2: Fixed-ends concentrated load uniform section.
Case 3: Cantilever concentrated load uniform section.
Case 4: Axial tension uniform section.
Case 5: Torsion: Rounded shaft.
Case 6: Simply supported uniform load uniform section.
Case 7: Fixed ends uniform load uniform section.
Case 8: Cantilever uniform load uniform section.
Case 9: Simply supported concentrated load variable section.
Enter applicable case number: 7
Energy absorbing capacity -- nondented tube:  8453297881.03
Results for all cases:
[5069902650.473422, 5069902650.473422, 5069902650.473422, 14282101.356154371, 346717295.90146893, 3169175643.9842114, 8453297881.026284, 8453297881.026284, 2541068961.1612463]

The big picture

Given the number of variables, I could easily keep generating energy absorption capacities, considering each case, one after the other, and I would still not have a useful realization to stand back and see what causes increase in energy absorption capacity. Instead, here’s what I did: I nailed down Fy (to 248MPa, mild steel) and length (by setting it at 5.0m, which is typical for a boat landing king post, where operational or accidental impact is typically expected), and plot all cases per D/t (slenderness) ratio (from 20 to 120).

I’ve omitted (non-bending) cases 4 & 5 in the above, because they are not good candidates for comparison with the others.

Some conclusions

The plots all look the same, except for Energy (MJ) in ordinates, from which the following can be observed:

  1. Capacity increases when D/t (slenderness ratio) is lower, and
  2. Capacity increases when impact load is uniform over tube length
  3. Fixed and cantilever end-conditions (Case 7 & 8) perform better than simply-supported (Case 6).

From item 1 above, it’s pretty obvious that section compactness matters when it comes to resisting impact. Item 2 indicates that the more contact area there is during impact, the more the tube has the ability to absorb energy. More realistic of impact, however are the first three cases (for concentrated load), represented by the blue line, when there is less control over contact area.

Here is the code used to generate each plot above.

#!/usr/bin/env python
# encoding: utf-8
"""
ecap.py: Energy absorption capacity of non-dented tube
2015 ckunte 
"""
import numpy as np
import matplotlib.pyplot as plt

def main():
    # Mechanical properties (steel) in N/m^2:
    E = 2.1E+11
    G = 8.0E+9
    # Yield strength (N/m^2)
    Fy = [248.0E+6, 345.0E+6]
    Fy = Fy[0]

    # x corresponds to tube diameter, which we are going to 
    # vary with a constant D / t
    x = np.linspace(0.2, 0.9)
    r = [20.0, 50.0, 80.0, 120.0]
    t = (x / r[0])
    L = 5.0

    # Section properties
    A = np.pi * (x - t) * t
    I = (np.pi / 64.0) * ((x**4 - (x - (2 * t))**4))

    # Energy absorption capacity of non-dented tube (J)
    # All c values below are in m (In Blodgett they are in inch)
    c = [0.0042, 0.0068, 0.0025, 0.0085]
    U1 = (Fy**2 * I * L) / (6 * E * c[0]**2)
    U6 = (4 * Fy**2 * I * L) / (15 * E * c[1]**2)
    U7 = (Fy**2 * I * L) / (10 * E * c[2]**2)
    U9 = (Fy**2 * I * L) / (3 * E * c[3]**2)
    U = [U1, U6, U7, U9]

    fig, ax = plt.subplots()
    ax.plot(x, U[0] / 1E6, label="Cases 1-3", linewidth=2)
    ax.plot(x, U[1] / 1E6, label="Case 6", linewidth=2)
    ax.plot(x, U[2] / 1E6, label="Case 7 & 8", linewidth=2)
    ax.plot(x, U[3] / 1E6, label="Case 9", linewidth=2)
    ax.legend(loc=0)
    ax.set_xlabel('Tube diameter, D (m) with D/t = %s'%r[0])
    ax.set_ylabel('Energy (MJ)')
    plt.show()

if __name__ == '__main__':
    main()

October 18, 2015

Testing code

Years ago, enamored by the theoretical purity of HTML, I wrote a couple of WordPress plugins, viz., extract-blockquote-info and small-caps. Using Dunstan Orchard’s javascript, the blockquote plugin did neat things — like extracting cite and title details from the blockquote element to display attribution and source link below the quote; while small-caps searched and replaced all capitalized words into small caps on the fly.

Plugins like these were obviously never meant for mainstream users, and those that do care about such are often capable of tending to keep them working, if need be. As for me, it has been many years since I stopped using WordPress for my own use. And so as I had no rig to test these version after version locally, I thought these were ideal candidates for automated virtual testing.

GitHub offers Travis CI as a service, and so I put that to use. Being a novice to code testing, I used the instructions kindly shared by Bryan Petty as a scaffold to begin. The nice thing about it is that it looks for the plugin from WordPress plugin directory and tests it for the latest version, and fallback versions of choice. So, even though on the official WordPress directory it says compatibility of each of these plugins has “not enough data”, these plugins appear to pass tests for a set of WordPress and PHP versions. I’ve updated the repos with build status badges. These would auto-update in future when tests are either requested manually (by clicking on Test service from within Settings → Webhooks & services → Travis CI), or upon every new push to the repository.

Another area I was interested was testing my unruly scripts for various versions of python (2.x), and so, I spent the afternoon running tests both offline using pylint, which I really like, before unleashing Travis CI over my repositories. Here’s a simple config. file (.travis.yml) using nose for running tests.

language: python
python:
    - 2.6
    - 2.7
script:
    - nosetests