Elbow duct
An elbow duct turns a flow by 90°, in a ventilation duct or at the intake of a machine. Its section stays the same along it, a rounded rectangle, and its center line is a path of a straight, a bend and a second straight. A section swept along a path is a sweep, occ.Pipe in OpenCASCADE. Each point of the section follows the path, and the section stays square to it all the way. The wall is 2 thick, the inside is swept the same way, and each end has a flange. Model the duct from the drawing in
planex2d or in any CAD system, then check its volume, mass and center of mass against the values at the end of the page. A solution in planex2d follows.

[1]:
import math
import netgen.occ as occ
import planex as px
The path and the section
The path is the duct’s center line, sketched in the front plane, the plane with its normal toward the viewer, \(-y\), with \(x\) to the right and \(z\) up and the origin in the middle of the duct’s lower end. It runs 50 up, through a bend of radius 80 to the right, and 40 along \(x\). The section lies on the xy plane, square to the path’s start. It is 60 by 40 with corners of radius 8, the 60 in the plane of the bend. The inside is the section offset by -2, the wall’s thickness, which also takes the corners’ radius down to 6 (Figure 1):
[3]:
front = px.XZ
path = px.QuickSketch(front).Move((0, 0), dir=(0, 1)).Line(50).Arc(80, -90).Line(40) # up, a bend to the right, along x
spine = path.Wire()
section = px.QuickSketch().RectangleC(60, 40)
section.Fillet([(30, 20), (-30, 20), (-30, -20), (30, -20)], 8) # the corners as points
outside = section.Face()
inside = outside.Offset(-2, inplace=False)
print("path", round(spine.mass, 4), "| section", round(outside.mass, 4), "| inside", round(inside.mass, 4))
path 215.6637 | section 2345.0619 | inside 1985.0973
The sweep
occ.Pipe sweeps a face along a wire. Swept along the path, the section gives the outside of the duct and its inside the passage, and - hollows the one by the other (Figure 2):
[5]:
duct = occ.Pipe(spine.ToOCCWire(), outside.ToOCCFace()) - occ.Pipe(spine.ToOCCWire(), inside.ToOCCFace())
The flanges
Each flange is 90 by 70 and 6 thick, with the duct’s inside as its opening and four holes of 7, 76 by 56 apart. The lower flange is sketched on the xy plane, under the duct’s lower end, and padded 6 down with reverse=True. The end flange is sketched on the duct’s end face, the face highest in \(x\), whose axes are \(y\) and \(z\), with the global origin projected onto it. The flange’s middle lies 130 up, at the path’s end, and there the 90 runs along \(z\), in the plane of
the bend (Figure 3):
[7]:
def flange(sketch, center, along_h):
"""the flange around center on the sketch's plane, 90 along h if along_h else along v, its opening the inside"""
(h, v), (a, b) = center, (90, 70) if along_h else (70, 90)
sketch.RectangleC(center, a, b)
sketch.RectangleC(center, a - 34, b - 34) # the inside's straight sides, 56 by 36
sketch.Fillet([(h + sh * (a - 34) / 2, v + sv * (b - 34) / 2) for sh in (-1, 1) for sv in (-1, 1)], 6)
for sh in (-1, 1):
for sv in (-1, 1):
sketch.Circle(7 / 2, center=(h + sh * (a - 14) / 2, v + sv * (b - 14) / 2))
return sketch
lower = flange(px.QuickSketch(), (0, 0), True)
upper = flange(px.QuickSketch(duct.faces.Max(occ.X)), (0, 130), False) # on the end face: h along y, v along z
elbow = duct + px.Pad(lower, 6, reverse=True) + px.Pad(upper, 6)
(a) The lower flange
(b) The duct with its flanges
The checks
These are the volume, the mass in aluminum AlMg3 (2.66 g/cm³) and the center of mass, with the origin in the middle of the lower end, to compare with your own model. A sweep whose section stays square to its path and has its centroid on it has the volume of the section’s area times the path’s length, by Pappus’s second theorem, here for the wall between the outside and the inside. The flanges add their faces’ areas times 6. The duct is symmetric about the plane \(y = 0\):
[9]:
exact = (outside.mass - inside.mass) * spine.mass + 6 * (lower.Face().mass + upper.Face().mass)
c = elbow.center
print("volume", round(elbow.mass, 1), "mm³, exact", round(exact, 1))
print("mass", round(elbow.mass * 2.66e-6, 3), "kg")
print("center of mass", (round(c.x, 2), round(c.z, 2)), "in the plane of the bend, off it by", round(abs(c.y), 6))
volume 127562.9 mm³, exact 127562.9
mass 0.339 kg
center of mass (44.33, 80.19) in the plane of the bend, off it by 0.0
The duct as STEP, for another CAD system:
[10]:
elbow.WriteStep("elbow_duct.step")
The duct as STEP: elbow_duct.step