Source code for ogs5py.fileclasses.gli.core

# -*- coding: utf-8 -*-
"""Core module for the ogs5py GLI file."""
import os
from copy import deepcopy as dcp

import numpy as np

from ogs5py.fileclasses.base import File
from ogs5py.fileclasses.gli.checker import (
    check_gli_dict,
    check_polyline,
    check_surface,
    check_volume,
)
from ogs5py.fileclasses.gli.tools import load_ogs5gli, save_ogs5gli
from ogs5py.tools.tools import (
    is_str_array,
    replace,
    rotate_points,
    shift_points,
    unique_rows,
)

# import ogs5py.fileclasses.gli.generator as gen
from ogs5py.tools.types import EMPTY_GLI, STRTYPE

# current working directory
CWD = os.getcwd()


[docs]class GLI(File): """ Class for the ogs GEOMETRY file. Parameters ---------- gli_dict : :class:`dict` or :class:`None`, optional dictionary containing the gli file Includes the following information (sorted by keys): points : ndarray Array with all point postions point_names : ndarray (of strings) Array with all point names point_md : ndarray Array with all Material-densities at the points if point_md should be undefined it takes the value -np.inf polylines : list of dict each containing information about - ``ID`` (int or None) - ``NAME`` (str) - ``POINTS`` (ndarray) - ``EPSILON`` (float or None) - ``TYPE`` (int or None) - ``MAT_GROUP`` (int or None) - ``POINT_VECTOR`` (str or None) surfaces : list of dict each containing information about - ``ID`` (int or None) - ``NAME`` (str) - ``POLYLINES`` (list of str) - ``EPSILON`` (float or None) - ``TYPE`` (int or None) - ``MAT_GROUP`` (int or None) - ``TIN`` (str or None) volumes : list of dict each containing information about - ``NAME`` (str) - ``SURFACES`` (list of str) - ``TYPE`` (int or None) - ``MAT_GROUP`` (int or None) - ``LAYER`` (int or None) Default: :class:`None` task_root : :class:`str`, optional Path to the destiny model folder. Default: cwd+"ogs5model" task_id : :class:`str`, optional Name for the ogs task. Default: "model" """ def __init__(self, gli_dict=None, **OGS_Config): super().__init__(**OGS_Config) self.file_ext = ".gli" self.force_writing = True if gli_dict is None: self.__dict = dcp(EMPTY_GLI) elif check_gli_dict(gli_dict): self.__dict = gli_dict else: print("given gli_dict is not valid.. will set default") self.__dict = dcp(EMPTY_GLI) # self.mainkw = [""] # Pretend that there is a main keyword in the standard BASE-FORMAT @property def is_empty(self): """:class:`bool`: State if the GLI File is empty.""" return self.POINTS is None ####################### # POINTS ####################### @property def POINTS(self): """ndarray: POINTS (n,3) of the gli, defined by xyz-coordinates.""" return self.__dict["points"] @POINTS.setter def POINTS(self, value): # del self.POINTS self.__dict["points"] = np.array(value, dtype=float) @POINTS.deleter def POINTS(self): self.__dict["points"] = None del self.POINT_NAMES del self.POINT_MD del self.POLYLINES del self.SURFACES del self.VOLUMES ####################### # POINT_NO ####################### @property def POINT_NO(self): """int: number of POINTS of the gli.""" if self.POINTS is None: return 0 return self.POINTS.shape[0] ####################### # POINT_NAMES ####################### @property def POINT_NAMES(self): """ndarray: names of POINTS of the gli.""" return self.__dict["point_names"] @POINT_NAMES.setter def POINT_NAMES(self, value): self.__dict["point_names"] = np.array(value, dtype=object) @POINT_NAMES.deleter def POINT_NAMES(self): if self.POINTS is not None: self.__dict["point_names"] = np.array( self.POINT_NO * [""], dtype=object ) else: self.__dict["point_names"] = None ####################### # POINT_MD ####################### @property def POINT_MD(self): """ndarray: material density values of POINTS of the gli.""" return self.__dict["point_md"] @POINT_MD.setter def POINT_MD(self, value): self.__dict["point_md"] = np.array(value, dtype=float) @POINT_MD.deleter def POINT_MD(self): if self.POINTS is not None: self.__dict["point_md"] = -np.inf * np.ones( self.POINT_NO, dtype=float ) else: self.__dict["point_md"] = None ####################### # POLYLINES ####################### @property def POLYLINES(self): """List of dict: POLYLINES of the gli.""" return self.__dict["polylines"] ####################### # POLYLINE_NAMES ####################### @property def POLYLINE_NAMES(self): """List of str: names of POLYLINES of the gli.""" ply_names = [] for ply in self.POLYLINES: ply_names.append(ply["NAME"]) return ply_names ####################### # POLYLINE_NO ####################### @property def POLYLINE_NO(self): """int: number of POLYLINES of the gli.""" return len(self.POLYLINES) ####################### # SURFACES ####################### @property def SURFACES(self): """List of dict: SURFACES of the gli.""" return self.__dict["surfaces"] ####################### # SURFACE_NAMES ####################### @property def SURFACE_NAMES(self): """List of str: names of SURFACES of the gli.""" srf_names = [] for srf in self.SURFACES: srf_names.append(srf["NAME"]) return srf_names ####################### # SURFACE_NO ####################### @property def SURFACE_NO(self): """int: number of SURFACES of the gli.""" return len(self.SURFACES) ####################### # VOLUMES ####################### @property def VOLUMES(self): """List of dict: VOLUMES of the gli.""" return self.__dict["volumes"] ####################### # VOLUME_NAMES ####################### @property def VOLUME_NAMES(self): """List of str: names of VOLUMES of the gli.""" vol_names = [] for vol in self.VOLUMES: vol_names.append(vol["NAME"]) return vol_names ####################### # VOLUME_NO ####################### @property def VOLUME_NO(self): """int: number of VOLUMES of the gli.""" return len(self.VOLUMES) ####################### # Class methods #######################
[docs] def reset(self): """Delete every content.""" self.__dict = EMPTY_GLI
[docs] def load(self, filepath, verbose=False, encoding=None, **kwargs): r""" Load an OGS5 gli from file. kwargs will be forwarded to "tools.load_ogs5gli" Parameters ---------- filepath : string path to the '\*.gli' OGS5 gli file to load verbose : bool, optional Print information of the reading process. Default: True """ tmp = load_ogs5gli( filepath, verbose=verbose, encoding=encoding, **kwargs ) if check_gli_dict(tmp, verbose=verbose): self.__dict = tmp else: raise ValueError( "GLI.load: " + filepath + ": given gli is not valid" )
[docs] def read_file(self, path, encoding=None, verbose=False): r""" Load an OGS5 gli from file. Parameters ---------- path : string path to the '\*.gli' OGS5 gli file to load encoding : str or None, optional encoding of the given file. If ``None`` is given, the system standard is used. Default: ``None`` verbose : bool, optional Print information of the reading process. Default: False """ self.load(path, verbose=verbose, encoding=encoding)
[docs] def set_dict(self, gli_dict): """ Set a gli dict as returned by tools methods or generators. Gli will be checked for validity. Parameters ---------- gli_dict : :class:`dict` dictionary containing the gli file Includes the following information (sorted by keys): points : ndarray Array with all point postions point_names : ndarray (of strings) Array with all point names point_md : ndarray Array with all Material-densities at the points if point_md should be undefined it takes the value -np.inf polylines : list of dict each containing information about - ``ID`` (int or None) - ``NAME`` (str) - ``POINTS`` (ndarray) - ``EPSILON`` (float or None) - ``TYPE`` (int or None) - ``MAT_GROUP`` (int or None) - ``POINT_VECTOR`` (str or None) surfaces : list of dict each containing information about - ``ID`` (int or None) - ``NAME`` (str) - ``POLYLINES`` (list of str) - ``EPSILON`` (float or None) - ``TYPE`` (int or None) - ``MAT_GROUP`` (int or None) - ``TIN`` (str or None) volumes : list of dict each containing information about - ``NAME`` (str) - ``SURFACES`` (list of str) - ``TYPE`` (int or None) - ``MAT_GROUP`` (int or None) - ``LAYER`` (int or None) """ if check_gli_dict(gli_dict): self.__dict = gli_dict else: print("given gli_dict is not valid")
[docs] def save(self, path, **kwargs): r""" Save the gli to an OGS5 gli file. kwargs will be forwarded to "tools.save_ogs5gli" Parameters ---------- path : string path to the '\*.gli' OGS5 gli file to save verbose : bool, optional Print information of the writing process. Default: True """ if "verbose" in kwargs: verbose = kwargs["verbose"] else: kwargs["verbose"] = verbose = False if self.check(verbose=verbose): save_ogs5gli( path, self.__dict, top_com=self.top_com, bot_com=self.bot_com, **kwargs, ) else: print("the mesh could not be saved since it is not valid")
[docs] def check(self, verbose=True): """ Check if the gli is valid. In the sence, that the contained data is consistent. Parameters ---------- verbose : bool, optional Print information for the executed checks. Default: True Returns ------- result : bool Validity of the given gli. """ return check_gli_dict(self.__dict, verbose=verbose)
[docs] def swap_axis(self, axis1="y", axis2="z"): """ Swap axis of the coordinate system. Parameters ---------- axis1 : :class:`str` or :class:`int`, optional First selected Axis. Either in ["x", "y", "z"] or in [0, 1, 2]. Default: "y" axis2 : :class:`str` or :class:`int`, optional Second selected Axis. Either in ["x", "y", "z"] or in [0, 1, 2]. Default: "z" """ axis = ["x", "y", "z"] if axis1 in range(3): axis1 = axis[axis1] if axis2 in range(3): axis2 = axis[axis2] if axis1 not in axis or axis2 not in axis: raise ValueError( "GLI.swap_axis: axis need to be 'x', 'y' or 'z': " + str(axis1) + ", " + str(axis2) ) if axis1 == axis2: raise ValueError( "GLI.swap_axis: please select distict axis: " + str(axis1) + " = " + str(axis2) ) ax1 = axis.index(axis1) ax2 = axis.index(axis2) self.POINTS[:, [ax1, ax2]] = self.POINTS[:, [ax2, ax1]]
[docs] def rotate( self, angle, rotation_axis=(0.0, 0.0, 1.0), rotation_point=(0.0, 0.0, 0.0), ): """ Rotate points around a given rotation point and axis with given angle. Parameters ---------- angle : float rotation angle given in radial length rotation_axis : array_like, optional Array containing the vector for ratation axis. Default: (0,0,1) rotation_point : array_like, optional Vector of the ratation base point. Default:(0,0,0) """ self.POINTS = rotate_points( self.POINTS, angle, rotation_axis, rotation_point )
[docs] def shift(self, vector): """ Shift points with a given vector. Parameters ---------- vector : ndarray array containing the shifting vector """ self.POINTS = shift_points(self.POINTS, vector)
[docs] def generate(self, generator="rectangular", **kwargs): """ Use a gli-generator from the generator module. See: :any:`ogs5py.fileclasses.gli.generator` Parameters ---------- generator : str set the generator from the generator module **kwargs kwargs will be forwarded to the generator in use Notes ----- .. currentmodule:: ogs5py.fileclasses.gli.generator The following generators are available: .. autosummary:: rectangular radial """ from ogs5py.fileclasses.gli import generator as gen self.__dict = getattr(gen, generator)(**kwargs)
[docs] def add_points(self, points, names=None, md=None, decimals=4): """ Add a list of points (ndarray with shape (n,3)). Keeps the pointlist unique. If a named point is added, that was already present, it will be renamed with the new name. Same for md. The pointlists of the polylines will be updated. Parameters ---------- points : ndarray Array with new points. names : ndarray of str or None, optional array containing the names. If None, all new points are unnamed. Default: None md : ndarray of float or None, optional array containing the material densitiy. If None, all new points will have unspecified md. Default: None decimals : int, optional Number of decimal places to round the added points to (default: 4). If decimals is negative, it specifies the number of positions to the left of the decimal point. This will not round the new points, it's just for comparison of the already present points to guarante uniqueness. Returns ------- new_pos : ndarray array with the IDs of the added points in the pointlist of the gli. """ # check if given points are unique points = np.array(points, dtype=float, ndmin=2) if names is not None: names = np.array(names, dtype=object, ndmin=1) if points.shape[0] != names.shape[0]: print("gli.add_points: Given names are not valid!") return np.zeros(0) if md is not None: md = np.array(md, dtype=float, ndmin=1) if points.shape[0] != md.shape[0]: print("gli.add_points: Given MDs are not valid!") return np.zeros(0) if points.shape[1] != 3: print("gli.add_points: Given points are not valid!") return np.zeros(0) check_points, __, __ = unique_rows(points, decimals=decimals) if check_points.shape[0] != points.shape[0]: print("gli.add_points: Given points are not unique!") return np.zeros(0) # workaround, if Points are None if self.POINT_NO == 0: self.POINTS = np.empty((0, 3), dtype=float) self.POINT_NAMES = np.empty(0, dtype=object) self.POINT_MD = np.empty(0, dtype=float) new_points = np.vstack((self.POINTS, points)) new_points, __, ixr = unique_rows(new_points, decimals=decimals) old_pos = ixr[: self.POINT_NO] new_pos = ixr[self.POINT_NO :] # set the new names new_names = np.array(new_points.shape[0] * [""], dtype=object) new_names[old_pos] = self.POINT_NAMES if names is not None: new_names[new_pos] = names # set the new MDs new_md = -np.inf * np.ones(new_points.shape[0], dtype=float) new_md[old_pos] = self.POINT_MD if md is not None: new_md[new_pos] = md # reset the point IDs within the polylines for ply_i in range(self.POLYLINE_NO): self.__dict["polylines"][ply_i]["POINTS"] = replace( self.__dict["polylines"][ply_i]["POINTS"], np.arange(self.POINT_NO), old_pos, ) # set the new points self.POINTS = new_points self.POINT_NAMES = new_names self.POINT_MD = new_md # return the new positions of the added points return new_pos
[docs] def add_polyline( self, name, points, ply_id=None, epsilon=None, ply_type=None, mat_group=None, point_vector=None, closed=False, decimals=4, ): """ Add a polyline to the gli. Parameters ---------- name : str name of the new polyline points : ndarray Array with the points. Either array of point IDs or new coordinates. ply_id : int or None, optional Default: None epsilon : float or None, optional Default: None ply_type : int or None, optional Default: None mat_group : int or None, optional Default: None point_vector : str or None, optional Default: None closed : bool, optional If the polyline shall be closed, the first point will be added as last point again. Default: False decimals : int, optional Number of decimal places to round the added points to (default: 4). If decimals is negative, it specifies the number of positions to the left of the decimal point. This will not round the new points, it's just for comparison of the already present points to guarante uniqueness. """ points = np.asanyarray(points) name = str(name) safe_dict = self() if name in self.POLYLINE_NAMES: print("gli.add_polyline: Polyline-name already present!") return # add by id if ( np.issubdtype(points.dtype, np.integer) and points.ndim == 1 and points.shape[0] >= 2 and np.min(points) >= 0 and np.max(points) < self.POINT_NO ): if closed: points = np.hstack((points, points[0])) new_ply = { "NAME": name, "POINTS": points, "ID": ply_id, "EPSILON": epsilon, "TYPE": ply_type, "MAT_GROUP": mat_group, "POINT_VECTOR": point_vector, } # add by name elif ( is_str_array(points) and points.ndim == 1 and points.shape[0] >= 2 and all([str(pnt) in self.POINT_NAMES for pnt in points]) ): if closed: points = np.hstack((points, points[0])) # get IDs from the given names # see: https://stackoverflow.com/a/32191125/6696397 # after the check, if points are IDs... points = np.array( [ np.where(self.POINT_NAMES == str(pnt))[0][0] for pnt in points ], dtype=int, ) new_ply = { "NAME": name, "POINTS": points, "ID": ply_id, "EPSILON": epsilon, "TYPE": ply_type, "MAT_GROUP": mat_group, "POINT_VECTOR": point_vector, } # add by coordinates elif ( points.ndim == 2 and points.shape[0] >= 2 and points.shape[1] == 3 ): if closed: points = np.vstack((points, points[0])) unique_pnt, __, ixr = unique_rows(points, decimals=decimals) new_pos = self.add_points(unique_pnt, decimals=decimals) new_points = replace(ixr, np.arange(unique_pnt.shape[0]), new_pos) new_ply = { "NAME": name, "POINTS": new_points, "ID": ply_id, "EPSILON": epsilon, "TYPE": ply_type, "MAT_GROUP": mat_group, "POINT_VECTOR": point_vector, } else: print("gli.add_polyline: Polyline-points not valid!") return # add the new polyline self.__dict["polylines"].append(new_ply) if not check_polyline(new_ply, self.POINT_NO, verbose=False): print("gli.add_polyline: Polyline not valid!") self.__dict = safe_dict
[docs] def add_surface( self, name, polylines, srf_id=None, epsilon=None, srf_type=0, mat_group=None, tin=None, ): """ Add a new surface. Parameters ---------- name : str name of the new surface polylines : list of str List of the surface-defining polyline-names srf_id : int or None, optional Default: None epsilon : float or None, optional Default: None srf_type : int or None, optional Default: None mat_group : int or None, optional Default: None tin : str or None, optional Default: None """ name = str(name) if name in self.SURFACE_NAMES: print("gli.add_surface: Surface-name already present!") return new_srf = { "NAME": name, "POLYLINES": polylines, "ID": srf_id, "EPSILON": epsilon, "TYPE": srf_type, "MAT_GROUP": mat_group, "TIN": tin, } if not check_surface(new_srf, self.POLYLINE_NAMES, verbose=False): print("gli.add_surface: Surface not valid!") else: self.__dict["surfaces"].append(new_srf)
[docs] def add_volume( self, name, surfaces, vol_type=None, mat_group=None, layer=None ): """ Add a new volume. Parameters ---------- name : str name of the new surface surfaces : list of str List of the volume-defining surface-names vol_type : int or None, optional Default: None mat_group : int or None, optional Default: None layer : int or None, optional Default: None """ name = str(name) if name in self.VOLUME_NAMES: print("gli.add_volume: Volume-name already present!") return new_vol = { "NAME": name, "SURFACES": surfaces, "TYPE": vol_type, "MAT_GROUP": mat_group, "LAYER": layer, } if not check_volume(new_vol, self.SURFACE_NAMES, verbose=False): print("gli.add_volume: Volume not valid!") else: self.__dict["volumes"].append(new_vol)
[docs] def remove_point(self, id_or_name): """ Remove a point by its name or ID. If Points are removed, that define polylines, they will be removed. Same for surfaces and volumes. Parameters ---------- id_or_name : int or str or list of int or list of str Points to be removed. Unknown names or IDs are ignored. """ if not isinstance(id_or_name, (list, tuple, set)): id_or_name = [id_or_name] index_list = [] for pnt in id_or_name: index = -1 if isinstance(pnt, STRTYPE) and pnt in self.POINT_NAMES: index = list(self.POINT_NAMES).index(pnt) else: try: pnt = int(pnt) except ValueError: continue if 0 <= pnt < self.POINT_NO: index = pnt if index == -1: continue if index not in index_list: index_list.append(index) ply_remove = [] for ply in self.POLYLINES: if any([idx in ply["POINTS"] for idx in index_list]): ply_remove.append(ply["NAME"]) self.remove_polyline(ply_remove) old_ids = np.delete(np.arange(self.POINT_NO), index_list, 0) new_ids = np.arange(len(old_ids)) for ply in self.POLYLINES: ply["POINTS"] = replace(ply["POINTS"], old_ids, new_ids) self.POINTS = np.delete(self.POINTS, index_list, 0) self.POINT_NAMES = np.delete(self.POINT_NAMES, index_list, 0) self.POINT_MD = np.delete(self.POINT_MD, index_list, 0)
[docs] def remove_polyline(self, names): """ Remove a polyline by its name. If Polylines are removed, that define surfaces, they will be removed. Same for volumes. Parameters ---------- names : str or list of str Polylines to be removed. Unknown names are ignored. """ if not isinstance(names, (list, tuple, set)): names = [names] for ply_name in names: if ply_name not in self.POLYLINE_NAMES: continue srf_remove = [] for srf in self.SURFACES: if ply_name in srf["POLYLINES"]: srf_remove.append(srf["NAME"]) self.remove_surface(srf_remove) del self.POLYLINES[self.POLYLINE_NAMES.index(ply_name)]
[docs] def remove_surface(self, names): """ Remove a surface by its name. If Surfaces are removed, that define Volumes, they will be removed. Parameters ---------- names : str or list of str Surfaces to be removed. Unknown names are ignored. """ if not isinstance(names, (list, tuple, set)): names = [names] for srf_name in names: if srf_name not in self.SURFACE_NAMES: continue vol_remove = [] for vol in self.VOLUMES: if srf_name in vol["SURFACES"]: vol_remove.append(vol["NAME"]) self.remove_volume(vol_remove) del self.SURFACES[self.SURFACE_NAMES.index(srf_name)]
[docs] def remove_volume(self, names): """ Remove a volume by its name. Parameters ---------- names : str or list of str Volumes to be removed. Unknown names are ignored. """ if not isinstance(names, (list, tuple, set)): names = [names] for vol_name in names: if vol_name not in self.VOLUME_NAMES: continue del self.VOLUMES[self.VOLUME_NAMES.index(vol_name)]
[docs] def pnt_coord(self, pnt_name=None, pnt_id=None): """ Get Point coordinates either by name or ID. Parameters ---------- pnt_name : str Point name. pnt_id : int Point ID. """ # standard output is None, if pnt_name not present, # pnt_id out of range or both are given (ununique) out = None # search by pnt_name if pnt_name is not None and pnt_id is None: # check if pnt_name is present if pnt_name in self.POINT_NAMES: # if pnt_names have duplicates, first occurrence is returend out = self.POINTS[np.where(self.POINT_NAMES == pnt_name)[0][0]] # serach by pnt_id elif pnt_name is None and pnt_id is not None: # check if pnt_id is in range if pnt_id in range(self.POINT_NO): out = self.POINTS[pnt_id] return out
####################### # Special methods #######################
[docs] def __call__(self): """ Return a copy of the underlying dictionary of the gli. Returns ------- Mesh : dict dictionary representation of the mesh """ return dcp(self.__dict)
[docs]class GLIext(File): """ Class for an external definition for the ogs GEOMETRY file. Parameters ---------- typ : :class:`str`, optional Type of the extermal geometry definition. Either ``TIN`` for a triangulated surface or ``POINT_VECTOR`` for a polyline. Default: ``"TIN"`` data : :any:`numpy.ndarray`, optional Data for the external geometry definition. Default: :class:`None` name : str, optional File name for the RFR file. If :class:`None`, the task_id is used. Default: :class:`None` file_ext : :class:`str`, optional extension of the file (with leading dot ".rfr") Default: ".rfr" task_root : str, optional Path to the destiny model folder. Default: cwd+"ogs5model" task_id : str, optional Name for the ogs task. Default: "model" """ def __init__( self, typ="TIN", data=None, name=None, file_ext=None, task_root=None, task_id="model", ): super().__init__(task_root, task_id) if typ not in ["TIN", "POINT_VECTOR"]: raise ValueError("typ needs to be either 'TIN' or 'POINT_VECTOR'") self.typ = typ self.name = name if file_ext is None: if typ == "TIN": file_ext = ".tin" else: file_ext = ".ply" self.file_ext = file_ext if data: self.data = np.array(data) else: if self.typ == "TIN": self.data = np.zeros((0, 10)) else: self.data = np.zeros((0, 3)) if not self.check(False): raise ValueError("Gli external: data not valid") @property def is_empty(self): """State if the OGS file is empty.""" return self.data.shape[0] == 0
[docs] def check(self, verbose=True): """ Check if the external geometry definition is valid. In the sence, that the contained data is consistent. Parameters ---------- verbose : bool, optional Print information for the executed checks. Default: True Returns ------- result : bool Validity of the given gli. """ if self.data.ndim == 2: if self.typ not in ["TIN", "POINT_VECTOR"]: print("Gli external: Wrong typ given") return False elif self.typ == "TIN" and self.data.shape[1] != 10: print( "Gli external: For 'TIN' the data must contain " + "id + 9 Values per line" ) return False elif self.typ == "POINT_VECTOR" and self.data.shape[1] != 3: print( "Gli external: For 'POINT_VECTOR' the data must " + "contain 3 Values per line" ) return False else: if verbose: print("Gli external: data dimension is not 2") return False return True
[docs] def save(self, path): """ Save the actual GLI external file in the given path. Parameters ---------- path : str path to where to file should be saved """ np.savetxt(path, self.data)
[docs] def read_file(self, path, **kwargs): """ Read a given GLI_EXT input file. Parameters ---------- path : str path to the file """ if "verbose" in kwargs: verbose = kwargs["verbose"] else: verbose = False # read data data = np.loadtxt(path, ndmin=2) if data.shape[1] == 10: self.typ = "TIN" self.data = data elif data.shape[1] == 3: self.typ = "POINT_VECTOR" self.data = data elif verbose: print("Gli external: File data not valid")