Peter Rice: An Engineer Imagines

Peter Rice is possibly the best known structural engineer of the 20th century – projects he worked on are some of the best known today: the Sydney Opera House, Beauborg (Pompidou Centre, Paris), Pavilion of the Future at the Seville Expo, Lloyds of London.

It is hard to work out whether is achieves mastery of each material, or is just clever enough to push his way to achieve remarkable and interesting results in any problem he chooses. I hope it is the former though the quotes sometimes suggest otherwise. ‘A juggernaught, crushing the obstacles of practicality and cost, for us to build what we liked’ and someone who sees the build world as a place for exploitation, a place that ‘will have to become more complex to absorb our energies and occupy us fully’? Or is he someone who shares Ove Arup’s position on man’s position within the environment, and how the man’s power should be best harnessed.

Rice never mentions real issues with cost or time in his projects, a constant backdrop to almost any modern work. It seems the engineering world has ‘tightened up’ a lot since his day. However, I don’t think his power to place himself at the center of some of the most exciting projects, largely through his relationships with several of the leading architects of his day, should be underestimated (also, the book is supposed to be a celebration of engineering life). Looking at the cross section through the roof at the Menil art gallery there is a sumptuous level of detail that I cannot believe would survive a modern project – though the situation of the project (wealthy and interested private client, influential and trusting architect) is probably more important than its time period. Rice’s ability to persuade others to work in a new material was clearly impressive – I am not convinced that he really needed to work in cast iron for the Manil Gallery. A similar prediciment occurs for the Pavillion of the Future – a little fussy in steel and stone – though he certainly met the ‘spectacular’ part of the design brief. And again on the Pompidou center, was an optimisation of column thickness and the use of unusual centrifugally spun steel sections really beneficial?

Rice really seems to design from the ‘material up’ – he takes all his details from the particular abilities of each material and often seems to work outwards from there. Glass takes carefully designed point loads from its strength – so hang it from the top corners in sheets and support those against the wind with a separate structure. Polycarbonate has flexibility and little strength – so contain it with large areas in contact with glue adhering them, and carefully clamped details – and limit its structural role to a shear link between timber chords.

Do read, though skip the chapter on horse racing.

Commute Length Analysis: user input with help from Google maps, a pretty version

It has been a long time since I made this, but it was an interesting exploration at the time – and it has languished on a back page of this site for too long.

Userform connecting google maps to a survey

This was made as an abortive attempt to improve my work on a ‘commute survey’ – which used a list of employee addresses to assess the change in commute times due to a move of office. This version shows the route google maps has calculated between the office at the time, the time expected, and allows you to press submit to put your response into a google sheet for further analysis.

In the end, I wasn’t fast enough to get this working well before I needed to start gathering results, and wasn’t able to overcome the barriers to doing many, many requests to google maps for journey details (about 50 responders, with a couple of commute modes each [people were assumed to walk or cycle if it were close enough – in reality this could have been scrapped], return trips, and multiple potential new offices). This lack of ability to collect lots of points also scuppered the idea of making a ‘commute time heat map’ of many locations through London.

You can have a look here:

Laban Dance Icosahedron: a prototype

Rudolf Laban was a dancer, choreographer and movement theorist ( One of Laban’s most enduring contributions to dance is his notation – this notation acts as a way of recording movement and communicating dance, much like a musical score communicates between a composer and a musician.

One part of this notation indicates how the dancer should move in space – is your body extended up and back and right, or low and forward and left? Other parts of the notation include the part of the body that is moving, how long the movement goes on for and the dynamic qualities of the movement (

Much as cellist might practice their scales to help learn the position of their hands, a dancer needs to increase their ability to sense where their body is in space – they must hone their proprioception. Sportspeople do this all the time – when I trampolined we talked about the angle of kickout in terms of time (“a twelve o’clock kickout” means someone exits a shape directly towards the ceiling) and we would hone this by aiming for some position, then feeding back via our coach or a video to do better next time.

Improving your proprioception is made much easier when you have immediate feedback. Fortunately, the positions considered in Laban dance, the points you aim for, which are the corners of three planes that pass through you as you stand, coincide with the vertices of an icosahedron. If you stand in the center of an icosahedron and point to its vertices, you are pointing towards the positions shown in Laban notation, and will be better able to use the notation, and other aspects of the theory, to improve your dancing.

Laban dance teachers and students have been dancing inside icosahedrons as a training tool since the creation of the theory, but their complex nature has meant these icosahedrons tend to be permanent, expensive fixtures – not a lightweight training tools that can be taken to beginners classes, which is where they are likely to have the greatest value. So there is a need for laban dance teachers to be able to carry icosahedrons to lessons, erect the icosahedron, use it during their lesson, deconstruct it and carry them away again.

The icosahedron seems like quite a complex shape to produce – but why? It is completely repetitive and platonic, once you have one rod design, one node design and a way to fit them together, the problem is solved. All the thought must go into the creation of the joints if a ‘rod and node’ solution is pursued. It is also possible to move the complexity around the model – using a tensegrity form to absorb the nodes into the rods and flexible tension members, using a series of face pieces that join at the edges. These alternative approaches were explored – with really interesting and unconservative results. Sadly, perhaps, none were good enough to displace an improved ‘node and rod’ approach, which shares much with what has gone before.

The complexity of the nodes can be largely eliminated by using a magnetic connection – it provides many benefits in this application: it snaps into positon so can be attached without need for precision or force, it cannot transmit enough force to allow damage to the rest of the structure, it can be connected and disconnected without wear or damage and allows extreme geometric simplicity – easing manufacture. The drawbacks of a magnetic connection: low strength, especially little moment capacity, and absolutely no ductility. All of these forthcomings can be designed around by choosing strong magnets and making sure the nodes are built accurately to avoid moments at the connections. A design of embedded magnets in wooden spheres, with embedded magnets in wooden rods was chosen. The extra magnet in each sphere is for holding extra training ribbons in position.

A full set of drawing and manufacturing instructions to follow.

Prototype node with 4 of 5 rods attached, addtional central magnet for ribbon attachment

Node used in icosahedron, with magnet for ribbon in center. Image: Peter Clarkson of Thomas Matthews.

Constructed prototype icosahedron, note that is rests on one edge so two extra feet help to keep it balanced. Image: Peter Clarkson of Thomas Matthews

Deconstructing the icosahedron by removing a node. Image: Charlie Cornish of Expedition

First experiments with virtual Harmonograph

The first experiments are working well, although they are certainly a bit untidy looking – different projections and more consistent behaviours of pendulums are needed.

Individual harmonograph

Image with individual pendulum routes and the harmonograph beneath.

Script taking the csv files of several pendulums into one harmonograph plot, using theory from previous post:

 import numpy
 import matplotlib
 import pandas
 import matplotlib.pyplot as plt
 from mpl_toolkits.mplot3d import Axes3D</pre>
course1 = pandas.read_csv('C:\Users\david\Desktop\spherical_pendulum_0.98m.csv')
course2 = pandas.read_csv('C:\Users\david\Desktop\spherical_pendulum_0.99m.csv')
course3 = pandas.read_csv('C:\Users\david\Desktop\spherical_pendulum_1.00m.csv')

#check each of course1 course2 and course3 have the same number of timesteps
if (course1.shape[0]==course2.shape[0]) and (course2.shape[0]==course3.shape[0]):
print("all the same shape, good news!")
if (course1.shape[1]==course2.shape[1]) and (course2.shape[1]==course3.shape[1]):
print("same number of columns, good news!")
if list(course1.columns.values) == list(course2.columns.values) and list(course2.columns.values) == list(course3.columns.values):
print("same column titles, good news!")

numberOfTimeSteps = course1.shape[0]
timeSteps = numpy.arange(numberOfTimeSteps)
columnTitles = ['t', 'x1', 'y1', 'z1', 'x2', 'y2', 'z2', 'x3', 'y3', 'z3', 'x', 'y', 'z']
constants = {'lambda1':0.8, 'lambda2':0.8, 'lambda3':0.8, 'r1':0.8, 'r2':0.8, 'r3':0.8, 'xsupport1':0.0,'ysupport1':0.0,'zsupport1':0.0,'xsupport2':1.0,'ysupport2':0.0,'zsupport2':0.0,'xsupport3':0.5,'ysupport3':0.86,'zsupport3':0.0,}
harmonographCourse = pandas.DataFrame(numpy.zeros([numberOfTimeSteps,13],dtype=long), index = timeSteps, columns = columnTitles)

harmonographCourse['t'] = course1['t']
harmonographCourse['x1'] = constants['lambda1']*course1['x']+constants['xsupport1']
harmonographCourse['y1'] = constants['lambda1']*course1['y']+constants['ysupport1']
harmonographCourse['z1'] = constants['lambda1']*course1['z']+constants['zsupport1']
harmonographCourse['x2'] = constants['lambda2']*course2['x']+constants['xsupport2']
harmonographCourse['y2'] = constants['lambda2']*course2['y']+constants['ysupport2']
harmonographCourse['z2'] = constants['lambda2']*course2['z']+constants['zsupport2']
harmonographCourse['x3'] = constants['lambda3']*course3['x']+constants['xsupport3']
harmonographCourse['y3'] = constants['lambda3']*course3['y']+constants['ysupport3']
harmonographCourse['z3'] = constants['lambda3']*course3['z']+constants['zsupport3']

xsupport1 = numpy.array([constants['xsupport1'],constants['ysupport1'],constants['zsupport1']])

for timeStep in timeSteps:
x1 = numpy.array([harmonographCourse.loc[timeStep,'x1'],harmonographCourse.loc[timeStep,'y1'],harmonographCourse.loc[timeStep,'z1']])
x2 = numpy.array([harmonographCourse.loc[timeStep,'x2'],harmonographCourse.loc[timeStep,'y2'],harmonographCourse.loc[timeStep,'z2']])
x3 = numpy.array([harmonographCourse.loc[timeStep,'x3'],harmonographCourse.loc[timeStep,'y3'],harmonographCourse.loc[timeStep,'z3']])
vector_a = x2 - x1
vector_c = numpy.cross((x2-x1),(x3-x1))
vector_b = numpy.cross((x2-x1),vector_c)
norm_vector_a = vector_a / numpy.sqrt(numpy.sum(numpy.square(vector_a)))
norm_vector_b = vector_b / numpy.sqrt(numpy.sum(numpy.square(vector_b)))
norm_vector_c = vector_c / numpy.sqrt(numpy.sum(numpy.square(vector_c)))
d =,norm_vector_a)
i =,norm_vector_a)
j =,norm_vector_b)
a = (numpy.square(constants['r1']) - numpy.square(constants['r2']) + numpy.square(d)) / (2*d)
b = (numpy.square(constants['r1']) - numpy.square(constants['r3']) + numpy.square(i) + numpy.square(j))/(2*j) - i/j*a
c = -1*numpy.sqrt(numpy.square(constants['r1'])-numpy.square(a)-numpy.square(b))
x = xsupport1 + x1 + a*norm_vector_a + b*norm_vector_b + c*norm_vector_c
harmonographCourse.loc[timeStep,'x'] = x[0]
harmonographCourse.loc[timeStep,'y'] = x[1]
harmonographCourse.loc[timeStep,'z'] = x[2]
print harmonographCourse

fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot(harmonographCourse['x'], harmonographCourse['y'], harmonographCourse['z'], label = 'harmonograph')
ax.plot(harmonographCourse['x1'], harmonographCourse['y1'], harmonographCourse['z1'], label = '0.98m pendulum')
ax.plot(harmonographCourse['x2'], harmonographCourse['y2'], harmonographCourse['z2'], label = '0.99m pendulum')
ax.plot(harmonographCourse['x3'], harmonographCourse['y3'], harmonographCourse['z3'], label = '1.00m pendulum')
ax.set_xlabel('x value')
ax.set_ylabel('y value')
ax.set_zlabel('z value')

Harmonograph from spherical pendulums

Spherical pendulums produce some attractive forms on their own, but they have been applied together for a long time now (it is quite a Victorian thing apparently) to create compound shapes. The machines that make these shapes are called harmonographs, so called due to their use in helping to visualise musical harmonies. As far as I know, Harmonographs have only ever been curiosities, without any real practical application beyond the production of attractive postcards.

Harmonographs use pendulums in a variety of configurations to produce different shapes, dragging a pen over paper to produce a shape. Some use simple pendulums in combination with spherical pendulums. In every case, the complexity and beauty of the form produced is much greater than the crude contraption it has come from.

A few harmonographs have been produced using light and long exposure and produce very beautiful images:

One artist (artist collective) has already done some of this before, though without interaction between the physical and the digital. I hear they are a big deal, so very happy to follow in their footsteps.

Someone online has made a 3D harmonograph using python (as I hope to do very soon!) but has just used sinusoidal patterns rather than any proper decay functions to try to model a spherical pendulum – I am not even sure they have used a model of a spherical pendulum rather than a series of simple pendulums. I wonder how mine might be different.

Solution for a simple harmonograph’s motion is here:

Download (PDF, 516KB)

Spherical Pendulums

Finding the governing equations, and forming a strategy for creating a really basic solver given some initial conditions:

Download (PDF, 122KB)

Then using this code to draw some pendulums and their movements through time:

For a simple pendulum, plotting its x and y coordinated with time, without any air resistance:


Then including some air resistance, see pdf for detail, shows a roughly exponential decay of the amplitude of the oscillations over time, as would be expected:


The same approach is then taken with the more complicated spherical pendulum, which, with no air resistance considered, has a path that looks like this:


Adding in a small air resistance of alpha=0.01 (see pdf for expanation), produces a slightly different path, as would be expected. The ‘no air resistance path’ is in blue, the ‘small air resistance path’ is in green:


Following the green path for a longer period of time, we see the following pattern (now plotted in blue):


Setting alpha=0.05 leads to the following difference from the no air resistance path (no air resistance in blue, with air resistance in green):


Following this green path for a longer period of time we see the following pattern (now plotted in blue rather than green):


Perhaps most nicely, the later, gentle, spiraling behaviour is clear here.


The code used to generate these plots was as follows, for detail on how it was derived see the pdf at the top of the post:

The simple pendulum:

import numpy
import matplotlib
import pandas
import matplotlib.pyplot as plt
import time
from mpl_toolkits.mplot3d import Axes3D

def inputInitialConditions():
 print("installising arrays and inputting initial conditions")
 t_initial = 0
 theta_initial = numpy.pi/5
 thetadot_initial = 0
 psi_initial = 0
 psidot_initial = 4
 numberOfTimeSteps = 20000
 lengthOfTimeStep = 0.001
 m = 1
 l = 1
 g = 9.81
 alpha = 0.04
 timeSteps = numpy.arange(numberOfTimeSteps)
 columnTitles = ['t', 'theta', 'thetadot', 'thetadotdot', 'psi', 'psidot', 'psidotdot', 'x', 'y', 'z']
 constants = {'m':m, 'l':l, 'g':g, 'lengthOfTimeStep':lengthOfTimeStep, 'numberOfTimeSteps':numberOfTimeSteps, 't_initial':t_initial, 'alpha':alpha}
 course = pandas.DataFrame(numpy.zeros([numberOfTimeSteps,10],dtype=long), index = timeSteps, columns = columnTitles)
 course.loc[0] = [t_initial, theta_initial,thetadot_initial,0,psi_initial,psidot_initial,0,0,0,0]
 return [course, constants, timeSteps]

def completeInitialTimeStep(course, constants, timeSteps):
 g = constants['g']
 l = constants['l']
 m = constants['m']
 alpha = constants['alpha']
 t_initial = constants['t_initial']
 theta = course.loc[0,'theta']
 psi = course.loc[0,'psi']
 if numpy.absolute(theta) < 0.000001:
 theta = 0.000001
 print 'Important Warning: Very small theta on timestep 0 generating errors, use simple pendulum program'
 if numpy.absolute(theta) > numpy.pi/2:
 theta = numpy.pi/2 
 print 'Important Warning: Very large theta, potential errors or unphysical behaviour'
 sintheta = numpy.sin(theta)
 costheta = numpy.cos(theta)
 tantheta = sintheta / costheta
 sinpsi = numpy.sin(psi)
 cospsi = numpy.cos(psi)
 psidot = course.loc[0, 'psidot']
 thetadot = course.loc[0,'thetadot']

 course.loc[0,'thetadotdot'] = ((psidot**2)*costheta-g/l)*sintheta-alpha*l/m*sintheta*numpy.absolute(thetadot)*thetadot
 course.loc[0,'psidotdot'] = -2*psidot*thetadot/tantheta-alpha*l/m*numpy.absolute(psidot)*psidot
 course.loc[0,'t'] = t_initial
 course.loc[0,'x'] = l*sintheta*cospsi
 course.loc[0,'y'] = l*sintheta*sinpsi
 course.loc[0,'z'] = l*costheta
 return course

def calculatePendulumCourse(course, constants, timeSteps):
 g = constants['g']
 l = constants['l']
 m = constants['m']
 alpha = constants['alpha']
 lengthOfTimeStep = constants['lengthOfTimeStep']
 previoustheta = course.loc[0,'theta']
 previousthetadot = course.loc[0, 'thetadot']
 previousthetadotdot = course.loc[0, 'thetadotdot']
 previouspsi = course.loc[0,'psi']
 previouspsidot = course.loc[0,'psidot']
 previouspsidotdot = course.loc[0,'psidotdot']
 for timeStep in timeSteps[1:]:
 theta = previoustheta + (previousthetadot * lengthOfTimeStep) + ((previousthetadotdot * lengthOfTimeStep**2)/2)
 course.loc[timeStep, 'theta'] = theta
 sintheta = numpy.sin(theta)
 costheta = numpy.cos(theta)
 tantheta = numpy.tan(theta)
 if numpy.absolute(theta) < 0.000001:
 sintheta = 0.000001
 tantheta = sintheta
 print 'Important Warning: Very small theta on timestep' + str(timeStep)
 psi = course.loc[timeStep, 'psi'] = previouspsi + (previouspsidot * lengthOfTimeStep) + ((previouspsidotdot * lengthOfTimeStep**2)/2)
 course.loc[timeStep, 'psi'] = psi
 sinpsi = numpy.sin(psi)
 cospsi = numpy.cos(psi)
 if numpy.absolute(psi) < 0.000001:
 sinpsi = 0.000001
 print 'Important Warning: Very small psi on timestep' + str(timeStep)
 tanpsi = sinpsi / cospsi 
 thetadot = previousthetadot + previousthetadotdot * lengthOfTimeStep
 course.loc[timeStep, 'thetadot'] = thetadot
 psidot = previouspsidot + previouspsidotdot * lengthOfTimeStep
 course.loc[timeStep, 'psidot'] = psidot 
 thetadotdot = ((psidot**2)*costheta-g/l)*sintheta-alpha*l/m*numpy.absolute(thetadot)*thetadot
 course.loc[timeStep,'thetadotdot'] = thetadotdot
 psidotdot = -2*psidot*thetadot/tantheta-alpha*l/m*numpy.absolute(psidot)*psidot*sintheta
 course.loc[timeStep,'psidotdot'] = psidotdot
 x = l*sintheta*cospsi
 y = l*sintheta*sinpsi
 z = l*costheta
 course.loc[timeStep,'x'] = x
 course.loc[timeStep,'y'] = y
 course.loc[timeStep,'z'] = z
 previoustheta = theta
 previousthetadot = thetadot
 previousthetadotdot = thetadotdot
 previouspsi = psi
 previouspsidot = psidot
 previouspsidotdot = psidotdot
 return course
def plotPendulumCourse(course):
 xs = course['x']
 ys = course['y']
 zs = course['z']
 fig = plt.figure()
 ax = fig.gca(projection='3d')
 ax.plot(xs, ys, zs)

def main():
 print("Starting spherical pendulum")
 t0 = time.time()
 [course,constants,timeSteps] = inputInitialConditions()
 course = completeInitialTimeStep(course, constants, timeSteps)
 course = calculatePendulumCourse(course, constants, timeSteps)
 print (time.time()-t0)
 print("Completed spherical pendulum")


The comparison plot:

import numpy
import matplotlib
import pandas
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

course0 = pandas.read_csv('C:\Users\david\Desktop\spherical_pendulum_no_air_resistance.csv')
course1 = pandas.read_csv('C:\Users\david\Desktop\spherical_pendulum_0.01_air_resistance.csv')
course5 = pandas.read_csv('C:\Users\david\Desktop\spherical_pendulum_0.05_air_resistance.csv')
course10 = pandas.read_csv('C:\Users\david\Desktop\spherical_pendulum_0.1_air_resistance.csv')

fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot(course0['x'], course0['y'], course0['z'], label = 'alpha = 0')
ax.plot(course1['x'], course1['y'], course1['z'], label = 'alpha = 0')
ax.plot(course5['x'], course5['y'], course5['z'], label = 'alpha = 0')
ax.plot(course10['x'], course10['y'], course10['z'], label = 'alpha = 0')
ax.set_xlabel('x value')
ax.set_ylabel('y value')
ax.set_zlabel('z value')

The world is inside out


Another day, another door on the tube. But, Donald J. Trump is now president elect of the United States, so I can’t be more odd than the median(ish) American.

The inside out

Door on the tube

Today I carried an ebay-ed door across London. The second door can wait, my forearms hurt.

But, so many people engaged with it – mostly with jokes, knocking on it, or making up stories as to why it was there.

There was something about this door, ripped from its doorway, that was strange and intruiging – the mundanity heightening the strangeness.

I agree, it is an unsettling thing to find. The inside has been taken out: literally – something pinned down has escaped, as a small insight into personal taste that is usually left unshown, and as something that brings forward doors other people have known.

Everyone liked doorways, not everyone liked the door, or doors.

Getting started with Grasshopper, working with set data

Sets are a subset of lists in grasshopper, with some useful restrictions, though on the whole their usefulness is quite limited.

Sets are limited to more primitive data types, such as numbers, strings and vectors, where there is an easy test for equality, it does not allow more complex objects like curves and breps. A set does not allow the same element to appear more than once, though note that items that look similar might be identical, for example an integer and float, with the same value. However, they are not the same.

There are many functions in the sets tab in grasshopper that seem better suited to lists – for example components that assume there might be identical elements in a set.

An introduction to the different containers for data available in grasshopper is here:

Download (PDF, 621KB)