def to_ifem(self, p, ngeom, ntexture, method='full', irange=[None,None], jrange=[None,None]): translate = { 'emodulus25' : 'stiffness', 'kx' : 'permx', 'ky' : 'permy', 'kz' : 'permz', 'poissonratio25': 'poisson'} h5_filename = 'textures.h5' h5_file = h5py.File(h5_filename, 'w') vol, textures = self.texture(p, ngeom, ntexture, method, irange, jrange) # augment dataset with missing information if 'kx' in textures and not 'ky' in textures: textures['ky'] = textures['kx'] # print information to png-images and hdf5-files print(r'<porotexturematerial>') for name, data in textures.items(): # translate to more IFEM-friendly terminology if name in translate: name = translate[name] h5_file.create_dataset(name, data=data, compression='gzip') a, b = min(data.flat), max(data.flat) img = ((data - a) / (b - a) * 255).astype(np.uint8) n = data.shape img = img.reshape(n[0], n[1]*n[2]) print(' <property file="{}.png" min="{}" max="{}" name="{}" nx="{}" ny="{}" nz="{}"/>'.format(name, a,b, name, n[0], n[1], n[2])) cv2.imwrite(name+'.png', img) print(r'</porotexturematerial>') h5_file.close() print(f'Written {h5_filename}') with G2('geom.g2') as myfile: myfile.write(vol)
def test_write_and_read_multipatch_surface(self): # write teapot to file and test if its there teapot = SurfaceFactory.teapot() with G2('teapot.g2') as myfile: myfile.write(teapot) self.assertTrue(os.path.isfile('teapot.g2')) # read the written file and compare that it's the same thing with G2('teapot.g2') as myfile: read_teapot = myfile.read() self.assertEqual(len(read_teapot), 32) for i in range(32): self.assertTrue(np.all(teapot[i].shape == read_teapot[i].shape)) # clean up after us os.remove('teapot.g2')
def test_read_elementary_curves(self): with G2(THIS_DIR + '/geometries/elementary_curves.g2') as myfile: my_curves = myfile.read() self.assertEqual(len(my_curves), 3) # check circle (r=3, center=(1,0,0), xaxis=(1,1,0) circle = my_curves[0] t = np.linspace(circle.start(0), circle.end(0), 25) x = circle(t) self.assertTrue(np.allclose((x[:,0]-1)**2 + x[:,1]**2 + x[:,2]**2, 3**2)) self.assertTrue(np.allclose(circle[0], [3/np.sqrt(2)+1,3/np.sqrt(2),0,1])) # check ellipse (r1=3, r2=5, center=(1,0,0), xaxis(0,1,0) ellipse = my_curves[1] t = np.linspace(ellipse.start(0), ellipse.end(0), 25) x = ellipse(t) self.assertTrue(np.allclose(ellipse[0], [1,3,0,1])) self.assertTrue(np.allclose(ellipse[2], [-4,0,0,1])) # check line piece (p1=(1,0,0), direction=(0,6,0), length=4 (times direction)) line = my_curves[2] self.assertAlmostEqual(line.length(), 24) self.assertFalse(line.rational) self.assertTrue(np.allclose(line[0], [1,0,0]))
def test_write_and_read_surface(self): # write disc to file and test if its there disc = SurfaceFactory.disc(type='square') with G2('disc.g2') as myfile: myfile.write(disc) self.assertTrue(os.path.isfile('disc.g2')) # read the written file and compare that it's the same thing with G2('disc.g2') as myfile: read_disc = myfile.read() self.assertEqual(len(read_disc), 1) read_disc = read_disc[0] self.assertTrue(np.all(disc.shape == read_disc.shape)) # clean up after us os.remove('disc.g2')
def write(self, fn, order=4): pids, patches = {}, [] for i, (name, patch) in enumerate(self.items()): pids[name] = i diff = [o - order for o in patch.order()] patches.append(patch.lower_order(*diff)) with G2(fn + '.g2') as f: f.write(patches) dom = xml.Element('geometry') dom.attrib['dim'] = str(patches[0].pardim) xml.SubElement(dom, 'patchfile').text = fn + '.g2' topology = xml.SubElement(dom, 'topology') for (master, medge), (slave, sedge, rev, periodic) in self.masters.items(): mid = pids[master] + 1 sid = pids[slave] + 1 if mid > sid: mid, sid = sid, mid medge, sedge = sedge, medge xml.SubElement(topology, 'connection').attrib.update({ 'master': str(mid), 'midx': str(medge), 'slave': str(sid), 'sidx': str(sedge), 'reverse': 'true' if rev else 'false', 'periodic': 'true' if periodic else 'false', }) for patch, direction in self.periodics: pid = pids[patch] + 1 xml.SubElement(topology, 'periodic').attrib.update({ 'patch': str(pid), 'dir': str(direction), }) topsets = xml.SubElement(dom, 'topologysets') for name, kinds in self.boundaries.items(): for kind, items in kinds.items(): topset = xml.SubElement(topsets, 'set') topset.attrib.update({'name': name, 'type': kind}) for patch, number in items: item = xml.SubElement(topset, 'item') item.attrib['patch'] = str(pids[patch] + 1) item.text = str(number) xml.ElementTree(dom).write(fn + '.xinp', encoding='utf-8', xml_declaration=True, pretty_print=True, standalone=False)
def test_read_elementary_surfaces(self): with G2(THIS_DIR + '/geometries/elementary_surfaces.g2') as myfile: my_surfaces = myfile.read() self.assertEqual(len(my_surfaces), 4) # check cylinder ( center=(1,0,0), x-axis=(0,1,0), radius=2) cylinder = my_surfaces[0] self.assertTrue(cylinder.rational) self.assertTrue(np.allclose(cylinder[0,0], [1,2,0,1])) # check sphere ( radius=1.5, center=(4,0,0), z-axis=(0,1,1)) sphere = my_surfaces[1] self.assertTrue(sphere.rational) self.assertTrue(np.allclose(sphere[0,-1], [4,3/np.sqrt(8),3/np.sqrt(8), 1])) # north pole # check disc ( radius=2.5, center=(6,0,0), x-axis=(0,0,-1 ), normal=(1,0,0) disc = my_surfaces[2] self.assertTrue(disc.rational) self.assertTrue(np.allclose(disc[ 0,0], [6,0,0,1])) # center self.assertTrue(np.allclose(disc[-1,0], [6,0,-2.5,1])) # center+x_axis # check torus ( ...) torus = my_surfaces[3] self.assertTrue(torus.rational)
def test_read_doublespaced(self): with G2(THIS_DIR + '/test_geometries/lshape.g2' ) as myfile: # controlpoints are separated by two spaces one_surf = myfile.read() self.assertEqual(len(one_surf), 1) self.assertEqual(one_surf[0].shape[0], 3) self.assertEqual(one_surf[0].shape[1], 2)
def test_write_and_read_volume(self): # write sphere to file and test if its there sphere = VolumeFactory.sphere(type='square') with G2('sphere.g2') as myfile: myfile.write(sphere) self.assertTrue(os.path.isfile('sphere.g2')) # read the written file and compare that it's the same thing with G2('sphere.g2') as myfile: read_sphere = myfile.read() self.assertEqual(len(read_sphere), 1) read_sphere = read_sphere[0] self.assertTrue(np.all(sphere.shape == read_sphere.shape)) self.assertTrue(sphere.rational == read_sphere.rational) # clean up after us os.remove('sphere.g2')
def write(self, filename): lines = [ "<?xml version='1.0' encoding='utf-8' standalone='no'?>", "<topology>", ] for connection in self.connections(): lines.append(' <connection master="{}" slave="{}" midx="{}" sidx="{}" orient="{}"/>'.format( connection.master, connection.slave, connection.midx, connection.sidx, connection.orient, )) lines.extend(["</topology>"]) with open(filename + '-topology.xinp', 'wb') as f: f.write('\n'.join(lines).encode('utf-8') + b'\n') lines = [ "<?xml version='1.0' encoding='utf-8' standalone='no'?>", "<topologysets>", ] names = sorted({ node.name for node in self.model.catalogue.nodes(self.model.pardim - 1) if node.name is not None }) for name in names: entries = {} for node in self.model.catalogue.nodes(self.model.pardim - 1): if node.name != name: continue parent = node.owner sub_idx = next(idx for idx, sub in enumerate(parent.lower_nodes[self.model.pardim - 1]) if sub is node) entries.setdefault(self.node_ids[parent], set()).add(sub_idx) if entries: kind = {2: 'face', 1: 'edge', 0: 'vertex'}[self.model.pardim - 1] lines.append(' <set name="{}" type="{}">'.format(name, kind)) for node_id, sub_ids in entries.items(): lines.append(' <item patch="{}">{}</item>'.format( node_id + 1, ' '.join(str(i+1) for i in sorted(sub_ids)) )) lines.append(' </set>') lines.extend(["</topologysets>"]) with open(filename + '-topologysets.xinp', 'wb') as f: f.write('\n'.join(lines).encode('utf-8') + b'\n') with G2(filename + '.g2') as f: f.write([n.obj for n in self.nodes])
def test_from_step(self): # quite large nasty g2 file which contains cylinders, planes, trimming etc with G2(THIS_DIR + '/geometries/winglet_from_step.g2') as myfile: my_surfaces = myfile.read() trim_curves = myfile.trimming_curves # we only test that we are able to parse this file. No claims as to the # correctness of this parsing self.assertEqual(len(my_surfaces), 19) self.assertEqual(len(trim_curves), 122)
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') from splipy.io import G2 with G2('out.g2') as myfile: myfile.write(surf) # 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] > 1e-10, surf[:, :, 1] > 1e-10)))
def ifem_solve(root, mu, i): with open('case.xinp') as f: template = Template(f.read()) with open(root / 'case.xinp', 'w') as f: f.write(template.render(geometry='square.g2', **mu)) patch = Surface() patch.set_dimension(3) with G2(str(root / 'square.g2')) as g2: g2.write(patch) try: g2.fstream.close() except: pass result = run([ '/home/eivind/repos/IFEM/Apps/Poisson/build/bin/Poisson', 'case.xinp', '-adap', '-hdf5' ], cwd=root, stdout=PIPE, stderr=PIPE) result.check_returncode() with h5py.File(root / 'case.hdf5', 'r') as h5: final = str(len(h5) - 1) group = h5[final]['Poisson-1'] patchbytes = group['basis']['1'][:].tobytes() geompatch = lr.LRSplineSurface(patchbytes) coeffs = group['fields']['u']['1'][:] solpatch = geompatch.clone() solpatch.controlpoints = coeffs.reshape(len(solpatch), -1) with open(f'poisson-mesh-single-{i}.ps', 'wb') as f: geompatch.write_postscript(f) return geompatch, solpatch
def test_read_rational_surf(self): with G2(THIS_DIR + '/geometries/torus.g2') as myfile: surf = myfile.read() self.assertEqual(len(surf), 1) surf = surf[0] self.assertTrue(surf.rational)
# Examples on how to write geometries to file # # Author: Kjetil Andre Johannessen # Institute: Norwegian University of Science and Technology (NTNU) # Date: March 2016 # from sys import path path.append('../') from splipy.io import G2, STL import splipy.surface_factory as surfaces # create a NURBS torus torus = surfaces.torus(minor_r=1, major_r=4) # STL files are tessellated linear triangles. View with i.e. meshlab with STL('torus.stl') as my_file: my_file.write(torus, n=(50, 150)) # specify resolution of 50x150 evaluation pts # G2 files are native GoTools (http://www.sintef.no/projectweb/geometry-toolkits/gotools/) with G2('torus.g2') as my_file: my_file.write(torus)
def export(polygon, project, manager, boundary_mode='exterior', rotation_mode='none', coords='utm33n', resolution=None, maxpts=None, format='png', structured=False, colormap='Terrain', invert=False, texture=False, zero_sea_level=True, filename=None, directory=None, axis_align=False): # Sanitize parameters image_mode = is_image_format(format) if not supports_texture(format): texture = False if not supports_structured_choice(format): structured = is_structured_format(format) if format == 'tiff' and coords != 'utm33n': raise ValueError( 'GeoTIFF output for other formats than UTM33N is not supported') manager.report_max(4 if texture else 3) manager.report_message('Generating geometry') if structured: (in_x, in_y), (out_x, out_y), trf = polygon.generate_meshgrid( boundary_mode, rotation_mode, in_coords=project.coords, out_coords=coords, resolution=resolution, maxpts=maxpts, axis_align=axis_align, ) else: (in_x, in_y), (out_x, out_y), tri = polygon.generate_triangulation( in_coords=project.coords, out_coords=coords, resolution=resolution, ) manager.increment_progress() if texture: manager.report_message('Generating texture coordinates') left = np.min(out_x) right = np.max(out_x) down = np.min(out_y) up = np.max(out_y) uvcoords = np.stack([(out_x - left) / (right - left), (out_y - down) / (up - down)], axis=out_x.ndim) manager.increment_progress() manager.report_message('Generating data') data = polygon.interpolate(project, in_x, in_y) if not zero_sea_level: data -= np.min(data) manager.increment_progress() manager.report_message('Saving file') if filename is None: filename = hashlib.sha256(data.data).hexdigest() + '.' + format filename = Path(directory) / filename if image_mode: array_to_image(data, format, filename, cmap=colormap, invert=invert) elif format == 'g2': from splipy import Surface, BSplineBasis from splipy.io import G2 cpts = np.stack([out_x, out_y, data[..., 0]], axis=2) knots = [[0.0] + list(map(float, range(n))) + [float(n - 1)] for n in data.shape[:2]] bases = [BSplineBasis(order=2, knots=kts) for kts in knots] srf = Surface(*bases, cpts, raw=True) with G2(filename) as g2: g2.write(srf) elif format == 'stl': from stl.mesh import Mesh as STLMesh mesh = STLMesh(np.zeros(tri.shape[0], STLMesh.dtype)) mesh.vectors[:, :, 0] = out_x[tri] mesh.vectors[:, :, 1] = out_y[tri] mesh.vectors[:, :, 2] = data[tri, 0] mesh.save(filename) elif format in ('vtk', 'vtu', 'vts'): import vtk import vtk.util.numpy_support as vtknp pointsarray = np.stack([out_x.flat, out_y.flat, data.flat], axis=1) points = vtk.vtkPoints() points.SetData(vtknp.numpy_to_vtk(pointsarray)) if structured: grid = vtk.vtkStructuredGrid() grid.SetDimensions(*out_x.shape[::-1], 1) else: ncells = len(tri) cellarray = np.concatenate( [3 * np.ones((ncells, 1), dtype=int), tri], axis=1) cells = vtk.vtkCellArray() cells.SetCells(ncells, vtknp.numpy_to_vtkIdTypeArray(cellarray.ravel())) grid = vtk.vtkUnstructuredGrid() grid.SetCells(vtk.VTK_TRIANGLE, cells) grid.SetPoints(points) if texture: grid.GetPointData().SetTCoords( vtknp.numpy_to_vtk(uvcoords.reshape(-1, 2))) if format == 'vts': writer = vtk.vtkXMLStructuredGridWriter() elif format == 'vtu': writer = vtk.vtkXMLUnstructuredGridWriter() else: writer = vtk.vtkStructuredGridWriter( ) if structured else vtk.vtkUnstructuredGridWriter() writer.SetFileName(filename) writer.SetInputData(grid) writer.Write() elif format == 'tiff': import tifffile tifffile.imwrite( filename, data[:, ::-1].T, extratags=[ # GeoKeyDirectoryTag ( 34735, 'h', 28, ( 1, 1, 1, 6, # Version and number of geo keys 1024, 0, 1, 1, # GTModelTypeGeoKey (2D projected CRS) 1025, 0, 1, 1, # GTRasterTypeGeoKey (pixels denote areas) 2048, 0, 1, 4258, # GeodeticCRSGeoKey (ETRS89) 2050, 0, 1, 6258, # GeodeticDatumGeoKey (ETRS89) 2051, 0, 1, 8901, # PrimeMeridianGeoKey (Greenwich) 3072, 0, 1, 25833, # ProjectedCRSGeoKey (ETRS89: UTM33N) ), True), # ModelTransformationTag (34264, 'd', 16, tuple(trf.flat), True), ]) manager.increment_progress() return filename
def test_orient(self): connections = [ [ IFEMConnection(1, 2, 2, 1, 0), IFEMConnection(1, 3, 4, 3, 0), IFEMConnection(1, 5, 6, 5, 0), IFEMConnection(2, 4, 4, 3, 0), IFEMConnection(2, 6, 6, 5, 0), IFEMConnection(3, 4, 2, 1, 0), IFEMConnection(3, 7, 6, 5, 0), IFEMConnection(4, 8, 6, 5, 0), IFEMConnection(5, 6, 2, 1, 0), IFEMConnection(5, 7, 4, 3, 0), IFEMConnection(6, 8, 4, 3, 0), IFEMConnection(7, 8, 2, 1, 0) ], [ IFEMConnection(1, 2, 2, 1, 3), IFEMConnection(1, 3, 4, 3, 0), IFEMConnection(1, 5, 6, 5, 0), IFEMConnection(2, 4, 3, 3, 1), IFEMConnection(2, 6, 5, 5, 1), IFEMConnection(3, 4, 2, 1, 0), IFEMConnection(3, 7, 6, 5, 0), IFEMConnection(4, 8, 6, 5, 0), IFEMConnection(5, 6, 2, 1, 0), IFEMConnection(5, 7, 4, 3, 0), IFEMConnection(6, 8, 4, 3, 0), IFEMConnection(7, 8, 2, 1, 0) ], [ IFEMConnection(1, 2, 2, 2, 2), IFEMConnection(1, 3, 4, 3, 0), IFEMConnection(1, 5, 6, 5, 0), IFEMConnection(2, 4, 3, 3, 2), IFEMConnection(2, 6, 6, 5, 3), IFEMConnection(3, 4, 2, 1, 0), IFEMConnection(3, 7, 6, 5, 0), IFEMConnection(4, 8, 6, 5, 0), IFEMConnection(5, 6, 2, 1, 0), IFEMConnection(5, 7, 4, 3, 0), IFEMConnection(6, 8, 4, 3, 0), IFEMConnection(7, 8, 2, 1, 0) ], [ IFEMConnection(1, 2, 2, 2, 1), IFEMConnection(1, 3, 4, 3, 0), IFEMConnection(1, 5, 6, 5, 0), IFEMConnection(2, 4, 4, 3, 3), IFEMConnection(2, 6, 5, 5, 2), IFEMConnection(3, 4, 2, 1, 0), IFEMConnection(3, 7, 6, 5, 0), IFEMConnection(4, 8, 6, 5, 0), IFEMConnection(5, 6, 2, 1, 0), IFEMConnection(5, 7, 4, 3, 0), IFEMConnection(6, 8, 4, 3, 0), IFEMConnection(7, 8, 2, 1, 0) ], [ IFEMConnection(1, 2, 2, 3, 1), IFEMConnection(1, 3, 4, 3, 0), IFEMConnection(1, 5, 6, 5, 0), IFEMConnection(2, 4, 2, 3, 1), IFEMConnection(2, 6, 5, 5, 4), IFEMConnection(3, 4, 2, 1, 0), IFEMConnection(3, 7, 6, 5, 0), IFEMConnection(4, 8, 6, 5, 0), IFEMConnection(5, 6, 2, 1, 0), IFEMConnection(5, 7, 4, 3, 0), IFEMConnection(6, 8, 4, 3, 0), IFEMConnection(7, 8, 2, 1, 0) ], [ IFEMConnection(1, 2, 2, 5, 5), IFEMConnection(1, 3, 4, 3, 0), IFEMConnection(1, 5, 6, 5, 0), IFEMConnection(2, 4, 3, 3, 4), IFEMConnection(2, 6, 2, 5, 5), IFEMConnection(3, 4, 2, 1, 0), IFEMConnection(3, 7, 6, 5, 0), IFEMConnection(4, 8, 6, 5, 0), IFEMConnection(5, 6, 2, 1, 0), IFEMConnection(5, 7, 4, 3, 0), IFEMConnection(6, 8, 4, 3, 0), IFEMConnection(7, 8, 2, 1, 0) ], [ IFEMConnection(1, 2, 2, 4, 0), IFEMConnection(1, 3, 4, 3, 0), IFEMConnection(1, 5, 6, 5, 0), IFEMConnection(2, 4, 2, 3, 2), IFEMConnection(2, 6, 6, 5, 6), IFEMConnection(3, 4, 2, 1, 0), IFEMConnection(3, 7, 6, 5, 0), IFEMConnection(4, 8, 6, 5, 0), IFEMConnection(5, 6, 2, 1, 0), IFEMConnection(5, 7, 4, 3, 0), IFEMConnection(6, 8, 4, 3, 0), IFEMConnection(7, 8, 2, 1, 0) ], [ IFEMConnection(1, 2, 6, 5, 0), IFEMConnection(1, 3, 4, 3, 0), IFEMConnection(1, 5, 2, 1, 0), IFEMConnection(2, 4, 4, 1, 1), IFEMConnection(2, 6, 2, 1, 0), IFEMConnection(3, 4, 6, 6, 4), IFEMConnection(3, 7, 2, 1, 0), IFEMConnection(4, 8, 4, 1, 1), IFEMConnection(5, 6, 6, 5, 0), IFEMConnection(5, 7, 4, 3, 0), IFEMConnection(6, 8, 4, 3, 0), IFEMConnection(7, 8, 6, 5, 0) ], [ IFEMConnection(1, 2, 6, 5, 0), IFEMConnection(1, 3, 4, 3, 0), IFEMConnection(1, 5, 2, 1, 0), IFEMConnection(2, 4, 4, 5, 4), IFEMConnection(2, 6, 2, 1, 0), IFEMConnection(3, 4, 6, 1, 0), IFEMConnection(3, 7, 2, 1, 0), IFEMConnection(4, 8, 4, 1, 4), IFEMConnection(5, 6, 6, 5, 0), IFEMConnection(5, 7, 4, 3, 0), IFEMConnection(6, 8, 4, 3, 0), IFEMConnection(7, 8, 6, 5, 0) ], [ IFEMConnection(1, 2, 6, 5, 0), IFEMConnection(1, 3, 4, 3, 0), IFEMConnection(1, 5, 2, 1, 0), IFEMConnection(2, 4, 4, 6, 0), IFEMConnection(2, 6, 2, 1, 0), IFEMConnection(3, 4, 6, 3, 1), IFEMConnection(3, 7, 2, 1, 0), IFEMConnection(4, 8, 2, 1, 6), IFEMConnection(5, 7, 4, 3, 0), IFEMConnection(5, 6, 6, 5, 0), IFEMConnection(6, 8, 4, 3, 0), IFEMConnection(7, 8, 6, 5, 0) ], [ IFEMConnection(1, 2, 6, 5, 0), IFEMConnection(1, 3, 4, 3, 0), IFEMConnection(1, 5, 2, 1, 0), IFEMConnection(2, 4, 4, 3, 5), IFEMConnection(2, 6, 2, 1, 0), IFEMConnection(3, 4, 6, 1, 5), IFEMConnection(3, 7, 2, 1, 0), IFEMConnection(4, 8, 5, 1, 4), IFEMConnection(5, 6, 6, 5, 0), IFEMConnection(5, 7, 4, 3, 0), IFEMConnection(6, 8, 4, 3, 0), IFEMConnection(7, 8, 6, 5, 0) ], [ IFEMConnection(1, 2, 6, 5, 0), IFEMConnection(1, 3, 4, 3, 0), IFEMConnection(1, 5, 2, 1, 0), IFEMConnection(2, 4, 4, 1, 7), IFEMConnection(2, 6, 2, 1, 0), IFEMConnection(3, 4, 6, 4, 5), IFEMConnection(3, 7, 2, 1, 0), IFEMConnection(4, 8, 5, 1, 1), IFEMConnection(5, 6, 6, 5, 0), IFEMConnection(5, 7, 4, 3, 0), IFEMConnection(6, 8, 4, 3, 0), IFEMConnection(7, 8, 6, 5, 0) ], [ IFEMConnection(1, 2, 6, 5, 0), IFEMConnection(1, 3, 4, 3, 0), IFEMConnection(1, 5, 2, 1, 0), IFEMConnection(2, 4, 4, 1, 2), IFEMConnection(2, 6, 2, 1, 0), IFEMConnection(3, 4, 6, 5, 5), IFEMConnection(3, 7, 2, 1, 0), IFEMConnection(4, 8, 3, 1, 0), IFEMConnection(5, 6, 6, 5, 0), IFEMConnection(5, 7, 4, 3, 0), IFEMConnection(6, 8, 4, 3, 0), IFEMConnection(7, 8, 6, 5, 0) ], [ IFEMConnection(1, 2, 6, 5, 0), IFEMConnection(1, 3, 4, 3, 0), IFEMConnection(1, 5, 2, 1, 0), IFEMConnection(2, 4, 4, 5, 7), IFEMConnection(2, 6, 2, 1, 0), IFEMConnection(3, 4, 6, 2, 2), IFEMConnection(3, 7, 2, 1, 0), IFEMConnection(4, 8, 3, 1, 5), IFEMConnection(5, 6, 6, 5, 0), IFEMConnection(5, 7, 4, 3, 0), IFEMConnection(6, 8, 4, 3, 0), IFEMConnection(7, 8, 6, 5, 0) ], [ IFEMConnection(1, 2, 6, 5, 0), IFEMConnection(1, 3, 4, 3, 0), IFEMConnection(1, 5, 2, 1, 0), IFEMConnection(2, 4, 4, 3, 6), IFEMConnection(2, 6, 2, 1, 0), IFEMConnection(3, 4, 6, 2, 4), IFEMConnection(3, 7, 2, 1, 0), IFEMConnection(4, 8, 6, 1, 5), IFEMConnection(5, 6, 6, 5, 0), IFEMConnection(5, 7, 4, 3, 0), IFEMConnection(6, 8, 4, 3, 0), IFEMConnection(7, 8, 6, 5, 0) ], [ IFEMConnection(1, 2, 6, 5, 0), IFEMConnection(1, 3, 4, 3, 0), IFEMConnection(1, 5, 2, 1, 0), IFEMConnection(2, 4, 4, 4, 7), IFEMConnection(2, 6, 2, 1, 0), IFEMConnection(3, 4, 6, 2, 7), IFEMConnection(3, 7, 2, 1, 0), IFEMConnection(4, 8, 5, 1, 7), IFEMConnection(5, 7, 4, 3, 0), IFEMConnection(5, 6, 6, 5, 0), IFEMConnection(6, 8, 4, 3, 0), IFEMConnection(7, 8, 6, 5, 0) ], ] for i, ref_topo in enumerate(connections): model = SplineModel(3, 3) with G2(THIS_DIR + '/geometries/cube-8-orient{}.g2'.format(i)) as myfile: model.add(myfile.read()) writer = IFEMWriter(model) my_topo = list(writer.connections()) my_topo = sorted(my_topo, key=lambda c: c.slave) my_topo = sorted(my_topo, key=lambda c: c.master) ref_topo = sorted(ref_topo, key=lambda c: c.slave) ref_topo = sorted(ref_topo, key=lambda c: c.master) for my_con, ref_con in zip(my_topo, ref_topo): assert my_con == ref_con assert len(my_topo) == len(ref_topo)