def test_edge_curves(self):
        # create an arrow-like 2D geometry with the pointy end at (-1,1) towards up and left
        # mixes rational and non-rational curves with different parametrization spaces
        c1 = cf.circle_segment(pi / 2)
        c2 = Curve(BSplineBasis(2, [0, 0, 1, 2, 2]),
                   [[0, 1], [-1, 1], [-1, 0]])
        c3 = cf.circle_segment(pi / 2)
        c3.rotate(pi)
        c4 = Curve(BSplineBasis(2), [[0, -1], [1, 0]])

        surf = sf.edge_curves(c1, c2, c3, c4)

        # srf spits out parametric space (0,1)^2, so we sync these up to input curves
        c3.reverse()
        c4.reverse()
        c1.reparam()
        c2.reparam()
        c3.reparam()
        c4.reparam()

        for u in np.linspace(0, 1, 7):
            self.assertAlmostEqual(surf(u, 0)[0],
                                   c1(u)[0])  # x-coord, bottom crv
            self.assertAlmostEqual(surf(u, 0)[1],
                                   c1(u)[1])  # y-coord, bottom crv
        for u in np.linspace(0, 1, 7):
            self.assertAlmostEqual(surf(u, 1)[0], c3(u)[0])  # x-coord, top crv
            self.assertAlmostEqual(surf(u, 1)[1], c3(u)[1])  # y-coord, top crv
        for v in np.linspace(0, 1, 7):
            self.assertAlmostEqual(surf(0, v)[0],
                                   c4(v)[0])  # x-coord, left crv
            self.assertAlmostEqual(surf(0, v)[1],
                                   c4(v)[1])  # y-coord, left crv
        for v in np.linspace(0, 1, 7):
            self.assertAlmostEqual(surf(1, v)[0],
                                   c2(v)[0])  # x-coord, right crv
            self.assertAlmostEqual(surf(1, v)[1],
                                   c2(v)[1])  # y-coord, right crv

        # add a case where opposing sites have mis-matching rationality
        crvs = Surface().edges()  # returned in order umin, umax, vmin, vmax
        crvs[0].force_rational()
        crvs[1].reverse()
        crvs[2].reverse()
        # input curves should be clockwise oriented closed loop
        srf = sf.edge_curves(crvs[0], crvs[3], crvs[1], crvs[2])
        crvs[1].reverse()
        u = np.linspace(0, 1, 7)
        self.assertTrue(np.allclose(srf(u, 0).reshape((7, 2)), crvs[0](u)))
        self.assertTrue(np.allclose(srf(u, 1).reshape((7, 2)), crvs[1](u)))

        # test self-organizing curve ordering when they are not sequential
        srf = sf.edge_curves(crvs[0], crvs[2].reverse(), crvs[3], crvs[1])
        u = np.linspace(0, 1, 7)
        self.assertTrue(np.allclose(srf(u, 0).reshape((7, 2)), crvs[0](u)))
        self.assertTrue(np.allclose(srf(u, 1).reshape((7, 2)), crvs[1](u)))

        # test error handling
        with self.assertRaises(ValueError):
            srf = sf.edge_curves(crvs + (Curve(), ))  # 5 input curves
    def test_edge_curves(self):
        # create an arrow-like 2D geometry with the pointy end at (-1,1) towards up and left
        # mixes rational and non-rational curves with different parametrization spaces
        c1 = CurveFactory.circle_segment(pi / 2)
        c2 = Curve(BSplineBasis(2, [0, 0, 1, 2, 2]), [[0, 1], [-1, 1], [-1, 0]])
        c3 = CurveFactory.circle_segment(pi / 2)
        c3.rotate(pi)
        c4 = Curve(BSplineBasis(2), [[0, -1], [1, 0]])

        surf = SurfaceFactory.edge_curves(c1, c2, c3, c4)

        # srf spits out parametric space (0,1)^2, so we sync these up to input curves
        c3.reverse()
        c4.reverse()
        c1.reparam()
        c2.reparam()
        c3.reparam()
        c4.reparam()

        for u in np.linspace(0, 1, 7):
            self.assertAlmostEqual(surf(u, 0)[0], c1(u)[0])  # x-coord, bottom crv
            self.assertAlmostEqual(surf(u, 0)[1], c1(u)[1])  # y-coord, bottom crv
        for u in np.linspace(0, 1, 7):
            self.assertAlmostEqual(surf(u, 1)[0], c3(u)[0])  # x-coord, top crv
            self.assertAlmostEqual(surf(u, 1)[1], c3(u)[1])  # y-coord, top crv
        for v in np.linspace(0, 1, 7):
            self.assertAlmostEqual(surf(0, v)[0], c4(v)[0])  # x-coord, left crv
            self.assertAlmostEqual(surf(0, v)[1], c4(v)[1])  # y-coord, left crv
        for v in np.linspace(0, 1, 7):
            self.assertAlmostEqual(surf(1, v)[0], c2(v)[0])  # x-coord, right crv
            self.assertAlmostEqual(surf(1, v)[1], c2(v)[1])  # y-coord, right crv

        # add a case where opposing sites have mis-matching rationality
        crvs = Surface().edges() # returned in order umin, umax, vmin, vmax
        crvs[0].force_rational()
        crvs[1].reverse()
        crvs[2].reverse()
        # input curves should be clockwise oriented closed loop
        srf = SurfaceFactory.edge_curves(crvs[0], crvs[3], crvs[1], crvs[2])
        crvs[1].reverse()
        u = np.linspace(0,1,7)
        self.assertTrue(np.allclose(srf(u,0).reshape((7,2)), crvs[0](u)))
        self.assertTrue(np.allclose(srf(u,1).reshape((7,2)), crvs[1](u)))

        # test self-organizing curve ordering when they are not sequential
        srf = SurfaceFactory.edge_curves(crvs[0], crvs[2].reverse(), crvs[3], crvs[1])
        u = np.linspace(0,1,7)
        self.assertTrue(np.allclose(srf(u,0).reshape((7,2)), crvs[0](u)))
        self.assertTrue(np.allclose(srf(u,1).reshape((7,2)), crvs[1](u)))

        # test error handling
        with self.assertRaises(ValueError):
            srf = SurfaceFactory.edge_curves(crvs + (Curve(),)) # 5 input curves
Exemple #3
0
    def test_edge_curves_elasticity(self):
        # create an arrow-like 2D geometry with the pointy end at (-1,1) towards up and left
        # rebuild to avoid rational representations
        c1 = CurveFactory.circle_segment(pi / 2).rebuild(3,11)
        c2 = Curve(BSplineBasis(2, [0, 0, 1, 2, 2]), [[0, 1], [-1, 1], [-1, 0]])
        c3 = CurveFactory.circle_segment(pi / 2).rebuild(3,11)
        c3.rotate(pi)
        c4 = Curve(BSplineBasis(2), [[0, -1], [1, 0]]).rebuild(3,10)
        c4 = c4.rebuild(4,11)

        surf = SurfaceFactory.edge_curves([c1, c2, c3, c4], type='elasticity')

        # check right dimensions of output
        self.assertEqual(surf.shape[0], 11) # 11 controlpoints in the circle segment
        self.assertEqual(surf.shape[1], 13) # 11 controlpoints in c4, +2 for C0-knot in c1
        self.assertEqual(surf.order(0), 3)
        self.assertEqual(surf.order(1), 4)

        # check that c1 edge conforms to surface edge
        u = np.linspace(0,1,7)
        c1.reparam()
        pts_surf = surf(u,0.0)
        pts_c1   = c1(u)
        for (xs,xc) in zip(pts_surf[:,0,:], pts_c1):
            self.assertTrue(np.allclose(xs, xc))

        # check that c2 edge conforms to surface edge
        v = np.linspace(0,1,7)
        c2.reparam()
        pts_surf = surf(1.0,v)
        pts_c2   = c2(v)
        for (xs,xc) in zip(pts_surf[:,0,:], pts_c2):
            self.assertTrue(np.allclose(xs, xc))
Exemple #4
0
    def test_edge_curves(self):
        # create an arrow-like 2D geometry with the pointy end at (-1,1) towards up and left
        # mixes rational and non-rational curves with different parametrization spaces
        c1 = CurveFactory.circle_segment(pi / 2)
        c2 = Curve(BSplineBasis(2, [0, 0, 1, 2, 2]), [[0, 1], [-1, 1], [-1, 0]])
        c3 = CurveFactory.circle_segment(pi / 2)
        c3.rotate(pi)
        c4 = Curve(BSplineBasis(2), [[0, -1], [1, 0]])

        surf = SurfaceFactory.edge_curves(c1, c2, c3, c4)

        # srf spits out parametric space (0,1)^2, so we sync these up to input curves
        c3.reverse()
        c4.reverse()
        c1.reparam()
        c2.reparam()
        c3.reparam()
        c4.reparam()

        for u in np.linspace(0, 1, 7):
            self.assertAlmostEqual(surf(u, 0)[0], c1(u)[0])  # x-coord, bottom crv
            self.assertAlmostEqual(surf(u, 0)[1], c1(u)[1])  # y-coord, bottom crv
        for u in np.linspace(0, 1, 7):
            self.assertAlmostEqual(surf(u, 1)[0], c3(u)[0])  # x-coord, top crv
            self.assertAlmostEqual(surf(u, 1)[1], c3(u)[1])  # y-coord, top crv
        for v in np.linspace(0, 1, 7):
            self.assertAlmostEqual(surf(0, v)[0], c4(v)[0])  # x-coord, left crv
            self.assertAlmostEqual(surf(0, v)[1], c4(v)[1])  # y-coord, left crv
        for v in np.linspace(0, 1, 7):
            self.assertAlmostEqual(surf(1, v)[0], c2(v)[0])  # x-coord, right crv
            self.assertAlmostEqual(surf(1, v)[1], c2(v)[1])  # y-coord, right crv
Exemple #5
0
def thingy(radius, elements, out):
    right = cf.circle_segment(np.pi / 2)
    right.rotate(-np.pi / 4).translate((radius - 1, 0, 0))

    left = right.clone().rotate(np.pi)
    right.reverse()

    thingy = sf.edge_curves(left, right)
    thingy.raise_order(0, 1)
    thingy.refine(*[e - 1 for e in elements])

    with G2(out + '.g2') as f:
        f.write([thingy])
Exemple #6
0
def image_convex_surface(filename):
    """Generate a single B-spline surface corresponding to convex black domain
    of a black/white mask image. The algorithm traces the boundary and searches
    for 4 natural corner points. It will then generate 4 boundary curves which
    will be used to create the surface by Coons Patch.

    :param str filename: Name of image file to read
    :return: B-spline surface
    :rtype: :class:`splipy.Surface`
    """
    # generate boundary curve
    crv = image_curves(filename)

    # error test input
    if len(crv) != 1:
        raise RuntimeError(
            'Error: image_convex_surface expects a single closed curve. Multiple curves detected'
        )

    crv = crv[0]

    # parametric value of corner candidates. These are all in the range [0,1] and both 0 and 1 is present
    kinks = crv.get_kinks()

    # generate 4 corners
    if len(kinks) == 2:
        corners = [0, .25, .5, .75]

    elif len(kinks) == 3:
        corners = [0, (0 + kinks[1]) / 2, kinks[1], (1 + kinks[1]) / 2]

    elif len(kinks) == 4:
        if kinks[1] - kinks[0] > kinks[2] - kinks[1] and kinks[1] - kinks[
                0] > kinks[3] - kinks[2]:
            corners = [0, (kinks[0] + kinks[1]) / 2] + kinks[1:3]
        elif kinks[2] - kinks[1] > kinks[3] - kinks[2]:
            corners = [0, kinks[1], (kinks[1] + kinks[2]) / 2], kinks[2]
        else:
            corners = [0] + kinks[1:3] + [(kinks[2] + kinks[3]) / 2]

    else:
        while len(kinks) > 5:
            max_span = 0
            max_span_i = 0
            for i in range(1, len(kinks) - 1):
                max_span = max(max_span, kinks[i + 1] - kinks[i - 1])
                max_span_i = i
            del kinks[max_span_i]
        corners = kinks[0:4]

    return surface_factory.edge_curves(crv.split(corners))
Exemple #7
0
 def test_edge_curves_finitestrain_lshape(self):
     # Create an L-shape geometry with an interior 270-degree angle at the origin (u=.5, v=1)
     c1 = CurveFactory.polygon([[-1, 1], [-1,-1], [1,-1]])
     c2 = CurveFactory.polygon([[ 1,-1], [ 1, 0]])
     c3 = CurveFactory.polygon([[ 1, 0], [ 0, 0], [0, 1]])
     c4 = CurveFactory.polygon([[ 0, 1], [-1, 1]])
     c1.refine(2).raise_order(1)
     c2.refine(2).raise_order(1)
     surf = SurfaceFactory.edge_curves([c1, c2, c3, c4], type='finitestrain')
     # the quickest way to check for self-intersecting geometry here is that
     # the normal is pointing the wrong way: down z-axis instead of up
     surf.reparam().set_dimension(3)
     self.assertTrue(surf.normal(0.5, 0.98)[2] > 0.0)
     # also check that no controlpoints leak away into the first quadrant
     self.assertFalse(np.any(np.logical_and(surf[:,:,0] > 0, surf[:,:,1] > 0)))
Exemple #8
0
def image_convex_surface(filename):
    """Generate a single B-spline surface corresponding to convex black domain
    of a black/white mask image. The algorithm traces the boundary and searches
    for 4 natural corner points. It will then generate 4 boundary curves which
    will be used to create the surface by Coons Patch.

    :param str filename: Name of image file to read
    :return: B-spline surface
    :rtype: :class:`splipy.Surface`
    """
    # generate boundary curve
    crv = image_curves(filename)

    # error test input
    if len(crv) != 1:
        raise RuntimeError('Error: image_convex_surface expects a single closed curve. Multiple curves detected')

    crv = crv[0]

    # parametric value of corner candidates. These are all in the range [0,1] and both 0 and 1 is present
    kinks = crv.get_kinks()

    # generate 4 corners
    if len(kinks) == 2:
        corners = [0, .25, .5, .75]

    elif len(kinks) == 3:
        corners = [0, (0+kinks[1])/2, kinks[1], (1+kinks[1])/2]

    elif len(kinks) == 4:
        if kinks[1]-kinks[0] > kinks[2]-kinks[1] and kinks[1]-kinks[0] > kinks[3]-kinks[2]:
            corners = [0, (kinks[0]+kinks[1])/2] + kinks[1:3]
        elif kinks[2]-kinks[1] > kinks[3]-kinks[2]:
            corners = [0, kinks[1], (kinks[1]+kinks[2])/2], kinks[2]
        else:
            corners = [0] + kinks[1:3] + [(kinks[2]+kinks[3])/2]

    else:
        while len(kinks) > 5:
            max_span   = 0
            max_span_i = 0
            for i in range(1,len(kinks)-1):
                max_span   = max(max_span, kinks[i+1]-kinks[i-1])
                max_span_i = i
            del kinks[max_span_i]
        corners = kinks[0:4]

    return surface_factory.edge_curves(crv.split(corners))
Exemple #9
0
    def test_3d_self_double_connection(self):
        c1 = curve_factory.circle(r=1, center=(3, 0, 0), normal=(0, 1, 0))
        c2 = curve_factory.circle(r=2, center=(3, 0, 0), normal=(0, 1, 0))
        ring = surface_factory.edge_curves(c1, c2)
        vol = volume_factory.revolve(ring)
        vol = vol.split(vol.knots('u')[0], direction='u')  # break periodicity
        vol = vol.split(vol.knots('w')[0], direction='w')
        model = SplineModel(3, 3)
        model.add(vol, raise_on_twins=False)

        writer = IFEMWriter(model)
        expected = [
            IFEMConnection(1, 1, 1, 2, 0),
            IFEMConnection(1, 1, 5, 6, 0)
        ]
        for connection, want in zip(writer.connections(), expected):
            self.assertEqual(connection, want)
Exemple #10
0
    def test_volume_loft(self):
        crv1 = Curve(BSplineBasis(3, range(11), 1),
                     [[1, -1], [1, 0], [1, 1], [-1, 1], [-1, 0], [-1, -1]])
        crv2 = CurveFactory.circle(2) + (0, 0, 1)
        crv3 = Curve(BSplineBasis(4, range(11), 2),
                     [[1, -1, 2], [1, 1, 2], [-1, 1, 2], [-1, -1, 2]])
        crv4 = CurveFactory.circle(2) + (0, 0, 3)

        surf = []
        for c in [crv1, crv2, crv3, crv4]:
            c2 = c.clone()
            c2.project('z')
            surf.append(SurfaceFactory.edge_curves(c, c2))

        vol = VolumeFactory.loft(surf)

        surf[0].set_dimension(3)  # for convenience when evaluating
        t = np.linspace(0, 1, 9)
        s = np.linspace(0, 1, 9)

        u = np.linspace(surf[0].start(0), surf[0].end(0), 9)
        v = np.linspace(surf[0].start(1), surf[0].end(1), 9)
        pt = surf[0](u, v)
        pt2 = vol(s, t, 0).reshape(9, 9, 3)
        self.assertAlmostEqual(np.linalg.norm(pt - pt2), 0.0)

        u = np.linspace(surf[1].start(0), surf[1].end(0), 9)
        u = np.linspace(surf[1].start(1), surf[1].end(1), 9)
        pt = surf[1](u, v)
        pt2 = vol(s, t, 1).reshape(9, 9, 3)
        self.assertAlmostEqual(np.linalg.norm(pt - pt2), 0.0)

        u = np.linspace(surf[2].start(0), surf[2].end(0), 9)
        v = np.linspace(surf[2].start(1), surf[2].end(1), 9)
        pt = surf[2](u, v)
        pt2 = vol(s, t, 2).reshape(9, 9, 3)
        self.assertAlmostEqual(np.linalg.norm(pt - pt2), 0.0)

        u = np.linspace(surf[3].start(0), surf[3].end(0), 9)
        v = np.linspace(surf[3].start(1), surf[3].end(1), 9)
        pt = surf[3](u, v)
        pt2 = vol(s, t, 3).reshape(9, 9, 3)
        self.assertAlmostEqual(np.linalg.norm(pt - pt2), 0.0)
Exemple #11
0
    def test_volume_loft(self):
        crv1 = Curve(BSplineBasis(3, range(11), 1), [[1,-1], [1,0], [1,1], [-1,1], [-1,0], [-1,-1]])
        crv2 = CurveFactory.circle(2) + (0,0,1)
        crv3 = Curve(BSplineBasis(4, range(11), 2), [[1,-1,2], [1,1,2], [-1,1,2], [-1,-1,2]])
        crv4 = CurveFactory.circle(2) + (0,0,3)
        
        surf = []
        for c in [crv1, crv2, crv3, crv4]:
            c2 = c.clone()
            c2.project('z')
            surf.append(SurfaceFactory.edge_curves(c, c2))

        vol = VolumeFactory.loft(surf)

        surf[0].set_dimension(3) # for convenience when evaluating
        t = np.linspace( 0, 1, 9)
        s = np.linspace( 0, 1, 9)

        u = np.linspace(surf[0].start(0), surf[0].end(0), 9)
        v = np.linspace(surf[0].start(1), surf[0].end(1), 9)
        pt  = surf[0](u,v)
        pt2 = vol(s,t,0).reshape(9,9,3)
        self.assertAlmostEqual(np.linalg.norm(pt-pt2), 0.0)

        u = np.linspace(surf[1].start(0), surf[1].end(0), 9)
        u = np.linspace(surf[1].start(1), surf[1].end(1), 9)
        pt  = surf[1](u,v)
        pt2 = vol(s,t,1).reshape(9,9,3)
        self.assertAlmostEqual(np.linalg.norm(pt-pt2), 0.0)

        u = np.linspace(surf[2].start(0), surf[2].end(0), 9)
        v = np.linspace(surf[2].start(1), surf[2].end(1), 9)
        pt  = surf[2](u,v)
        pt2 = vol(s,t,2).reshape(9,9,3)
        self.assertAlmostEqual(np.linalg.norm(pt-pt2), 0.0)

        u = np.linspace(surf[3].start(0), surf[3].end(0), 9)
        v = np.linspace(surf[3].start(1), surf[3].end(1), 9)
        pt  = surf[3](u,v)
        pt2 = vol(s,t,3).reshape(9,9,3)
        self.assertAlmostEqual(np.linalg.norm(pt-pt2), 0.0)
Exemple #12
0
def loft(*surfaces):
    if len(surfaces) == 1:
        surfaces = surfaces[0]

    # clone input, so we don't change those references
    # make sure everything has the same dimension since we need to compute length
    surfaces = [s.clone().set_dimension(3) for s in surfaces]
    if len(surfaces) == 2:
        return SurfaceFactory.edge_curves(surfaces)
    elif len(surfaces) == 3:
        # can't do cubic spline interpolation, so we'll do quadratic
        basis3 = BSplineBasis(3)
        dist = basis3.greville()
    else:
        x = [s.center() for s in surfaces]

        # create knot vector from the euclidian length between the surfaces
        dist = [0]
        for (x1, x0) in zip(x[1:], x[:-1]):
            dist.append(dist[-1] + np.linalg.norm(x1 - x0))

        # using "free" boundary condition by setting N'''(u) continuous at second to last and second knot
        knot = [dist[0]] * 4 + dist[2:-2] + [dist[-1]] * 4
        basis3 = BSplineBasis(4, knot)

    n = len(surfaces)
    for i in range(n):
        for j in range(i + 1, n):
            Surface.make_splines_identical(surfaces[i], surfaces[j])

    basis1 = surfaces[0].bases[0]
    basis2 = surfaces[0].bases[1]
    m1 = basis1.num_functions()
    m2 = basis2.num_functions()
    dim = len(surfaces[0][0])
    u = basis1.greville()  # parametric interpolation points
    v = basis2.greville()
    w = dist

    # compute matrices
    Nu = basis1(u)
    Nv = basis2(v)
    Nw = basis3(w)
    Nu_inv = np.linalg.inv(Nu)
    Nv_inv = np.linalg.inv(Nv)
    Nw_inv = np.linalg.inv(Nw)

    # compute interpolation points in physical space
    x = np.zeros((m1, m2, n, dim))
    for i in range(n):
        tmp = np.tensordot(Nv, surfaces[i].controlpoints, axes=(1, 1))
        x[:, :, i, :] = np.tensordot(Nu, tmp, axes=(1, 1))

    # solve interpolation problem
    cp = np.tensordot(Nw_inv, x, axes=(1, 2))
    cp = np.tensordot(Nv_inv, cp, axes=(1, 2))
    cp = np.tensordot(Nu_inv, cp, axes=(1, 2))

    # re-order controlpoints so they match up with Surface constructor
    cp = np.reshape(cp.transpose((2, 1, 0, 3)), (m1 * m2 * n, dim))

    return Volume(basis1, basis2, basis3, cp, surfaces[0].rational)
def loft(*surfaces):
    if len(surfaces) == 1:
        surfaces = surfaces[0]

    # clone input, so we don't change those references
    # make sure everything has the same dimension since we need to compute length
    surfaces = [s.clone().set_dimension(3) for s in surfaces]
    if len(surfaces)==2:
        return SurfaceFactory.edge_curves(surfaces)
    elif len(surfaces)==3:
        # can't do cubic spline interpolation, so we'll do quadratic
        basis3 = BSplineBasis(3)
        dist  = basis3.greville()
    else:
        x = [s.center() for s in surfaces]

        # create knot vector from the euclidian length between the surfaces
        dist = [0]
        for (x1,x0) in zip(x[1:],x[:-1]):
            dist.append(dist[-1] + np.linalg.norm(x1-x0))

        # using "free" boundary condition by setting N'''(u) continuous at second to last and second knot
        knot = [dist[0]]*4 + dist[2:-2] + [dist[-1]]*4
        basis3 = BSplineBasis(4, knot)

    n = len(surfaces)
    for i in range(n):
        for j in range(i+1,n):
            Surface.make_splines_identical(surfaces[i], surfaces[j])

    basis1 = surfaces[0].bases[0]
    basis2 = surfaces[0].bases[1]
    m1     = basis1.num_functions()
    m2     = basis2.num_functions()
    dim    = len(surfaces[0][0])
    u      = basis1.greville() # parametric interpolation points
    v      = basis2.greville()
    w      = dist

    # compute matrices
    Nu     = basis1(u)
    Nv     = basis2(v)
    Nw     = basis3(w)
    Nu_inv = np.linalg.inv(Nu)
    Nv_inv = np.linalg.inv(Nv)
    Nw_inv = np.linalg.inv(Nw)

    # compute interpolation points in physical space
    x      = np.zeros((m1,m2,n, dim))
    for i in range(n):
        tmp        = np.tensordot(Nv, surfaces[i].controlpoints, axes=(1,1))
        x[:,:,i,:] = np.tensordot(Nu, tmp                      , axes=(1,1))

    # solve interpolation problem
    cp = np.tensordot(Nw_inv, x,  axes=(1,2))
    cp = np.tensordot(Nv_inv, cp, axes=(1,2))
    cp = np.tensordot(Nu_inv, cp, axes=(1,2))

    # re-order controlpoints so they match up with Surface constructor
    cp = np.reshape(cp.transpose((2, 1, 0, 3)), (m1*m2*n, dim))

    return Volume(basis1, basis2, basis3, cp, surfaces[0].rational)
Exemple #14
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 #15
0
def loft(*surfaces):
    """  Generate a volume by lofting a series of surfaces

    The resulting volume is interpolated at all input surfaces and a smooth transition
    between these surfaces is computed as a cubic spline interpolation in the lofting
    direction. In the case that insufficient surfaces are provided as input (less than 4
    surfaces), then a quadratic or linear interpolation is performed.

    Note that the order of input surfaces matter as they will be interpolated in this
    particular order. Also note that the surfaces need to be parametrized in the same
    direction, otherwise you will encounter self-intersecting result volume as it is
    wrapping in on itself.

    :param Surfaces surfaces:  A sequence of surfaces to be lofted
    :return: Lofted volume
    :rtype: Volume

    Examples:

    .. code:: python

        from splipy import surface_factory, volume_factory
        srf1 = surface_factory.disc(r=1)
        srf2 = 1.5 * srf1 + [0,0,1]
        srf3 = 1.0 * srf1 + [0,0,2]
        srf4 = 1.3 * srf1 + [0,0,4]
        vol = volume_factory.loft(srf1, srf2, srf3, srf4)

        # alternatively you can provide all input curves as a list
        all_my_surfaces = [srf1, srf2, srf3, srf4]
        vol = volume_factory.loft(all_my_surfaces)

    """
    if len(surfaces) == 1:
        surfaces = surfaces[0]

    # clone input, so we don't change those references
    # make sure everything has the same dimension since we need to compute length
    surfaces = [s.clone().set_dimension(3) for s in surfaces]
    if len(surfaces) == 2:
        return SurfaceFactory.edge_curves(surfaces)
    elif len(surfaces) == 3:
        # can't do cubic spline interpolation, so we'll do quadratic
        basis3 = BSplineBasis(3)
        dist = basis3.greville()
    else:
        x = [s.center() for s in surfaces]

        # create knot vector from the euclidian length between the surfaces
        dist = [0]
        for (x1, x0) in zip(x[1:], x[:-1]):
            dist.append(dist[-1] + np.linalg.norm(x1 - x0))

        # using "free" boundary condition by setting N'''(u) continuous at second to last and second knot
        knot = [dist[0]] * 4 + dist[2:-2] + [dist[-1]] * 4
        basis3 = BSplineBasis(4, knot)

    n = len(surfaces)
    for i in range(n):
        for j in range(i + 1, n):
            Surface.make_splines_identical(surfaces[i], surfaces[j])

    basis1 = surfaces[0].bases[0]
    basis2 = surfaces[0].bases[1]
    m1 = basis1.num_functions()
    m2 = basis2.num_functions()
    dim = len(surfaces[0][0])
    u = basis1.greville()  # parametric interpolation points
    v = basis2.greville()
    w = dist

    # compute matrices
    Nu = basis1(u)
    Nv = basis2(v)
    Nw = basis3(w)
    Nu_inv = np.linalg.inv(Nu)
    Nv_inv = np.linalg.inv(Nv)
    Nw_inv = np.linalg.inv(Nw)

    # compute interpolation points in physical space
    x = np.zeros((m1, m2, n, dim))
    for i in range(n):
        tmp = np.tensordot(Nv, surfaces[i].controlpoints, axes=(1, 1))
        x[:, :, i, :] = np.tensordot(Nu, tmp, axes=(1, 1))

    # solve interpolation problem
    cp = np.tensordot(Nw_inv, x, axes=(1, 2))
    cp = np.tensordot(Nv_inv, cp, axes=(1, 2))
    cp = np.tensordot(Nu_inv, cp, axes=(1, 2))

    # re-order controlpoints so they match up with Surface constructor
    cp = np.reshape(cp.transpose((2, 1, 0, 3)), (m1 * m2 * n, dim))

    return Volume(basis1, basis2, basis3, cp, surfaces[0].rational)
Exemple #16
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))
Exemple #17
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)
def cylinder(radius, length, elements_rad, elements_len, out):
    square = sf.square(size=2 * radius / 3,
                       lower_left=(-radius / 3, -radius / 3))
    square.set_dimension(3)
    square.raise_order(2, 2)
    square.refine(elements_rad - 1, elements_rad - 1)

    pts = np.zeros((elements_rad + 1, 3))
    angles = np.linspace(-np.pi / 4, np.pi / 4, elements_rad + 1)
    pts[:, 0] = radius * np.cos(angles)
    pts[:, 1] = radius * np.sin(angles)
    curve = cf.cubic_curve(pts, t=square.knots('v'))

    sector = sf.edge_curves(square.section(u=-1), curve)
    sector.raise_order(0, 2)
    sector.refine(0, elements_rad - 1)
    sector.swap()

    sectors = [
        sector.clone().rotate(angle)
        for angle in [0, np.pi / 2, np.pi, np.pi * 3 / 2]
    ]
    patches = [
        vf.extrude(patch, (0, 0, length)) for patch in [square] + sectors
    ]
    for patch in patches:
        patch.raise_order(0, 0, 2)
        patch.refine(0, 0, elements_len)

    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, 1, True),
                                      (1, 4, 1, 1, True), (1, 5, 3, 1, False),
                                      (2, 3, 4, 3, False), (2, 5, 3, 4, False),
                                      (3, 4, 4, 3, False),
                                      (4, 5, 4, 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, start, idx in [('wall', 2, 2), ('inflow', 1, 5),
                             ('outflow', 1, 6)]:
        topset = etree.SubElement(topsets, 'set')
        topset.attrib.update({'name': name, 'type': 'face'})
        for i in range(start, 6):
            item = etree.SubElement(topset, 'item')
            item.attrib['patch'] = str(i)
            item.text = str(idx)

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