Oregon Curriculum Network
A cube is composed of 24 identical not-regular tetrahedrons, each with a corner at the cube's center, an edge from cube's center to a face center, and two more to adjacent cube corners on that face, defining six edges in all (Fig. 1).
If we define the cube's edges to be √2 then the whole cube would have volume √2 √2 √2 in XYZ units.
However, in IVM units, the very same cube has a volume of 3, owing to the differently-shaped volume unit, a tetrahedron of edges 2, inscribed in this same cube. Fig. 986.210 from Synergetics:
Those lengths would be in R-units, where R is the radius of a unit sphere. In D-units, twice as long (D = 2R), the tetrahedron has edges 1 and the cube has edges √2/2.
By XYZ we mean the XYZ coordinate system of René Descartes (1596 – 1650).
By IVM we mean the "octet-truss", a space-frame consisting of tetrahedrons and octahedrons in a space-filling matrix, with twice as many tetrahedrons as octahedrons.
The tetrahedron and octahedron have relative volumes of 1:4. The question then becomes, how to superimpose the two.
The canonical solution is to start with unit-radius balls (spheres) of radius R. R = 1 in other words, whereas D, the diameter, is 2. Alternatively, we may set D = 1 and R = 0.5, keeping the same 2:1 ratio for D:R.
The XYZ cube has edges R, whereas the IVM tetrahedron has edges D. That relative sizing convention brings their respective volumes fairly close together, with the cube's volume exceeding the tetrahedron's by about six percent.
In :import math xyz_volume = math.sqrt(2)**3 ivm_volume = 3 print("XYZ units:", xyz_volume) print("IVM units:", ivm_volume) print("Conversion constant:", ivm_volume/xyz_volume)
XYZ units: 2.8284271247461907 IVM units: 3 Conversion constant: 1.060660171779821
The Python code below encodes a Tetrahedron type based solely on its six edge lengths. The code makes no attempt to determine the consequent angles.
A complicated volume formula, mined from the history books and streamlined by mathematician Gerald de Jong, outputs the volume of said tetrahedron in both IVM and XYZ units.
In :from math import sqrt as rt2 from qrays import Qvector, Vector R =0.5 D =1.0 S3 = pow(9/8, 0.5) root2 = rt2(2) root3 = rt2(3) root5 = rt2(5) root6 = rt2(6) PHI = (1 + root5)/2.0 class Tetrahedron: """ Takes six edges of tetrahedron with faces (a,b,d)(b,c,e)(c,a,f)(d,e,f) -- returns volume in ivm and xyz units """ def __init__(self, a,b,c,d,e,f): self.a, self.a2 = a, a**2 self.b, self.b2 = b, b**2 self.c, self.c2 = c, c**2 self.d, self.d2 = d, d**2 self.e, self.e2 = e, e**2 self.f, self.f2 = f, f**2 def ivm_volume(self): ivmvol = ((self._addopen() - self._addclosed() - self._addopposite())/2) ** 0.5 return ivmvol def xyz_volume(self): xyzvol = rt2(8/9) * self.ivm_volume() return xyzvol def _addopen(self): a2,b2,c2,d2,e2,f2 = self.a2, self.b2, self.c2, self.d2, self.e2, self.f2 sumval = f2*a2*b2 sumval += d2 * a2 * c2 sumval += a2 * b2 * e2 sumval += c2 * b2 * d2 sumval += e2 * c2 * a2 sumval += f2 * c2 * b2 sumval += e2 * d2 * a2 sumval += b2 * d2 * f2 sumval += b2 * e2 * f2 sumval += d2 * e2 * c2 sumval += a2 * f2 * e2 sumval += d2 * f2 * c2 return sumval def _addclosed(self): a2,b2,c2,d2,e2,f2 = self.a2, self.b2, self.c2, self.d2, self.e2, self.f2 sumval = a2 * b2 * d2 sumval += d2 * e2 * f2 sumval += b2 * c2 * e2 sumval += a2 * c2 * f2 return sumval def _addopposite(self): a2,b2,c2,d2,e2,f2 = self.a2, self.b2, self.c2, self.d2, self.e2, self.f2 sumval = a2 * e2 * (a2 + e2) sumval += b2 * f2 * (b2 + f2) sumval += c2 * d2 * (c2 + d2) return sumval def make_tet(v0,v1,v2): """ three edges from any corner, remaining three edges computed """ tet = Tetrahedron(v0.length(), v1.length(), v2.length(), (v0-v1).length(), (v1-v2).length(), (v2-v0).length()) return tet.ivm_volume(), tet.xyz_volume() tet = Tetrahedron(D, D, D, D, D, D) print(tet.ivm_volume())
make_tet function takes three vectors from a common corner, in terms of vectors with coordinates, and computes the remaining missing lengths, thereby getting the information it needs to use the Tetrahedron class as before.
In :import unittest from qrays import Vector, Qvector class Test_Tetrahedron(unittest.TestCase): def test_unit_volume(self): tet = Tetrahedron(D, D, D, D, D, D) self.assertEqual(tet.ivm_volume(), 1, "Volume not 1") def test_e_module(self): e0 = D e1 = root3 * PHI**-1 e2 = rt2((5 - root5)/2) e3 = (3 - root5)/2 e4 = rt2(5 - 2*root5) e5 = 1/PHI tet = Tetrahedron(e0, e1, e2, e3, e4, e5) self.assertTrue(1/23 > tet.ivm_volume()/8 > 1/24, "Wrong E-mod") def test_unit_volume2(self): tet = Tetrahedron(R, R, R, R, R, R) self.assertAlmostEqual(float(tet.xyz_volume()), 0.117851130) def test_phi_edge_tetra(self): tet = Tetrahedron(D, D, D, D, D, PHI) self.assertAlmostEqual(float(tet.ivm_volume()), 0.70710678) def test_right_tetra(self): e = pow((root3/2)**2 + (root3/2)**2, 0.5) # right tetrahedron tet = Tetrahedron(D, D, D, D, D, e) self.assertAlmostEqual(tet.xyz_volume(), 1) def test_quadrant(self): qA = Qvector((1,0,0,0)) qB = Qvector((0,1,0,0)) qC = Qvector((0,0,1,0)) tet = make_tet(qA, qB, qC) self.assertAlmostEqual(tet, 0.25) def test_octant(self): x = Vector((0.5, 0, 0)) y = Vector((0 , 0.5, 0)) z = Vector((0 , 0 , 0.5)) tet = make_tet(x,y,z) self.assertAlmostEqual(tet, 1/6, 5) # good to 5 places def test_quarter_octahedron(self): a = Vector((1,0,0)) b = Vector((0,1,0)) c = Vector((0.5,0.5,root2/2)) tet = make_tet(a, b, c) self.assertAlmostEqual(tet, 1, 5) # good to 5 places def test_xyz_cube(self): a = Vector((0.5, 0.0, 0.0)) b = Vector((0.0, 0.5, 0.0)) c = Vector((0.0, 0.0, 0.5)) R_octa = make_tet(a,b,c) self.assertAlmostEqual(6 * R_octa, 1, 4) # good to 4 places def test_s3(self): D_tet = Tetrahedron(D, D, D, D, D, D) a = Vector((0.5, 0.0, 0.0)) b = Vector((0.0, 0.5, 0.0)) c = Vector((0.0, 0.0, 0.5)) R_cube = 6 * make_tet(a,b,c) self.assertAlmostEqual(D_tet.xyz_volume() * S3, R_cube, 4) def test_martian(self): p = Qvector((2,1,0,1)) q = Qvector((2,1,1,0)) r = Qvector((2,0,1,1)) result = make_tet(5*q, 2*p, 2*r) self.assertAlmostEqual(result, 20, 7) def test_phi_tet(self): "edges from common vertex: phi, 1/phi, 1" p = Vector((1, 0, 0)) q = Vector((1, 0, 0)).rotz(60) * PHI r = Vector((0.5, root3/6, root6/3)) * 1/PHI result = make_tet(p, q, r) self.assertAlmostEqual(result, 1, 7) def test_phi_tet_2(self): p = Qvector((2,1,0,1)) q = Qvector((2,1,1,0)) r = Qvector((2,0,1,1)) result = make_tet(PHI*q, (1/PHI)*p, r) self.assertAlmostEqual(result, 1, 7) def test_phi_tet_3(self): T = Tetrahedron(PHI, 1/PHI, 1.0, root2, root2/PHI, root2) result = T.ivm_volume() self.assertAlmostEqual(result, 1, 7) def test_koski(self): a = 1 b = PHI ** -1 c = PHI ** -2 d = (root2) * PHI ** -1 e = (root2) * PHI ** -2 f = (root2) * PHI ** -1 T = Tetrahedron(a,b,c,d,e,f) result = T.ivm_volume() self.assertAlmostEqual(result, PHI ** -3, 7) a = Test_Tetrahedron() R =0.5 D =1.0 suite = unittest.TestLoader().loadTestsFromModule(a) unittest.TextTestRunner().run(suite)
............... ---------------------------------------------------------------------- Ran 15 tests in 0.025s OKOut:<unittest.runner.TextTestResult run=15 errors=0 failures=0>
The above tetrahedron has a=2, b=2, c=5, for a volume of 20. The remaining three lengths have not been computed as it's sufficient to know only a, b, c if the angles between them are those of the regular tetrahedron.
That's how IVM volume is computed: multiply a b c from a regular tetrahedron corner, then "close the lid" to see the volume.
In :a = 2 b = 4 c = 5 d = 3.4641016151377544 e = 4.58257569495584 f = 4.358898943540673 tetra = Tetrahedron(a,b,c,d,e,f) print("IVM volume of tetra:", round(tetra.ivm_volume(),5))
IVM volume of tetra: 40.0
Lets define a MITE, one of these 24 identical space-filling tetrahedrons, with reference to D=1, R=0.5, as this is how our Tetrahedron class is calibrated. The cubes 12 edges will all be √2/2.
Edges 'a' 'b' 'c' fan out from the cube center, with 'b' going up to a face center, with 'a' and 'c' to adjacent ends of the face's edge.
From the cube's center to mid-face is √2/4 (half an edge), our 'b'. 'a' and 'c' are both half the cube's body diagonal of √(3/2)/2 or √(3/8).
Edges 'd', 'e' and 'f' define the facet opposite the cube's center.
'd' and 'e' are both half face diagonals or 0.5, whereas 'f' is a cube edge, √2/2. This gives us our tetrahedron:
In :b = rt2(2)/4 a = c = rt2(3/8) d = e = 0.5 f = rt2(2)/2 mite = Tetrahedron(a, b, c, d, e, f) print("IVM volume of Mite:", round(mite.ivm_volume(),5)) print("XYZ volume of Mite:", round(mite.xyz_volume(),5))
IVM volume of Mite: 0.125 XYZ volume of Mite: 0.11785
Allowing for floating point error, this space-filling right tetrahedron has a volume of 0.125 or 1/8. Since 24 of them form a cube, said cube has a volume of 3. The XYZ volume, on the other hand, is what we'd expect from a regular tetrahedron of edges 0.5 in the current calibration system.
In :regular = Tetrahedron(0.5, 0.5, 0.5, 0.5, 0.5, 0.5) print("MITE volume in XYZ units:", round(regular.xyz_volume(),5)) print("XYZ volume of 24-Mite Cube:", round(24 * regular.xyz_volume(),5))
MITE volume in XYZ units: 0.11785 XYZ volume of 24-Mite Cube: 2.82843
The MITE (minimum tetrahedron) further dissects into component modules, a left and right A module, then either a left or right B module. Outwardly, the positive and negative MITEs look the same. Here are some drawings from R. Buckminster Fuller's research, the chief popularizer of the A and B modules.
In a different Jupyter Notebook, we could run these tetrahedra through our volume computer to discover both As and Bs have a volume of 1/24 in IVM units.
Instead, lets take a look at the E-module and compute its volume.
The black hub is at the center of the RT, as shown here...
In :from math import sqrt as rt2 from tetravolume import make_tet, Vector ø = (rt2(5)+1)/2 e0 = Black_Yellow = rt2(3)*ø**-1 e1 = Black_Blue = 1 e3 = Yellow_Blue = (3 - rt2(5))/2 e6 = Black_Red = rt2((5 - rt2(5))/2) e7 = Blue_Red = 1/ø # E-mod is a right tetrahedron, so xyz is easy v0 = Vector((Black_Blue, 0, 0)) v1 = Vector((Black_Blue, Yellow_Blue, 0)) v2 = Vector((Black_Blue, 0, Blue_Red)) # assumes R=0.5 so computed result is 8x needed # volume, ergo divide by 8. ivm, xyz = make_tet(v0,v1,v2) print("IVM volume:", round(ivm/8, 5)) print("XYZ volume:", round(xyz/8, 5))
IVM volume: 0.04173 XYZ volume: 0.03934
This information is being shared around Portland in various contexts. Below, an image from a hands-on workshop in 2010 organized by the Portland Free School.