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
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))
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
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])
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))
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)))
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))
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)
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)
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)
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)
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 ))
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)
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))
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))