Square-to-round transition
A square-to-round transition joins a square duct to a round one, in ventilation and in the ducts of machines. Its wall runs from a square at one end to a circle at the other, a shape that neither padding nor revolving makes. It is a loft, a solid through two sections, the multi-section solid of other CAD systems. A loft joins the sections edge by edge, so the circle is drawn as four quarter arcs, one for each side of the square, and the arcs start where the square’s first corner points. The inside is a second loft, 3 smaller all around, and each end has a flange with four bolt holes. Model the transition 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 sections
The transition stands on the xy plane. The square of 100 lies at its bottom, 6 above the base, on top of the square flange, and the circle of 80 at its top, 114 above the base, under the round flange. RectangleC draws the square around the origin, its sides counterclockwise from its lower left corner. The circle’s four quarter arcs run counterclockwise from the point at 225°, so that each arc lies across from one side of the square. square and circle sketch the sections on the xy
plane lifted to their heights (Figure 1):
[3]:
def square(a, z):
return px.QuickSketch(px.Plane(px.XY, z)).RectangleC(a, a) # its first corner the lower left one
def circle(d, z):
corner = math.radians(225) # across from the square's first corner
sk = px.QuickSketch(px.Plane(px.XY, z)).Move((d / 2 * math.cos(corner), d / 2 * math.sin(corner)))
for _ in range(4):
sk.Arc(center=(0, 0), sweep=90) # a quarter of the circle, around the axis
return sk
sections = [square(100, 6), circle(80, 114)]
The lofts
occ.ThruSections lofts a solid through wires. A sketch’s ToOCCWire merges edges that lie on one carrier, so the four quarter arcs would become one circle. unify=False keeps every drawn edge, and the loft joins the square’s sides to the circle’s arcs one by one. The inside is the same transition 3 smaller all around, from a square of 94 to a circle of 74, and - hollows the outside by it (Figure 2):
[5]:
def loft(a, d):
return occ.ThruSections([square(a, 6).ToOCCWire(unify=False), circle(d, 114).ToOCCWire(unify=False)], solid=True)
outside = loft(100, 80)
inside = loft(94, 74)
duct = outside - inside
(a) The loft from the square to the circle
(b) Hollowed by the inside's loft
The flanges
The square flange is 140 by 140 and 6 thick, under the duct, with the duct’s inside as its opening and four holes of 9, 120 apart in both directions. The round flange is 120 across and 6 thick, on top of the duct, with its opening of 74 and four holes of 9 on a bolt circle of 100, at 45° to the square’s sides. Both are padded from their sketches, the square one from the base and the round one from the duct’s top (Figure 3):
[7]:
bottom = px.QuickSketch().RectangleC(140, 140).RectangleC(94, 94) # the opening a hole: nested in the flange
bottom.Circle(9 / 2, center=(60, 60), id="hole")
bottom.CircularPattern("hole", "origin", 4)
top = px.QuickSketch(px.Plane(px.XY, 114)).Circle(120 / 2).Circle(74 / 2)
top.Circle(9 / 2, center=(50 * math.cos(math.pi / 4), 50 * math.sin(math.pi / 4)), id="hole")
top.CircularPattern("hole", "origin", 4)
transition = duct + px.Pad(bottom, 6) + px.Pad(top, 6)
(a) The square flange
(b) The round flange
(c) The transition
The checks
These are the volume, the mass in steel S235 (7.85 g/cm³) and the center of mass, with the origin in the middle of the bottom, to compare with your own model. A loft through two sections joins them linearly, so its cross-section’s area is a quadratic function of the height. Simpson’s rule then gives its volume exactly from the areas at its ends and in its middle, \(\tfrac{h}{6}\,(A_\mathrm{bottom} + 4 A_\mathrm{middle} + A_\mathrm{top})\), over its height of 108. px.Intersect3d cuts the
two lofts at mid height. The flanges add their faces’ areas times 6:
[9]:
def simpson(solid, bottom_area, top_area):
middle = px.Intersect3d(px.Plane(px.XY, 60), solid).mass # the section at mid height
return 108 / 6 * (bottom_area + 4 * middle + top_area)
exact = (simpson(outside, 100**2, math.pi * 40**2) - simpson(inside, 94**2, math.pi * 37**2)
+ 6 * bottom.Face().mass + 6 * top.Face().mass) # each flange's face: its holes nested in it
c = transition.center
print("volume", round(transition.mass, 1), "mm³, exact", round(exact, 1))
print("mass", round(transition.mass * 7.85e-6, 3), "kg")
print("center of mass", round(c.z, 2), "above the bottom, off the axis by", round(math.hypot(c.x, c.y), 6))
volume 205450.7 mm³, exact 205450.7
mass 1.613 kg
center of mass 51.68 above the bottom, off the axis by 0.0
The transition as STEP, for another CAD system:
[10]:
transition.WriteStep("square_to_round.step")
The transition as STEP: square_to_round.step