--- jupyter: description: 'A slip-on pipe flange as a 3D CAD exercise: a dimensioned drawing with a full section to model in planex2d or any CAD system, a solution from a half section revolved and a circular pattern of bolt holes, and the exact volume, mass and center of mass to check a model against.' --- # Pipe flange A slip-on flange is pushed over the end of a pipe and welded to it, so that the pipe can be bolted to a valve or to the next pipe. It is a turned part, a plate with a hub at the back and a raised face at the front, which takes the gasket, revolved about the pipe's axis. The bolt holes are a circular pattern through the plate. Model the flange 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. ![The pipe flange exercise: a dimensioned drawing of a slip-on flange with a hub, a raised face and four bolt holes, in a full section, a view onto the raised face and an isometric](../_static/gallery/pipe-flange.png){.result} ```python import math import netgen.occ as occ import planex as px ``` ```python nbsphinx="hidden" from docs_figures import Row, Views ``` ## The half section The half section is sketched in the front plane, the plane with its normal toward the viewer, $-y$, with $x$ out from the axis, $z$ along it and the origin on the axis at the hub's end. The outline runs out along the hub's end, up the hub, out along the plate's back, up its rim, in along its front, up the raised face and in to the bore. Every line is horizontal or vertical, and with the auto-constraint dict for level and plumb lines it stays so. The bore is 49 across, for a pipe of 48.3. The hub is 70 across and 12 long, the plate 130 across and 16 thick, and the raised face 78 across and 2 high, clear of the bolt holes (Figure 1): ```python front = px.XZ half = px.Sketch(front, auto_constraint={"horizontal": True, "vertical": True}) half.Move((24.5, 0)).Line(10.5, id="hub_end") half.Turn(90).Line(12, id="hub") # Turn(90) turns the pen left, Turn(-90) right half.Turn(-90).Line(30, id="plate_back") half.Turn(90).Line(16, id="rim") half.Turn(90).Line(26, id="plate_front") half.Turn(-90).Line(2, id="raised_side") half.Turn(90).Line(14.5, id="raised_face") half.Close(id="bore") half.Constrain("hub_end.start", "H", px.Coincident) half.Constrain("bore", "V", px.Distance, 49 / 2, id="bore_r") # from the axis: half the diameter half.Constrain("hub", "V", px.Distance, 70 / 2, id="hub_r") half.Constrain("rim", "V", px.Distance, 130 / 2, id="plate_r") half.Constrain("raised_side", "V", px.Distance, 78 / 2, id="raised_r") half.Constrain("hub", px.Distance, 12, id="hub_l") half.Constrain("rim", px.Distance, 16, id="plate_t") half.Constrain("raised_side", px.Distance, 2, id="raised_h") print("status", half.status, "| degrees of freedom", half.dof) ``` ```python nbsphinx="hide-input" Row((half, "Every dimension and tie of the half section", {"constraints": True}), caption="The half section") ``` The section's face, taken to OpenCASCADE with `ToOCCFace`, is revolved a full turn about the $z$ axis (Figure 2): ```python flange = half.Face().ToOCCFace().Revolve(occ.Axis((0, 0, 0), occ.Z), 360) ``` ```python nbsphinx="hide-input" Views(("The half section revolved", [(flange, "shade"), (flange, "ink")]), angles=(-60, 0, 30), caption="The flange") ``` ## The bolt holes The four bolt holes of 18 lie on a bolt circle of 100, between the axes. The first one lies at 45°, where its center's $h$ and $v$ are equal, which `Expression` states, and `CircularPattern` repeats it 90° apart. They are sketched on the plate's front, the xy plane lifted to 28, and pocketed 16 down through the plate (Figure 3): ```python holes = px.Sketch(px.Plane(px.XY, 28), auto_constraint=True) holes.Circle(100 / 2, id="bolt_circle", construction=True) holes.Constrain("bolt_circle.center", "origin", px.Coincident) holes.Constrain("bolt_circle", px.Diameter, 100, id="bolt_circle_d") holes.Circle(18 / 2, center=(36, 34), id="hole") holes.Constrain("hole.center", "bolt_circle", px.Coincident) # a point on a circle holes.Expression("hole.center.h = hole.center.v", id="at_45") # the first hole at 45° holes.Constrain("hole", px.Diameter, 18, id="hole_d") holes.CircularPattern(["hole"], "origin", 4, id="pattern") print("status", holes.status, "| degrees of freedom", holes.dof) flange = px.Pocket(flange, holes, 16) ``` ```python nbsphinx="hide-input" Row((holes, "The bolt holes on the plate's front", {"plot": {"show_parent": flange, "show_constraints": True}}), (flange, "Four holes through the plate", {"views": [(flange, "shade"), (flange, "ink")], "angles": (-60, 0, 30)}), caption="The bolt holes") ``` ## The checks These are the volume, the mass in steel P250GH (7.85 g/cm³) and the center of mass, with the origin on the axis at the hub's end, to compare with your own model. The flange is three rings, each $\pi\,(R^2 - r^2)$ times its length around the bore of 24.5, less the four holes through the plate. It is symmetric about its axis, so its center of mass lies on it, at the height where the moments of the rings and the holes balance: ```python parts = [(math.pi * (35**2 - 24.5**2) * 12, 6), (math.pi * (65**2 - 24.5**2) * 16, 20), (math.pi * (39**2 - 24.5**2) * 2, 29), (-4 * math.pi * 9**2 * 16, 20)] # (volume, height of its middle) exact = sum(v for v, z in parts) c = flange.center print("volume", round(flange.mass, 1), "mm³, exact", round(exact, 1)) print("mass", round(flange.mass * 7.85e-6, 4), "kg") print("center of mass", round(c.z, 4), "above the hub's end, exact", round(sum(v * z for v, z in parts) / exact, 4), "| off the axis by", round(math.hypot(c.x, c.y), 6)) ``` The flange as STEP, for another CAD system: ```python flange.WriteStep("pipe_flange.step") ``` The flange as STEP: [pipe_flange.step](pipe_flange.step) ```python nbsphinx="hidden" import sys sys.path.insert(0, "..") import matplotlib.pyplot as plt from drawings import LANDSCAPE, Pictorial, SheetFigure, View from gallery import save_figure # first angle: the full section through the plane y = 0 in front (the part behind it seen from the front, its cut faces # hatched), the view from above onto the raised face below it behind = flange * occ.Box((-70, 0, -1), (70, 70, 31)) cut = [f for f in behind.faces if abs(f.center.y) < 1e-6 and f.bounding_box[1].y - f.bounding_box[0].y < 1e-6] section = View([], solid=behind, plane=front, hidden_edges=False, hatch=cut, axes=[((0, -4), (0, 34))], measures=[((-65, 12), (65, 12), -24, "⌀130"), ((-35, 0), (35, 0), -12, "⌀70"), ((-24.5, 30), (24.5, 30), 36, "⌀49"), ((-39, 30), (39, 30), 44, "⌀78"), ((65, 0), (65, 12), 74, 12), ((65, 12), (65, 28), 74, 16), ((39, 0), (39, 30), 86, 30)]) top = View([(holes, {"bolt_circle_d": (120, "leader"), "hole_d": (45, "leader", 4)}, {"bolt_circle": "pitch"})], solid=flange, plane=px.XY, at=(0, -105), hidden_edges=False, axes=[((-70, 0), (70, 0)), ((0, -70), (0, 70))]) pictorial = Pictorial(flange, at=(125, -40), width=70, angles=(-60, 0, 30)) drawing = SheetFigure([section, top, pictorial], margin=16, notes=("planex2d.org", "3D exercise 27"), first_angle=True, sheet=LANDSCAPE) print("scale", drawing.planex_scale, "| paper", [round(v * 25.4) for v in drawing.get_size_inches()], "mm") save_figure(drawing, "pipe-flange", dpi=200) plt.close(drawing) ```