diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 6565e9ce9..789b80e88 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,4 +1,4 @@ [bumpversion] -current_version = 0.1.12 +current_version = 0.1.13 files = setup.py discretize/__init__.py docs/conf.py diff --git a/.travis.yml b/.travis.yml index b9be95d37..e1f51fba3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,8 +58,7 @@ after_success: # deploy to pypi on tags - mv credentials/.pypirc ~/.pypirc ; - - python setup.py sdist bdist_wheel --universal ; - - travis_wait 20 twine upload -r pypi --skip-existing dist/* ; + - python setup.py sdist bdist_wheel --universal upload; notifications: diff --git a/discretize/MeshIO.py b/discretize/MeshIO.py index f4b0c3a2b..18be2471e 100644 --- a/discretize/MeshIO.py +++ b/discretize/MeshIO.py @@ -30,7 +30,10 @@ class TensorMeshIO(object): def _readUBC_3DMesh(TensorMesh, fileName): """Read UBC GIF 3D tensor mesh and generate same dimension TensorMesh. + Input: :param string fileName: path to the UBC GIF mesh file + + Output: :rtype: TensorMesh :return: The tensor mesh for the fileName. """ @@ -69,7 +72,10 @@ def readCellLine(line): def _readUBC_2DMesh(TensorMesh, fileName): """Read UBC GIF 2DTensor mesh and generate 2D Tensor mesh in simpeg + Input: :param string fileName: path to the UBC GIF mesh file + + Output: :rtype: TensorMesh :return: SimPEG TensorMesh 2D object """ @@ -118,51 +124,55 @@ def unpackdx(fid, nrows): return tensMsh @classmethod - def readUBC(TensorMesh, fileName, meshdim=None): + def readUBC(TensorMesh, fileName, directory=''): """Wrapper to Read UBC GIF 2D and 3D tensor mesh and generate same dimension TensorMesh. - :param string fileName: path to the UBC GIF mesh file - :param int meshdim: expected dimension of the mesh, if unknown the default argument is None + Input: + :param str fileName: path to the UBC GIF mesh file or just its name if directory is specified + :param str directory: directory where the UBC GIF file lives + + Output: :rtype: TensorMesh :return: The tensor mesh for the fileName. """ # Check the expected mesh dimensions - if meshdim == None: - # Read the file as line strings, remove lines with comment = ! - msh = np.genfromtxt(fileName, delimiter='\n', dtype=np.str, comments='!', max_rows=1) - # Fist line is the size of the model - sizeM = np.array(msh.ravel()[0].split(), dtype=float) - # Check if the mesh is a UBC 2D mesh - if sizeM.shape[0] == 1: - Tnsmsh = TensorMesh._readUBC_2DMesh(fileName) - # Check if the mesh is a UBC 3D mesh - elif sizeM.shape[0] == 3: - Tnsmsh = TensorMesh._readUBC_3DMesh(fileName) - else: - raise Exception('File format not recognized') - # expected dimension is 2 - elif meshdim == 2: - Tnsmsh = TensorMesh._readUBC_2DMesh(fileName) - # expected dimension is 3 - elif meshdim == 3: - Tnsmsh = TensorMesh._readUBC_3DMesh(fileName) + fname = os.path.join(directory, fileName) + # Read the file as line strings, remove lines with comment = ! + msh = np.genfromtxt( + fname, delimiter='\n', dtype=np.str, + comments='!', max_rows=1 + ) + # Fist line is the size of the model + sizeM = np.array(msh.ravel()[0].split(), dtype=float) + # Check if the mesh is a UBC 2D mesh + if sizeM.shape[0] == 1: + Tnsmsh = TensorMesh._readUBC_2DMesh(fname) + # Check if the mesh is a UBC 3D mesh + elif sizeM.shape[0] == 3: + Tnsmsh = TensorMesh._readUBC_3DMesh(fname) + else: + raise Exception('File format not recognized') return Tnsmsh @classmethod - def readVTK(TensorMesh, fileName): + def readVTK(TensorMesh, fileName, directory=''): """Read VTK Rectilinear (vtr xml file) and return Tensor mesh and model Input: - :param string fileName: path to the vtr model file to read + :param str fileName: path to the vtr model file to read or just its name if directory is specified + :param str directory: directory where the UBC GIF file lives + + Output: :rtype: tuple :return: (TensorMesh, modelDictionary) """ from vtk import vtkXMLRectilinearGridReader as vtrFileReader from vtk.util.numpy_support import vtk_to_numpy + fname = os.path.join(directory, fileName) # Read the file vtrReader = vtrFileReader() - vtrReader.SetFileName(fileName) + vtrReader.SetFileName(fname) vtrReader.Update() vtrGrid = vtrReader.GetOutput() # Sort information @@ -198,17 +208,20 @@ def readVTK(TensorMesh, fileName): # Return the data return tensMsh, models - def writeVTK(mesh, fileName, models=None): + def writeVTK(mesh, fileName, models=None, directory=''): """Makes and saves a VTK rectilinear file (vtr) for a Tensor mesh and model. Input: - :param string fileName: path to the output vtk file - :param dict models: dictionary of numpy.array - Name('s) and array('s). Match number of cells + :param str fileName: path to the output vtk file or just its name if directory is specified + :param str directory: directory where the UBC GIF file lives + :param dict models: dictionary of numpy.array - Name('s) and array('s). + Match number of cells """ from vtk import vtkRectilinearGrid as rectGrid, vtkXMLRectilinearGridWriter as rectWriter, VTK_VERSION from vtk.util.numpy_support import numpy_to_vtk + fname = os.path.join(directory, fileName) # Deal with dimensionalities if mesh.dim >= 1: vX = mesh.vectorNx @@ -240,18 +253,18 @@ def writeVTK(mesh, fileName, models=None): vtkObj.GetCellData().SetActiveScalars(models.keys()[0]) # Check the extension of the fileName - ext = os.path.splitext(fileName)[1] + ext = os.path.splitext(fname)[1] if ext is '': - fileName = fileName + '.vtr' + fname = fname + '.vtr' elif ext not in '.vtr': raise IOError('{:s} is an incorrect extension, has to be .vtr') # Write the file. vtrWriteFilter = rectWriter() - if float(VTK_VERSION.split('.')[0]) >=6: + if float(VTK_VERSION.split('.')[0]) >= 6: vtrWriteFilter.SetInputData(vtkObj) else: vtuWriteFilter.SetInput(vtuObj) - vtrWriteFilter.SetFileName(fileName) + vtrWriteFilter.SetFileName(fname) vtrWriteFilter.Update() def _toVTRObj(mesh, models=None): @@ -300,48 +313,126 @@ def _toVTRObj(mesh, models=None): vtkObj.GetCellData().SetActiveScalars(models.keys()[0]) return vtkObj - def readModelUBC(mesh, fileName): + def _readModelUBC_2D(mesh, fileName): + """ + Read UBC GIF 2DTensor model and generate 2D Tensor model in simpeg + + Input: + :param string fileName: path to the UBC GIF 2D model file + + Output: + :rtype: numpy.ndarray + :return: model with TensorMesh ordered + """ + + # Open fileand skip header... assume that we know the mesh already + obsfile = np.genfromtxt( + fileName, delimiter=' \n', + dtype=np.str, comments='!' + ) + + dim = np.array(obsfile[0].split(), dtype=int) + if not np.all([mesh.nCx, mesh.nCy] == dim): + raise Exception('Dimension of the model and mesh mismatch') + + # Make a list of the lines + model = [line.split() for line in obsfile[1:]] + # Address the case where lines are not split equally + model = [cellvalue for sublist in model[::-1] for cellvalue in sublist] + # Make the vector + model = utils.mkvc(np.array(model, dtype=float).T) + if not len(model) == mesh.nC: + raise Exception( + """Something is not right, expected size is {:d} + but unwrap vector is size {:d}""".format(mesh.nC, len(model)) + ) + + return model + + def _readModelUBC_3D(mesh, fileName): """Read UBC 3DTensor mesh model and generate 3D Tensor mesh model + Input: :param string fileName: path to the UBC GIF mesh file to read + + Output: :rtype: numpy.ndarray :return: model with TensorMesh ordered """ f = open(fileName, 'r') model = np.array(list(map(float, f.readlines()))) f.close() - model = np.reshape(model, (mesh.nCz, mesh.nCx, mesh.nCy), order = 'F') + model = np.reshape(model, (mesh.nCz, mesh.nCx, mesh.nCy), order='F') model = model[::-1, :, :] model = np.transpose(model, (1, 2, 0)) model = utils.mkvc(model) return model - def writeModelUBC(mesh, fileName, model): + def readModelUBC(mesh, fileName, directory=''): + """Read UBC 2D or 3D Tensor mesh model + and generate Tensor mesh model + + Input: + :param str fileName: path to the UBC GIF mesh file to read + or just its name if directory is specified + :param str directory: directory where the UBC GIF file lives + + Output: + :rtype: numpy.ndarray + :return: model with TensorMesh ordered + """ + fname = os.path.join(directory, fileName) + if mesh.dim == 3: + model = mesh._readModelUBC_3D(fname) + elif mesh.dim == 2: + model = mesh._readModelUBC_2D(fname) + else: + raise Exception('mesh must be a Tensor Mesh 2D or 3D') + return model + + def writeModelUBC(mesh, fileName, model, directory=''): """Writes a model associated with a TensorMesh to a UBC-GIF format model file. - :param string fileName: File to write to + Input: + :param str fileName: File to write to + or just its name if directory is specified + :param str directory: directory where the UBC GIF file lives :param numpy.ndarray model: The model """ + fname = os.path.join(directory, fileName) + if mesh.dim == 3: + # Reshape model to a matrix + modelMat = mesh.r(model, 'CC', 'CC', 'M') + # Transpose the axes + modelMatT = modelMat.transpose((2, 0, 1)) + # Flip z to positive down + modelMatTR = utils.mkvc(modelMatT[::-1, :, :]) + np.savetxt(fname, modelMatTR.ravel()) + + elif mesh.dim == 2: + modelMat = mesh.r(model, 'CC', 'CC', 'M').T[::-1] + f = open(fname, 'w') + f.write('{:d} {:d}\n'.format(mesh.nCx, mesh.nCy)) + f.close() + f = open(fname, 'ab') + np.savetxt(f, modelMat) + f.close() - # Reshape model to a matrix - modelMat = mesh.r(model, 'CC', 'CC', 'M') - # Transpose the axes - modelMatT = modelMat.transpose((2, 0, 1)) - # Flip z to positive down - modelMatTR = utils.mkvc(modelMatT[::-1, :, :]) - - np.savetxt(fileName, modelMatTR.ravel()) + else: + raise Exception('mesh must be a Tensor Mesh 2D or 3D') - def writeUBC(mesh, fileName, models=None): + def _writeUBC_3DMesh(mesh, fileName, comment_lines=''): """Writes a TensorMesh to a UBC-GIF format mesh file. + Input: :param string fileName: File to write to :param dict models: A dictionary of the models - """ - assert mesh.dim == 3 - s = '' + if not mesh.dim == 3: + raise Exception('Mesh must be 3D') + + s = comment_lines s += '{0:d} {1:d} {2:d}\n'.format(*tuple(mesh.vnC)) # Have to it in the same operation or use mesh.x0.copy(), # otherwise the mesh.x0 is updated. @@ -349,18 +440,99 @@ def writeUBC(mesh, fileName, models=None): origin.dtype = float s += '{0:.6f} {1:.6f} {2:.6f}\n'.format(*tuple(origin)) - s += ('%.6f '*mesh.nCx+'\n')%tuple(mesh.hx) - s += ('%.6f '*mesh.nCy+'\n')%tuple(mesh.hy) - s += ('%.6f '*mesh.nCz+'\n')%tuple(mesh.hz[::-1]) + s += ('%.6f '*mesh.nCx+'\n') % tuple(mesh.hx) + s += ('%.6f '*mesh.nCy+'\n') % tuple(mesh.hy) + s += ('%.6f '*mesh.nCz+'\n') % tuple(mesh.hz[::-1]) f = open(fileName, 'w') f.write(s) f.close() - if models is None: return + def _writeUBC_2DMesh(mesh, fileName, comment_lines=''): + """Writes a TensorMesh to a UBC-GIF format mesh file. + + Input: + :param string fileName: File to write to + :param dict models: A dictionary of the models + + """ + if not mesh.dim == 2: + raise Exception('Mesh must be 2D') + + def writeF(fx, outStr=''): + # Init + i = 0 + origin = True + x0 = fx[i] + f = fx[i] + number_segment = 0 + auxStr = '' + + while True: + i = i + 1 + if i >= fx.size: + break + dx = -f + fx[i] + f = fx[i] + n = 1 + + for j in range(i+1, fx.size): + if -f + fx[j] == dx: + n += 1 + i += 1 + f = fx[j] + else: + break + + number_segment += 1 + if origin: + auxStr += '{:.10f} {:.10f} {:d} \n'.format(x0, f, n) + origin = False + else: + auxStr += '{:.10f} {:d} \n'.format(f, n) + + auxStr = '{:d}\n'.format(number_segment) + auxStr + outStr += auxStr + + return outStr + + # Grab face coordinates + fx = mesh.vectorNx + fz = -mesh.vectorNy[::-1] + + # Create the string + outStr = comment_lines + outStr = writeF(fx, outStr=outStr) + outStr += '\n' + outStr = writeF(fz, outStr=outStr) + + # Write file + f = open(fileName, 'w') + f.write(outStr) + f.close() + + def writeUBC(mesh, fileName, models=None, directory='', comment_lines=''): + """Writes a TensorMesh to a UBC-GIF format mesh file. + + Input: + :param str fileName: File to write to + :param str directory: directory where to save model + :param dict models: A dictionary of the models + :param str comment_lines: comment lines preceded with '!' to add + """ + fname = os.path.join(directory, fileName) + if mesh.dim == 3: + mesh._writeUBC_3DMesh(fname, comment_lines=comment_lines) + elif mesh.dim == 2: + mesh._writeUBC_2DMesh(fname, comment_lines=comment_lines) + else: + raise Exception('mesh must be a Tensor Mesh 2D or 3D') + + if models is None: + return assert type(models) is dict, 'models must be a dict' for key in models: assert type(key) is str, 'The dict key is a file name' - mesh.writeModelUBC(key, models[key]) + mesh.writeModelUBC(key, models[key], directory=directory) class TreeMeshIO(object): diff --git a/discretize/__init__.py b/discretize/__init__.py index 4e82431be..59809608a 100644 --- a/discretize/__init__.py +++ b/discretize/__init__.py @@ -18,7 +18,7 @@ """ ) -__version__ = '0.1.12' +__version__ = '0.1.13' __author__ = 'SimPEG Team' __license__ = 'MIT' __copyright__ = '2013 - 2017, SimPEG Developers, http://simpeg.xyz' diff --git a/docs/conf.py b/docs/conf.py index 753fbcec6..c87f7faa3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -60,9 +60,9 @@ # built documents. # # The short X.Y version. -version = '0.1.12' +version = '0.1.13' # The full version, including alpha/beta/rc tags. -release = '0.1.12' +release = '0.1.13' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index c54ef0ccc..30dff5527 100644 --- a/setup.py +++ b/setup.py @@ -56,7 +56,7 @@ def configuration(parent_package='', top_path=None): setup( name="discretize", - version="0.1.12", + version="0.1.13", install_requires=[ 'numpy>=1.7', 'scipy>=0.13', diff --git a/tests/base/test_tensor_io.py b/tests/base/test_tensor_io.py index 9d4014d9e..5e0d6d6c8 100644 --- a/tests/base/test_tensor_io.py +++ b/tests/base/test_tensor_io.py @@ -18,81 +18,135 @@ def setUp(self): h = np.ones(16) mesh = discretize.TensorMesh([h, 2*h, 3*h]) self.mesh = mesh + self.basePath = os.path.expanduser('~/TestIO') - def test_UBCfiles(self): + def test_write_read_ubc_mesh_model_3d(self): + if not os.path.exists(self.basePath): + os.mkdir(self.basePath) mesh = self.mesh # Make a vector vec = np.arange(mesh.nC) # Write and read - mesh.writeUBC('temp.msh', {'arange.txt': vec}) - meshUBC = discretize.TensorMesh.readUBC('temp.msh') - vecUBC = meshUBC.readModelUBC('arange.txt') + mshfname = 'temp.msh' + modelfname = 'arange.txt' + modelfname1 = 'arange2.txt' + modeldict = {modelfname: vec, modelfname1: vec + 1} + mesh.writeUBC('temp.msh', modeldict, directory=self.basePath) + meshUBC = discretize.TensorMesh.readUBC( + mshfname, directory=self.basePath + ) + vecUBC = meshUBC.readModelUBC(modelfname, directory=self.basePath) + vec2UBC = mesh.readModelUBC(modelfname1, directory=self.basePath) # The mesh - assert mesh.__str__() == meshUBC.__str__() - assert np.sum(mesh.gridCC - meshUBC.gridCC) == 0 - assert np.sum(vec - vecUBC) == 0 - assert np.all(np.array(mesh.h) - np.array(meshUBC.h) == 0) + self.assertTrue(mesh.__str__() == meshUBC.__str__()) + self.assertTrue(np.sum(mesh.gridCC - meshUBC.gridCC) == 0) + self.assertTrue(np.sum(vec - vecUBC) == 0) + self.assertTrue(np.all(np.array(mesh.h) - np.array(meshUBC.h) == 0)) - vecUBC = mesh.readModelUBC('arange.txt') - assert np.sum(vec - vecUBC) == 0 - - mesh.writeModelUBC('arange2.txt', vec + 1) - vec2UBC = mesh.readModelUBC('arange2.txt') - assert np.sum(vec + 1 - vec2UBC) == 0 - - print('IO of UBC tensor mesh files is working') - os.remove('temp.msh') - os.remove('arange.txt') - os.remove('arange2.txt') - - def test_read_ubc_mesh(self): - fname = os.path.join(os.path.split(__file__)[0], 'ubc_tensor_mesh.msh') - mesh = discretize.TensorMesh.readUBC(fname) - assert mesh.nCx == 78 - assert mesh.nCy == 50 - assert mesh.nCz == 51 - # spot check a few things in the file - assert mesh.hx[0] == 55000 - assert mesh.hy[0] == 70000 - # The x0 is in a different place (-z) - assert mesh.x0[-1] == 3000 - np.sum(mesh.hz) - # the z axis is flipped - assert mesh.hz[0] == 20000 - assert mesh.hz[-1] == 250 + # The models + self.assertTrue(np.sum(vec - vecUBC) == 0) + self.assertTrue(np.sum(vec + 1 - vec2UBC) == 0) + print('IO of UBC 3D tensor mesh files is working') + os.remove(os.path.join(self.basePath, mshfname)) + os.remove(os.path.join(self.basePath, modelfname)) + os.remove(os.path.join(self.basePath, modelfname1)) + os.rmdir(self.basePath) if has_vtk: def test_VTKfiles(self): + if not os.path.exists(self.basePath): + os.mkdir(self.basePath) + mesh = self.mesh vec = np.arange(mesh.nC) - - mesh.writeVTK('temp.vtr', {'arange.txt': vec}) - meshVTR, models = discretize.TensorMesh.readVTK('temp.vtr') - - assert mesh.__str__() == meshVTR.__str__() - assert np.all(np.array(mesh.h) - np.array(meshVTR.h) == 0) - - assert 'arange.txt' in models - vecVTK = models['arange.txt'] - assert np.sum(vec - vecVTK) == 0 + vtrfname = 'temp.vtr' + modelfname = 'arange.txt' + modeldict = {modelfname: vec} + mesh.writeVTK(vtrfname, modeldict, directory=self.basePath) + meshVTR, models = discretize.TensorMesh.readVTK( + vtrfname, directory=self.basePath + ) + + self.assertTrue(mesh.__str__() == meshVTR.__str__()) + self.assertTrue( + np.all(np.array(mesh.h) - np.array(meshVTR.h) == 0) + ) + + self.assertTrue(modelfname in models) + vecVTK = models[modelfname] + self.assertTrue(np.sum(vec - vecVTK) == 0) print('IO of VTR tensor mesh files is working') - os.remove('temp.vtr') - - def test_read_ubc_2Dmesh(self): - fname = os.path.join(os.path.split(__file__)[0], 'ubc_DC2D_tensor_mesh.msh') + os.remove(os.path.join(self.basePath, vtrfname)) + os.rmdir(self.basePath) + + def test_write_read_ubc_mesh_model_2d(self): + if not os.path.exists(self.basePath): + os.mkdir(self.basePath) + + fname = 'ubc_DC2D_tensor_mesh.msh' + # Create 2D Mesh + # Cells size + csx, csz = 0.25, 0.25 + # Number of core cells in each direction + ncx, ncz = 123, 41 + # Number of padding cells and expansion rate + npad, expansion = 6, 1.5 + # Vectors of cell lengthts in each direction + hx = [(csx, npad, -expansion), (csx, ncx), (csx, npad, expansion)] + hz = [(csz, npad, -expansion), (csz, ncz)] + mesh = discretize.TensorMesh([hx, hz], x0="CN") + + # Create 2-cylinders Model Creation + # Spheres parameters + x0, z0, r0 = -6., -5., 3. + x1, z1, r1 = 6., -5., 3. + background = -5. + sphere0 = -3. + sphere1 = -6. + + # Create Model + model = background*np.ones(mesh.nC) + csph = (np.sqrt((mesh.gridCC[:, 1]-z0)**2.+(mesh.gridCC[:, 0]-x0)**2.)) < r0 + model[csph] = sphere0*np.ones_like(model[csph]) + # Define the sphere limit + rsph = (np.sqrt((mesh.gridCC[:, 1]-z1)**2.+(mesh.gridCC[:, 0]-x1)**2.)) < r1 + model[rsph] = sphere1*np.ones_like(model[rsph]) + modeldict = {'2d_2cyl_model': model} + + # Write Mesh and model + comment_lines = '!comment line\n'+'!again\n'+'!and again\n' + mesh.writeUBC( + fname, models=modeldict, + directory=self.basePath, comment_lines=comment_lines + ) + + # Read back mesh and model + fname = os.path.sep.join([self.basePath, 'ubc_DC2D_tensor_mesh.msh']) mesh = discretize.TensorMesh.readUBC(fname) - assert mesh.nCx == 178 - assert mesh.nCy == 67 + modelfname = os.path.sep.join([self.basePath, '2d_2cyl_model']) + readmodel = mesh.readModelUBC(modelfname) + self.assertTrue(mesh.nCx == 135) + self.assertTrue(mesh.nCy == 47) # spot check a few things in the file - assert mesh.hx[0] == 600. + self.assertTrue(mesh.hx[0] == 2.84765625) # The x0 is in a different place (-z) - assert mesh.x0[-1] == - np.sum(mesh.hy) + self.assertTrue(mesh.x0[-1] == - np.sum(mesh.hy)) # the z axis is flipped - assert mesh.hy[0] == 600. - assert mesh.hy[-1] == 10. + self.assertTrue(mesh.hy[0] == 2.84765625) + self.assertTrue(mesh.hy[-1] == csz) + self.assertTrue(mesh.dim == 2) + self.assertTrue(np.all(model == readmodel)) + + # Clean up the working directory + print('IO of UBC 2D tensor mesh files is working') + os.remove(os.path.join(self.basePath, fname)) + os.remove(os.path.join(self.basePath, modelfname)) + os.rmdir(self.basePath) + if __name__ == '__main__': unittest.main() diff --git a/tests/base/ubc_DC2D_tensor_mesh.msh b/tests/base/ubc_DC2D_tensor_mesh.msh deleted file mode 100644 index dbdb04cc0..000000000 --- a/tests/base/ubc_DC2D_tensor_mesh.msh +++ /dev/null @@ -1,35 +0,0 @@ -! comment line -! again -! and again -! and again and again -19 --2000 -1400 1 --1000 1 --700 1 --500 1 --350 1 --250 1 --200 1 --150 1 --125 1 -2675 160 -2700 1 -2750 1 -2800 1 -2900 1 -3050 1 -3250 1 -3550 1 -3950 1 -4550 1 - -9 --0 400 40 -800 20 -850 1 -950 1 -1100 1 -1300 1 -1600 1 -2000 1 -2600 1 \ No newline at end of file diff --git a/tests/base/ubc_tensor_mesh.msh b/tests/base/ubc_tensor_mesh.msh deleted file mode 100644 index fe9263df2..000000000 --- a/tests/base/ubc_tensor_mesh.msh +++ /dev/null @@ -1,9 +0,0 @@ -! comment line -! again -! and again -! and again and again -78 50 51 --200000.000000 -200000.000000 3000.000000 -55000 45000 40000 35000 25000 10*20000 3*10000 43*10000 2*10000 10*20000 25000 35000 40000 45000 55000 -70000 50000 40000 25000 15000 40*20000 15000 25000 40000 50000 70000 -12*250 24*2500 8*5000 7500 10000 12500 15000 15000 20000 20000