diff --git a/ogstools/msh2vtu/__init__.py b/ogstools/msh2vtu/__init__.py index aaeb297945220c98a4ebf19cbe55b8b231c3b430..ac2a24d6e224523cd716e46e8f5d53183f802288 100644 --- a/ogstools/msh2vtu/__init__.py +++ b/ogstools/msh2vtu/__init__.py @@ -150,19 +150,49 @@ def find_connected_domain_cells( # TODO: rename def msh2vtu( - input_filename: Path, + filename: Path, output_path: Path = Path(), output_prefix: str = "", dim: Union[int, list[int]] = 0, delz: bool = False, swapxy: bool = False, - rdcd: bool = True, - ogs: bool = True, + reindex: bool = False, + keep_ids: bool = False, ascii: bool = False, log_level: Union[int, str] = "DEBUG", ): + """ + Convert a gmsh mesh (.msh) to an unstructured grid file (.vtu). + + Prepares a Gmsh-mesh for use in OGS by extracting domain-, + boundary- and physical group-submeshes, and saves them in + vtu-format. Note that all mesh entities should belong to + physical groups. + + :param filename: Gmsh mesh file (.msh) as input data + :param output_path: Path of output files, defaults to current working dir + :param output_prefix: Output files prefix, defaults to basename of inputfile + :param dim: Spatial dimension (1, 2 or 3), trying automatic detection, + if not given. If multiple dimensions are provided, all elements + of these dimensions are embedded in the resulting domain mesh. + :param delz: Delete z-coordinate, for 2D-meshes with z=0. + Note that vtu-format requires 3D points. + :param swapxy: Swap x and y coordinate + :param reindex: Renumber physical group / region / Material IDs to be + renumbered beginning with zero. + :param keep_ids: By default, rename 'gmsh:physical' to 'MaterialIDs' + and change type of corresponding cell data to INT32. + If True, this is skipped. + :param ascii: Save output files (.vtu) in ascii format. + :param log_level: Level of log output. Possible values: + <https://docs.python.org/3/library/logging.html#levels> + + :returns: A MeshSeries object + """ logging.getLogger().setLevel(log_level) - input_filename = Path(input_filename) + if isinstance(dim, list): + assert len(dim) < 3, "Specify at most 3 dim values." + filename = Path(filename) # some variable declarations ph_index = 0 # to access physical group id in field data geo_index = 1 # to access geometrical dimension in field data @@ -178,55 +208,39 @@ def msh2vtu( dim0: {"vertex"}, dim1: {"line", "line3", "line4"}, dim2: { - "triangle", - "triangle6", - "triangle9", - "triangle10", - "quad", - "quad8", - "quad9", + *["triangle", "triangle6", "triangle9", "triangle10"], + *["quad", "quad8", "quad9"], }, dim3: { - "tetra", - "tetra10", - "pyramid", - "pyramid13", - "pyramid15", - "pyramid14", - "wedge", # outdated, keep for backward compatibility - "wedge15", - "wedge18", - "hexahedron", - "hexahedron20", - "hexahedron27", - "prism", - "prism15", - "prism18", + *["tetra", "tetra10"], + *["pyramid", "pyramid13", "pyramid15", "pyramid14"], + *["wedge", "wedge15", "wedge18"], + *["hexahedron", "hexahedron20", "hexahedron27"], + *["prism", "prism15", "prism18"], }, } gmsh_physical_cell_data_key = "gmsh:physical" ogs_domain_cell_data_key = "MaterialIDs" ogs_boundary_cell_data_key = "bulk_elem_ids" + ogs = not keep_ids # check if input file exists and is in gmsh-format - if not input_filename.is_file(): + if not filename.is_file(): logging.warning("No input file (mesh) found.") # raise FileNotFoundError return 1 - if input_filename.suffix != ".msh": + if filename.suffix != ".msh": logging.warning( "Warning, input file seems not to be in gmsh-format (*.msh)" ) # if no parameter given, use same basename as input file - output_basename = ( - input_filename.stem if output_prefix == "" else output_prefix - ) + output_basename = filename.stem if output_prefix == "" else output_prefix logging.info("Output: %s", output_basename) # read in mesh (be aware of shallow copies, i.e. by reference) - mesh: meshio.Mesh = meshio.read(str(input_filename)) + mesh: meshio.Mesh = meshio.read(str(filename)) points, point_data = mesh.points, mesh.point_data cells_dict, cell_data, cell_data_dict = ( mesh.cells_dict, @@ -323,24 +337,12 @@ def msh2vtu( pg_key = "PhysicalGroup_" + str(pg_id) field_data[pg_key] = np.array([pg_id, pg_dim]) - # if user wants physical group numbering of domains beginning with zero - id_offset = 0 # initial value, zero will not change anything - if rdcd: # prepare renumber-domain-cell-data (rdcd) - # find minimum physical_id of domains (dim) - id_list_domains = [] - for ( - dataset - ) in field_data.values(): # go through all physical groups - if ( - dataset[geo_index] == domain_dim - ): # only for domains, ignore lower dimensional entities - id_list_domains.append( - dataset[ph_index] - ) # append physical id - if len(id_list_domains): # if there are some domains.. - id_offset = min( - id_list_domains - ) # ..then find minimal physical id + id_list_domains = [ + physical_group[ph_index] + for physical_group in field_data.values() + if physical_group[geo_index] == domain_dim + ] + id_offset = min(id_list_domains, default=0) if reindex else 0 else: logging.info("No physical groups found.") @@ -354,9 +356,8 @@ def msh2vtu( ############################################################################ all_points = np.copy(points) # copy all, superfluous get deleted later if ogs: - original_point_numbers = np.arange( - number_of_original_points - ) # to associate domain points later + # to associate domain points later + original_point_numbers = np.arange(number_of_original_points) all_point_data = {} all_point_data[ogs_domain_point_data_key] = np.uint64( original_point_numbers @@ -555,9 +556,8 @@ def msh2vtu( gmsh_physical_cell_data_key ][boundary_cell_type] else: - number_of_boundary_cells = len( - boundary_cells_values - ) # cells of specific type + # cells of specific type + number_of_boundary_cells = len(boundary_cells_values) boundary_cell_data_values = np.zeros( (number_of_boundary_cells), dtype=int ) @@ -630,9 +630,8 @@ def msh2vtu( # use gmsh, as the requirements from OGS subdomain_cell_data_key = gmsh_physical_cell_data_key else: - subdomain_cell_data_key = ( - gmsh_physical_cell_data_key # same for all dimensions - ) + # same for all dimensions + subdomain_cell_data_key = gmsh_physical_cell_data_key subdomain_cell_data[subdomain_cell_data_key] = [] # list # flag to indicate invalid bulk_element_ids, then no cell data will be # written diff --git a/ogstools/msh2vtu/_cli.py b/ogstools/msh2vtu/_cli.py index 495b1a0b361a45c281b37c773d8e1b09c87a87bf..5721a8edd05a61fa8aa5360e0f3bc83fee3215cf 100644 --- a/ogstools/msh2vtu/_cli.py +++ b/ogstools/msh2vtu/_cli.py @@ -6,107 +6,43 @@ from ogstools.msh2vtu import msh2vtu def argparser(): # parsing command line arguments + def get_help(arg: str): + assert msh2vtu.__doc__ is not None + return msh2vtu.__doc__.split(arg + ":")[1].split(":param")[0].strip() + parser = argparse.ArgumentParser( - description=( - "Prepares a Gmsh-mesh for use in OGS by extracting domain-," - " boundary- and physical group-submeshes, and saves them in" - " vtu-format. Note that all mesh entities should belong to" - " physical groups." - ), - ) - parser.add_argument("filename", help="Gmsh mesh file (*.msh) as input data") - parser.add_argument( - "-g", - "--ogs", - action="store_true", - help=( - 'rename "gmsh:physical" to "MaterialIDs" for domains and change ' - "type of corresponding cell data to INT32" - ), - ) - parser.add_argument( - "-r", - "--rdcd", - action="store_true", - help=( - "renumber domain cell data, physical IDs (cell data) of domains " - "get numbered beginning with zero" - ), - ) - parser.add_argument( - "-a", - "--ascii", - action="store_true", - help="save output files (*.vtu) in ascii format", - ) - parser.add_argument( - "-d", - "--dim", - type=int, - nargs="*", - default=0, - help=( - "spatial dimension (1, 2 or 3), trying automatic detection, " - "if not given. If multiple dimensions are provided, all elements of" - "these dimensions are embedded in the resulting domain mesh." - ), - ) - parser.add_argument( - "-o", - "--output_path", - default="", - help=("path of output files; if not given, then it defaults to cwd"), - ) - parser.add_argument( - "-p", - "--prefix", - default="", - help=( - "basename of output files; if not given, then it defaults to" - " basename of inputfile" - ), - ) - parser.add_argument( - "-z", - "--delz", - action="store_true", - help=( - "deleting z-coordinate, for 2D-meshes with z=0, note that" - " vtu-format requires 3D points" - ), - ) - parser.add_argument( - "-s", - "--swapxy", - action="store_true", - help="swap x and y coordinate", - ) - parser.add_argument( - "-v", - "--version", - action="version", - version=f"msh2vtu (part of ogstools {__version__}, Dominik Kern)", - ) + description=msh2vtu.__doc__.split(":param")[0].strip() + ) + add_arg = parser.add_argument + add_arg("filename", help=get_help("filename")) + add_arg("-o", "--output_path", default="", help=get_help("output_path")) + add_arg("-p", "--prefix", default="", help=get_help("prefix")) + add_arg("-d", "--dim", type=int, nargs="*", default=0, help=get_help("dim")) + add_arg("-z", "--delz", action="store_true", help=get_help("delz")) + add_arg("-s", "--swapxy", action="store_true", help=get_help("swapxy")) + add_arg("-r", "--reindex", action="store_true", help=get_help("reindex")) + add_arg("-k", "--keep_ids", action="store_true", help=get_help("keep_ids")) + add_arg("-a", "--ascii", action="store_true", help=get_help("ascii")) + add_arg("-l", "--log_level", default="DEBUG", help=get_help("log_level")) + version = f"msh2vtu (part of ogstools {__version__}, Dominik Kern)" + add_arg("-v", "--version", action="version", version=version) return parser -def cli(): +def cli() -> int: """command line use""" args = argparser().parse_args() - ErrorCode = msh2vtu( - input_filename=args.filename, + return msh2vtu( + filename=args.filename, output_path=args.output_path, output_prefix=args.prefix, dim=args.dim, delz=args.delz, swapxy=args.swapxy, - rdcd=args.rdcd, - ogs=args.ogs, + reindex=args.reindex, + keep_ids=args.keep_ids, ascii=args.ascii, + log_level=args.log_level, ) - if ErrorCode == 0: - print("msh2vtu successfully finished") - else: - print("msh2vtu stopped with errors") diff --git a/tests/test_studies_convergence.py b/tests/test_studies_convergence.py index d45fe5968e28694795e20121b24312e132092001..c0cae8d69a62d2f0935b2bfbef326b8e49aa36fa 100644 --- a/tests/test_studies_convergence.py +++ b/tests/test_studies_convergence.py @@ -31,9 +31,7 @@ class ConvergenceTest(unittest.TestCase): structured_grid=True, out_name=msh_path, ) - msh2vtu( - input_filename=msh_path, output_path=temp_dir, log_level="ERROR" - ) + msh2vtu(filename=msh_path, output_path=temp_dir, log_level="ERROR") model = ogs.OGS( PROJECT_FILE=temp_dir / "default.prj", INPUT_FILE=convergence.examples.steady_state_diffusion_prj,