/comet/pyhed/IO/saveload.py
https://gitlab.com/Skibbe/comet · Python · 325 lines · 144 code · 39 blank · 142 comment · 50 complexity · 3ef02089e537e6788d281d774b3e783f MD5 · raw file
- """
- Part of comet/pyhed/IO
- """
- # COMET - COupled Magnetic resonance Electrical resistivity Tomography
- # Copyright (C) 2019 Nico Skibbe
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- # You should have received a copy of the GNU General Public License
- # along with this program. If not, see <https://www.gnu.org/licenses/>.
- import numpy as np
- import os
- from pathlib import Path
- class ArgsError(Exception):
- def __init__(self, value):
- self.value = value
- def __str__(self):
- return repr(self.value)
- def cutExtension(path):
- filename = os.path.split(path)[1].split('.')[0]
- return os.path.join(os.path.dirname(path), filename)
- class TetgenNotFoundError(Exception):
- """ Special Exception to catch in a try except. """
- pass
- def searchforTetgen(returnPathfile=False):
- """ Try to find a valid tetgen installation for meshing purposes.
- Returns:
- --------
- path: string
- Path to tetgen installation or path to pathfile of pyhed itself.
- """
- import comet.pyhed as ph
- import sys
- import subprocess as sp
- if sys.version_info[:2] < (3, 3):
- error = IOError
- else:
- error = FileNotFoundError
- pathfile = os.path.join(os.path.dirname(ph.__file__), '_PATH_.txt')
- try:
- p = sp.Popen(['tetgen'], stdout=sp.PIPE)
- _, err = p.communicate()
- PATH = None
- except FileNotFoundError:
- try:
- with open(pathfile, 'r') as pathf:
- PATH = pathf.readline()
- while PATH.lstrip().startswith('#'):
- PATH = pathf.readline()
- try:
- try:
- PATH = PATH.split('TETGENPATH: ')[1].rstrip()
- except TypeError:
- PATH = PATH.decode().split('TETGENPATH: ')[1].rstrip()
- except IndexError:
- PATH = ''
- if PATH in ['Enter/path/here/', '']:
- raise error
- except error:
- with open(pathfile, 'w') as pathf:
- try:
- p = sp.Popen(['which', 'tetgen'], stdout=sp.PIPE)
- out, err = p.communicate()
- # print(out, err)
- if len(out) > 0:
- try:
- PATH = Path(out.split('tetgen')[0])
- except TypeError:
- PATH = Path(out.decode().split('tetgen')[0])
- if not PATH.exists():
- # windows root not detected corretly
- # there is a special case that can be intercepted
- import platform
- if platform.system() == 'Windows':
- if PATH.drive == '':
- parts = PATH.parts
- if parts[0] == '\\':
- PATH = Path('{}:/'.format(parts[1]),
- *parts[2:])
- if PATH.exists():
- pathf.writelines('TETGENPATH: {}'
- .format(PATH.as_posix()))
- else:
- raise error
- except error:
- pathf.writelines('TETGENPATH: Enter/path/here/')
- raise Exception(
- 'Tetgen not found. Please add or link a '
- 'executabe tetgen instance to your PATH or alter file'
- 'in "{}"'.format(pathfile))
- if returnPathfile is True:
- return pathfile
- else:
- if PATH is None:
- return None
- else:
- return Path(PATH).as_posix()
- def getItem(archive, key, default=None):
- """
- Get item for *key* from numpy *archive* via try except with given *default*
- value for None.
- """
- try:
- item = archive[key].item()
- except ValueError:
- item = archive[key]
- except KeyError:
- item = default
- return item
- def checkDirectory(savename, filename=False, verbose=False):
- """ Checks for directory and creates if not. """
- from comet.pyhed import log
- if '.' in savename and filename is False and not savename.startswith('.'):
- filename = True
- if filename is True:
- dirname = os.path.dirname(savename)
- if dirname == '':
- if '.' in savename:
- directory = '.'
- else:
- return
- else:
- directory = os.path.abspath(dirname)
- else:
- directory = savename
- exists = os.path.exists(directory)
- log.debug('checkDirectory: "{}" exists? : {}'.format(directory, exists))
- log.debug('todo: replace "checkDirectory" with proper pathlib tools')
- if not exists:
- os.makedirs(directory)
- def checkForFile(name):
- """ Checks if file exists and creates a directory if it does not."""
- if os.path.exists(name):
- return True
- else:
- checkDirectory(name)
- return False
- def delLastLine(opened_file, line_ending='\n'):
- """ Efficient way of deleting the last line of a large file. """
- opened_file.seek(0, os.SEEK_END) # end of file
- pos = opened_file.tell() - 1
- while pos > 0 and opened_file.read(1) != line_ending:
- pos -= 1
- opened_file.seek(pos, os.SEEK_SET)
- if pos > 0:
- opened_file.seek(pos, os.SEEK_SET)
- opened_file.truncate()
- return opened_file
- def addVolumeConstraintToPoly(name, regions, float_format='6.3f'):
- '''
- Append region information in form of volume constraints to a tetgen.poly
- file. The given regions has to be of shape (n, 5 or 6), with
- n times: [number, x, y, z, regional attribute, volume constraint]
- '''
- with open(name, "r+") as in_file:
- string = '{}'
- for i in range(len(regions[0]) - 1): # either 4 or 5 floats possible
- string += ' {:%s}' % (float_format)
- string += os.linesep
- delLastLine(in_file)
- in_file.seek(0, os.SEEK_END)
- in_file.write(os.linesep)
- lines = '# volume constraints{}'.format(os.linesep)
- lines += '{:d}{}'.format(len(regions), os.linesep)
- for region in regions:
- lines += string.format(*region)
- in_file.write(lines)
- def createCustEMDirectories(m_dir='.', r_dir='.'):
- """ Creates the used custEM directories based on *m_dir* and *r_dir*. """
- m_path = Path(m_dir)
- r_path = Path(r_dir)
- m_path.joinpath('_h5').mkdir(exist_ok=True, parents=True)
- m_path.joinpath('_mesh').mkdir(exist_ok=True)
- m_path.joinpath('para').mkdir(exist_ok=True)
- r_path.joinpath('primary_fields/custom').mkdir(exist_ok=True, parents=True)
- r_path.joinpath('E_s').mkdir(exist_ok=True)
- r_path.joinpath('H_s').mkdir(exist_ok=True)
- #def exportTetgenPolyFile(filename, poly, float_format='.12e'):
- # '''
- # Writes a given piecewise linear complex (mesh/poly ) into a Ascii file in
- # tetgen's .poly format.
- #
- # Parameters
- # ----------
- #
- # filename: string
- # Name in which the result will be written. The recommended file
- # ending is '.poly'.
- #
- # poly: pg.Mesh
- # Piecewise linear complex as pygimli mesh to be exported.
- #
- # float_format: format string ('.12e')
- # Format that will be used to write float values in the Ascii file.
- # Default is the exponential float form with a precision of 12 digits.
- #
- # '''
- # filename = filename.rstrip('.poly') + '.poly'
- # polytxt = ''
- # sep = '\t' # standard tab seperated file
- # assert poly.dim() == 3, 'Exit, only for 3D meshes.'
- # boundary_marker = 1
- # attribute_count = 0
- #
- # # Part 1/4: node list
- # # intro line
- # # <nodecount> <dimension (3)> <# of attributes> <boundary markers (0 or 1)>
- # polytxt += '{0}{5}{1}{5}{2}{5}{3}{4}'.format(poly.nodeCount(), 3,
- # attribute_count,
- # boundary_marker,
- # os.linesep, sep)
- # # loop over positions, attributes and marker(node)
- # # <point idx> <x> <y> <z> [attributes] [boundary marker]
- # point_str = '{:d}' # index of the point
- # for i in range(3):
- # # coords as float with given precision
- # point_str += sep + '{:%s}' % (float_format)
- # point_str += sep + '{:d}' + os.linesep # node marker
- # for j, node in enumerate(poly.nodes()):
- # fill = [node.id()]
- # fill.extend([pos for pos in node.pos()])
- # fill.append(node.marker())
- # polytxt += point_str.format(*fill)
- #
- # # Part 2/4: boundary list
- # # intro line
- # # <# of facets> <boundary markers (0 or 1)>
- # polytxt += '{0:d}{2}1{1}'.format(poly.boundaryCount(), os.linesep, sep)
- # # loop over facets, each facet can contain an arbitrary number of holes
- # # and polygons, in our case, there is always one polygon per facet.
- # for k, bound in enumerate(poly.boundaries()):
- # # one line per facet
- # # <# of polygons> [# of holes] [boundary marker]
- # npolys = 1
- # polytxt += '1{2}0{2}{0:d}{1}'.format(bound.marker(), os.linesep, sep)
- # # inner loop over polygons
- # # <# of corners> <corner 1> <corner 2> ... <corner #>
- # for l in range(npolys):
- # poly_str = '{:d}'.format(bound.nodeCount())
- # for ind in bound.ids():
- # poly_str += sep + '{:d}'.format(ind)
- # polytxt += '{0}{1}'.format(poly_str, os.linesep)
- # # inner loop over holes
- # # not necessary yet ?! why is there an extra hole section?
- # # because this is for 2D holes in facets only
- #
- # # part 3/4: hole list
- # # intro line
- # # <# of holes>
- # holes = poly.holeMarker()
- # polytxt += '{:d}{}'.format(len(holes), os.linesep)
- # # loop over hole markers
- # # <hole #> <x> <y> <z>
- # hole_str = '{:d}'
- # for m in range(3):
- # hole_str += sep + '{:%s}' % float_format
- # hole_str += os.linesep
- # for n, hole in enumerate(holes):
- # polytxt += hole_str.format(n, *hole)
- #
- # # part 4/4: region attributes and volume constraints (optional)
- # # intro line
- # # <# of regions>
- # regions = poly.regionMarker()
- # polytxt += '{:d}{}'.format(len(regions), os.linesep)
- # # loop over region markers
- # # <region #> <x> <y> <z> <region number> <region attribute>
- # region_str = '{:d}'
- # for o in range(3):
- # region_str += sep + '{:%s}' % (float_format)
- # region_str += sep + '{:d}%s{:%s}' % (sep, float_format) + os.linesep
- # for p, region in enumerate(regions):
- # polytxt += region_str.format(p, region.x(), region.y(), region.z(),
- # region.marker(),
- # region.area())
- #
- # # writing file
- # with open(filename, 'w') as out:
- # out.write(polytxt)
- # THE END