Exemple #1
0
    def test_revolve(self):
        # square torus
        square = CurveFactory.n_gon(4)
        square.rotate(pi / 2, (1, 0, 0))
        square.translate((2, 0, 0))  # in xz-plane with corners at (3,0),(2,1),(1,0),(2,-1)
        surf = SurfaceFactory.revolve(square)
        surf.reparam()  # set parametric space to (0,1)^2
        v = np.linspace(0, 1, 13)
        x = surf.evaluate(0, v)  # outer ring evaluation u=0
        for pt in np.array(x[0, :, :]):
            self.assertAlmostEqual(np.linalg.norm(pt, 2), 3.0)  # check radius=3
        x = surf.evaluate(.25, v)  # top ring evaluation u=.25
        for pt in np.array(x[0, :, :]):
            self.assertAlmostEqual(pt[0] * pt[0] + pt[1] * pt[1], 2 * 2)  # check radius=2
            self.assertAlmostEqual(pt[2], 1)  # check height=1
        x = surf.evaluate(.375, v)  # mid inner ring evaluation u=.375
        for pt in np.array(x[0, :, :]):
            self.assertAlmostEqual(pt[0] * pt[0] + pt[1] * pt[1], 1.5 * 1.5)  # check radius=1.5
            self.assertAlmostEqual(pt[2], .5)  # check height=0.5

        # incomplete revolve
        c    = CurveFactory.line([1,0], [0,1], relative=True)
        surf = SurfaceFactory.revolve(c, theta=4.2222, axis=[0,1,0])
        surf.reparam()
        u = np.linspace(0,1,7)
        v = np.linspace(0,1,7)
        x = surf(u,v)
        for uPt in x:
            for pt in uPt:
                self.assertAlmostEqual(pt[0]**2 + pt[2]**2, 1.0) # radius 1 from y-axis
    def test_revolve(self):
        # square torus
        square = cf.n_gon(4)
        square.rotate(pi / 2, (1, 0, 0))
        square.translate(
            (2, 0, 0))  # in xz-plane with corners at (3,0),(2,1),(1,0),(2,-1)
        surf = sf.revolve(square)
        surf.reparam()  # set parametric space to (0,1)^2
        v = np.linspace(0, 1, 13)
        x = surf.evaluate(0, v)  # outer ring evaluation u=0
        for pt in np.array(x[0, :, :]):
            self.assertAlmostEqual(np.linalg.norm(pt, 2),
                                   3.0)  # check radius=3
        x = surf.evaluate(.25, v)  # top ring evaluation u=.25
        for pt in np.array(x[0, :, :]):
            self.assertAlmostEqual(pt[0] * pt[0] + pt[1] * pt[1],
                                   2 * 2)  # check radius=2
            self.assertAlmostEqual(pt[2], 1)  # check height=1
        x = surf.evaluate(.375, v)  # mid inner ring evaluation u=.375
        for pt in np.array(x[0, :, :]):
            self.assertAlmostEqual(pt[0] * pt[0] + pt[1] * pt[1],
                                   1.5 * 1.5)  # check radius=1.5
            self.assertAlmostEqual(pt[2], .5)  # check height=0.5

        # incomplete revolve
        c = cf.line([1, 0], [0, 1], relative=True)
        surf = sf.revolve(c, theta=4.2222, axis=[0, 1, 0])
        surf.reparam()
        u = np.linspace(0, 1, 7)
        v = np.linspace(0, 1, 7)
        x = surf(u, v)
        for uPt in x:
            for pt in uPt:
                self.assertAlmostEqual(pt[0]**2 + pt[2]**2,
                                       1.0)  # radius 1 from y-axis
Exemple #3
0
    def test_line(self):

        # 2D line
        c = CurveFactory.line([1, 1], [2, 0])
        self.assertEqual(c.order(0), 2)  # linear discretization
        self.assertEqual(len(c), 2)  # two control points
        self.assertEqual(c.dimension, 2)
        self.assertEqual(c[0][0], 1)
        self.assertEqual(c[0][1], 1)
        self.assertEqual(c[-1][0], 2)
        self.assertEqual(c[-1][1], 0)

        # 3D line
        c = CurveFactory.line([1, 2, 3], [8, 7, 6])
        self.assertEqual(c.order(0), 2)  # linear discretization
        self.assertEqual(len(c), 2)  # two control points
        self.assertEqual(c.dimension, 3)
Exemple #4
0
    def test_line(self):

        # 2D line
        c = cf.line([1, 1], [2, 0])
        self.assertEqual(c.order(0), 2)  # linear discretization
        self.assertEqual(len(c), 2)  # two control points
        self.assertEqual(c.dimension, 2)
        self.assertEqual(c[0][0], 1)
        self.assertEqual(c[0][1], 1)
        self.assertEqual(c[-1][0], 2)
        self.assertEqual(c[-1][1], 0)

        # 3D line
        c = cf.line([1, 2, 3], [8, 7, 6])
        self.assertEqual(c.order(0), 2)  # linear discretization
        self.assertEqual(len(c), 2)  # two control points
        self.assertEqual(c.dimension, 3)
Exemple #5
0
    def test_2d_self_connection(self):
        c = curve_factory.line([1, 0], [2, 0])
        surf = surface_factory.revolve(c)
        surf = surf.split(surf.knots('v')[0],
                          direction='v')  # break periodicity
        surf.set_dimension(2)
        model = SplineModel(2, 2)
        model.add(surf)

        writer = IFEMWriter(model)
        expected = [IFEMConnection(1, 1, 3, 4, 0)]
        for connection, want in zip(writer.connections(), expected):
            self.assertEqual(connection, want)
Exemple #6
0
    def line(self):
        dim      = int(     self.read_next_non_whitespace().strip())
        start    = np.array(next(self.fstream).split(), dtype=float)
        direction= np.array(next(self.fstream).split(), dtype=float)
        finite   =          next(self.fstream).strip() != '0'
        param    = np.array(next(self.fstream).split(), dtype=float)
        reverse  =          next(self.fstream).strip() != '0'
        d = np.array(direction)
        s = np.array(start)
        # d /= np.linalg.norm(d)
        if not finite:
            param = [-state.unlimited, +state.unlimited]

        result = CurveFactory.line(s+d*param[0], s+d*param[1])
        if reverse:
            result.reverse()
        return result
Exemple #7
0
    def read(self):
        if not hasattr(self, 'fstream'):
            self.onlywrite = False
            self.fstream = File3dm.Read(self.filename)

        if self.onlywrite:
            raise IOError('Could not read from file %s' % (self.filename))

        result = []

        for obj in self.fstream.Objects:
            geom = obj.Geometry
            print(geom)
            if type(geom) is Extrusion:
                geom = geom.ToBrep(splitKinkyFaces=True)
            if type(geom) is Brep:
                for idx in range(len(geom.Faces)):
                    print('  ', geom.Faces[idx], "(",
                          geom.Faces[idx].UnderlyingSurface(), ")")
                    nsrf = geom.Faces[idx].UnderlyingSurface().ToNurbsSurface()
                    result.append(self.read_surface(nsrf))

            if type(geom) is Line:
                geom = result.append(curve_factory.line(geom.From, geom.To))
                continue
            if type(geom) is PolylineCurve:
                geom = geom.ToPolyline()
            if type(geom) is Polyline or \
               type(geom) is Circle or \
               type(geom) is threedmCurve or \
               type(geom) is BezierCurve or \
               type(geom) is Arc:
                geom = geom.ToNurbsCurve()

            if type(geom) is NurbsCurve:
                result.append(self.read_curve(geom))

            if type(geom) is Cylinder or \
               type(geom) is Sphere or \
               type(geom) is threedmSurface:
                geom = geom.ToNurbsSurface()
            if type(geom) is NurbsSurface:
                result.append(self.read_surface(geom))

        return result
Exemple #8
0
    def test_constructor(self):
        trim_loop = [
            CurveFactory.line([.25, .25], [.75, .25]),
            CurveFactory.line([.75, .25], [.75, .75]),
            CurveFactory.line([.75, .75], [.25, .75]),
            CurveFactory.line([.25, .75], [.25, .25])
        ]
        # error with non-closed trimming loop
        with self.assertRaises(RuntimeError):
            s = TrimmedSurface(loops=[trim_loop[:3]])
        # error with trimming curves in 3D space
        with self.assertRaises(RuntimeError):
            loop_3d = [
                CurveFactory.line([.25, .25, 1], [.75, .25, 1]),
                CurveFactory.line([.75, .25, 1], [.75, .75, 1]),
                CurveFactory.line([.75, .75, 1], [.25, .75, 1]),
                CurveFactory.line([.25, .75, 1], [.25, .25, 1])
            ]
            s = TrimmedSurface(loops=[loop_3d])

        s = TrimmedSurface(loops=[trim_loop])
        self.assertEqual(s.dimension, 2)
        self.assertFalse(s.rational)
Exemple #9
0
def cut_square(width, height, radius, inner_radius, nel_ang, order, out):

    # Compute number of elements along each part
    rest1 = height - radius - inner_radius
    rest2 = width - radius - inner_radius
    nel_cyl = int(np.ceil(4/np.pi * (inner_radius - radius) / radius * nel_ang))
    nel_rest1 = int(np.ceil(4/np.pi * rest1 / radius * nel_ang))
    nel_rest2 = int(np.ceil(4/np.pi * rest2 / radius * nel_ang))

    # Create quarter circles
    theta = np.linspace(0, np.pi/2, 2*nel_ang+1)[::-1]
    pts = np.array([radius * np.cos(theta), radius * np.sin(theta)]).T
    circle = cf.cubic_curve(pts, boundary=cf.Boundary.NATURAL).set_dimension(3)
    knots = circle.knots('u')
    inner1, inner2 = circle.split(knots[len(knots) // 2])

    # Fill the cylinder patches
    factor = inner_radius / radius
    outer1, outer2 = inner1 * factor, inner2 * factor
    cyl1 = sf.edge_curves(inner1, outer1).set_order(4,4).refine(0, nel_cyl-1)
    cyl2 = sf.edge_curves(inner2, outer2).set_order(4,4).refine(0, nel_cyl-1)

    # Create the "curved rectangles"
    dist = np.sqrt(2) * radius
    edge1 = cf.line((0, height), (dist, height)).set_order(4).set_dimension(3).refine(nel_ang-1)
    rect1 = sf.edge_curves(outer1, edge1).set_order(4,4).refine(0, nel_rest1-1)
    edge2 = cf.line((width, dist), (width, 0)).set_order(4).set_dimension(3).refine(nel_ang-1)
    rect2 = sf.edge_curves(outer2, edge2).set_order(4,4).refine(0, nel_rest2-1)

    # Final square
    edge1 = rect2.section(u=0)
    edge2 = edge1 + (0, height - dist, 0)
    rect = sf.edge_curves(edge1, edge2).set_order(4,4).refine(0, nel_rest1-1)

    diff = 4 - order
    patches = [patch.lower_order(diff, diff) for patch in [cyl1, cyl2, rect1, rect2, rect]]

    with G2(out + '.g2') as f:
        f.write(patches)

    root = etree.Element('geometry')
    etree.SubElement(root, 'patchfile').text = out + '.g2'
    topology = etree.SubElement(root, 'topology')
    for mid, sid, midx, sidx, rev in [(1,2,2,1,False), (1,3,4,3,False), (2,4,4,3,False),
                                      (3,5,2,1,False), (4,5,1,3,False)]:
        etree.SubElement(topology, 'connection').attrib.update({
            'master': str(mid), 'slave': str(sid),
            'midx': str(midx), 'sidx': str(sidx),
            'reverse': 'true' if rev else 'false',
        })

    topsets = etree.SubElement(root, 'topologysets')
    for name, entries in [('Circle', [(1, (3,)), (2, (3,))]),
                          ('Left',   [(1, (1,)), (3, (1,))]),
                          ('Right',  [(4, (4,)), (5, (2,))]),
                          ('Top',    [(3, (4,)), (5, (4,))]),
                          ('Bottom', [(2, (2,)), (4, (2,))])]:
        topset = etree.SubElement(topsets, 'set')
        topset.attrib.update({'name': name, 'type': 'edge'})
        for pid, indices in entries:
            item = etree.SubElement(topset, 'item')
            item.attrib['patch'] = str(pid)
            item.text = ' '.join(str(i) for i in indices)

    with open(out + '.xinp', 'wb') as f:
        f.write(etree.tostring(
            root, pretty_print=True, encoding='utf-8', xml_declaration=True, standalone=False
        ))
Exemple #10
0
def cylinder(diam, width, front, back, side, height, re, grad, inner_elsize,
             nel_side, nel_bndl, nel_circ, nel_height, order, out,
             outer_graded, thickness):
    assert all(f >= width for f in [front, back, side])

    rad_cyl = diam / 2
    width *= rad_cyl
    back = back * rad_cyl - width
    front = front * rad_cyl - width
    side = side * rad_cyl - width
    elsize = width * 2 / nel_circ

    dim = 2 if height == 0.0 else 3
    vx_add = 8 if dim == 3 else 0
    patches = PatchDict(dim)

    if inner_elsize and nel_side:
        # Calculate grading factor based on first element size,
        # total length and number of elements
        dr = rad_cyl * inner_elsize
        grad = find_factor(dr, width - rad_cyl, nel_side)

    elif nel_bndl and grad:
        # Calculate first element size based on total length
        # and number of elements

        # We want nel_bndl elements inside the boundary layer
        # Calculate how small the inner element must be
        size_bndl = 1 / sqrt(re) * diam
        dr = (1 - grad) / (1 - grad**nel_bndl) * size_bndl

        # Potentially reduce element size so we get a whole number of elements
        # on either side of the cylinder
        #nel_side = int(ceil(log(1 - 1/dr * (1 - grad) * (width - rad_cyl)) / log(grad)))
        nel_side = int(
            round(
                log(1 - 1 / dr * (1 - grad) * (width - rad_cyl)) / log(grad)))
        dr = (1 - grad) / (1 - grad**nel_side) * (width - rad_cyl)
    else:
        print('Specify (inner-elsize and nel-side) or (nel-bndl and grad)',
              file=sys.stderr)
        sys.exit(1)

    # Graded radial space from cylinder to edge of domain
    radial_kts = graded_space(rad_cyl, dr, grad, nel_side) + [width]

    # Create a radial and divide it
    radial = cf.cubic_curve(np.matrix(radial_kts).T,
                            boundary=cf.Boundary.NATURAL)
    radial.set_dimension(3)
    radial_kts = radial.knots('u')
    # middle = radial_kts[len(radial_kts) // 2]
    middle = next(k for k in radial_kts if k >= thickness * diam)
    radial_inner, radial_outer = radial.split(middle, 'u')
    radial_inner.rotate(pi / 4)
    dl = np.linalg.norm(
        radial_outer(radial_outer.knots('u')[-2]) -
        radial_outer.section(u=-1)) * grad

    # Revolve the inner radial and divide it
    radials = [
        radial_inner.clone().rotate(v)
        for v in np.linspace(0, 2 * pi, 4 * nel_circ + 1)
    ]
    inner = sf.loft(radials)
    ikts = inner.knots('v')
    inner.insert_knot((ikts[0] + ikts[1]) / 2, 'v')
    inner.insert_knot((ikts[-1] + ikts[-2]) / 2, 'v')

    ikts = inner.knots('v')
    patches.add('iu', 'il', 'id', 'ir',
                inner.split([ikts[k * nel_circ] for k in range(5)][1:-1], 'v'))
    patches.connect(
        ('ir', 4, 'iu', 3),
        ('iu', 4, 'il', 3),
        ('il', 4, 'id', 3),
        ('id', 4, 'ir', 3),
    )
    patches.boundary('cylinder', 'ir', 1)
    patches.boundary('cylinder', 'iu', 1)
    patches.boundary('cylinder', 'il', 1)
    patches.boundary('cylinder', 'id', 1)

    # Create an outer section
    rc = radial_outer.section(u=0)
    alpha = (sqrt(2) * width - rc[0]) / (width - rc[0])
    right = ((radial_outer - rc) * alpha + rc).rotate(pi / 4)
    left = right.clone().rotate(pi / 2).reverse()
    outer = cf.line((-width, width, 0), (width, width, 0))
    outer.set_order(4).refine(nel_circ - 1)
    inner = patches['iu'].section(u=-1)
    outer = sf.edge_curves(right, outer, left, inner).reverse('v')

    patches.add('ou', 'ol', 'od', 'or',
                [outer.clone().rotate(v) for v in [0, pi / 2, pi, 3 * pi / 2]])
    patches.connect(
        ('or', 4, 'ou', 3),
        ('ou', 4, 'ol', 3),
        ('ol', 4, 'od', 3),
        ('od', 4, 'or', 3),
        ('or', 1, 'ir', 2),
        ('ou', 1, 'iu', 2),
        ('ol', 1, 'il', 2),
        ('od', 1, 'id', 2),
    )

    if front > 0:
        la = patches['ol'].section(u=-1)
        lb = la.clone() - (front, 0, 0)
        front_srf = sf.edge_curves(lb, la).set_order(4, 4).swap().reverse('v')
        nel = int(ceil(log(1 - 1 / dl * (1 - grad) * front) / log(grad)))
        geometric_refine(front_srf, grad, nel - 1, reverse=True)
        patches['fr'] = front_srf
        patches.connect(('fr', 2, 'ol', 2, 'rev'))
        patches.boundary('inflow', 'fr', 1)
    else:
        patches.boundary('inflow', 'ol', 2)
        patches.boundary('inflow', 'ou', 4, dim=-2, add=vx_add)
        patches.boundary('inflow', 'od', 2, dim=-2, add=vx_add)

    if back > 0:
        la = patches['or'].section(u=-1)
        lb = la.clone() + (back, 0, 0)
        back_srf = sf.edge_curves(la, lb).set_order(4, 4).swap()
        if outer_graded:
            nel = int(ceil(log(1 - 1 / dl * (1 - grad) * back) / log(grad)))
            geometric_refine(back_srf, grad, nel - 1)
        else:
            #nel = int(ceil(back / dl))
            nel = int(round(back / (2 * width) * nel_circ))
            back_srf.refine(nel - 1, direction='u')
        patches['ba'] = back_srf
        patches.connect(('ba', 1, 'or', 2))
        patches.boundary('outflow', 'ba', 2)
    else:
        patches.boundary('outflow', 'or', 2)

    if side > 0:
        la = patches['ou'].section(u=-1).reverse()
        lb = la + (0, side, 0)
        patches['up'] = sf.edge_curves(la, lb).set_order(4, 4)
        patches.connect(('up', 3, 'ou', 2, 'rev'), ('dn', 4, 'od', 2))
        patches.boundary('top', 'up', 4)
        patches.boundary('bottom', 'dn', 3)

        if 'fr' in patches:
            btm = front_srf.section(v=-1)
            right = patches['up'].section(u=0)
            top = (btm + (0, side, 0)).reverse()
            left = (right - (front, 0, 0)).reverse()
            patches['upfr'] = sf.edge_curves(btm, right, top, left)
            patches.connect(
                ('upfr', 3, 'fr', 4),
                ('upfr', 2, 'up', 1),
                ('dnfr', 4, 'fr', 3),
                ('dnfr', 2, 'dn', 1),
            )
            patches.boundary('wall', 'upfr', 4)
            patches.boundary('inflow', 'upfr', 1)
            patches.boundary('wall', 'dnfr', 3)
            patches.boundary('inflow', 'dnfr', 1)
        else:
            patches.boundary('inflow', 'up', 1)
            patches.boundary('inflow', 'dn', 1)

        if 'ba' in patches:
            btm = back_srf.section(v=-1)
            left = patches['up'].section(u=-1).reverse()
            top = (btm + (0, side, 0)).reverse()
            right = (left + (back, 0, 0)).reverse()
            patches['upba'] = sf.edge_curves(btm, right, top, left)
            patches.connect(
                ('upba', 3, 'ba', 4),
                ('upba', 1, 'up', 2),
                ('dnba', 4, 'ba', 3),
                ('dnba', 1, 'dn', 2),
            )
            patches.boundary('wall', 'upba', 4)
            patches.boundary('outflow', 'upfr', 2)
            patches.boundary('wall', 'dnba', 3)
            patches.boundary('outflow', 'dnba', 2)
        else:
            patches.boundary('outflow', 'up', 2)
            patches.boundary('outflow', 'dn', 2)

        nel = int(ceil(log(1 - 1 / dl * (1 - grad) * side) / log(grad)))
        for uk in {'up', 'upfr', 'upba'} & patches.keys():
            dk = 'dn' + uk[2:]
            patches[dk] = patches[uk] - (0, side + 2 * width, 0)
            geometric_refine(patches[uk], grad, nel - 1, direction='v')
            geometric_refine(patches[dk],
                             grad,
                             nel - 1,
                             direction='v',
                             reverse=True)

    else:
        patches.boundary('wall', 'ou', 2)
        patches.boundary('wall', 'od', 2)
        patches.boundary('wall', 'or', 4, dim=-2, add=vx_add)
        patches.boundary('wall', 'or', 2, dim=-2, add=vx_add)

        if 'fr' in patches:
            patches.boundary('wall', 'fr', 4)
            patches.boundary('wall', 'fr', 3)
            patches.boundary('wall', 'ol', 2, dim=-2, add=vx_add)
            patches.boundary('wall', 'ol', 4, dim=-2, add=vx_add)

        if 'ba' in patches:
            patches.boundary('wall', 'ba', 4)
            patches.boundary('wall', 'ba', 3)

    if height > 0.0:
        names = [
            'iu', 'il', 'id', 'ir', 'ou', 'ol', 'od', 'or', 'fr', 'ba', 'up',
            'upba', 'upfr', 'dn', 'dnba', 'dnfr'
        ]
        for pname in names:
            if pname not in patches:
                continue
            patch = patches[pname]
            patch = extrude(patch, (0, 0, height))
            patch.raise_order(0, 0, 2)
            patch.refine(nel_height - 1, direction='w')
            patches[pname] = patch
            patches.boundary('zup', pname, 6)
            patches.boundary('zdown', pname, 5)
            patches.connect((pname, 5, pname, 6, 'per'))

    patches.write(out, order=order)
Exemple #11
0
def flag(diam, flag_width, flag_length, width, back, flag_grad, grad, nel_rad,
         nel_circ, nel_flag, order, out):
    assert (back > width)

    rad_cyl = diam / 2
    width *= rad_cyl
    back = back * rad_cyl - width

    # Create a circle
    angle = 2 * np.arcsin(flag_width / rad_cyl / 2)
    pts = rad_cyl * np.array(
        [(np.cos(a), np.sin(a))
         for a in np.linspace(angle, 2 * np.pi - angle, nel_circ + 1)])
    circle = cf.cubic_curve(pts, boundary=cf.Boundary.NATURAL)
    circle.set_dimension(3)

    # Subdivide it
    nels_side = int(round(nel_circ // 2 * 3 * np.pi / 4 / (np.pi - angle)))
    nels_front = (nel_circ // 2 - nels_side) * 2
    S = (-nel_flag * width + nels_side *
         (width + back)) / (nel_flag + nels_side)

    kts = circle.knots('u')
    kts = kts[nels_side], kts[nels_side + nels_front]
    circ_up, circ_front, circ_down = circle.split(kts)

    # Extend to boundary
    front = cf.line((-width, width),
                    (-width, -width)).set_order(4).refine(nels_front - 1)
    front = sf.edge_curves(front, circ_front).raise_order(0, 2)
    geometric_refine(front, grad, nel_rad - 1, direction='v', reverse=True)

    up = cf.line((S, width),
                 (-width, width)).set_order(4).refine(nels_side - 1)
    up = sf.edge_curves(up, circ_up).raise_order(0, 2)
    geometric_refine(up, grad, nel_rad - 1, direction='v', reverse=True)

    down = cf.line((-width, -width),
                   (S, -width)).set_order(4).refine(nels_side - 1)
    down = sf.edge_curves(down, circ_down).raise_order(0, 2)
    geometric_refine(down, grad, nel_rad - 1, direction='v', reverse=True)

    # Create the flag
    upt = circle(circle.start('u'))
    fl_up = cf.line((flag_length + rad_cyl, upt[1], 0), upt).raise_order(2)
    geometric_refine(fl_up,
                     flag_grad,
                     nel_flag - 1,
                     direction='u',
                     reverse=True)
    ln_up = cf.cubic_curve(np.array([((1 - i) * (width + back) + i * S, width)
                                     for i in np.linspace(0, 1, nel_flag + 1)
                                     ]),
                           boundary=cf.Boundary.NATURAL,
                           t=fl_up.knots('u'))
    fl_up = sf.edge_curves(ln_up, fl_up).raise_order(0, 2)
    geometric_refine(fl_up, grad, nel_rad - 1, direction='v', reverse=True)

    dpt = circle(circle.end('u'))
    fl_down = cf.line(dpt, (flag_length + rad_cyl, dpt[1], 0))
    geometric_refine(fl_down, flag_grad, nel_flag - 1, direction='u')
    ln_down = cf.cubic_curve(np.array([
        ((1 - i) * S + i * (width + back), -width)
        for i in np.linspace(0, 1, nel_flag + 1)
    ]),
                             boundary=cf.Boundary.NATURAL,
                             t=fl_down.knots('u'))
    fl_down = sf.edge_curves(ln_down, fl_down).raise_order(0, 2)
    geometric_refine(fl_down, grad, nel_rad - 1, direction='v', reverse=True)

    fl_back = cf.line((flag_length + rad_cyl, dpt[1], 0),
                      (flag_length + rad_cyl, upt[1], 0))
    ln_back = cf.line((width + back, -width, 0), (width + back, width, 0))
    fl_back = sf.edge_curves(ln_back,
                             fl_back).raise_order(2, 2).refine(40,
                                                               direction='u')
    geometric_refine(fl_back, grad, nel_rad - 1, direction='v', reverse=True)

    with G2(out + '.g2') as f:
        f.write([up, front, down, fl_up, fl_down, fl_back])

    root = etree.Element('geometry')
    etree.SubElement(root, 'patchfile').text = out + '.g2'
    topology = etree.SubElement(root, 'topology')
    for mid, sid, midx, sidx, rev in [(1, 2, 2, 1, False), (1, 4, 1, 2, False),
                                      (2, 3, 2, 1, False), (3, 5, 2, 1, False),
                                      (4, 6, 1, 2, False),
                                      (5, 6, 2, 1, False)]:
        etree.SubElement(topology, 'connection').attrib.update({
            'master':
            str(mid),
            'slave':
            str(sid),
            'midx':
            str(midx),
            'sidx':
            str(sidx),
            'reverse':
            'true' if rev else 'false',
        })

    topsets = etree.SubElement(root, 'topologysets')
    for name, index, entries in [('inflow', 3, [2]), ('outflow', 3, [6]),
                                 ('top', 3, [1, 4]), ('bottom', 3, [3, 5]),
                                 ('cylinder', 4, [1, 2, 3]),
                                 ('flag', 4, [4, 5, 6])]:
        topset = etree.SubElement(topsets, 'set')
        topset.attrib.update({'name': name, 'type': 'edge'})
        for pid in entries:
            item = etree.SubElement(topset, 'item')
            item.attrib['patch'] = str(pid)
            item.text = str(index)
    topset = etree.SubElement(topsets, 'set')
    topset.attrib.update({'name': 'inflow', 'type': 'vertex'})
    item = etree.SubElement(topset, 'item')
    item.attrib['patch'] = '2'
    item.text = '1 2'

    with open(out + '.xinp', 'wb') as f:
        f.write(
            etree.tostring(root,
                           pretty_print=True,
                           encoding='utf-8',
                           xml_declaration=True,
                           standalone=False))