diff --git a/src/porepy/utils/sort_points.py b/src/porepy/utils/sort_points.py index 071b08459a..4ae918f11a 100644 --- a/src/porepy/utils/sort_points.py +++ b/src/porepy/utils/sort_points.py @@ -102,6 +102,104 @@ def sort_point_pairs( return sorted_lines, sort_ind +def sort_multiple_point_pairs(lines: np.ndarray) -> np.ndarray: + """Function to sort multiple pairs of points to form circular chains. + + The routine contains essentially the same functionality as sort_point_pairs, + but stripped down to the special case of circular chains. Differently to + sort_point_pairs, this variant sorts an arbitrary amount of independent + point pairs. The chains are restricted by the assumption that each contains + equally many line segments. Finally, this routine uses numba. + + Parameters: + lines (np.ndarray): Array of size 2 * num_chains x num_lines_per_chain, + containing node indices. For each pair of two rows, each column + represents a line segment connectng the two nodes in the two entries + of this column. + + Returns: + np.ndarray: Sorted version of lines, where for each chain, the collection + of l + + Raises: + ImportError ifine segments has been potentially flipped and sorted. + """ + + try: + import numba + except ImportError: + raise ImportError("Numba not available on the system") + + @numba.njit("f8[:,:](i4[:,:])", cache=True) + def _function_to_compile(lines): + """ + Copy of pp.utils.sort_points.sort_point_pairs. This version is extended + to multiple chains. Each chain is implicitly assumed to be circular. + """ + + # Retrieve number of chains and lines per chain from the shape. + # Implicitly expect that all chains have the same length + num_chains, chain_length = lines.shape + # Since for each chain lines includes two rows, divide by two + num_chains = int(num_chains / 2) + + # Initialize array of sorted lines to be the final output + sorted_lines = np.zeros((2 * num_chains, chain_length)) + # Fix the first line segment for each chain and identify + # it as in place regarding the sorting. + sorted_lines[:, 0] = lines[:, 0] + # Keep track of which lines have been fixed and which are still candidates + found = np.zeros(chain_length) + found[0] = 1 + + # Loop over chains and consider each chain separately. + for c in range(num_chains): + # Initialize found making any line segment aside of the first a candidate + found[1:] = 0 + + # Define the end point of the previous and starting point for the next + # line segment + prev = sorted_lines[2 * c + 1, 0] + + # The sorting algorithm: Loop over all positions in the chain to be set next. + # Find the right candidate to be moved to this position and possibly flipped + # if needed. A candidate is identified as fitting if it contains one point + # equal to the current starting point. This algorithm uses a double loop, + # which is the most naive approach. However, assume chain_length is in + # general small. + for i in range(1, chain_length): # The first line has already been found + for j in range( + 1, chain_length + ): # The first line has already been found + # A candidate line segment with matching start and end point + # in the first component of the point pair. + if np.abs(found[j]) < 1e-6 and lines[2 * c, j] == prev: + # Copy the segment to the right place + sorted_lines[2 * c : 2 * c + 2, i] = lines[2 * c : 2 * c + 2, j] + # Mark as used + found[j] = 1 + # Define the starting point for the next line segment + prev = lines[2 * c + 1, j] + break + # A candidate line segment with matching start and end point + # in the second component of the point pair. + elif np.abs(found[j]) < 1e-6 and lines[2 * c + 1, j] == prev: + # Flip and copy the segment to the right place + sorted_lines[2 * c, i] = lines[2 * c + 1, j] + sorted_lines[2 * c + 1, i] = lines[2 * c, j] + # Mark as used + found[j] = 1 + # Define the starting point for the next line segment + prev = lines[2 * c, j] + break + + # Return the sorted lines defining chains. + return sorted_lines + + # Run numba compiled function + return _function_to_compile(lines) + + def sort_point_plane( pts: np.ndarray, centre: np.ndarray, diff --git a/src/porepy/viz/exporter.py b/src/porepy/viz/exporter.py index 0dc6d3537c..fbe364a68b 100644 --- a/src/porepy/viz/exporter.py +++ b/src/porepy/viz/exporter.py @@ -7,88 +7,84 @@ time steps, a single pvd file takes care of the ordering of all printed vtu files. """ +from __future__ import annotations + import os import sys -from typing import Dict, Generator, Iterable, List, Optional, Tuple, Union +from collections import namedtuple +from typing import Any, Dict, Iterable, List, Optional, Tuple, Union import meshio import numpy as np -import scipy.sparse as sps import porepy as pp +# Object type to store data to export. +Field = namedtuple("Field", ["name", "values"]) + +# Object for managing meshio-relevant data, as well as a container +# for its storage, taking dimensions as inputs. Since 0d grids are +# stored as 'None', allow for such values. +Meshio_Geom = namedtuple("Meshio_Geom", ["pts", "connectivity", "cell_ids"]) +MD_Meshio_Geom = Dict[int, Union[None, Meshio_Geom]] + +# All allowed data structures to define data for exporting +DataInput = Union[ + # Keys for states + str, + # Subdomain specific data types + Tuple[List[pp.Grid], str], + Tuple[pp.Grid, str, np.ndarray], + Tuple[str, np.ndarray], + # Interface specific data types + Tuple[List[pp.MortarGrid], str], + Tuple[pp.MortarGrid, str, np.ndarray], +] + +# Data structure in which data is stored after preprocessing +SubdomainData = Dict[Tuple[pp.Grid, str], np.ndarray] +InterfaceData = Dict[Tuple[pp.MortarGrid, str], np.ndarray] -class Field: - """ - Internal class to store information for the data to export. - """ - - def __init__(self, name: str, values: Optional[np.ndarray] = None) -> None: - # name of the field - self.name = name - self.values = values - - def __repr__(self) -> str: - """ - Repr function - """ - return self.name + " - values: " + str(self.values) - - def check(self, values: Optional[np.ndarray], grid: pp.Grid) -> None: - """ - Consistency checks making sure the field self.name is filled and has - the right dimension. - """ - if values is None: - raise ValueError( - "Field " + str(self.name) + " must be filled. It can not be None" - ) - if np.atleast_2d(values).shape[1] != grid.num_cells: - raise ValueError("Field " + str(self.name) + " has wrong dimension.") - def _check_values(self) -> None: - if self.values is None: - raise ValueError("Field " + str(self.name) + " values not valid") +class Exporter: + """ + Class for exporting data to vtu files. + The Exporter allows for various way to express which state variables, + on which grids, and which extra data should be exported. A thorough + demonstration is available as a dedicated tutorial. Check out + tutorials/exporter.ipynb. -class Fields: - """ - Internal class to store a list of field. - """ + In general, pvd files gather data exported in separate files, including + data on differently dimensioned grids, constant data, and finally time steps. - def __init__(self) -> None: - self._fields: List[Field] = [] + In the case of different keywords, change the file name with + "change_name". - def __iter__(self) -> Generator[Field, None, None]: - """ - Iterator on all the fields. - """ - for f in self._fields: - yield f + NOTE: the following names are reserved for constant data exporting + and should not be used otherwise (otherwise data is overwritten): + grid_dim, is_mortar, mortar_side, cell_id, grid_node_number, + grid_edge_number. - def __repr__(self) -> str: - """ - Repr function - """ - return "\n".join([repr(f) for f in self]) + Examples: + # Here, merely a brief demonstration of the use of Exporter is presented. - def extend(self, new_fields: List[Field]) -> None: - """ - Extend the list of fields with additional fields. - """ - if isinstance(new_fields, list): - self._fields.extend(new_fields) - else: - raise ValueError + # If you need to export the state with key "pressure" on a single grid: + save = Exporter(g, "solution", folder_name="results") + save.write_vtu(["pressure"]) - def names(self) -> List[str]: - """ - Return the list of name of the fields. - """ - return [f.name for f in self] + # In a time loop, if you need to export states with keys "pressure" and + # "displacement" stored in a mixed-dimensional grid. + save = Exporter(mdg, "solution", folder_name="results") + while time: + save.write_vtu(["pressure", "displacement"], time_step=i) + save.write_pvd(times) + # where times is a list of actual times (not time steps), associated + # to the previously exported time steps. If times is not provided + # the time steps will be used instead. + """ -class Exporter: def __init__( self, grid: Union[pp.Grid, pp.MixedDimensionalGrid], @@ -96,185 +92,301 @@ def __init__( folder_name: Optional[str] = None, **kwargs, ) -> None: - """ - Class for exporting data to vtu files. + """Initialization of Exporter. Args: - grid: a single or mixed-dimensional grid - file_name: the root of file name without any extension. - folder_name: (optional) the name of the folder to save the file. - If the folder does not exist it will be created. - kwargs (optional): Keyword arguments: - fixed_grid: (optional) in a time dependent simulation specify if the - grid changes in time or not. The default is True. - binary: export in binary format, default is True. - - Example: - # If you need to export a single grid: - save = Exporter(g, "solution", folder_name="results") - save.write_vtu({"cells_id": cells_id, "pressure": pressure}) - - # In a time loop: - save = Exporter(mdg, "solution", folder_name="results") - while time: - save.write_vtu({"conc": conc}, time_step=i) - save.write_pvd(steps*deltaT) - - # if you need to export the state of variables as stored in the MixedDimensionalGrid: - save = Exporter(mdg, "solution", folder_name="results") - # export the field stored in data[pp.STATE]["pressure"] - save.write_vtu(mdg, ["pressure"]) - - # In a time loop: - while time: - save.write_vtu(["conc"], time_step=i) - save.write_pvd(steps*deltaT) - - In the case of different keywords, change the file name with - "change_name". - - NOTE: the following names are reserved for data exporting: grid_dim, - is_mortar, mortar_side, cell_id - + grid (Union[pp.Grid, pp.MixedDimensionalGrid]): subdomain or mixed-dimensional grid + containing all mesh information (and data in the latter case) to be exported. + file_name (str): basis for file names used for storing the output + folder_name (str, optional): name of the folder in which files are stored + kwargs: Optional keywords arguments: + fixed_grid (boolean): to control whether the grid may be redfined later + (default True) + binary (boolean): controlling whether data is stored in binary format + (default True) + export_constants_separately (boolean): controlling whether + constant data is exported in separate files, which may be of interest + when exporting large data sets (in particular of constant data) for + many time steps (default True); note, however, that the mesh is + exported to each vtu file, which may also require significant amount + of storage. + + Raises: + TypeError if grid has other type than pp.Grid or pp.MixedDimensional grid + TypeError if kwargs contains unexpected keyword arguments """ - - if isinstance(grid, pp.MixedDimensionalGrid): - self.is_MixedDimensionalGrid = True - self.mdg: pp.MixedDimensionalGrid = grid - + # Exporter is operating on mixed-dimensional grids. Convert to mixed-dimensional + # grids if subdomain grid is provided. + if isinstance(grid, pp.Grid): + self._mdg = pp.MixedDimensionalGrid() + self._mdg.add_subdomains(grid) + elif isinstance(grid, pp.MixedDimensionalGrid): + self._mdg = grid else: - self.is_MixedDimensionalGrid = False - self.grid: pp.Grid = grid # type: ignore + raise TypeError( + "Exporter only supports subdomain and mixed-dimensional grids." + ) - self.file_name = file_name - self.folder_name = folder_name - self.fixed_grid: bool = kwargs.pop("fixed_grid", True) - self.binary: bool = kwargs.pop("binary", True) + # Store target location for storing vtu and pvd files. + self._file_name = file_name + self._folder_name = folder_name + + # Check for optional keywords + self._fixed_grid: bool = kwargs.pop("fixed_grid", True) + self._binary: bool = kwargs.pop("binary", True) + self._export_constants_separately: bool = kwargs.pop( + "export_constants_separately", True + ) if kwargs: msg = "Exporter() got unexpected keyword argument '{}'" raise TypeError(msg.format(kwargs.popitem()[0])) - self.cell_id_key = "cell_id" - - if self.is_MixedDimensionalGrid: - # Fixed-dimensional grids to be included in the export. We include - # all but the 0-d grids - sd_all_dims = np.unique([sd.dim for sd in self.mdg.subdomains()]) - self.dims = np.setdiff1d(sd_all_dims, [0]) - num_dims = self.dims.size - self.meshio_geom = dict(zip(self.dims, [tuple()] * num_dims)) # type: ignore - - # mortar grid variables - self.m_dims = np.unique([intf.dim for intf in self.mdg.interfaces()]) - num_m_dims = self.m_dims.size - self.m_meshio_geom = dict(zip(self.m_dims, [tuple()] * num_m_dims)) # type: ignore - else: - self.meshio_geom = tuple() # type: ignore + # Generate infrastructure for storing fixed-dimensional grids in + # meshio format. Include all but the 0-d grids + self._dims = np.unique([sd.dim for sd in self._mdg.subdomains() if sd.dim > 0]) + self.meshio_geom: MD_Meshio_Geom = dict() - # Assume numba is available - self.has_numba: bool = True + # Generate infrastructure for storing fixed-dimensional mortar grids + # in meshio format. + self._m_dims = np.unique([intf.dim for intf in self._mdg.interfaces()]) + self.m_meshio_geom: MD_Meshio_Geom = dict() + # Generate geometrical information in meshio format self._update_meshio_geom() + self._update_constant_mesh_data() # Counter for time step. Will be used to identify files of individual time step, # unless this is overridden by optional parameters in write self._time_step_counter: int = 0 + # Storage for file name extensions for time steps - self._exported_time_step_file_names: List[int] = [] + self._exported_timesteps: list[int] = [] + + # Reference to the last time step used for exporting constant data. + self._time_step_constants: int = 0 + # Storage for file name extensions for time steps, regarding constant data. + self._exported_timesteps_constants: list[int] = list() + # Identifier for whether constant data is up-to-date. + self._exported_constant_data_up_to_date: bool = False + + # Parameter to be used in several occasions for adding time stamps. + self._padding = 6 - def change_name(self, file_name: str) -> None: + def add_constant_data( + self, + data: Optional[Union[DataInput, list[DataInput]]] = None, + ) -> None: """ - Change the root name of the files, useful when different keywords are - considered but on the same grid. + Collect user-defined constant-in-time data, associated to grids, + and to be exported to separate files instead of the main files. + + In principle, constant data is not different from standard output + data. It is merely printed to another file and just when any constant + data is updated (via updates of the grid, or the use of this routine). + Hence, the same input formats are allowed as for the usual field data, + which is varying in time. As part of the routine, the data is converted + to the same unified format. Args: - file_name: the new root name of the files. + data (Union[DataInput, list[DataInput]], optional): subdomain and + interface data, prescribed through strings, or tuples of + subdomains/interfaces, keys and values. If not provided only + geometical infos are exported. + + NOTE: The user has to make sure that each unique key has + associated data values for all or no grids of each specific + dimension. """ - self.file_name = file_name + # Interpret change in constant data. Has the effect that + # the constant data container will be exported at the + # next application of write_vtu(). + self._exported_constant_data_up_to_date = False + + # Preprocessing of the user-defined constant data + # Has two main goals: + # 1. Sort wrt. whether data is associated to subdomains or interfaces. + # 2. Unify data type. + subdomain_data, interface_data = self._sort_and_unify_data(data) + + # Add the user-defined data to the containers for constant data. + for key_s, value in subdomain_data.items(): + self._constant_subdomain_data[key_s] = value.copy() + for key_i, value in interface_data.items(): + self._constant_interface_data[key_i] = value.copy() def write_vtu( self, - data: Optional[Union[Dict, List[str]]] = None, + data: Optional[Union[DataInput, list[DataInput]]] = None, time_dependent: bool = False, - time_step: int = None, + time_step: Optional[int] = None, grid: Optional[Union[pp.Grid, pp.MixedDimensionalGrid]] = None, ) -> None: """ Interface function to export the grid and additional data with meshio. - In 1d the cells are represented as lines, 2d the cells as polygon or triangle/quad, - while in 3d as polyhedra/tetrahedra/hexahedra. + In 1d the cells are represented as lines, 2d the cells as polygon or + triangle/quad, while in 3d as polyhedra/tetrahedra/hexahedra. In all the dimensions the geometry of the mesh needs to be computed. Args: - data: if grid is a single grid then data is a dictionary (see example) - if grid is a mixed-dimensional grid then list of names for optional data, - they are the keys in the mixed-dimensional grid (see example). - time_dependent: (bolean, optional) If False, file names will not be appended with - an index that markes the time step. Can be overridden by giving - a value to time_step. - time_step: (optional) in a time dependent problem defines the part of the file - name associated with this time step. If not provided, subsequent - time steps will have file names ending with 0, 1, etc. - grid: (optional) in case of changing grid set a new one. - + data (Union[DataInput, list[DataInput]], optional): subdomain and + interface data, prescribed through strings, or tuples of + subdomains/interfaces, keys and values. If not provided only + geometrical infos are exported. + + NOTE: The user has to make sure that each unique key has + associated data values for all or no grids of each specific + dimension. + time_dependent (boolean): If False (default), file names will + not be appended with an index that marks the time step. + Can be overwritten by giving a value to time_step; if not, + the file names will subsequently be ending with 1, 2, etc. + time_step (int, optional): will be used as appendix to define + the file corresponding to this specific time step. + grid (Union[pp.Grid, pp.MixedDimensionalGrid], optional): subdomain or + mixed-dimensional grid if it is not fixed and should be updated. + + Raises: + ValueError if a grid is provided as argument although the exporter + has been instructed that the grid is fixed """ - if self.fixed_grid and grid is not None: + + # Update the grid but only if allowed, i.e., the initial grid + # has not been characterized as fixed. + if self._fixed_grid and grid is not None: raise ValueError("Inconsistency in exporter setting") - elif not self.fixed_grid and grid is not None: - if self.is_MixedDimensionalGrid: - self.mdg = grid # type: ignore + elif not self._fixed_grid and grid is not None: + # Require a mixed-dimensional grid. Thus convert if single subdomain grid provided. + if isinstance(grid, pp.Grid): + # Create a new mixed-dimensional solely with grid as single subdomain. + self._mdg = pp.MixedDimensionalGrid() + self._mdg.add_subdomains(grid) else: - self.grid = grid # type: ignore + self._mdg = grid + # Update geometrical info in meshio format for the updated grid self._update_meshio_geom() + self._update_constant_mesh_data() # If the problem is time dependent, but no time step is set, we set one + # using the updated, internal counter. if time_dependent and time_step is None: time_step = self._time_step_counter self._time_step_counter += 1 - # If the problem is time dependent (with specified or automatic time step index) - # add the time step to the exported files + # If time step is prescribed, store it. if time_step is not None: - self._exported_time_step_file_names.append(time_step) + self._exported_timesteps.append(time_step) + + # Preprocessing step with two main goals: + # 1. Sort wrt. whether data is associated to subdomains or interfaces. + # 2. Unify data type. + subdomain_data, interface_data = self._sort_and_unify_data(data) + + # Export constant data to separate or standard files + if self._export_constants_separately: + # Export constant data to vtu when outdated + if not self._exported_constant_data_up_to_date: + # Export constant subdomain data to vtu + self._export_data_vtu( + self._constant_subdomain_data, time_step, constant_data=True + ) + + # Export constant interface data to vtu + self._export_data_vtu( + self._constant_interface_data, + time_step, + constant_data=True, + interface_data=True, + ) + + # Store the time step counter for later reference + # (required for pvd files) + self._time_step_constant_data = time_step + + # Identify the constant data as fixed. Has the effect + # that the constant data won't be exported if not the + # mesh is updated or new constant data is added. + self._exported_constant_data_up_to_date = True - if self.is_MixedDimensionalGrid: - self._export_mdg(data, time_step) # type: ignore + # Store the timestep referring to the origin of the constant data + if self._time_step_constant_data: + self._exported_timesteps_constants.append(self._time_step_constant_data) else: - self._export_sd(data, time_step) # type: ignore + # Append constant subdomain and interface data to the + # standard containers for subdomain and interface data. + for key_sd, value in self._constant_subdomain_data.items(): + subdomain_data[key_sd] = value.copy() + for key_intf, value in self._constant_interface_data.items(): + interface_data[key_intf] = value.copy() + + # Export subdomain and interface data to vtu format if existing + if subdomain_data: + self._export_data_vtu(subdomain_data, time_step) + if interface_data: + self._export_data_vtu(interface_data, time_step, interface_data=True) + + # Export mixed-dimensional grid to pvd format + file_name = self._make_file_name(self._file_name, time_step, extension=".pvd") + file_name = self._append_folder_name(self._folder_name, file_name) + self._export_mdg_pvd(file_name, time_step) def write_pvd( self, - timestep: np.ndarray, - file_extension: Optional[Union[np.ndarray, List[int]]] = None, + times: Optional[np.ndarray] = None, + file_extension: Optional[Union[np.ndarray, list[int]]] = None, ) -> None: """ Interface function to export in PVD file the time loop information. The user should open only this file in paraview. - We assume that the VTU associated files have the same name. - We assume that the VTU associated files are in the same folder. + We assume that the VTU associated files have the same name, and that. + the VTU associated files are in the working directory. Args: - timestep: numpy of times to be exported. These will be the time associated with - indivdiual time steps in, say, Paraview. By default, the times will be - associated with the order in which the time steps were exported. This can - be overridden by the file_extension argument. - file_extension (np.array-like, optional): End of file names used in the export - of individual time steps, see self.write_vtu(). If provided, it should have - the same length as time. If not provided, the file names will be picked - from those used when writing individual time steps. - + times (np.ndarray, optional): array of actual times to be exported. These will + be the times associated with indivdiual time steps in, say, Paraview. + By default, the times will be associated with the order in which the time + steps were exported. This can be overridden by the file_extension argument. + If no times are provided, the exported time steps are used. + file_extension (np.array-like, optional): End of file names used in the export + of individual time steps, see self.write_vtu(). If provided, it should have + the same length as time. If not provided, the file names will be picked + from those used when writing individual time steps. """ + + if times is None: + times = np.array(self._exported_timesteps) + if file_extension is None: - file_extension = self._exported_time_step_file_names + file_extension = self._exported_timesteps elif isinstance(file_extension, np.ndarray): file_extension = file_extension.tolist() - assert file_extension is not None # make mypy happy + # Make sure that the inputs are consistent + assert isinstance(file_extension, list) + assert len(file_extension) == times.shape[0] + + # Extract the time steps related to constant data and + # complying with file_extension. Implicitly test whether + # file_extension is a subset of _exported_timesteps. + # Only if constant data has been exported. + include_constant_data = ( + self._export_constants_separately + and len(self._exported_timesteps_constants) > 0 + ) + if include_constant_data: + indices = [self._exported_timesteps.index(e) for e in file_extension] + file_extension_constants = [ + self._exported_timesteps_constants[i] for i in indices + ] - o_file = open(self._make_folder(self.folder_name, self.file_name) + ".pvd", "w") + # Perform the same procedure as in _export_mdg_pvd + # but looping over all designated time steps. + + o_file = open( + self._append_folder_name(self._folder_name, self._file_name) + ".pvd", "w" + ) b = "LittleEndian" if sys.byteorder == "little" else "BigEndian" c = ' compressor="vtkZLibDataCompressor"' header = ( @@ -286,199 +398,684 @@ def write_pvd( o_file.write(header) fm = '\t\n' - if self.is_MixedDimensionalGrid: - for time, fn in zip(timestep, file_extension): - for dim in self.dims: + # Gather all data, and assign the actual time. + for time, fn in zip(times, file_extension): + # Go through all possible data types, analogously to + # _export_mdg_pvd. + + # Subdomain data + for dim in self._dims: + if self.meshio_geom[dim] is not None: o_file.write( - fm % (time, self._make_file_name(self.file_name, fn, dim)) + fm % (time, self._make_file_name(self._file_name, fn, dim)) ) - else: - for time, fn in zip(timestep, file_extension): - o_file.write(fm % (time, self._make_file_name(self.file_name, fn))) + + # Interface data. + for dim in self._m_dims: + if self.m_meshio_geom[dim] is not None: + o_file.write( + fm + % ( + time, + self._make_file_name(self._file_name + "_mortar", fn, dim), + ) + ) + + # Optionally, do the same for constant data. + if include_constant_data: + for time, fn_constants in zip(times, file_extension_constants): + # Constant subdomain data. + for dim in self._dims: + if self.meshio_geom[dim] is not None: + o_file.write( + fm + % ( + time, + self._make_file_name( + self._file_name + "_constant", fn_constants, dim + ), + ) + ) + + # Constant interface data. + for dim in self._m_dims: + if self.m_meshio_geom[dim] is not None: + o_file.write( + fm + % ( + time, + self._make_file_name( + self._file_name + "_constant_mortar", + fn_constants, + dim, + ), + ) + ) o_file.write("\n" + "") o_file.close() - def _export_sd(self, data: Dict[str, np.ndarray], time_step): + # Some auxiliary routines used in write_vtu() + + def _sort_and_unify_data( + self, + data=None, # ignore type which is essentially Union[DataInput, list[DataInput]] + ) -> tuple[SubdomainData, InterfaceData]: """ - Export a single grid (subdomain) to a vtu file. + Preprocess data. + + The routine has two goals: + 1. Splitting data into subdomain and interface data. + 2. Unify the data format. Store data in dictionaries, with keys given by + subdomains/interfaces and names, and values given by the data arrays. + + Args: + data (Union[DataInput, list[DataInput]], optional): data + provided by the user in the form of strings and/or tuples + of subdomains/interfaces. + + Returns: + tuple[SubdomainData, InterfaceData]: Subdomain and interface data decomposed and + brought into unified format. + + Raises: + ValueError if the data type provided is not supported """ - # No need of special naming, create the folder - name = self._make_folder(self.folder_name, self.file_name) - name = self._make_file_name(name, time_step) - # Provide an empty dict if data is None - if data is None: - data = dict() + # The strategy is to traverse the input data and check each data point + # for its type and apply a corresponding conversion to a unique format. + # For each supported input data format, a dedicated routine is implemented + # to 1. identify whether the type is present; and 2. update the data + # dictionaries (to be returned in this routine) in the correct format: + # The dictionaries use (subdomain,key) and (interface,key) as key and the data + # array as value. + + # Now a list of these subroutines follows before the definition of the + # actual routine. + + # Aux. method: Transform scalar- to vector-ranged values. + def _toVectorFormat( + value: np.ndarray, grid: Union[pp.Grid, pp.MortarGrid] + ) -> np.ndarray: + """ + Check whether the value array has the right dimension corresponding + to the grid size. If possible, translate the value to a vectorial + object, but do nothing if the data naturally can be interpreted as + scalar data. + + Args: + value (np.ndarray): input array to be converted + grid (pp.Grid or pp.MortarGrid): subdomain or interface to which value + is associated to + + Raises: + ValueError if the value array is not compatible with the grid + """ + # Make some checks + if not value.size % grid.num_cells == 0: + # This line will raise an error if node or face data is exported. + raise ValueError("The data array is not compatible with the grid.") + + # Convert to vectorial data if more data provided than grid cells available, + # and the value array is not already in vectorial format + if not value.size == grid.num_cells and not ( + len(value.shape) > 1 and value.shape[1] == grid.num_cells + ): + value = np.reshape(value, (-1, grid.num_cells), "F") + + return value + + # TODO rename pt to data_pt? + # TODO typing + def add_data_from_str( + data_pt, subdomain_data: dict, interface_data: dict + ) -> tuple[dict, dict, bool]: + """ + Check whether data is provided by a key of a field - could be both subdomain + and interface data. If so, collect all data corresponding to subdomains and + interfaces identified by the key. - fields = Fields() - if len(data) > 0: - fields.extend([Field(n, v) for n, v in data.items()]) + Raises: + ValueError if no data available in the mixed-dimensional grid for given key + """ - grid_dim = self.grid.dim * np.ones(self.grid.num_cells, dtype=int) + # Only continue in case data is of type str + if isinstance(data_pt, str): + + # Identify the key provided through the data. + key = data_pt + + # Initialize tag storing whether data corrspeonding to the key has been found. + has_key = False + + def _add_data( + key: str, + grid: Union[pp.Grid, pp.MortarGrid], + grid_data: dict, + export_data: dict, + ) -> bool: + if pp.STATE in grid_data and key in grid_data[pp.STATE]: + # Fetch data and convert to vectorial format if suggested by the size + value: np.ndarray = _toVectorFormat( + grid_data[pp.STATE][key], grid + ) - fields.extend([Field("grid_dim", grid_dim)]) + # Add data point in correct format to the collection + export_data[(grid, key)] = value - self._write(fields, name, self.meshio_geom) + # Mark as succes + return True + else: + return False + + # Check data associated to subdomain field data + for sd, sd_data in self._mdg.subdomains(return_data=True): + if _add_data(key, sd, sd_data, subdomain_data): + has_key = True + + # Check data associated to interface field data + for intf, intf_data in self._mdg.interfaces(return_data=True): + if _add_data(key, intf, intf_data, interface_data): + has_key = True + + # Make sure the key exists + if not has_key: + raise ValueError( + f"No data with provided key {key} present in the grid." + ) - def _export_mdg(self, data: List[str], time_step: float) -> None: - """Export the mixed-dimensional grid and additional data to vtu. + # Return updated dictionaries and indicate succesful conversion. + return subdomain_data, interface_data, True - Args: - data (List[str]): Data to be exported in addition to default MixedDimensionalGrid - data. - time_step (float) : Time step, to be appended at the vtu output file. - """ - # Convert data to list, or provide an empty list - if data is not None: - data = np.atleast_1d(data).tolist() - else: + else: + # Return original data dictionaries and indicate no modification. + return subdomain_data, interface_data, False + + def add_data_from_tuple_subdomains_str( + data_pt: tuple[list[pp.Grid], str], + subdomain_data: dict, + interface_data: dict, + ) -> tuple[dict, dict, bool]: + """ + Check whether data is provided as tuple (subdomains, key), + where subdomains is a list of subdomains, and key is a string. + This routine explicitly checks only for subdomain data. + + Raises: + ValueError if there exists no state in the subdomain data with given key + """ + + # Implementation of isinstance(data_pt, tuple[list[pp.Grid], str]). + isinstance_tuple_subdomains_str = list(map(type, data_pt)) == [ + list, + str, + ] and all([isinstance(sd, pp.Grid) for sd in data_pt[0]]) + + # If of correct type, convert to unique format and update subdomain data. + if isinstance_tuple_subdomains_str: + + # By construction, the 1. and 2. components are a list of grids and a key. + subdomains: list[pp.Grid] = data_pt[0] + key = data_pt[1] + + # Loop over grids and fetch the states corresponding to the key + for sd in subdomains: + + # Fetch the data dictionary containing the data value + sd_data = self._mdg.subdomain_data(sd) + + # Make sure the data exists. + if not (pp.STATE in sd_data and key in sd_data[pp.STATE]): + raise ValueError( + f"""No state with prescribed key {key} + available on selected subdomains.""" + ) + + # Fetch data and convert to vectorial format if suitable + value = _toVectorFormat(sd_data[pp.STATE][key], sd) + + # Add data point in correct format to collection + subdomain_data[(sd, key)] = value + + # Return updated dictionaries and indicate succesful conversion. + return subdomain_data, interface_data, True + + else: + # Return original data dictionaries and indicate no modification. + return subdomain_data, interface_data, False + + # Aux. method: Detect and convert data of form ([interfaces], "key"). + def add_data_from_tuple_interfaces_str( + data_pt, subdomain_data, interface_data + ) -> tuple[dict, dict, bool]: + """ + Check whether data is provided as tuple (interfaces, key), + where interfaces is a list of interfaces, and key is a string. + This routine explicitly checks only for interface data. + + This routine is a translation of add_data_from_tuple_subdomains_str to interfaces + + Raises: + ValueError if there exists no state in the interface data with given key + """ + + # Implementation of isinstance(t, tuple[list[pp.MortarGrid], str]). + isinstance_tuple_interfaces_str = list(map(type, data_pt)) == [ + list, + str, + ] and all([isinstance(intf, pp.MortarGrid) for intf in data_pt[0]]) + + # If of correct type, convert to unique format and update subdomain data. + if isinstance_tuple_interfaces_str: + + # By construction, the 1. and 2. components are a list of interfaces and a key. + interfaces: list[pp.MortarGrid] = data_pt[0] + key = data_pt[1] + + # Loop over interfaces and fetch the states corresponding to the key + for intf in interfaces: + + # Fetch the data dictionary containing the data value + intf_data = self._mdg.interface_data(intf) + + # Make sure the data exists. + if not (pp.STATE in intf_data and key in intf_data[pp.STATE]): + raise ValueError( + f"""No state with prescribed key {key} + available on selected interfaces.""" + ) + + # Fetch data and convert to vectorial format if suitable + value = _toVectorFormat(intf_data[pp.STATE][key], intf) + + # Add data point in correct format to collection + interface_data[(intf, key)] = value + + # Return updated dictionaries and indicate succesful conversion. + return subdomain_data, interface_data, True + + else: + # Return original data dictionaries and indicate no modification. + return subdomain_data, interface_data, False + + def add_data_from_tuple_subdomain_str_array( + data_pt, subdomain_data, interface_data + ) -> tuple[dict, dict, bool]: + """ + Check whether data is provided as tuple (sd, key, data), + where sd is a single subdomain, key is a string, and data is a user-defined data + array. This routine explicitly checks only for subdomain data. + """ + + # Implementation of isinstance(t, tuple[pp.Grid, str, np.ndarray]). + # NOTE: The type of a grid is identifying the specific grid type (simplicial + # etc.) Thus, isinstance is used here to detect whether a grid is provided. + isinstance_tuple_subdomain_str_array = isinstance( + data_pt[0], pp.Grid + ) and list(map(type, data_pt))[1:] == [str, np.ndarray] + + # Convert data to unique format and update the subdomain data dictionary. + if isinstance_tuple_subdomain_str_array: + + # Interpret (sd, key, value) = (data_pt[0], data_pt[1], data_pt[2]); + sd = data_pt[0] + key = data_pt[1] + value = _toVectorFormat(data_pt[2], sd) + + # Add data point in correct format to collection + subdomain_data[(sd, key)] = value + + # Return updated dictionaries and indicate succesful conversion. + return subdomain_data, interface_data, True + + else: + # Return original data dictionaries and indicate no modification. + return subdomain_data, interface_data, False + + def add_data_from_tuple_interface_str_array( + data_pt, subdomain_data: dict, interface_data: dict + ) -> tuple[dict, dict, bool]: + """ + Check whether data is provided as tuple (g, key, data), + where e is a single interface, key is a string, and data is a user-defined + data array. This routine explicitly checks only for interface data. + + Translation of add_data_from_tuple_subdomain_str_array to interfaces. + """ + + # Implementation of isinstance(t, tuple[pp.MortarGrid, str, np.ndarray]). + isinstance_tuple_interface_str_array = list(map(type, data_pt)) == [ + tuple, + str, + np.ndarray, + ] and isinstance(data_pt[0], pp.MortarGrid) + + # Convert data to unique format and update the interface data dictionary. + if isinstance_tuple_interface_str_array: + # Interpret (intf, key, value) = (data_pt[0], data_pt[1], data_pt[2]); + intf = data_pt[0] + key = data_pt[1] + value = _toVectorFormat(data_pt[2], intf) + + # Add data point in correct format to collection + interface_data[(intf, key)] = value + + # Return updated dictionaries and indicate succesful conversion. + return subdomain_data, interface_data, True + + else: + # Return original data dictionaries and indicate no modification. + return subdomain_data, interface_data, False + + # TODO can we unify to add_data_from_tuple_grid_str_array ? + + def add_data_from_tuple_str_array( + data_pt, subdomain_data, interface_data + ) -> tuple[dict, dict, bool]: + """ + Check whether data is provided by a tuple (key, data), + where key is a string, and data is a user-defined data array. + This only works when the mixed-dimensional grid contains a single subdomain. + + Raises: + ValueError if the mixed-dimensional grid contains more than one subdomain + """ + + # Implementation if isinstance(data_pt, tuple[str, np.ndarray]. + isinstance_tuple_str_array = list(map(type, data_pt)) == [str, np.ndarray] + + # Convert data to unique format and update the interface data dictionary. + if isinstance_tuple_str_array: + + # Fetch the correct grid. This option is only supported for mixed-dimensional + # grids containing a single subdomain. + subdomains = self._mdg.subdomains() + if not len(subdomains) == 1: + raise ValueError( + f"""The data type used for {data_pt} is only + supported if the mixed-dimensional grid only contains a single + subdomain.""" + ) + + # Fetch remaining ingredients required to define subdomain data element + sd = subdomains[0] + key = data_pt[0] + value = _toVectorFormat(data_pt[1], sd) + + # Add data point in correct format to collection + subdomain_data[(sd, key)] = value + + # Return updated dictionaries and indicate succesful conversion. + return subdomain_data, interface_data, True + + else: + # Return original data dictionaries and indicate no modification. + return subdomain_data, interface_data, False + + ################################################ + # The actual routine _sort_and_unify_data() + ################################################ + + # Convert data to list, while keeping the data structures provided, + # or provide an empty list if no data provided + if data is None: data = list() + elif not isinstance(data, list): + data = [data] + + # Initialize container for data associated to subdomains and interfaces + subdomain_data: SubdomainData = dict() + interface_data: InterfaceData = dict() + + # Define methods to be used for checking the data type and performing + # the conversion. This list implicitly also defines which input data is + # allowed. + methods = [ + add_data_from_str, + add_data_from_tuple_subdomains_str, + add_data_from_tuple_interfaces_str, + add_data_from_tuple_subdomain_str_array, + add_data_from_tuple_interface_str_array, + add_data_from_tuple_str_array, + ] + + # Loop over all data points and collect them in a unified format. + # For this, two separate dictionaries (for subdomain and interface data) + # are used. The final format uses (subdomain/interface,key) as key of + # the dictionaries, and the value is given by the corresponding data. + for data_pt in data: + + # Initialize tag storing whether the conversion process for data_pt is successful. + success = False + + for method in methods: - # Extract data which associated to subdomains (not interfaces). - # IMPLEMENTATION NOTE: We need a unique set of keywords for node_data. The simpler - # option would have been to gather all keys and uniquify by converting to a set, - # and then back to a list. However, this will make the ordering of the keys random, - # and it turned out that this complicates testing (see tests/unit/test_vtk). - # It was therefore considered better to use a more complex loop which - # (seems to) guarantee a deterministic ordering of the keys. - node_data = list() - # For each element in data, apply a brute force approach and check whether there - # exists a data dictionary associated to a node which contains a state variable - # with same key. If so, add the key and move on to the next key. - for key in data: - for sd, sd_data in self.mdg.subdomains(return_data=True): - if pp.STATE in sd_data and key in sd_data[pp.STATE]: - node_data.append(key) - # After successfully identifying data contained in nodes, break the loop - # over nodes to avoid any unintended repeated listing of key. + # Check whether data point of right type and convert to + # the unique data type. + subdomain_data, interface_data, success = method( + data_pt, subdomain_data, interface_data + ) + + # Stop, once a supported data format has been detected. + if success: break - # Transfer data to fields. - node_fields = Fields() - if len(node_data) > 0: - node_fields.extend([Field(d) for d in node_data]) + # Check if provided data has non-supported data format. + if not success: + raise ValueError( + f"Input data {data_pt} has wrong format and cannot be exported." + ) + + return subdomain_data, interface_data + + def _update_constant_mesh_data(self) -> None: + """ + Construct/update subdomain and interface data related with geometry and topology. + + The data is identified as constant data. The final containers, stored as + attributes _constant_subdomain_data and _constant_interface_data, have + the same format as the output of _sort_and_unify_data. + """ + # Assume a change in constant data (it is not checked whether + # the data really has been modified). Has the effect that + # the constant data container will be exported at the + # next application of write_vtu(). + self._exported_constant_data_up_to_date = False + + # Define constant subdomain data related to the mesh - # consider the mixed-dimension grid node data - extra_node_names = ["grid_dim", "grid_node_number", "is_mortar", "mortar_side"] - extra_node_fields = [Field(name) for name in extra_node_names] - node_fields.extend(extra_node_fields) + # Initialize container for constant subdomain data + if not hasattr(self, "_constant_subdomain_data"): + self._constant_subdomain_data = dict() - self.mdg.assign_subdomain_ordering(overwrite_existing=False) - # self.mdg.add_node_props(extra_node_names) - # fill the extra data - for sd, sd_data in self.mdg.subdomains(return_data=True): + # Add mesh related, constant subdomain data by direct assignment + for sd, sd_data in self._mdg.subdomains(return_data=True): ones = np.ones(sd.num_cells, dtype=int) - sd_data["grid_dim"] = sd.dim * ones - sd_data["grid_node_number"] = sd_data["node_number"] * ones - sd_data["is_mortar"] = 0 * ones - sd_data["mortar_side"] = ( + self._constant_subdomain_data[(sd, "cell_id")] = np.arange( + sd.num_cells, dtype=int + ) + self._constant_subdomain_data[(sd, "grid_dim")] = sd.dim * ones + if "node_number" in sd_data: + self._constant_subdomain_data[(sd, "grid_node_number")] = ( + sd_data["node_number"] * ones + ) + self._constant_subdomain_data[(sd, "is_mortar")] = 0 * ones + self._constant_subdomain_data[(sd, "mortar_side")] = ( pp.grids.mortar_grid.MortarSides.NONE_SIDE.value * ones ) - # collect the data and extra data in a single stack for each dimension - for dim in self.dims: - file_name = self._make_file_name(self.file_name, time_step, dim) - file_name = self._make_folder(self.folder_name, file_name) - for field in node_fields: - values = [] - for sd in self.mdg.subdomains(dim=dim): - if field.name in data: - values.append(self.mdg.subdomain_data(sd)[pp.STATE][field.name]) - else: - values.append(self.mdg.subdomain_data(sd)[field.name]) - field.check(values[-1], sd) - field.values = np.hstack(values) + # Define constant interface data related to the mesh - if self.meshio_geom[dim] is not None: - self._write(node_fields, file_name, self.meshio_geom[dim]) - - # Not used in the new exporter anyhow - # self.mdg.remove_node_props(extra_node_names) - - # Extract data which is associated to interfaces (and not subdomains). - # IMPLEMENTATION NOTE: See the above loop to construct node_data for an explanation - # of this elaborate construction of `edge_data` - interface_data = list() - for key in data: - for _, intf_data in self.mdg.interfaces(return_data=True): - if pp.STATE in intf_data and key in intf_data[pp.STATE]: - interface_data.append(key) - # After successfully identifying data associated to interfaces, break the - # loop over interfaces to avoid any unintended repeated listing of key. - break + # Initialize container for constant interface data + if not hasattr(self, "_constant_interface_data"): + self._constant_interface_data = dict() - # Transfer data to fields. - interface_fields = Fields() - if len(interface_data) > 0: - interface_fields.extend([Field(d) for d in interface_data]) - - # consider the mixed-dimensional grid edge data - extra_interface_names = [ - "grid_dim", - "grid_edge_number", - "is_mortar", - "mortar_side", - ] - extra_interface_fields = [Field(name) for name in extra_interface_names] - interface_fields.extend(extra_interface_fields) - - # fill the extra data - for intf, intf_data in self.mdg.interfaces(return_data=True): - intf_data["grid_dim"] = {} - intf_data["cell_id"] = {} - intf_data["grid_edge_number"] = {} - intf_data["is_mortar"] = {} - intf_data["mortar_side"] = {} - mg_num_cells = 0 - for side, g in intf.side_grids.items(): - ones = np.ones(g.num_cells, dtype=int) - intf_data["grid_dim"][side] = g.dim * ones - intf_data["is_mortar"][side] = ones - intf_data["mortar_side"][side] = side.value * ones - intf_data["cell_id"][side] = ( - np.arange(g.num_cells, dtype=int) + mg_num_cells - ) - mg_num_cells += g.num_cells - intf_data["grid_edge_number"][side] = intf_data["edge_number"] * ones + # Add mesh related, constant interface data by direct assignment. + for intf, intf_data in self._mdg.interfaces(return_data=True): - # collect the data and extra data in a single stack for each dimension - for dim in self.m_dims: - file_name = self._make_file_name_mortar( - self.file_name, time_step=time_step, dim=dim + # Construct empty arrays for all extra interface data + self._constant_interface_data[(intf, "grid_dim")] = np.empty(0, dtype=int) + self._constant_interface_data[(intf, "cell_id")] = np.empty(0, dtype=int) + self._constant_interface_data[(intf, "grid_edge_number")] = np.empty( + 0, dtype=int + ) + self._constant_interface_data[(intf, "is_mortar")] = np.empty(0, dtype=int) + self._constant_interface_data[(intf, "mortar_side")] = np.empty( + 0, dtype=int ) - file_name = self._make_folder(self.folder_name, file_name) - for field in interface_fields: - values = [] - for intf in self.mdg.interfaces(dim=dim): - if field.name in data: - # TODO why no sides here? - values.append( - self.mdg.interface_data(intf)[pp.STATE][field.name] - ) - else: - for side, _ in intf.side_grids.items(): - # Convert edge to tuple to be compatible with MixedDimensionalGrid - # data structure - values.append( - self.mdg.interface_data(intf)[field.name][side] - ) + # Initialize offset + side_grid_num_cells: int = 0 - field.values = np.hstack(values) + # Assign extra interface data by collecting values on both sides. + for side, grid in intf.side_grids.items(): + ones = np.ones(grid.num_cells, dtype=int) - if self.m_meshio_geom[dim] is not None: - self._write(interface_fields, file_name, self.m_meshio_geom[dim]) + # Grid dimension of the mortar grid + self._constant_interface_data[(intf, "grid_dim")] = np.hstack( + (self._constant_interface_data[(intf, "grid_dim")], grid.dim * ones) + ) + + # Cell ids of the mortar grid + self._constant_interface_data[(intf, "cell_id")] = np.hstack( + ( + self._constant_interface_data[(intf, "cell_id")], + np.arange(grid.num_cells, dtype=int) + side_grid_num_cells, + ) + ) + + # Grid edge number of each interface + self._constant_interface_data[(intf, "grid_edge_number")] = np.hstack( + ( + self._constant_interface_data[(intf, "grid_edge_number")], + intf_data["edge_number"] * ones, + ) + ) + + # Whether the interface is mortar + self._constant_interface_data[(intf, "is_mortar")] = np.hstack( + (self._constant_interface_data[(intf, "is_mortar")], ones) + ) + + # Side of the mortar + self._constant_interface_data[(intf, "mortar_side")] = np.hstack( + ( + self._constant_interface_data[(intf, "mortar_side")], + side.value * ones, + ) + ) + + # Update offset + side_grid_num_cells += grid.num_cells + + def _export_data_vtu( + self, + data: Union[SubdomainData, InterfaceData], + time_step: Optional[int], + **kwargs, + ) -> None: + """ + Collect data associated to a single grid dimension + and passing further to the final writing routine. + + For each fixed dimension, all subdomains of that dimension and the data + related to those subdomains will be exported simultaneously. + Analogously for interfaces. + + Args: + data (Union[SubdomainData, InterfaceData]): Subdomain or interface data. + time_step (int): time_step to be used to append the file name + kwargs: Optional keyword arguments: + 'interface_data' (boolean) indicates whether data is associated to + an interface, default is False; + 'constant_data' (boolean) indicates whether data is treated as + constant in time, default is False. + + Raises: + TypeError if keyword arguments contain unsupported keyword + ValueError if data provided for some but not all subdomains or interfaces + of particular dimension + """ + # Check for optional keywords + self.interface_data: bool = kwargs.pop("interface_data", False) + self.constant_data: bool = kwargs.pop("constant_data", False) + if kwargs: + msg = "_export_data_vtu got unexpected keyword argument '{}'" + raise TypeError(msg.format(kwargs.popitem()[0])) + + # Fetch the dimensions to be traversed. For subdomains, fetch the dimensions + # of the available grids, and for interfaces fetch the dimensions of the available + # mortar grids. + is_subdomain_data: bool = not self.interface_data + dims = self._dims if is_subdomain_data else self._m_dims + + # Define file name base + file_name_base: str = self._file_name + # Extend in case of constant data + file_name_base += "_constant" if self.constant_data else "" + # Extend in case of interface data + file_name_base += "_mortar" if self.interface_data else "" + # Append folder name to file name base + file_name_base = self._append_folder_name(self._folder_name, file_name_base) + + # Collect unique keys, and for unique sorting, sort by alphabet + keys = list(set([key for _, key in data])) + keys.sort() + + # Collect the data and extra data in a single stack for each dimension + for dim in dims: + # Define the full file name + file_name: str = self._make_file_name(file_name_base, time_step, dim) + + # Get all geometrical entities of dimension dim: + if is_subdomain_data: + entities: list[Any] = self._mdg.subdomains(dim=dim) + else: + entities = self._mdg.interfaces(dim=dim) + + # Construct the list of fields represented on this dimension. + fields: list[Field] = [] + for key in keys: + # Collect the values associated to all entities + values = [] + for e in entities: + if (e, key) in data: + values.append(data[(e, key)]) + + # Require data for all or none entities of that dimension. + if len(values) not in [0, len(entities)]: + raise ValueError( + f"""Insufficient amount of data provided for + key {key} on dimension {dim}.""" + ) - file_name = self._make_file_name(self.file_name, time_step, extension=".pvd") - file_name = self._make_folder(self.folder_name, file_name) - self._export_pvd_mdg(file_name, time_step) + # If data has been found, append data to list after stacking + # values for different entities + if values: + field = Field(key, np.hstack(values)) + fields.append(field) - # Not used anymore in the new exporter anyhow. - # self.mdg.remove_edge_props(extra_edge_names) + # Print data for the particular dimension. Since geometric info + # is required distinguish between subdomain and interface data. + meshio_geom = ( + self.meshio_geom[dim] if is_subdomain_data else self.m_meshio_geom[dim] + ) + if meshio_geom is not None: + self._write(fields, file_name, meshio_geom) + + def _export_mdg_pvd(self, file_name: str, time_step: Optional[int]) -> None: + """ + Routine to export to pvd format and collect all data scattered over + several files for distinct grid dimensions. - def _export_pvd_mdg(self, file_name: str, time_step: float) -> None: + Args: + file_name (str): storage path for pvd file + time_step (int): used as appendix for the file name + """ + # Open the file o_file = open(file_name, "w") + + # Write VTK header to file b = "LittleEndian" if sys.byteorder == "little" else "BigEndian" c = ' compressor="vtkZLibDataCompressor"' header = ( @@ -490,28 +1087,94 @@ def _export_pvd_mdg(self, file_name: str, time_step: float) -> None: o_file.write(header) fm = '\t\n' - # Include the grids and mortar grids of all dimensions, but only if the - # grids (or mortar grids) of this dimension are included in the vtk export - for dim in self.dims: + # Subdomain data. + for dim in self._dims: if self.meshio_geom[dim] is not None: - o_file.write( - fm % self._make_file_name(self.file_name, time_step, dim=dim) - ) - for dim in self.m_dims: + o_file.write(fm % self._make_file_name(self._file_name, time_step, dim)) + + # Interface data. + for dim in self._m_dims: if self.m_meshio_geom[dim] is not None: o_file.write( - fm % self._make_file_name_mortar(self.file_name, dim, time_step) + fm + % self._make_file_name(self._file_name + "_mortar", time_step, dim) ) + # If constant data is exported to separate vtu files, also include + # these here. The procedure is similar to the above, but the file names + # incl. the relevant time step have to be adjusted. + if self._export_constants_separately: + + # Constant subdomain data. + for dim in self._dims: + if self.meshio_geom[dim] is not None: + o_file.write( + fm + % self._make_file_name( + self._file_name + "_constant", + self._time_step_constants, + dim, + ) + ) + + # Constant interface data. + for dim in self._m_dims: + if self.m_meshio_geom[dim] is not None: + o_file.write( + fm + % self._make_file_name( + self._file_name + "_constant_mortar", + self._time_step_constants, + dim, + ) + ) + o_file.write("\n" + "") o_file.close() + def _update_meshio_geom(self) -> None: + """ + Manager for storing the grid information in meshio format. + + The internal variables meshio_geom and m_meshio_geom, storing all + essential (mortar) grid information as points, connectivity and cell + ids, are created and stored. + """ + # Subdomains + for dim in self._dims: + # Get subdomains with dimension dim + subdomains = self._mdg.subdomains(dim=dim) + # Export and store + self.meshio_geom[dim] = self._export_grid(subdomains, dim) + + # Interfaces + for dim in self._m_dims: + # Extract the mortar grids for dimension dim, unrolled by sides + interface_side_grids = [ + grid + for intf in self._mdg.interfaces(dim=dim) + for _, grid in intf.side_grids.items() + ] + # Export and store + self.m_meshio_geom[dim] = self._export_grid(interface_side_grids, dim) + def _export_grid( self, grids: Iterable[pp.Grid], dim: int - ) -> Union[None, Tuple[np.ndarray, np.ndarray, np.ndarray]]: + ) -> Union[None, Meshio_Geom]: """ Wrapper function to export grids of dimension dim. Calls the appropriate dimension specific export function. + + Args: + grids (Iterable[pp.Grid]): Subdomains of same dimension. + dim (int): Dimension of the subdomains. + + Returns: + Meshio_Geom: Points, cells (storing the connectivity), and cell ids + in correct meshio format. + + Raises: + ValueError if dim not 0, 1, 2, 3 """ if dim == 0: return None @@ -524,550 +1187,799 @@ def _export_grid( else: raise ValueError(f"Unknown dimension {dim}") - def _export_grid_1d( - self, grids: Iterable[pp.Grid] - ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: + def _export_grid_1d(self, grids: Iterable[pp.Grid]) -> Meshio_Geom: """ Export the geometrical data (point coordinates) and connectivity information from the 1d PorePy grids to meshio. + + Args: + grids (Iterable[pp.Grid]): 1d grids. + + Returns: + Meshio_Geom: Points, 1d cells (storing the connectivity), + and cell ids in correct meshio format. """ - # in 1d we have only one cell type + # In 1d each cell is a line cell_type = "line" - # cell connectivity information - num_cells = np.sum(np.array([grid.num_cells for grid in grids])) - cell_to_nodes = {cell_type: np.empty((num_cells, 2))} # type: ignore - # cell id map - cell_id = {cell_type: np.empty(num_cells, dtype=int)} # type: ignore - cell_pos = 0 + # Dictionary storing cell->nodes connectivity information + cell_to_nodes: dict[str, np.ndarray] = {cell_type: np.empty((0, 2), dtype=int)} - # points - num_pts = np.sum([grid.num_nodes for grid in grids]) + # Dictionary collecting all cell ids for each cell type. + # Since each cell is a line, the list of cell ids is trivial + total_num_cells = np.sum(np.array([grid.num_cells for grid in grids])) + cell_id: dict[str, list[int]] = {cell_type: [i for i in range(total_num_cells)]} + + # Data structure for storing node coordinates of all 1d grids. + num_pts = np.sum([grid.num_nodes for grid in grids]).astype(int) meshio_pts = np.empty((num_pts, 3)) # type: ignore - pts_pos = 0 - # loop on all the 1d grids + # Initialize offset. All data associated to 1d grids is stored in + # the same vtu file, essentially using concatenation. To identify + # each grid, keep track of number of nodes for each grid. + nodes_offset = 0 + + # Loop over all 1d grids for grid in grids: - # save the points information - sl = slice(pts_pos, pts_pos + grid.num_nodes) + + # Store node coordinates + sl = slice(nodes_offset, nodes_offset + grid.num_nodes) meshio_pts[sl, :] = grid.nodes.T - # Cell-node relations - g_cell_nodes = grid.cell_nodes() - g_nodes_cells, g_cells, _ = sps.find(g_cell_nodes) - # Ensure ordering of the cells - g_nodes_cells = g_nodes_cells[np.argsort(g_cells)] + # Lines are 1-simplices, and have a trivial connectvity. + cn_indices = self._simplex_cell_to_nodes(1, grid) + + # Add to previous connectivity information + cell_to_nodes[cell_type] = np.vstack( + (cell_to_nodes[cell_type], cn_indices + nodes_offset) + ) - # loop on all the grid cells - for c in np.arange(grid.num_cells): - loc = slice(g_cell_nodes.indptr[c], g_cell_nodes.indptr[c + 1]) - # get the local nodes and save them - cell_to_nodes[cell_type][cell_pos, :] = g_nodes_cells[loc] + pts_pos - cell_id[cell_type][cell_pos] = cell_pos - cell_pos += 1 + # Update offsets + nodes_offset += grid.num_nodes - pts_pos += grid.num_nodes + # Construct the meshio data structure + meshio_cells = list() + meshio_cell_id = list() - # construct the meshio data structure - num_block = len(cell_to_nodes) - meshio_cells = np.empty(num_block, dtype=object) - meshio_cell_id = np.empty(num_block, dtype=object) + # For each cell_type store the connectivity pattern cell_to_nodes for + # the corresponding cells with ids from cell_id. + for (cell_type, cell_block) in cell_to_nodes.items(): + meshio_cells.append(meshio.CellBlock(cell_type, cell_block.astype(int))) + meshio_cell_id.append(np.array(cell_id[cell_type])) - for block, (cell_type, cell_block) in enumerate(cell_to_nodes.items()): - meshio_cells[block] = meshio.CellBlock(cell_type, cell_block.astype(int)) - meshio_cell_id[block] = np.array(cell_id[cell_type]) + # Return final meshio data: points, cell (connectivity), cell ids + return Meshio_Geom(meshio_pts, meshio_cells, meshio_cell_id) - return meshio_pts, meshio_cells, meshio_cell_id + def _simplex_cell_to_nodes( + self, n: int, grid: pp.Grid, cells: Optional[np.ndarray] = None + ) -> np.ndarray: + """ + Determine cell to node connectivity for a general n-simplex mesh. + + Args: + n (int): dimension of the simplices in the grid. + grid (pp.Grid): grid containing cells and nodes. + cells (np.ndarray, optional): all n-simplex cells - def _export_grid_2d( - self, grids: Iterable[pp.Grid] - ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: + Returns: + np.ndarray: cell to node connectivity array, in which for each row + (associated to cells) all associated nodes are marked. + """ + # Determine cell-node ptr + cn_indptr = ( + grid.cell_nodes().indptr[:-1] + if cells is None + else grid.cell_nodes().indptr[cells] + ) + + # Each n-simplex has n+1 nodes + num_nodes = n + 1 + + # Collect the indptr to all nodes of the cell. + expanded_cn_indptr = np.vstack( + [cn_indptr + i for i in range(num_nodes)] + ).reshape(-1, order="F") + + # Detect all corresponding nodes by applying the expanded mask to the indices + expanded_cn_indices = grid.cell_nodes().indices[expanded_cn_indptr] + + # Convert to right format. + cn_indices = np.reshape(expanded_cn_indices, (-1, num_nodes), order="C") + + return cn_indices + + def _export_grid_2d(self, grids: Iterable[pp.Grid]) -> Meshio_Geom: """ Export the geometrical data (point coordinates) and connectivity information from the 2d PorePy grids to meshio. + + Args: + grids (Iterable[pp.Grid]): 2d grids. + + Returns: + Meshio_Geom: Points, 2d cells (storing the connectivity), and + cell ids in correct meshio format. """ - # use standard name for simple object type + # Use standard names for simple object types: in this routine only triangle + # and quad cells are treated in a special manner. polygon_map = {"polygon3": "triangle", "polygon4": "quad"} - # cell->nodes connectivity information - cell_to_nodes: Dict[str, np.ndarray] = {} - # cell id map - cell_id: Dict[str, List[int]] = {} + # Dictionary storing cell->nodes connectivity information for all + # cell types. For this, the nodes have to be sorted such that + # they form a circular chain, describing the boundary of the cell. + cell_to_nodes: dict[str, np.ndarray] = {} + # Dictionary collecting all cell ids for each cell type. + cell_id: dict[str, list[int]] = {} - # points + # Data structure for storing node coordinates of all 2d grids. num_pts = np.sum([grid.num_nodes for grid in grids]) meshio_pts = np.empty((num_pts, 3)) # type: ignore - pts_pos = 0 - cell_pos = 0 - # loop on all the 2d grids + # Initialize offsets. All data associated to 2d grids is stored in + # the same vtu file, essentially using concatenation. To identify + # each grid, keep track of number of nodes and cells for each grid. + nodes_offset = 0 + cell_offset = 0 + + # Loop over all 2d grids for grid in grids: - # save the points information - sl = slice(pts_pos, pts_pos + grid.num_nodes) + + # Store node coordinates + sl = slice(nodes_offset, nodes_offset + grid.num_nodes) meshio_pts[sl, :] = grid.nodes.T - # Cell-face and face-node relations - g_faces_cells, g_cells, _ = sps.find(grid.cell_faces) - # Ensure ordering of the cells - g_faces_cells = g_faces_cells[np.argsort(g_cells)] - g_nodes_faces, _, _ = sps.find(grid.face_nodes) - - # loop on all the grid cells - for c in np.arange(grid.num_cells): - loc = slice(grid.cell_faces.indptr[c], grid.cell_faces.indptr[c + 1]) - # get the nodes for the current cell - nodes = np.array( - [ - g_nodes_faces[ - grid.face_nodes.indptr[f] : grid.face_nodes.indptr[f + 1] - ] - for f in g_faces_cells[loc] - ] - ).T - # sort the nodes - nodes_loc, *_ = pp.utils.sort_points.sort_point_pairs(nodes) + # Determine cell types based on number of faces=nodes per cell. + num_faces_per_cell = grid.cell_faces.getnnz(axis=0) + + # Loop over all available cell types and group cells of one type. + g_cell_map = dict() + for n in np.unique(num_faces_per_cell): + + # Define cell type; check if it coincides with a predefined cell type + cell_type = polygon_map.get(f"polygon{n}", f"polygon{n}") + + # Find all cells with n faces, and store for later use + cells = np.nonzero(num_faces_per_cell == n)[0] + g_cell_map[cell_type] = cells + + # Store cell ids in global container; init if entry not yet established + if cell_type not in cell_id: + cell_id[cell_type] = [] + + # Add offset taking into account previous grids + cell_id[cell_type] += (cells + cell_offset).tolist() - # define the type of cell we are currently saving - cell_type_raw = "polygon" + str(nodes_loc.shape[1]) + # Determine cell-node connectivity for each cell type and all cells. + # Treat triangle, quad and polygonal cells differently + # aiming for optimized performance. + for n in np.unique(num_faces_per_cell): - # Map to triangle/quad if relevant, or simple polygon for general cells. - cell_type = polygon_map.get(cell_type_raw, cell_type_raw) - # if the cell type is not present, then add it + # Define the cell type + cell_type = polygon_map.get(f"polygon{n}", f"polygon{n}") + + # Check if cell_type already defined in cell_to_nodes, otherwise construct if cell_type not in cell_to_nodes: - cell_to_nodes[cell_type] = np.atleast_2d(nodes_loc[0] + pts_pos) - cell_id[cell_type] = [cell_pos] + cell_to_nodes[cell_type] = np.empty((0, n), dtype=int) + + # Special case: Triangle cells, i.e., n=3. + if cell_type == "triangle": + + # Triangles are simplices and have a trivial connectivity. + + # Fetch triangle cells + cells = g_cell_map[cell_type] + # Determine the trivial connectivity - triangles are 2-simplices. + cn_indices = self._simplex_cell_to_nodes(2, grid, cells) + + # Quad and polygon cells. else: - cell_to_nodes[cell_type] = np.vstack( - (cell_to_nodes[cell_type], nodes_loc[0] + pts_pos) + # For quads/polygons, grid.cell_nodes cannot be blindly used as for + # triangles, since the ordering of the nodes may define a cell of + # the type (here specific for quads) + # x--x and not x--x + # \/ | | + # /\ | | + # x--x x--x . + # Therefore, use both grid.cell_faces and grid.face_nodes to make use of + # face information and sort those to retrieve the correct connectivity. + + # Strategy: Collect all cell nodes including their connectivity in a + # matrix of double num cell size. The goal will be to gather starting + # and end points for all faces of each cell, sort those faces, such that + # they form a circlular graph, and then choose the resulting starting + # points of all faces to define the connectivity. + + # Fetch corresponding cells + cells = g_cell_map[cell_type] + # Determine all faces of all cells. Use an analogous approach as used to + # determine all cell nodes for triangle cells. And use that a polygon with + # n nodes has also n faces. + cf_indptr = grid.cell_faces.indptr[cells] + expanded_cf_indptr = np.vstack( + [cf_indptr + i for i in range(n)] + ).reshape(-1, order="F") + cf_indices = grid.cell_faces.indices[expanded_cf_indptr] + + # Determine the associated (two) nodes of all faces for each cell. + fn_indptr = grid.face_nodes.indptr[cf_indices] + # Extract nodes for first and second node of each face; reshape such + # that all first nodes of all faces for each cell are stored in one row, + # i.e., end up with an array of size num_cells (for this cell type) x n. + cfn_indices = [ + grid.face_nodes.indices[fn_indptr + i].reshape(-1, n) + for i in range(2) + ] + # Group first and second nodes, with alternating order of rows. + # By this, each cell is respresented by two rows. The first and second + # rows contain first and second nodes of faces. And each column stands + # for one face. + cfn = np.ravel(cfn_indices, order="F").reshape(n, -1).T + + # Sort faces for each cell such that they form a chain. Use a function + # compiled with Numba. This step is the bottleneck of this routine. + cfn = pp.utils.sort_points.sort_multiple_point_pairs(cfn).astype( + int ) - cell_id[cell_type] += [cell_pos] - cell_pos += 1 + # For each cell pick the sorted nodes such that they form a chain + # and thereby define the connectivity, i.e., skip every second row. + cn_indices = cfn[::2, :] + + # Add offset to account for previous grids, and store + cell_to_nodes[cell_type] = np.vstack( + (cell_to_nodes[cell_type], cn_indices + nodes_offset) + ) + + # Update offsets + nodes_offset += grid.num_nodes + cell_offset += grid.num_cells - pts_pos += grid.num_nodes + # Construct the meshio data structure + meshio_cells = list() + meshio_cell_id = list() - # construct the meshio data structure - num_block = len(cell_to_nodes) - meshio_cells = np.empty(num_block, dtype=object) - meshio_cell_id = np.empty(num_block, dtype=object) + # For each cell_type store the connectivity pattern cell_to_nodes for + # the corresponding cells with ids from cell_id. + for (cell_type, cell_block) in cell_to_nodes.items(): - for block, (cell_type, cell_block) in enumerate(cell_to_nodes.items()): - # remove the number of nodes associated to the polygon since meshio requires only - # the keyword "polygon" for polygons - cell_type_ = "polygon" if "polygon" in cell_type else cell_type - meshio_cells[block] = meshio.CellBlock(cell_type_, cell_block.astype(int)) - meshio_cell_id[block] = np.array(cell_id[cell_type]) + # Meshio requires the keyword "polygon" for general polygons. + # Thus, remove the number of nodes associated to polygons. + cell_type_meshio_format = "polygon" if "polygon" in cell_type else cell_type + meshio_cells.append( + meshio.CellBlock(cell_type_meshio_format, cell_block.astype(int)) + ) + meshio_cell_id.append(np.array(cell_id[cell_type])) - return meshio_pts, meshio_cells, meshio_cell_id + # Return final meshio data: points, cell (connectivity), cell ids + return Meshio_Geom(meshio_pts, meshio_cells, meshio_cell_id) - def _export_grid_3d( - self, grids: Iterable[pp.Grid] - ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: + def _export_grid_3d(self, grids: Iterable[pp.Grid]) -> Meshio_Geom: """ Export the geometrical data (point coordinates) and connectivity - information from the 3d PorePy grids to meshio. + information from 3d PorePy grids to meshio. + + Args: + grids (Iterable[pp.Grid]): 3d grids. + + Returns: + Meshio_Geom: Points, 3d cells (storing the connectivity), and + cell ids in correct meshio format. """ - # use standard name for simple object type - # NOTE: this part is not developed - # polygon_map = {"polyhedron4": "tetra", "polyhedron8": "hexahedron"} + # Three different cell types will be distinguished: Tetrahedra, hexahedra, + # and general polyhedra. meshio does not allow for a mix of cell types + # in 3d within a single grid (and we export all 3d grids at once). Thus, + # if the 3d grids contains cells of varying types, all cells will be cast + # as polyhedra. + + # Determine the cell types present among all grids. + cell_types: set[str] = set() + for grid in grids: + + # The number of faces per cell wil be later used to determining + # the cell types + num_faces_per_cell = np.unique(grid.cell_faces.getnnz(axis=0)) + + if num_faces_per_cell.shape[0] == 1: + n = num_faces_per_cell[0] + if n == 4: + cell_types.add("tetra") + elif n == 6 and isinstance(grid, pp.CartGrid): + cell_types.add("hexahedron") + else: + cell_types.add("polyhedron") + else: + cell_types.add("polyhedron") + + # Use dedicated export for each grid type + if len(cell_types) == 1 and "tetra" in cell_types: + return self._export_simplex_3d(grids) + elif len(cell_types) == 1 and "hexahedron" in cell_types: + return self._export_hexahedron_3d(grids) + else: + return self._export_polyhedron_3d(grids) + + def _export_simplex_3d(self, grids: Iterable[pp.Grid]) -> Meshio_Geom: + """ + Export the geometrical data (point coordinates) and connectivity + information from 3d PorePy simplex grids to meshio. + + Args: + grids (Iterable[pp.Grid]): 3d simplex grids. + + Returns: + Meshio_Geom: Points, 3d cells (storing the connectivity), and + cell ids in correct meshio format. + """ + # For the special meshio geometric type "tetra", cell->nodes will + # be used to store connectivity information. Each simplex has 4 nodes. + cell_to_nodes = np.empty((0, 4), dtype=int) + + # Dictionary collecting all cell ids for each cell type. + cell_id: list[int] = [] + + # Data structure for storing node coordinates of all 3d grids. + num_pts = np.sum([grid.num_nodes for grid in grids]) + meshio_pts = np.empty((num_pts, 3)) # type: ignore + + # Initialize offsets. All data associated to 3d grids is stored in + # the same vtu file, essentially using concatenation. To identify + # each grid, keep track of number of nodes and cells for each grid. + nodes_offset = 0 + cell_offset = 0 + + # Treat each 3d grid separately. + for grid in grids: + + # Store node coordinates + sl = slice(nodes_offset, nodes_offset + grid.num_nodes) + meshio_pts[sl, :] = grid.nodes.T + + # Identify all cells as tetrahedra. + cells = np.arange(grid.num_cells) + + # Add offset taking into account previous grids + cell_id += (cells + cell_offset).tolist() + + # Determine local connectivity for all cells. Simplices are invariant + # under permuatations. Thus, no specific ordering of nodes is required. + # Tetrahedra are essentially 3-simplices. + cn_indices = self._simplex_cell_to_nodes(3, grid, cells) + + # Store cell-node connectivity, and add offset taking into account + # previous grids + cell_to_nodes = np.vstack((cell_to_nodes, cn_indices + nodes_offset)) + + # Update offset + nodes_offset += grid.num_nodes + cell_offset += grid.num_cells + + # Initialize the meshio data structure for the connectivity and cell ids. + # There is only one cell type, and meshio expects iterable data structs. + meshio_cells = [meshio.CellBlock("tetra", cell_to_nodes.astype(int))] + meshio_cell_id = [np.array(cell_id)] + + # Return final meshio data: points, cell (connectivity), cell ids + return Meshio_Geom(meshio_pts, meshio_cells, meshio_cell_id) + + def _export_hexahedron_3d(self, grids: Iterable[pp.Grid]) -> Meshio_Geom: + """ + Export the geometrical data (point coordinates) and connectivity + information from 3d PorePy hexahedron grids to meshio. + + NOTE: Optimally, a StructuredGrid would be exported in this case. + However, meshio does solely handle unstructured grids and cannot + for instance write to *.vtr format. - # cell-faces and cell nodes connectivity information - cell_to_faces: Dict[str, List[List[int]]] = {} - cell_to_nodes: Dict[str, np.ndarray] = {} - # cell id map - cell_id: Dict[str, List[int]] = {} + Args: + grids (Iterable[pp.Grid]): 3d hexahedron grids. + + Returns: + Meshio_Geom: Points, 3d cells (storing the connectivity), and + cell ids in correct meshio format. + """ + # For the special meshio geometric type "hexahedron", cell->nodes will + # be used to store connectivity information. Each hexahedron has 8 nodes. + cell_to_nodes = np.empty((0, 8), dtype=int) + + # Dictionary collecting all cell ids for each cell type. + cell_id: list[int] = [] - # points + # Data structure for storing node coordinates of all 3d grids. num_pts = np.sum([grid.num_nodes for grid in grids]) meshio_pts = np.empty((num_pts, 3)) # type: ignore - pts_pos = 0 - cell_pos = 0 - # loop on all the 3d grids + # Initialize offsets. Required taking into account multiple 3d grids. + nodes_offset = 0 + cell_offset = 0 + + # Treat each 3d grid separately. for grid in grids: - # save the points information - sl = slice(pts_pos, pts_pos + grid.num_nodes) + + # Store node coordinates + sl = slice(nodes_offset, nodes_offset + grid.num_nodes) meshio_pts[sl, :] = grid.nodes.T - # Cell-face and face-node relations - g_faces_cells, g_cells, _ = sps.find(grid.cell_faces) - # Ensure ordering of the cells - g_faces_cells = g_faces_cells[np.argsort(g_cells)] + # Identify all cells as tetrahedra. + cells = np.arange(grid.num_cells) + + # Add offset taking into account previous grids + cell_id += (cells + cell_offset).tolist() - g_nodes_faces, _, _ = sps.find(grid.face_nodes) + # Determine local connectivity for all cells. Simplices are invariant + # under permuatations. Thus, no specific ordering of nodes is required. - cptr = grid.cell_faces.indptr - fptr = grid.face_nodes.indptr - face_per_cell = np.diff(cptr) - nodes_per_face = np.diff(fptr) + # After all, the procedure is fairly similar to the treatment of + # tetra cells. Most of the following code is analogous to + # _simplex_cell_to_nodes(). However, for hexaedron cells the order + # of the nodes is important and has to be adjusted. Based on the + # specific definition of TensorGrids however, the node numbering + # can be hardcoded, which results in better performance compared + # to a modular procedure. - # Total number of nodes to be written in the face-node relation - num_cell_nodes = np.array( - [nodes_per_face[i] for i in grid.cell_faces.indices] + # Determine cell-node ptr + cn_indptr = ( + grid.cell_nodes().indptr[:-1] + if cells is None + else grid.cell_nodes().indptr[cells] ) - n = grid.nodes - fc = grid.face_centers - normal_vec = grid.face_normals / grid.face_areas - - # Use numba if available, unless the problem is very small, in which - # case the pure python version probably is faster than combined compile - # and runtime for numba - # The number 1000 here is somewhat random. - if self.has_numba and grid.num_cells > 1000: - cell_nodes = self._point_ind_numba( - cptr, - fptr, - g_faces_cells, - g_nodes_faces, - n, - fc, - normal_vec, - num_cell_nodes, + # Collect the indptr to all nodes of the cell; + # each n-simplex cell contains n nodes + expanded_cn_indptr = np.vstack([cn_indptr + i for i in range(8)]).reshape( + -1, order="F" + ) + + # Detect all corresponding nodes by applying the expanded mask to indices + expanded_cn_indices = grid.cell_nodes().indices[expanded_cn_indptr] + + # Convert to right format. + cn_indices = np.reshape(expanded_cn_indices, (-1, 8), order="C") + + # Reorder nodes to comply with the node numbering convention of meshio + # regarding hexahedron cells. + cn_indices = cn_indices[:, [0, 2, 6, 4, 1, 3, 7, 5]] + + # Test whether the hard-coded numbering really defines a hexahedron. + assert self._test_hex_meshio_format(grid.nodes, cn_indices) + + # Store cell-node connectivity, and add offset taking into account + # previous grids + cell_to_nodes = np.vstack((cell_to_nodes, cn_indices + nodes_offset)) + + # Update offset + nodes_offset += grid.num_nodes + cell_offset += grid.num_cells + + # Initialize the meshio data structure for the connectivity and cell ids. + # There is only one cell type, and meshio expects iterable data structs. + meshio_cells = [meshio.CellBlock("hexahedron", cell_to_nodes.astype(int))] + meshio_cell_id = [np.array(cell_id)] + + # Return final meshio data: points, cell (connectivity), cell ids + return Meshio_Geom(meshio_pts, meshio_cells, meshio_cell_id) + + def _test_hex_meshio_format( + self, nodes: np.ndarray, cn_indices: np.ndarray + ) -> bool: + """Test whether the node numbering for each cell complies + with the hardcoded numbering used by meshio, cf. documentation + of meshio. + + Raises: + ImportError if numba is not installed + """ + + try: + import numba + except ImportError: + raise ImportError("Numba not available on the system") + + # Just in time compilation + @numba.njit("b1(f8[:,:], i4[:,:])", cache=True, parallel=True) + def _function_to_compile(nodes, cn_indices): + """ + Test whether the node numbering for each cell complies + with the hardcoded numbering used by meshio, cf. documentation + of meshio. + + For this, fetch three potential sides of the hex and + test whether they lie circular chain within a plane. + Here, the test is successful, if the node sets [0,1,2,3], + [4,5,6,7], and [0,1,5,4] define planes. + """ + + # Assume initially correct format + correct_format = True + + # Test each cell separately + for i in numba.prange(cn_indices.shape[0]): + + # Assume initially that the cell is a hex + is_hex = True + + # Visit three node sets and check whether + # they define feasible sides of a hex. + # FIXME use shorter code of the following style - note: the order in the + # array is not the same troubling numba + # global_ind_0 = cn_indices[i, 0:4] + # global_ind_1 = cn_indices[i, 4:8] + global_ind_0 = np.array( + [ + cn_indices[i, 0], + cn_indices[i, 1], + cn_indices[i, 2], + cn_indices[i, 3], + ] ) - else: - cell_nodes = self._point_ind( - cptr, - fptr, - g_faces_cells, - g_nodes_faces, - n, - fc, - normal_vec, - num_cell_nodes, + global_ind_1 = np.array( + [ + cn_indices[i, 4], + cn_indices[i, 5], + cn_indices[i, 6], + cn_indices[i, 7], + ] + ) + global_ind_2 = np.array( + [ + cn_indices[i, 0], + cn_indices[i, 1], + cn_indices[i, 5], + cn_indices[i, 4], + ] ) - # implementation note: I did not even try feeding this to numba, my - # guess is that it will not like the vtk specific stuff. - nc = 0 - f_counter = 0 - - # loop on all the grid cells - for c in np.arange(grid.num_cells): - faces_loc: List[int] = [] - # loop on all the cell faces - for _ in np.arange(face_per_cell[c]): - fi = grid.cell_faces.indices[f_counter] - faces_loc += [cell_nodes[nc : (nc + nodes_per_face[fi])]] - nc += nodes_per_face[fi] - f_counter += 1 - - # collect all the nodes for the cell - nodes_loc = np.unique(np.hstack(faces_loc)).astype(int) - - # define the type of cell we are currently saving - cell_type = "polyhedron" + str(nodes_loc.size) - # cell_type = polygon_map.get(cell_type, cell_type) - - # if the cell type is not present, then add it - if cell_type not in cell_to_nodes: - cell_to_nodes[cell_type] = np.atleast_2d(nodes_loc + pts_pos) - cell_to_faces[cell_type] = [faces_loc] - cell_id[cell_type] = [cell_pos] - else: - cell_to_nodes[cell_type] = np.vstack( - (cell_to_nodes[cell_type], nodes_loc + pts_pos) - ) - cell_to_faces[cell_type] += [faces_loc] - cell_id[cell_type] += [cell_pos] - cell_pos += 1 + # Check each side separately + for global_ind in [global_ind_0, global_ind_1, global_ind_2]: + + # Fetch coordinates associated to the four nodes + coords = nodes[:, global_ind] + + # Check orientation by determining normalized cross products + # of all two consecutive connections. + normalized_cross = np.zeros((3, 3)) + for j in range(3): + cross = np.cross( + coords[:, j + 1] - coords[:, j], + coords[:, (j + 2) % 4] - coords[:, j + 1], + ) + normalized_cross[:, j] = cross / np.linalg.norm(cross) + + # Consider the points lying in a plane when each connection + # produces the same normalized cross product. + is_plane = np.linalg.matrix_rank(normalized_cross) == 1 + + # Combine with the remaining sides + is_hex = is_hex and is_plane + + # Combine the results for all cells + correct_format = correct_format and is_hex + + return correct_format + + return _function_to_compile(nodes, cn_indices) + + def _export_polyhedron_3d(self, grids: Iterable[pp.Grid]) -> Meshio_Geom: + """ + Export the geometrical data (point coordinates) and connectivity + information from 3d PorePy polyhedron grids to meshio. + + Args: + grids (Iterable[pp.Grid]): 3d polyhedron grids. + + Returns: + Meshio_Geom: Points, 3d cells (storing the connectivity), and + cell ids in correct meshio format. + """ + # For the general geometric type "polyhedron", cell->faces->nodes will + # be used to store connectivity information, which can become significantly + # larger than cell->nodes information used for special type grids. + cell_to_faces: dict[str, list[list[int]]] = {} + + # Dictionary collecting all cell ids for each cell type. + cell_id: dict[str, list[int]] = {} + + # Data structure for storing node coordinates of all 3d grids. + num_pts = np.sum([grid.num_nodes for grid in grids]) + meshio_pts = np.empty((num_pts, 3)) # type: ignore - pts_pos += grid.num_nodes + # Initialize offsets. Required taking into account multiple 3d grids. + nodes_offset = 0 + cell_offset = 0 + + # Treat each 3d grid separately. + for grid in grids: - # NOTE: only cell->faces relation will be kept, so far we export only polyhedron - # otherwise in the next lines we should consider also the cell->nodes map + # Store node coordinates + sl = slice(nodes_offset, nodes_offset + grid.num_nodes) + meshio_pts[sl, :] = grid.nodes.T + + # The number of faces per cell wil be later used to determining + # the cell types + num_faces_per_cell = grid.cell_faces.getnnz(axis=0) + + # Categorize all polyhedron cells by their number of faces. + # Each category will be treated separatrely allowing for using + # fitting datastructures. + g_cell_map = dict() + for n in np.unique(num_faces_per_cell): + + cell_type = f"polyhedron{n}" + + # Find all cells with n faces, and store for later use + cells = np.nonzero(num_faces_per_cell == n)[0] + g_cell_map[cell_type] = cells + + # Store cell ids in global container; init if entry not yet established + if cell_type not in cell_id: + cell_id[cell_type] = [] + + # Add offset taking into account previous grids + cell_id[cell_type] += (cells + cell_offset).tolist() + + # Determine local connectivity for all cells. Due to differnt + # treatment of special and general cell types and to conform + # with array sizes (for polyhedra), treat each cell type + # separately. + for cell_type in g_cell_map.keys(): + + # The general strategy is to define the connectivity as cell-face-nodes + # information, where the faces are defined by nodes. Hence, this + # information is significantly larger than the info provided for + # tetra cells. Here, we make use of the fact that grid.face_nodes + # provides nodes ordered wrt. the right-hand rule. + + # Fetch cells with n faces + cells = g_cell_map[cell_type] + + # Store short cuts to cell-face and face-node information + cf_indptr = grid.cell_faces.indptr + cf_indices = grid.cell_faces.indices + fn_indptr = grid.face_nodes.indptr + fn_indices = grid.face_nodes.indices + + # Determine the cell-face connectivity (with faces described by their + # nodes ordered such that they form a chain and are identified by the + # face boundary. The final data format is a list[list[np.ndarray]]. + # The outer list loops over all cells. Each cell entry contains a + # list over faces, and each face entry is given by the face nodes. + if cell_type not in cell_to_faces: + cell_to_faces[cell_type] = [] + cell_to_faces[cell_type] += [ + [ + fn_indices[fn_indptr[f] : fn_indptr[f + 1]] # nodes + for f in cf_indices[cf_indptr[c] : cf_indptr[c + 1]] # faces + ] + for c in cells # cells + ] - # construct the meshio data structure - num_block = len(cell_to_nodes) - meshio_cells = np.empty(num_block, dtype=object) - meshio_cell_id = np.empty(num_block, dtype=object) + # Update offset + nodes_offset += grid.num_nodes + cell_offset += grid.num_cells - for block, (cell_type, cell_block) in enumerate(cell_to_faces.items()): - meshio_cells[block] = meshio.CellBlock(cell_type, cell_block) - meshio_cell_id[block] = np.array(cell_id[cell_type]) + # Initialize the meshio data structure for the connectivity and cell ids. + meshio_cells = list() + meshio_cell_id = list() - return meshio_pts, meshio_cells, meshio_cell_id + # Store the cells in meshio format + for (cell_type, cell_block) in cell_to_faces.items(): + # Adapt the block number taking into account of previous cell types. + meshio_cells.append(meshio.CellBlock(cell_type, cell_block)) + meshio_cell_id.append(np.array(cell_id[cell_type])) - def _write(self, fields: Iterable[Field], file_name: str, meshio_geom) -> None: + # Return final meshio data: points, cell (connectivity), cell ids + return Meshio_Geom(meshio_pts, meshio_cells, meshio_cell_id) - cell_data = {} + def _write( + self, + fields: Iterable[Field], + file_name: str, + meshio_geom: Meshio_Geom, + ) -> None: + """ + Interface to meshio for exporting cell data. - # we need to split the data for each group of geometrically uniform cells - cell_id = meshio_geom[2] - num_block = cell_id.size + Args: + fields (iterable, Field): fields which shall be exported + file_name (str): name of final file of export + meshio_geom (Meshio_Geom): Namedtuple of points, + connectivity information, and cell ids in + meshio format (for a single dimension). + + Raises: + ValueError if some data has wrong dimension + """ + # Initialize empty cell data dictinary + cell_data: dict[str, list[np.ndarray]] = {} + # Split the data for each group of geometrically uniform cells + # Utilize meshio_geom for this. for field in fields: - if field.values is None: - continue - # for each field create a sub-vector for each geometrically uniform group of cells - cell_data[field.name] = np.empty(num_block, dtype=object) - # fill up the data - for block, ids in enumerate(cell_id): + # Although technically possible, as implemented, field.values should never be None. + assert field.values is not None + + # For each field create a sub-vector for each geometrically uniform group of cells + cell_data[field.name] = list() + + # Fill up the data + for ids in meshio_geom.cell_ids: if field.values.ndim == 1: - cell_data[field.name][block] = field.values[ids] + cell_data[field.name].append(field.values[ids]) elif field.values.ndim == 2: - cell_data[field.name][block] = field.values[:, ids].T + cell_data[field.name].append(field.values[:, ids].T) else: - raise ValueError + raise ValueError("Data values have wrong dimension") - # add also the cells ids - cell_data.update({self.cell_id_key: cell_id}) - - # create the meshio object + # Create the meshio object meshio_grid_to_export = meshio.Mesh( - meshio_geom[0], meshio_geom[1], cell_data=cell_data + meshio_geom.pts, meshio_geom.connectivity, cell_data=cell_data ) - meshio.write(file_name, meshio_grid_to_export, binary=self.binary) - def _update_meshio_geom(self): - if self.is_MixedDimensionalGrid: - for dim in self.dims: - subdomain_grids = list(self.mdg.subdomains(dim=dim)) - self.meshio_geom[dim] = self._export_grid(subdomain_grids, dim) + # Write mesh information and data to VTK format. + meshio.write(file_name, meshio_grid_to_export, binary=self._binary) - for dim in self.m_dims: - # extract the mortar grids for dimension dim unrolled by sides - interface_side_grids = np.array( - [ - grid - for intf in self.mdg.interfaces(dim=dim) - for _, grid in intf.side_grids.items() - ] - ) - self.m_meshio_geom[dim] = self._export_grid(interface_side_grids, dim) - else: - self.meshio_geom = self._export_grid( - np.atleast_1d(self.grid), self.grid.dim - ) + def _append_folder_name( + self, folder_name: Optional[str] = None, name: str = "" + ) -> str: + """ + Auxiliary method setting up potentially non-existent folder structure and + setting up a path for exporting. + + Args: + folder_name (str, optional): name of the folder + name (str): prefix of the name of the files to be written + + Returns: + str: complete path to the files to be written. + """ - def _make_folder(self, folder_name: Optional[str] = None, name: str = "") -> str: + # If no folder_name is prescribed, the files will be stored in the + # working directory. if folder_name is None: return name + # Set up folder structure if not existent. if not os.path.exists(folder_name): os.makedirs(folder_name) + + # Return full path. return os.path.join(folder_name, name) def _make_file_name( self, file_name: str, - time_step: Optional[float] = None, + time_step: Optional[int] = None, dim: Optional[int] = None, extension: str = ".vtu", ) -> str: + """ + Auxiliary method to setting up file name. - padding = 6 - if dim is None: # normal grid - if time_step is None: - return file_name + extension - else: - time = str(time_step).zfill(padding) - return file_name + "_" + time + extension - else: # part of a mixed-dimensional grid - if time_step is None: - return file_name + "_" + str(dim) + extension - else: - time = str(time_step).zfill(padding) - return file_name + "_" + str(dim) + "_" + time + extension - - def _make_file_name_mortar( - self, file_name: str, dim: int, time_step: Optional[float] = None - ) -> str: - - # we keep the order as in _make_file_name - assert dim is not None - - extension = ".vtu" - file_name = file_name + "_mortar_" - padding = 6 - if time_step is None: - return file_name + str(dim) + extension - else: - time = str(time_step).zfill(padding) - return file_name + str(dim) + "_" + time + extension - - ### Below follows utility functions for sorting points in 3d + The final name is built as combination of a prescribed prefix, + and possibly the dimension of underlying grid and time (step) + the related data is associated to. - def _point_ind( - self, - cell_ptr, - face_ptr, - face_cells, - nodes_faces, - nodes, - fc, - normals, - num_cell_nodes, - ): - cell_nodes = np.zeros(num_cell_nodes.sum(), dtype=int) - counter = 0 - for ci in range(cell_ptr.size - 1): - loc_c = slice(cell_ptr[ci], cell_ptr[ci + 1]) - - for fi in face_cells[loc_c]: - loc_f = slice(face_ptr[fi], face_ptr[fi + 1]) - ptsId = nodes_faces[loc_f] - num_p_loc = ptsId.size - nodes_loc = nodes[:, ptsId] - # Sort points. Cut-down version of - # sort_points.sort_points_plane() and subfunctions - reference = np.array([0.0, 0.0, 1]) - angle = np.arccos(np.dot(normals[:, fi], reference)) - vect = np.cross(normals[:, fi], reference) - # Cut-down version of cg.rot() - W = np.array( - [ - [0.0, -vect[2], vect[1]], - [vect[2], 0.0, -vect[0]], - [-vect[1], vect[0], 0.0], - ] - ) - R = ( - np.identity(3) - + np.sin(angle) * W - + (1.0 - np.cos(angle)) * np.linalg.matrix_power(W, 2) - ) - # pts is now a npt x 3 matrix - pts = np.array( - [R.dot(nodes_loc[:, i]) for i in range(nodes_loc.shape[1])] - ) - center = R.dot(fc[:, fi]) - # Distance from projected points to center - delta = np.array([pts[i] - center for i in range(pts.shape[0])])[:, :2] - nrm = np.sqrt(delta[:, 0] ** 2 + delta[:, 1] ** 2) - delta = delta / nrm[:, np.newaxis] + Args: + file_name (str): prefix of the name of file to be exported + time_step (Union[float int], optional): time or time step (index) + dim (int, optional): dimension of the exported grid + extension (str): extension of the file, typically file ending + defining the format + + Returns: + str: complete name of file + """ - argsort = np.argsort(np.arctan2(delta[:, 0], delta[:, 1])) - cell_nodes[counter : (counter + num_p_loc)] = ptsId[argsort] - counter += num_p_loc + # Define non-empty time step extension including zero padding. + time_extension = ( + "" if time_step is None else "_" + str(time_step).zfill(self._padding) + ) - return cell_nodes + # Define non-empty dim extension + dim_extension = "" if dim is None else "_" + str(dim) - def _point_ind_numba( - self, - cell_ptr, - face_ptr, - faces_cells, - nodes_faces, - nodes, - fc, - normals, - num_cell_nodes, - ): - import numba - - @numba.jit( - "i4[:](i4[:],i4[:],i4[:],i4[:],f8[:,:],f8[:,:],f8[:,:],i4[:])", - nopython=True, - nogil=False, - ) - def _function_to_compile( - cell_ptr, - face_ptr, - faces_cells, - nodes_faces, - nodes, - fc, - normals, - num_cell_nodes, - ): - """Implementation note: This turned out to be less than pretty, and quite - a bit more explicit than the corresponding pure python implementation. - The process was basically to circumvent whatever statements numba did not - like. Not sure about why this ended so, but there you go. - """ - cell_nodes = np.zeros(num_cell_nodes.sum(), dtype=np.int32) - counter = 0 - fc.astype(np.float64) - for ci in range(cell_ptr.size - 1): - loc_c = slice(cell_ptr[ci], cell_ptr[ci + 1]) - for fi in faces_cells[loc_c]: - loc_f = np.arange(face_ptr[fi], face_ptr[fi + 1]) - ptsId = nodes_faces[loc_f] - num_p_loc = ptsId.size - nodes_loc = np.zeros((3, num_p_loc)) - for iter1 in range(num_p_loc): - nodes_loc[:, iter1] = nodes[:, ptsId[iter1]] - # # Sort points. Cut-down version of - # # sort_points.sort_points_plane() and subfunctions - reference = np.array([0.0, 0.0, 1]) - angle = np.arccos(np.dot(normals[:, fi], reference)) - # Hand code cross product, not supported by current numba version - vect = np.array( - [ - normals[1, fi] * reference[2] - - normals[2, fi] * reference[1], - normals[2, fi] * reference[0] - - normals[0, fi] * reference[2], - normals[0, fi] * reference[1] - - normals[1, fi] * reference[0], - ], - dtype=np.float64, - ) - ## # Cut-down version of cg.rot() - W = np.array( - [ - 0.0, - -vect[2], - vect[1], - vect[2], - 0.0, - -vect[0], - -vect[1], - vect[0], - 0.0, - ] - ).reshape((3, 3)) - R = ( - np.identity(3) - + np.sin(angle) * W.reshape((3, 3)) - + ( - (1.0 - np.cos(angle)) * np.linalg.matrix_power(W, 2).ravel() - ).reshape((3, 3)) - ) - ## # pts is now a npt x 3 matrix - num_p = nodes_loc.shape[1] - pts = np.zeros((3, num_p)) - fc_loc = fc[:, fi] - center = np.zeros(3) - for i in range(3): - center[i] = ( - R[i, 0] * fc_loc[0] - + R[i, 1] * fc_loc[1] - + R[i, 2] * fc_loc[2] - ) - for i in range(num_p): - for j in range(3): - pts[j, i] = ( - R[j, 0] * nodes_loc[0, i] - + R[j, 1] * nodes_loc[1, i] - + R[j, 2] * nodes_loc[2, i] - ) - ## # Distance from projected points to center - delta = 0 * pts - for i in range(num_p): - delta[:, i] = pts[:, i] - center - nrm = np.sqrt(delta[0] ** 2 + delta[1] ** 2) - for i in range(num_p): - delta[:, i] = delta[:, i] / nrm[i] - ## - argsort = np.argsort(np.arctan2(delta[0], delta[1])) - cell_nodes[counter : (counter + num_p_loc)] = ptsId[argsort] - counter += num_p_loc - - return cell_nodes - - return _function_to_compile( - cell_ptr, - face_ptr, - faces_cells, - nodes_faces, - nodes, - fc, - normals, - num_cell_nodes, - ) + # Combine prefix and extensions to define the complete name + return file_name + dim_extension + time_extension + extension diff --git a/tests/unit/test_vtk.py b/tests/unit/test_vtk.py index 711ebad544..dfc76e2d70 100644 --- a/tests/unit/test_vtk.py +++ b/tests/unit/test_vtk.py @@ -32,10 +32,16 @@ def test_single_subdomain_1d(self): dummy_scalar = np.ones(sd.num_cells) * sd.dim dummy_vector = np.ones((3, sd.num_cells)) * sd.dim - save = pp.Exporter(sd, self.file_name, self.folder, binary=False) - save.write_vtu({"dummy_scalar": dummy_scalar, "dummy_vector": dummy_vector}) + save = pp.Exporter( + sd, + self.file_name, + self.folder, + binary=False, + export_constants_separately=False, + ) + save.write_vtu([("dummy_scalar", dummy_scalar), ("dummy_vector", dummy_vector)]) - with open(self.folder + self.file_name + ".vtu", "r") as content_file: + with open(self.folder + self.file_name + "_1.vtu", "r") as content_file: content = self.sliceout(content_file.read()) self.assertTrue(content == self._single_subdomain_1d_vtu()) @@ -46,10 +52,16 @@ def test_single_subdomain_2d_simplex_grid(self): dummy_scalar = np.ones(sd.num_cells) * sd.dim dummy_vector = np.ones((3, sd.num_cells)) * sd.dim - save = pp.Exporter(sd, self.file_name, self.folder, binary=False) - save.write_vtu({"dummy_scalar": dummy_scalar, "dummy_vector": dummy_vector}) + save = pp.Exporter( + sd, + self.file_name, + self.folder, + binary=False, + export_constants_separately=False, + ) + save.write_vtu([("dummy_scalar", dummy_scalar), ("dummy_vector", dummy_vector)]) - with open(self.folder + self.file_name + ".vtu", "r") as content_file: + with open(self.folder + self.file_name + "_2.vtu", "r") as content_file: content = self.sliceout(content_file.read()) self.assertTrue(content == self._single_subdomain_2d_simplex_grid_vtu()) @@ -60,26 +72,38 @@ def test_single_subdomain_2d_cart_grid(self): dummy_scalar = np.ones(sd.num_cells) * sd.dim dummy_vector = np.ones((3, sd.num_cells)) * sd.dim - save = pp.Exporter(sd, self.file_name, self.folder, binary=False) - save.write_vtu({"dummy_scalar": dummy_scalar, "dummy_vector": dummy_vector}) + save = pp.Exporter( + sd, + self.file_name, + self.folder, + binary=False, + export_constants_separately=False, + ) + save.write_vtu([("dummy_scalar", dummy_scalar), ("dummy_vector", dummy_vector)]) - with open(self.folder + self.file_name + ".vtu", "r") as content_file: + with open(self.folder + self.file_name + "_2.vtu", "r") as content_file: content = self.sliceout(content_file.read()) self.assertTrue(content == self._single_subdomain_2d_cart_grid_vtu()) - def test_single_subdomain_2d_polytop_grid(self): - sd = pp.CartGrid([3, 2], [1] * 2) + def test_single_subdomain_2d_polytop(self): + sd = pp.StructuredTriangleGrid([2] * 2, [1] * 2) sd.compute_geometry() - pp.coarsening.generate_coarse_grid(sd, [0, 0, 1, 0, 1, 1]) + pp.coarsening.generate_coarse_grid(sd, [0, 1, 3, 3, 1, 1, 2, 2]) sd.compute_geometry() dummy_scalar = np.ones(sd.num_cells) * sd.dim dummy_vector = np.ones((3, sd.num_cells)) * sd.dim - save = pp.Exporter(sd, self.file_name, self.folder, binary=False) - save.write_vtu({"dummy_scalar": dummy_scalar, "dummy_vector": dummy_vector}) + save = pp.Exporter( + sd, + self.file_name, + self.folder, + binary=False, + export_constants_separately=False, + ) + save.write_vtu([("dummy_scalar", dummy_scalar), ("dummy_vector", dummy_vector)]) - with open(self.folder + self.file_name + ".vtu", "r") as content_file: + with open(self.folder + self.file_name + "_2.vtu", "r") as content_file: content = self.sliceout(content_file.read()) self.assertTrue(content == self._single_subdomain_2d_polytop_grid_vtu()) @@ -90,10 +114,16 @@ def test_single_subdomain_3d_simplex_grid(self): dummy_scalar = np.ones(sd.num_cells) * sd.dim dummy_vector = np.ones((3, sd.num_cells)) * sd.dim - save = pp.Exporter(sd, self.file_name, self.folder, binary=False) - save.write_vtu({"dummy_scalar": dummy_scalar, "dummy_vector": dummy_vector}) + save = pp.Exporter( + sd, + self.file_name, + self.folder, + binary=False, + export_constants_separately=False, + ) + save.write_vtu([("dummy_scalar", dummy_scalar), ("dummy_vector", dummy_vector)]) - with open(self.folder + self.file_name + ".vtu", "r") as content_file: + with open(self.folder + self.file_name + "_3.vtu", "r") as content_file: content = self.sliceout(content_file.read()) self.assertTrue(content == self._single_subdomain_3d_simplex_grid_vtu()) @@ -104,10 +134,16 @@ def test_single_subdomain_3d_cart_grid(self): dummy_scalar = np.ones(sd.num_cells) * sd.dim dummy_vector = np.ones((3, sd.num_cells)) * sd.dim - save = pp.Exporter(sd, self.file_name, self.folder, binary=False) - save.write_vtu({"dummy_scalar": dummy_scalar, "dummy_vector": dummy_vector}) + save = pp.Exporter( + sd, + self.file_name, + self.folder, + binary=False, + export_constants_separately=False, + ) + save.write_vtu([("dummy_scalar", dummy_scalar), ("dummy_vector", dummy_vector)]) - with open(self.folder + self.file_name + ".vtu", "r") as content_file: + with open(self.folder + self.file_name + "_3.vtu", "r") as content_file: content = self.sliceout(content_file.read()) self.assertTrue(content == self._single_subdomain_3d_cart_grid_vtu()) @@ -122,13 +158,20 @@ def test_single_subdomain_3d_polytop_grid(self): dummy_scalar = np.ones(sd.num_cells) * sd.dim dummy_vector = np.ones((3, sd.num_cells)) * sd.dim - save = pp.Exporter(sd, self.file_name, self.folder, binary=False) - save.write_vtu({"dummy_scalar": dummy_scalar, "dummy_vector": dummy_vector}) + save = pp.Exporter( + sd, + self.file_name, + self.folder, + binary=False, + export_constants_separately=False, + ) + save.write_vtu([("dummy_scalar", dummy_scalar), ("dummy_vector", dummy_vector)]) - with open(self.folder + self.file_name + ".vtu", "r") as content_file: + with open(self.folder + self.file_name + "_3.vtu", "r") as content_file: content = self.sliceout(content_file.read()) self.assertTrue(content == self._single_subdomain_3d_polytop_grid_vtu()) + # NOTE: Suggest removing this test. def test_mdg_1(self): mdg, _ = pp.md_grids_2d.single_horizontal([4, 4], simplex=False) @@ -141,7 +184,13 @@ def test_mdg_1(self): }, ) - save = pp.Exporter(mdg, self.file_name, self.folder, binary=False) + save = pp.Exporter( + mdg, + self.file_name, + self.folder, + binary=False, + export_constants_separately=False, + ) save.write_vtu(["dummy_scalar", "dummy_vector"]) with open(self.folder + self.file_name + "_1.vtu", "r") as content_file: @@ -156,6 +205,7 @@ def test_mdg_1(self): content = self.sliceout(content_file.read()) self.assertTrue(content == self._mdg_1_mortar_grid_vtu()) + # TODO Do we need this test if we have the subsequent one? def test_mdg_2(self): mdg, _ = pp.md_grids_2d.two_intersecting( [4, 4], y_endpoints=[0.25, 0.75], simplex=False @@ -179,7 +229,13 @@ def test_mdg_2(self): }, ) - save = pp.Exporter(mdg, self.file_name, self.folder, binary=False) + save = pp.Exporter( + mdg, + self.file_name, + self.folder, + binary=False, + export_constants_separately=False, + ) save.write_vtu(["dummy_scalar", "dummy_vector", "unique_dummy_scalar"]) with open(self.folder + self.file_name + "_1.vtu", "r") as content_file: @@ -194,6 +250,88 @@ def test_mdg_2(self): content = self.sliceout(content_file.read()) self.assertTrue(content == self._mdg_2_mortar_grid_1_vtu()) + def test_mdg_3(self): + mdg, _ = pp.md_grids_2d.two_intersecting( + [4, 4], y_endpoints=[0.25, 0.75], simplex=False + ) + + for sd, sd_data in mdg.subdomains(return_data=True): + pp.set_state( + sd_data, + { + "dummy_scalar": np.ones(sd.num_cells) * sd.dim, + "dummy_vector": np.ones((3, sd.num_cells)) * sd.dim, + }, + ) + + for intf, intf_data in mdg.interfaces(return_data=True): + pp.set_state( + intf_data, + { + "dummy_scalar": np.zeros(intf.num_cells), + "unique_dummy_scalar": np.zeros(intf.num_cells), + }, + ) + + subdomains_1d = mdg.subdomains(dim=1) + subdomains_2d = mdg.subdomains(dim=2) + sd_2d = subdomains_2d[0] + # interfaces_1d = mdg.interfaces(dim=1) # FIXME not used below + + save = pp.Exporter( + mdg, + self.file_name, + self.folder, + binary=False, + export_constants_separately=False, + ) + save.write_vtu( + [ + (subdomains_1d, "dummy_scalar"), + "dummy_vector", + "unique_dummy_scalar", + (sd_2d, "cc", sd_2d.cell_centers), + ] + ) + + with open(self.folder + self.file_name + "_1.vtu", "r") as content_file: + content = self.sliceout(content_file.read()) + self.assertTrue(content == self._mdg_3_grid_1_vtu()) + + with open(self.folder + self.file_name + "_2.vtu", "r") as content_file: + content = self.sliceout(content_file.read()) + self.assertTrue(content == self._mdg_3_grid_2_vtu()) + + with open(self.folder + self.file_name + "_mortar_1.vtu", "r") as content_file: + content = self.sliceout(content_file.read()) + self.assertTrue(content == self._mdg_3_mortar_grid_1_vtu()) + + def test_constant_data(self): + g = pp.StructuredTriangleGrid([3] * 2, [1] * 2) + g.compute_geometry() + + dummy_scalar = np.ones(g.num_cells) * g.dim + dummy_vector = np.ones((3, g.num_cells)) * g.dim + + save = pp.Exporter( + g, + self.file_name, + self.folder, + binary=False, + ) + save.add_constant_data([(g, "cc", g.cell_centers)]) + save.write_vtu([("dummy_scalar", dummy_scalar), ("dummy_vector", dummy_vector)]) + + with open(self.folder + self.file_name + "_2.vtu", "r") as content_file: + content = self.sliceout(content_file.read()) + self.assertTrue(content == self._nonconstant_data_grid_vtu()) + + with open( + self.folder + self.file_name + "_constant_2.vtu", "r" + ) as content_file: + content = self.sliceout(content_file.read()) + self.assertTrue(content == self._constant_data_grid_vtu()) + def test_fractures_2d(self): p = np.array([[0, 2, 1, 2, 1], [0, 0, 0, 1, 2]]) e = np.array([[0, 2, 3], [1, 3, 4]]) @@ -289,6 +427,12 @@ def _single_subdomain_1d_vtu(self): + +0 +1 +2 + + 1.00000000000e+00 1.00000000000e+00 @@ -313,10 +457,16 @@ def _single_subdomain_1d_vtu(self): 1 - + +0 +0 +0 + + + +0 +0 0 -1 -2 @@ -398,21 +548,21 @@ def _single_subdomain_2d_simplex_grid_vtu(self): 1 5 6 +2 3 7 2 -2 6 7 4 5 9 +4 8 9 -4 5 -10 6 +10 5 9 10 @@ -422,12 +572,12 @@ def _single_subdomain_2d_simplex_grid_vtu(self): 6 10 11 +8 9 13 8 12 13 -8 9 10 14 @@ -435,11 +585,11 @@ def _single_subdomain_2d_simplex_grid_vtu(self): 13 14 10 -15 11 -10 15 +10 14 +15 @@ -486,6 +636,27 @@ def _single_subdomain_2d_simplex_grid_vtu(self): + +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 + + 2.00000000000e+00 2.00000000000e+00 @@ -585,25 +756,46 @@ def _single_subdomain_2d_simplex_grid_vtu(self): 2 - + +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 + + + +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 @@ -712,10 +904,10 @@ def _single_subdomain_2d_cart_grid_vtu(self): 7 8 3 +3 8 9 4 -3 5 10 11 @@ -724,14 +916,14 @@ def _single_subdomain_2d_cart_grid_vtu(self): 11 12 7 +7 12 13 8 -7 8 -9 -14 13 +14 +9 10 15 16 @@ -744,26 +936,26 @@ def _single_subdomain_2d_cart_grid_vtu(self): 17 18 13 +13 18 19 14 -13 15 20 21 16 +16 21 22 17 -16 17 22 23 18 18 -19 -24 23 +24 +19 @@ -806,6 +998,25 @@ def _single_subdomain_2d_cart_grid_vtu(self): + +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 + + 2.00000000000e+00 2.00000000000e+00 @@ -895,46 +1106,62 @@ def _single_subdomain_2d_cart_grid_vtu(self): 2 - + +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 - - - - -""" - - def _single_subdomain_2d_polytop_grid_vtu(self): - return """ - - - - - - -0.00000000000e+00 -0.00000000000e+00 -0.00000000000e+00 -3.33333333333e-01 -0.00000000000e+00 + +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 + + + + + + +""" + + def _single_subdomain_2d_polytop_grid_vtu(self): + return """ + + + + + + 0.00000000000e+00 -6.66666666667e-01 +0.00000000000e+00 +0.00000000000e+00 +5.00000000000e-01 0.00000000000e+00 0.00000000000e+00 1.00000000000e+00 @@ -943,10 +1170,7 @@ def _single_subdomain_2d_polytop_grid_vtu(self): 0.00000000000e+00 5.00000000000e-01 0.00000000000e+00 -3.33333333333e-01 5.00000000000e-01 -0.00000000000e+00 -6.66666666667e-01 5.00000000000e-01 0.00000000000e+00 1.00000000000e+00 @@ -955,10 +1179,7 @@ def _single_subdomain_2d_polytop_grid_vtu(self): 0.00000000000e+00 1.00000000000e+00 0.00000000000e+00 -3.33333333333e-01 -1.00000000000e+00 -0.00000000000e+00 -6.66666666667e-01 +5.00000000000e-01 1.00000000000e+00 0.00000000000e+00 1.00000000000e+00 @@ -970,38 +1191,51 @@ def _single_subdomain_2d_polytop_grid_vtu(self): 0 +1 +4 4 -8 -9 5 -6 -2 +8 +7 1 2 -6 5 -9 -10 -11 -7 +4 +0 3 +6 +7 +4 -8 +3 +7 +11 16 -7 +5 +9 +9 7 + +0 +2 +3 +1 + + 2.00000000000e+00 2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 @@ -1011,16 +1245,33 @@ def _single_subdomain_2d_polytop_grid_vtu(self): 2.00000000000e+00 2.00000000000e+00 2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 2 2 +2 +2 - + +0 +0 +0 +0 + + + +0 +0 +0 0 -1 @@ -2050,3094 +2301,337 @@ def _single_subdomain_3d_simplex_grid_vtu(self): -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 - -4 -3 -0 -4 -1 -3 -0 -16 -1 -3 -16 -4 + + + 0 -3 -16 -4 -1 -4 -3 -16 -4 -1 -3 -20 -4 -1 -3 -1 -20 -16 -3 -4 -20 -16 -4 -3 -16 -20 -17 -3 -1 -20 -17 -3 -16 -1 -17 -3 -1 -20 -16 -4 -3 -4 -5 -1 -3 -20 -4 -1 -3 -20 -5 1 +2 3 4 -20 5 -4 -3 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 17 -5 -1 -3 +18 +19 20 -5 -1 -3 -1 -20 -17 -3 -5 -20 -17 -4 -3 -20 -21 -17 -3 -20 -5 -21 -3 -5 -20 -17 -3 -5 -21 -17 -4 -3 -1 -5 -2 -3 -1 -17 -2 -3 -17 -5 -1 -3 -17 -5 -2 -4 -3 -5 -21 -17 -3 -17 -5 -2 -3 -21 -5 -2 -3 -2 -21 -17 -4 -3 -17 -21 -18 -3 -2 -21 -18 -3 -17 -2 -18 -3 -2 -21 -17 -4 -3 -21 -6 -2 -3 -5 -6 -2 -3 -21 -5 -2 -3 -5 -21 -6 -4 -3 -6 21 -18 -3 -2 -21 -18 -3 -18 -6 -2 -3 -21 -6 -2 -4 -3 -6 -21 -18 -3 -6 -22 -18 -3 -21 -6 -22 -3 -21 -22 -18 -4 -3 -2 -6 -3 -3 -2 -18 -3 -3 -18 -6 -2 -3 -18 -6 -3 -4 -3 -6 -22 -18 -3 -3 -22 -18 -3 -18 -6 -3 -3 -22 -6 -3 -4 -3 -18 -3 -19 -3 -3 -22 -18 -3 -3 -22 -19 -3 -18 -22 -19 -4 -3 -6 -22 -7 -3 -22 -7 -3 -3 -22 -6 -3 -3 -6 -7 -3 -4 -3 -19 -7 -3 -3 -22 -7 -3 -3 -3 -22 -19 -3 -7 -22 -19 -4 -3 -22 -7 -23 -3 -22 -23 -19 -3 -7 22 -19 -3 -7 23 -19 -4 -3 -4 -8 -5 -3 -4 -20 -5 -3 -20 -8 -4 -3 -20 -8 -5 -4 -3 -5 -24 -20 -3 -20 -8 -5 -3 24 -8 -5 -3 -8 -24 -20 -4 -3 -20 -24 -21 -3 -5 -24 -21 -3 -20 -5 -21 -3 -5 -24 -20 -4 -3 -8 -9 -5 -3 -24 -8 -5 -3 -24 -9 -5 -3 -8 -24 -9 -4 -3 -5 -24 -21 -3 -9 -24 -21 -3 -21 -9 -5 -3 -24 -9 -5 -4 -3 -9 -24 -21 -3 -9 -25 -21 -3 -24 -9 -25 -3 -24 -25 -21 -4 -3 -21 -9 -6 -3 -5 -9 -6 -3 -5 -21 -6 -3 -21 -9 -5 -4 -3 -9 -25 -21 -3 -6 -25 -21 -3 -21 -9 -6 -3 -25 -9 -6 -4 -3 -21 -6 -22 -3 -6 -25 -21 -3 -6 -25 -22 -3 -21 -25 -22 -4 -3 -9 -25 -10 -3 25 -10 -6 -3 -9 -10 -6 -3 -25 -9 -6 -4 -3 -22 -10 -6 -3 -25 -10 -6 -3 -6 -25 -22 -3 -10 -25 -22 -4 -3 -25 -10 -26 -3 -10 -25 -22 -3 -10 -26 -22 -3 -25 -26 -22 -4 -3 -22 -10 -7 -3 -22 -10 -6 -3 -6 -22 -7 -3 -6 -10 -7 -4 -3 -22 -10 -7 -3 -26 -10 -7 -3 -7 -26 -22 -3 -10 -26 -22 -4 -3 -22 -7 -23 -3 -7 -26 -22 -3 -7 -26 -23 -3 -22 -26 -23 -4 -3 -10 -26 -11 -3 -26 -11 -7 -3 -10 -11 -7 -3 -26 -10 -7 -4 -3 -23 -11 -7 -3 -26 -11 -7 -3 -7 -26 -23 -3 -11 26 -23 -4 -3 -26 -27 -23 -3 -26 -11 27 -3 -11 -27 -23 -3 -11 -26 -23 -4 -3 -8 -12 -9 -3 -8 -24 -9 -3 -24 -12 -8 -3 -24 -12 -9 -4 -3 -9 -28 -24 -3 -12 -28 -24 -3 -24 -12 -9 -3 -28 -12 -9 -4 -3 -24 -9 -25 -3 -9 -28 -24 -3 -9 -28 -25 -3 -24 -28 -25 -4 -3 -28 -13 -9 -3 -12 -13 -9 -3 -28 -12 -9 -3 -12 -28 -13 -4 -3 -13 -28 -25 -3 -9 -28 -25 -3 -25 -13 -9 -3 -28 -13 -9 -4 -3 -13 -28 -25 -3 -13 -29 -25 -3 -28 -13 -29 -3 28 29 -25 -4 -3 -9 -13 -10 -3 -9 -25 -10 -3 -25 -13 -9 -3 -25 -13 -10 -4 -3 -13 -29 -25 -3 -10 -29 -25 -3 -25 -13 -10 -3 -29 -13 -10 -4 -3 -25 -10 -26 -3 -10 -29 -25 -3 -10 -29 -26 -3 -25 -29 -26 -4 -3 -13 -29 -14 -3 -29 -14 -10 -3 -29 -13 -10 -3 -13 -14 -10 -4 -3 -26 -14 -10 -3 -29 -14 -10 -3 -10 -29 -26 -3 -14 -29 -26 -4 -3 -29 -14 -30 -3 -29 -30 -26 -3 -14 -29 -26 -3 -14 -30 -26 -4 -3 -10 -14 -11 -3 -10 -26 -11 -3 -26 -14 -10 -3 -26 -14 -11 -4 -3 -14 -30 -26 -3 -26 -14 -11 -3 -30 -14 -11 -3 -11 -30 -26 -4 -3 -26 -30 -27 -3 -11 -30 -27 -3 -11 -30 -26 -3 -26 -11 -27 -4 -3 -14 -15 -11 -3 -30 -14 -11 -3 -30 -15 -11 -3 -14 -30 -15 -4 -3 -11 -30 -27 -3 -15 -30 -27 -3 -27 -15 -11 -3 -30 -15 -11 -4 -3 -15 -30 -27 -3 -15 -31 -27 -3 -30 -15 -31 -3 30 31 -27 -4 -3 -16 -20 -17 -3 -16 -32 -17 -3 -32 -20 -16 -3 -32 -20 -17 -4 -3 -17 -36 -32 -3 -20 -36 -32 -3 -36 -20 -17 -3 -32 -20 -17 -4 -3 -32 -17 -33 -3 -17 -36 32 -3 -17 -36 -33 -3 -32 -36 -33 -4 -3 -20 -21 -17 -3 -36 -20 -17 -3 -36 -21 -17 -3 -20 -36 -21 -4 -3 -21 -36 -33 -3 -33 -21 -17 -3 -36 -21 -17 -3 -17 -36 -33 -4 -3 -36 -21 -37 -3 -36 -37 -33 -3 -21 -36 -33 -3 -21 -37 -33 -4 -3 -33 -21 -18 -3 -33 -21 -17 -3 -17 -33 -18 -3 -17 -21 -18 -4 -3 -33 -21 -18 -3 -37 -21 -18 -3 -18 -37 33 -3 -21 -37 -33 -4 -3 -33 -18 -34 -3 -18 -37 -33 -3 -18 -37 -34 -3 -33 -37 -34 -4 -3 -21 -22 -18 -3 -37 -21 -18 -3 -37 -22 -18 -3 -21 -37 -22 -4 -3 -18 -37 -34 -3 -22 -37 -34 -3 34 -22 -18 -3 -37 -22 -18 -4 -3 -37 -38 -34 -3 +35 +36 37 -22 38 -3 -22 -37 -34 -3 -22 -38 -34 -4 -3 -18 -22 -19 -3 -18 -34 -19 -3 -34 -22 -18 -3 -34 -22 -19 -4 -3 -34 -22 -19 -3 -38 -22 -19 -3 -19 -38 -34 -3 -22 -38 -34 -4 -3 -19 -38 -35 -3 -34 -19 -35 -3 -19 -38 -34 -3 -34 -38 -35 -4 -3 -22 -38 -23 -3 -38 -23 -19 -3 -38 -22 -19 -3 -22 -23 -19 -4 -3 -35 -23 -19 -3 -38 -23 -19 -3 -19 -38 -35 -3 -23 -38 -35 -4 -3 -23 -38 -35 -3 -23 -39 -35 -3 -38 -23 39 -3 -38 -39 -35 -4 -3 -20 -24 -21 -3 -20 -36 -21 -3 -36 -24 -20 -3 -36 -24 -21 -4 -3 -21 40 -36 -3 -24 -40 -36 -3 -36 -24 -21 -3 -40 -24 -21 -4 -3 -36 -21 -37 -3 -21 -40 -36 -3 -21 -40 -37 -3 -36 -40 -37 -4 -3 -40 -25 -21 -3 -24 -40 -25 -3 -24 -25 -21 -3 -40 -24 -21 -4 -3 -37 -25 -21 -3 -40 -25 -21 -3 -21 -40 -37 -3 -25 -40 -37 -4 -3 -40 -41 -37 -3 -25 -40 -37 -3 -25 -41 -37 -3 -40 -25 -41 -4 -3 -37 -25 -21 -3 -37 -25 -22 -3 -21 -25 -22 -3 -21 -37 -22 -4 -3 -22 -41 -37 -3 -25 -41 -37 -3 -37 -25 -22 -3 41 -25 -22 -4 -3 -22 -41 -38 -3 -37 -41 -38 -3 -37 -22 -38 -3 -22 -41 -37 -4 -3 -25 -26 -22 -3 -41 -25 -22 -3 -41 -26 -22 -3 -25 -41 -26 -4 -3 -22 -41 -38 -3 -26 -41 -38 -3 -38 -26 -22 -3 -41 -26 -22 -4 -3 -26 -41 -38 -3 -26 -42 -38 -3 -41 -26 -42 -3 -41 -42 -38 -4 -3 -22 -26 -23 -3 -22 -38 -23 -3 -38 -26 -22 -3 -38 -26 -23 -4 -3 -26 -42 -38 -3 -23 -42 -38 -3 -42 -26 -23 -3 -38 -26 -23 -4 -3 -38 -23 -39 -3 -23 -42 -38 -3 -23 -42 -39 -3 -38 -42 -39 -4 -3 -26 -27 -23 -3 -42 -26 -23 -3 -42 -27 -23 -3 -26 -42 -27 -4 -3 -27 -42 -39 -3 -23 -42 -39 -3 -39 -27 -23 -3 -42 -27 -23 -4 -3 -27 -42 -39 -3 -27 -43 -39 -3 -42 -27 -43 -3 42 43 -39 -4 -3 -40 -28 -25 -3 -40 -28 -24 -3 -24 -40 -25 -3 -24 -28 -25 -4 -3 -40 -28 -25 -3 -44 -28 -25 -3 -25 -44 -40 -3 -28 -44 -40 -4 -3 -40 -25 -41 -3 -25 -44 -40 -3 -25 -44 -41 -3 -40 -44 -41 -4 -3 -28 -44 -29 -3 -44 -29 -25 -3 -28 -29 -25 -3 -44 -28 -25 -4 -3 -41 -29 -25 -3 -44 -29 -25 -3 -25 -44 -41 -3 -29 -44 -41 -4 -3 -44 -45 -41 -3 44 -29 -45 -3 -29 45 -41 -3 -29 -44 -41 -4 -3 -25 -29 -26 -3 -25 -41 -26 -3 -41 -29 -25 -3 -41 -29 -26 -4 -3 -26 -45 -41 -3 -29 -45 -41 -3 -41 -29 -26 -3 -45 -29 -26 -4 -3 -41 -26 -42 -3 -26 -45 -41 -3 -26 -45 -42 -3 -41 -45 -42 -4 -3 -29 -45 -30 -3 -29 -30 -26 -3 -45 -29 -26 -3 -45 -30 -26 -4 -3 -26 -45 -42 -3 -30 -45 -42 -3 -42 -30 -26 -3 -45 -30 -26 -4 -3 -30 -45 -42 -3 -30 -46 -42 -3 -45 -30 -46 -3 -45 -46 -42 -4 -3 -42 -30 -26 -3 -42 -30 -27 -3 -26 -30 -27 -3 -26 -42 -27 -4 -3 -42 -30 -27 -3 -46 -30 -27 -3 -27 46 -42 -3 -30 -46 -42 -4 -3 -42 -27 -43 -3 -27 -46 -42 -3 -27 -46 -43 -3 -42 -46 -43 -4 -3 -30 -46 -31 -3 -46 -31 -27 -3 -46 -30 -27 -3 -30 -31 -27 -4 -3 -43 -31 -27 -3 -46 -31 -27 -3 -27 -46 -43 -3 -31 -46 -43 -4 -3 -46 -31 -47 -3 -46 -47 -43 -3 -31 -46 -43 -3 -31 47 -43 -4 -3 -32 -36 -33 -3 -32 -48 -33 -3 -48 -36 -32 -3 -48 -36 -33 -4 -3 -36 -52 -48 -3 -48 -36 -33 -3 -52 -36 -33 -3 -33 -52 -48 -4 -3 -33 -52 -49 -3 48 -52 -49 -3 -48 -33 49 -3 -33 +50 +51 52 -48 -4 -3 -36 -37 -33 -3 -52 -36 -33 -3 -52 -37 -33 -3 -36 -52 -37 -4 -3 -33 -52 -49 -3 -37 -52 -49 -3 -49 -37 -33 -3 -52 -37 -33 -4 -3 -37 -52 -49 -3 -37 -53 -49 -3 -52 -37 -53 -3 -52 -53 -49 -4 -3 -49 -37 -34 -3 -33 -37 -34 -3 -33 -49 -34 -3 -49 -37 -33 -4 -3 -34 -53 -49 -3 -37 -53 -49 -3 -49 -37 -34 -3 -53 -37 -34 -4 -3 -49 -34 -50 -3 -34 -53 -49 -3 -34 -53 -50 -3 -49 -53 -50 -4 -3 -37 -38 -34 -3 -53 -37 -34 -3 -53 -38 -34 -3 -37 -53 -38 -4 -3 -38 -53 -50 -3 -34 -53 -50 -3 -53 -38 -34 -3 -50 -38 -34 -4 -3 -38 -53 -50 -3 -38 -54 -50 -3 -53 -38 -54 -3 53 54 -50 -4 -3 -34 -38 -35 -3 -34 -50 -35 -3 -50 -38 -34 -3 -50 -38 -35 -4 -3 -38 -54 -50 -3 -35 -54 -50 -3 -50 -38 -35 -3 -54 -38 -35 -4 -3 -50 -35 -51 -3 -35 -54 -50 -3 -35 -54 -51 -3 -50 -54 -51 -4 -3 -54 -39 -35 -3 -38 -54 -39 -3 -38 -39 -35 -3 -54 -38 -35 -4 -3 -51 -39 -35 -3 -54 -39 -35 -3 -35 -54 -51 -3 -39 -54 -51 -4 -3 -54 -55 -51 -3 -39 -54 -51 -3 -39 -55 -51 -3 -54 -39 55 -4 -3 -52 -40 -37 -3 -52 -40 -36 -3 -36 -40 -37 -3 -36 -52 -37 -4 -3 -52 -40 -37 -3 -56 -40 -37 -3 -37 -56 -52 -3 -40 -56 -52 -4 -3 -52 -56 -53 -3 -37 -56 -53 -3 -52 -37 -53 -3 -37 -56 -52 -4 -3 -40 -41 -37 -3 -56 -40 -37 -3 -56 -41 -37 -3 -40 -56 -41 -4 -3 -37 -56 -53 -3 -53 -41 -37 -3 -56 -41 -37 -3 -41 -56 -53 -4 -3 -56 -57 -53 -3 -56 -41 -57 -3 -41 -57 -53 -3 -41 56 -53 -4 -3 -37 -41 -38 -3 -37 -53 -38 -3 -53 -41 -37 -3 -53 -41 -38 -4 -3 -53 -41 -38 -3 -57 -41 -38 -3 -38 -57 -53 -3 -41 -57 -53 -4 -3 -53 57 -54 -3 -38 -57 -54 -3 -53 -38 -54 -3 -38 -57 -53 -4 -3 -41 -42 -38 -3 -57 -41 -38 -3 -57 -42 -38 -3 -41 -57 -42 -4 -3 -42 -57 -54 -3 -38 -57 -54 -3 -57 -42 -38 -3 -54 -42 -38 -4 -3 -42 -57 -54 -3 -42 -58 -54 -3 -57 -42 -58 -3 -57 -58 -54 -4 -3 -54 -42 -38 -3 -54 -42 -39 -3 -38 -42 -39 -3 -38 -54 -39 -4 -3 -54 -42 -39 -3 -58 -42 -39 -3 -39 -58 -54 -3 -42 58 -54 -4 -3 -39 -58 -55 -3 -54 -39 -55 -3 -39 -58 -54 -3 -54 -58 -55 -4 -3 -42 -58 -43 -3 -58 -43 -39 -3 -58 -42 -39 -3 -42 -43 -39 -4 -3 -55 -43 -39 -3 -58 -43 -39 -3 -39 -58 -55 -3 -43 -58 -55 -4 -3 -58 -43 -59 -3 -58 -59 -55 -3 -43 -58 -55 -3 -43 59 -55 -4 -3 -40 -44 -41 -3 -40 -56 -41 -3 -56 -44 -40 -3 -56 -44 -41 -4 -3 -44 -60 -56 -3 -56 -44 -41 -3 -60 -44 -41 -3 -41 -60 -56 -4 -3 -56 -60 -57 -3 -41 -60 -57 -3 -56 -41 -57 -3 -41 -60 -56 -4 -3 -44 -45 -41 -3 -60 -44 -41 -3 -60 -45 -41 -3 -44 -60 -45 -4 -3 -45 -60 -57 -3 -41 -60 -57 -3 -57 -45 -41 -3 -60 -45 -41 -4 -3 -45 -60 -57 -3 -45 -61 -57 -3 -60 -45 -61 -3 60 61 -57 -4 -3 -57 -45 -41 -3 -41 -45 -42 -3 -41 -57 -42 -3 -57 -45 -42 -4 -3 -45 -61 -57 -3 -42 -61 -57 -3 -61 -45 -42 -3 -57 -45 -42 -4 -3 -57 -42 -58 -3 -42 -61 -57 -3 -42 -61 -58 -3 -57 -61 -58 -4 -3 -45 -46 -42 -3 -61 -45 -42 -3 -61 -46 -42 -3 -45 -61 -46 -4 -3 -46 -61 -58 -3 -42 -61 -58 -3 -58 -46 -42 -3 -61 -46 -42 -4 -3 -46 -61 -58 -3 -46 -62 -58 -3 -61 -46 -62 -3 -61 -62 -58 -4 -3 -58 -46 -43 -3 -58 -46 -42 -3 -42 -58 -43 -3 -42 -46 -43 -4 -3 -58 -46 -43 -3 -62 -46 -43 -3 -43 -62 -58 -3 -46 -62 -58 -4 -3 -43 -62 -59 -3 -58 -62 -59 -3 -58 -43 -59 -3 -43 -62 -58 -4 -3 -46 -47 -43 -3 -62 -46 -43 -3 -62 -47 -43 -3 -46 -62 -47 -4 -3 -47 -62 -59 -3 -59 -47 -43 -3 -62 -47 -43 -3 -43 -62 -59 -4 -3 -47 -63 -59 -3 -62 -47 -63 -3 -47 -62 -59 -3 62 63 -59 - - - -17 -34 -51 +64 +65 +66 +67 68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 153 -170 -187 -204 -221 -238 -255 -272 -289 -306 -323 -340 -357 -374 -391 -408 -425 -442 -459 -476 -493 -510 -527 -544 -561 -578 -595 -612 -629 -646 -663 -680 -697 -714 -731 -748 -765 -782 -799 -816 -833 -850 -867 -884 -901 -918 -935 -952 -969 -986 -1003 -1020 -1037 -1054 -1071 -1088 -1105 -1122 -1139 -1156 -1173 -1190 -1207 -1224 -1241 -1258 -1275 -1292 -1309 -1326 -1343 -1360 -1377 -1394 -1411 -1428 -1445 -1462 -1479 -1496 -1513 -1530 -1547 -1564 -1581 -1598 -1615 -1632 -1649 -1666 -1683 -1700 -1717 -1734 -1751 -1768 -1785 -1802 -1819 -1836 -1853 -1870 -1887 -1904 -1921 -1938 -1955 -1972 -1989 -2006 -2023 -2040 -2057 -2074 -2091 -2108 -2125 -2142 -2159 -2176 -2193 -2210 -2227 -2244 -2261 -2278 -2295 -2312 -2329 -2346 -2363 -2380 -2397 -2414 -2431 -2448 -2465 -2482 -2499 -2516 -2533 -2550 -2567 -2584 -2601 -2618 -2635 -2652 -2669 -2686 -2703 -2720 -2737 -2754 +154 +155 +156 +157 +158 +159 +160 +161 - - 3.00000000000e+00 3.00000000000e+00 @@ -5957,3663 +3451,3128 @@ def _single_subdomain_3d_simplex_grid_vtu(self): 3 - + +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 - - - - - - -""" - - def _single_subdomain_3d_cart_grid_vtu(self): - return """ - - - - - - -0.00000000000e+00 -0.00000000000e+00 -0.00000000000e+00 -2.50000000000e-01 -0.00000000000e+00 -0.00000000000e+00 -5.00000000000e-01 -0.00000000000e+00 -0.00000000000e+00 -7.50000000000e-01 -0.00000000000e+00 -0.00000000000e+00 -1.00000000000e+00 -0.00000000000e+00 -0.00000000000e+00 -0.00000000000e+00 -2.50000000000e-01 -0.00000000000e+00 -2.50000000000e-01 -2.50000000000e-01 -0.00000000000e+00 -5.00000000000e-01 -2.50000000000e-01 -0.00000000000e+00 -7.50000000000e-01 -2.50000000000e-01 -0.00000000000e+00 -1.00000000000e+00 -2.50000000000e-01 -0.00000000000e+00 -0.00000000000e+00 -5.00000000000e-01 -0.00000000000e+00 -2.50000000000e-01 -5.00000000000e-01 -0.00000000000e+00 -5.00000000000e-01 -5.00000000000e-01 -0.00000000000e+00 -7.50000000000e-01 -5.00000000000e-01 -0.00000000000e+00 -1.00000000000e+00 -5.00000000000e-01 -0.00000000000e+00 -0.00000000000e+00 -7.50000000000e-01 -0.00000000000e+00 -2.50000000000e-01 -7.50000000000e-01 -0.00000000000e+00 -5.00000000000e-01 -7.50000000000e-01 -0.00000000000e+00 -7.50000000000e-01 -7.50000000000e-01 -0.00000000000e+00 -1.00000000000e+00 -7.50000000000e-01 -0.00000000000e+00 -0.00000000000e+00 -1.00000000000e+00 -0.00000000000e+00 -2.50000000000e-01 -1.00000000000e+00 -0.00000000000e+00 -5.00000000000e-01 -1.00000000000e+00 -0.00000000000e+00 -7.50000000000e-01 -1.00000000000e+00 -0.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -0.00000000000e+00 -0.00000000000e+00 -0.00000000000e+00 -2.50000000000e-01 -2.50000000000e-01 -0.00000000000e+00 -2.50000000000e-01 -5.00000000000e-01 -0.00000000000e+00 -2.50000000000e-01 -7.50000000000e-01 -0.00000000000e+00 -2.50000000000e-01 -1.00000000000e+00 -0.00000000000e+00 -2.50000000000e-01 -0.00000000000e+00 -2.50000000000e-01 -2.50000000000e-01 -2.50000000000e-01 -2.50000000000e-01 -2.50000000000e-01 -5.00000000000e-01 -2.50000000000e-01 -2.50000000000e-01 -7.50000000000e-01 -2.50000000000e-01 -2.50000000000e-01 -1.00000000000e+00 -2.50000000000e-01 -2.50000000000e-01 -0.00000000000e+00 -5.00000000000e-01 -2.50000000000e-01 -2.50000000000e-01 -5.00000000000e-01 -2.50000000000e-01 -5.00000000000e-01 -5.00000000000e-01 -2.50000000000e-01 -7.50000000000e-01 -5.00000000000e-01 -2.50000000000e-01 -1.00000000000e+00 -5.00000000000e-01 -2.50000000000e-01 -0.00000000000e+00 -7.50000000000e-01 -2.50000000000e-01 -2.50000000000e-01 -7.50000000000e-01 -2.50000000000e-01 -5.00000000000e-01 -7.50000000000e-01 -2.50000000000e-01 -7.50000000000e-01 -7.50000000000e-01 -2.50000000000e-01 -1.00000000000e+00 -7.50000000000e-01 -2.50000000000e-01 -0.00000000000e+00 -1.00000000000e+00 -2.50000000000e-01 -2.50000000000e-01 -1.00000000000e+00 -2.50000000000e-01 -5.00000000000e-01 -1.00000000000e+00 -2.50000000000e-01 -7.50000000000e-01 -1.00000000000e+00 -2.50000000000e-01 -1.00000000000e+00 -1.00000000000e+00 -2.50000000000e-01 -0.00000000000e+00 -0.00000000000e+00 -5.00000000000e-01 -2.50000000000e-01 -0.00000000000e+00 -5.00000000000e-01 -5.00000000000e-01 -0.00000000000e+00 -5.00000000000e-01 -7.50000000000e-01 -0.00000000000e+00 -5.00000000000e-01 -1.00000000000e+00 -0.00000000000e+00 -5.00000000000e-01 -0.00000000000e+00 -2.50000000000e-01 -5.00000000000e-01 -2.50000000000e-01 -2.50000000000e-01 -5.00000000000e-01 -5.00000000000e-01 -2.50000000000e-01 -5.00000000000e-01 -7.50000000000e-01 -2.50000000000e-01 -5.00000000000e-01 -1.00000000000e+00 -2.50000000000e-01 -5.00000000000e-01 -0.00000000000e+00 -5.00000000000e-01 -5.00000000000e-01 -2.50000000000e-01 -5.00000000000e-01 -5.00000000000e-01 -5.00000000000e-01 -5.00000000000e-01 -5.00000000000e-01 -7.50000000000e-01 -5.00000000000e-01 -5.00000000000e-01 -1.00000000000e+00 -5.00000000000e-01 -5.00000000000e-01 -0.00000000000e+00 -7.50000000000e-01 -5.00000000000e-01 -2.50000000000e-01 -7.50000000000e-01 -5.00000000000e-01 -5.00000000000e-01 -7.50000000000e-01 -5.00000000000e-01 -7.50000000000e-01 -7.50000000000e-01 -5.00000000000e-01 -1.00000000000e+00 -7.50000000000e-01 -5.00000000000e-01 -0.00000000000e+00 -1.00000000000e+00 -5.00000000000e-01 -2.50000000000e-01 -1.00000000000e+00 -5.00000000000e-01 -5.00000000000e-01 -1.00000000000e+00 -5.00000000000e-01 -7.50000000000e-01 -1.00000000000e+00 -5.00000000000e-01 -1.00000000000e+00 -1.00000000000e+00 -5.00000000000e-01 -0.00000000000e+00 -0.00000000000e+00 -7.50000000000e-01 -2.50000000000e-01 -0.00000000000e+00 -7.50000000000e-01 -5.00000000000e-01 -0.00000000000e+00 -7.50000000000e-01 -7.50000000000e-01 -0.00000000000e+00 -7.50000000000e-01 -1.00000000000e+00 -0.00000000000e+00 -7.50000000000e-01 -0.00000000000e+00 -2.50000000000e-01 -7.50000000000e-01 -2.50000000000e-01 -2.50000000000e-01 -7.50000000000e-01 -5.00000000000e-01 -2.50000000000e-01 -7.50000000000e-01 -7.50000000000e-01 -2.50000000000e-01 -7.50000000000e-01 -1.00000000000e+00 -2.50000000000e-01 -7.50000000000e-01 -0.00000000000e+00 -5.00000000000e-01 -7.50000000000e-01 -2.50000000000e-01 -5.00000000000e-01 -7.50000000000e-01 -5.00000000000e-01 -5.00000000000e-01 -7.50000000000e-01 -7.50000000000e-01 -5.00000000000e-01 -7.50000000000e-01 -1.00000000000e+00 -5.00000000000e-01 -7.50000000000e-01 -0.00000000000e+00 -7.50000000000e-01 -7.50000000000e-01 -2.50000000000e-01 -7.50000000000e-01 -7.50000000000e-01 -5.00000000000e-01 -7.50000000000e-01 -7.50000000000e-01 -7.50000000000e-01 -7.50000000000e-01 -7.50000000000e-01 -1.00000000000e+00 -7.50000000000e-01 -7.50000000000e-01 -0.00000000000e+00 -1.00000000000e+00 -7.50000000000e-01 -2.50000000000e-01 -1.00000000000e+00 -7.50000000000e-01 -5.00000000000e-01 -1.00000000000e+00 -7.50000000000e-01 -7.50000000000e-01 -1.00000000000e+00 -7.50000000000e-01 -1.00000000000e+00 -1.00000000000e+00 -7.50000000000e-01 -0.00000000000e+00 -0.00000000000e+00 -1.00000000000e+00 -2.50000000000e-01 -0.00000000000e+00 -1.00000000000e+00 -5.00000000000e-01 -0.00000000000e+00 -1.00000000000e+00 -7.50000000000e-01 -0.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -0.00000000000e+00 -1.00000000000e+00 -0.00000000000e+00 -2.50000000000e-01 -1.00000000000e+00 -2.50000000000e-01 -2.50000000000e-01 -1.00000000000e+00 -5.00000000000e-01 -2.50000000000e-01 -1.00000000000e+00 -7.50000000000e-01 -2.50000000000e-01 -1.00000000000e+00 -1.00000000000e+00 -2.50000000000e-01 -1.00000000000e+00 -0.00000000000e+00 -5.00000000000e-01 -1.00000000000e+00 -2.50000000000e-01 -5.00000000000e-01 -1.00000000000e+00 -5.00000000000e-01 -5.00000000000e-01 -1.00000000000e+00 -7.50000000000e-01 -5.00000000000e-01 -1.00000000000e+00 -1.00000000000e+00 -5.00000000000e-01 -1.00000000000e+00 -0.00000000000e+00 -7.50000000000e-01 -1.00000000000e+00 -2.50000000000e-01 -7.50000000000e-01 -1.00000000000e+00 -5.00000000000e-01 -7.50000000000e-01 -1.00000000000e+00 -7.50000000000e-01 -7.50000000000e-01 -1.00000000000e+00 -1.00000000000e+00 -7.50000000000e-01 -1.00000000000e+00 -0.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -2.50000000000e-01 -1.00000000000e+00 -1.00000000000e+00 -5.00000000000e-01 -1.00000000000e+00 -1.00000000000e+00 -7.50000000000e-01 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 - - - - - -0 -1 -5 -6 -25 -26 -30 -31 -1 -2 -6 -7 -26 -27 -31 -32 -2 -3 -7 -8 -27 -28 -32 -33 -3 -4 -8 -9 -28 -29 -33 -34 -5 -6 -10 -11 -30 -31 -35 -36 -6 -7 -11 -12 -31 -32 -36 -37 -7 -8 -12 -13 -32 -33 -37 -38 -8 -9 -13 -14 -33 -34 -38 -39 -10 -11 -15 -16 -35 -36 -40 -41 -11 -12 -16 -17 -36 -37 -41 -42 -12 -13 -17 -18 -37 -38 -42 -43 -13 -14 -18 -19 -38 -39 -43 -44 -15 -16 -20 -21 -40 -41 -45 -46 -16 -17 -21 -22 -41 -42 -46 -47 -17 -18 -22 -23 -42 -43 -47 -48 -18 -19 -23 -24 -43 -44 -48 -49 -25 -26 -30 -31 -50 -51 -55 -56 -26 -27 -31 -32 -51 -52 -56 -57 -27 -28 -32 -33 -52 -53 -57 -58 -28 -29 -33 -34 -53 -54 -58 -59 -30 -31 -35 -36 -55 -56 -60 -61 -31 -32 -36 -37 -56 -57 -61 -62 -32 -33 -37 -38 -57 -58 -62 -63 -33 -34 -38 -39 -58 -59 -63 -64 -35 -36 -40 -41 -60 -61 -65 -66 -36 -37 -41 -42 -61 -62 -66 -67 -37 -38 -42 -43 -62 -63 -67 -68 -38 -39 -43 -44 -63 -64 -68 -69 -40 -41 -45 -46 -65 -66 -70 -71 -41 -42 -46 -47 -66 -67 -71 -72 -42 -43 -47 -48 -67 -68 -72 -73 -43 -44 -48 -49 -68 -69 -73 -74 -50 -51 -55 -56 -75 -76 -80 -81 -51 -52 -56 -57 -76 -77 -81 -82 -52 -53 -57 -58 -77 -78 -82 -83 -53 -54 -58 -59 -78 -79 -83 -84 -55 -56 -60 -61 -80 -81 -85 -86 -56 -57 -61 -62 -81 -82 -86 -87 -57 -58 -62 -63 -82 -83 -87 -88 -58 -59 -63 -64 -83 -84 -88 -89 -60 -61 -65 -66 -85 -86 -90 -91 -61 -62 -66 -67 -86 -87 -91 -92 -62 -63 -67 -68 -87 -88 -92 -93 -63 -64 -68 -69 -88 -89 -93 -94 -65 -66 -70 -71 -90 -91 -95 -96 -66 -67 -71 -72 -91 -92 -96 -97 -67 -68 -72 -73 -92 -93 -97 -98 -68 -69 -73 -74 -93 -94 -98 -99 -75 -76 -80 -81 -100 -101 -105 -106 -76 -77 -81 -82 -101 -102 -106 -107 -77 -78 -82 -83 -102 -103 -107 -108 -78 -79 -83 -84 -103 -104 -108 -109 -80 -81 -85 -86 -105 -106 -110 -111 -81 -82 -86 -87 -106 -107 -111 -112 -82 -83 -87 -88 -107 -108 -112 -113 -83 -84 -88 -89 -108 -109 -113 -114 -85 -86 -90 -91 -110 -111 -115 -116 -86 -87 -91 -92 -111 -112 -116 -117 -87 -88 -92 -93 -112 -113 -117 -118 -88 -89 -93 -94 -113 -114 -118 -119 -90 -91 -95 -96 -115 -116 -120 -121 -91 -92 -96 -97 -116 -117 -121 -122 -92 -93 -97 -98 -117 -118 -122 -123 -93 -94 -98 -99 -118 -119 -123 -124 - - - -8 -16 -24 -32 -40 -48 -56 -64 -72 -80 -88 -96 -104 -112 -120 -128 -136 -144 -152 -160 -168 -176 -184 -192 -200 -208 -216 -224 -232 -240 -248 -256 -264 -272 -280 -288 -296 -304 -312 -320 -328 -336 -344 -352 -360 -368 -376 -384 -392 -400 -408 -416 -424 -432 -440 -448 -456 -464 -472 -480 -488 -496 -504 -512 - -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 -42 + +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 + + + + + + +""" + + def _single_subdomain_3d_cart_grid_vtu(self): + return """ + + + + + + +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +2.50000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +5.00000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +7.50000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +1.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +2.50000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +2.50000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +2.50000000000e-01 +0.00000000000e+00 +7.50000000000e-01 +2.50000000000e-01 +0.00000000000e+00 +1.00000000000e+00 +2.50000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +5.00000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +7.50000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +1.00000000000e+00 +5.00000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +7.50000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +7.50000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +7.50000000000e-01 +0.00000000000e+00 +7.50000000000e-01 +7.50000000000e-01 +0.00000000000e+00 +1.00000000000e+00 +7.50000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +1.00000000000e+00 +0.00000000000e+00 +2.50000000000e-01 +1.00000000000e+00 +0.00000000000e+00 +5.00000000000e-01 +1.00000000000e+00 +0.00000000000e+00 +7.50000000000e-01 +1.00000000000e+00 +0.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +2.50000000000e-01 +2.50000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +7.50000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +1.00000000000e+00 +0.00000000000e+00 +2.50000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +2.50000000000e-01 +2.50000000000e-01 +2.50000000000e-01 +2.50000000000e-01 +5.00000000000e-01 +2.50000000000e-01 +2.50000000000e-01 +7.50000000000e-01 +2.50000000000e-01 +2.50000000000e-01 +1.00000000000e+00 +2.50000000000e-01 +2.50000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +2.50000000000e-01 +2.50000000000e-01 +5.00000000000e-01 +2.50000000000e-01 +5.00000000000e-01 +5.00000000000e-01 +2.50000000000e-01 +7.50000000000e-01 +5.00000000000e-01 +2.50000000000e-01 +1.00000000000e+00 +5.00000000000e-01 +2.50000000000e-01 +0.00000000000e+00 +7.50000000000e-01 +2.50000000000e-01 +2.50000000000e-01 +7.50000000000e-01 +2.50000000000e-01 +5.00000000000e-01 +7.50000000000e-01 +2.50000000000e-01 +7.50000000000e-01 +7.50000000000e-01 +2.50000000000e-01 +1.00000000000e+00 +7.50000000000e-01 +2.50000000000e-01 +0.00000000000e+00 +1.00000000000e+00 +2.50000000000e-01 +2.50000000000e-01 +1.00000000000e+00 +2.50000000000e-01 +5.00000000000e-01 +1.00000000000e+00 +2.50000000000e-01 +7.50000000000e-01 +1.00000000000e+00 +2.50000000000e-01 +1.00000000000e+00 +1.00000000000e+00 +2.50000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +5.00000000000e-01 +2.50000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +7.50000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +1.00000000000e+00 +0.00000000000e+00 +5.00000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +5.00000000000e-01 +2.50000000000e-01 +2.50000000000e-01 +5.00000000000e-01 +5.00000000000e-01 +2.50000000000e-01 +5.00000000000e-01 +7.50000000000e-01 +2.50000000000e-01 +5.00000000000e-01 +1.00000000000e+00 +2.50000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +2.50000000000e-01 +5.00000000000e-01 +5.00000000000e-01 +5.00000000000e-01 +5.00000000000e-01 +5.00000000000e-01 +7.50000000000e-01 +5.00000000000e-01 +5.00000000000e-01 +1.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +7.50000000000e-01 +5.00000000000e-01 +2.50000000000e-01 +7.50000000000e-01 +5.00000000000e-01 +5.00000000000e-01 +7.50000000000e-01 +5.00000000000e-01 +7.50000000000e-01 +7.50000000000e-01 +5.00000000000e-01 +1.00000000000e+00 +7.50000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +1.00000000000e+00 +5.00000000000e-01 +2.50000000000e-01 +1.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +1.00000000000e+00 +5.00000000000e-01 +7.50000000000e-01 +1.00000000000e+00 +5.00000000000e-01 +1.00000000000e+00 +1.00000000000e+00 +5.00000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +7.50000000000e-01 +2.50000000000e-01 +0.00000000000e+00 +7.50000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +7.50000000000e-01 +7.50000000000e-01 +0.00000000000e+00 +7.50000000000e-01 +1.00000000000e+00 +0.00000000000e+00 +7.50000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +7.50000000000e-01 +2.50000000000e-01 +2.50000000000e-01 +7.50000000000e-01 +5.00000000000e-01 +2.50000000000e-01 +7.50000000000e-01 +7.50000000000e-01 +2.50000000000e-01 +7.50000000000e-01 +1.00000000000e+00 +2.50000000000e-01 +7.50000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +7.50000000000e-01 +2.50000000000e-01 +5.00000000000e-01 +7.50000000000e-01 +5.00000000000e-01 +5.00000000000e-01 +7.50000000000e-01 +7.50000000000e-01 +5.00000000000e-01 +7.50000000000e-01 +1.00000000000e+00 +5.00000000000e-01 +7.50000000000e-01 +0.00000000000e+00 +7.50000000000e-01 +7.50000000000e-01 +2.50000000000e-01 +7.50000000000e-01 +7.50000000000e-01 +5.00000000000e-01 +7.50000000000e-01 +7.50000000000e-01 +7.50000000000e-01 +7.50000000000e-01 +7.50000000000e-01 +1.00000000000e+00 +7.50000000000e-01 +7.50000000000e-01 +0.00000000000e+00 +1.00000000000e+00 +7.50000000000e-01 +2.50000000000e-01 +1.00000000000e+00 +7.50000000000e-01 +5.00000000000e-01 +1.00000000000e+00 +7.50000000000e-01 +7.50000000000e-01 +1.00000000000e+00 +7.50000000000e-01 +1.00000000000e+00 +1.00000000000e+00 +7.50000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +1.00000000000e+00 +2.50000000000e-01 +0.00000000000e+00 +1.00000000000e+00 +5.00000000000e-01 +0.00000000000e+00 +1.00000000000e+00 +7.50000000000e-01 +0.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +0.00000000000e+00 +1.00000000000e+00 +0.00000000000e+00 +2.50000000000e-01 +1.00000000000e+00 +2.50000000000e-01 +2.50000000000e-01 +1.00000000000e+00 +5.00000000000e-01 +2.50000000000e-01 +1.00000000000e+00 +7.50000000000e-01 +2.50000000000e-01 +1.00000000000e+00 +1.00000000000e+00 +2.50000000000e-01 +1.00000000000e+00 +0.00000000000e+00 +5.00000000000e-01 +1.00000000000e+00 +2.50000000000e-01 +5.00000000000e-01 +1.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +1.00000000000e+00 +7.50000000000e-01 +5.00000000000e-01 +1.00000000000e+00 +1.00000000000e+00 +5.00000000000e-01 +1.00000000000e+00 +0.00000000000e+00 +7.50000000000e-01 +1.00000000000e+00 +2.50000000000e-01 +7.50000000000e-01 +1.00000000000e+00 +5.00000000000e-01 +7.50000000000e-01 +1.00000000000e+00 +7.50000000000e-01 +7.50000000000e-01 +1.00000000000e+00 +1.00000000000e+00 +7.50000000000e-01 +1.00000000000e+00 +0.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +2.50000000000e-01 +1.00000000000e+00 +1.00000000000e+00 +5.00000000000e-01 +1.00000000000e+00 +1.00000000000e+00 +7.50000000000e-01 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 - -6 -4 -25 -30 -5 + + + 0 -4 -26 -31 -6 -1 -4 +5 +30 25 -0 1 -26 -4 -30 -5 -6 -31 -4 -0 -5 6 -1 -4 -25 -30 31 26 -6 -4 -26 -31 -32 -27 -4 1 6 -7 -2 -4 -31 -6 -7 -32 -4 -27 -32 -7 -2 -4 -26 31 -6 -1 -4 26 -1 2 -27 -6 -4 -27 -32 7 -2 -4 -28 -33 -8 -3 -4 -27 -2 -3 -28 -4 32 -7 -8 -33 -4 +27 2 7 -8 -3 -4 -27 32 -33 -28 -6 -4 -28 -33 -34 -29 -4 +27 3 8 -9 -4 -4 33 -8 -9 -34 -4 -29 -34 -9 -4 -4 28 -33 -8 3 -4 +8 +33 28 -3 4 +9 +34 29 -6 -4 -30 -35 -10 5 -4 -31 -36 -11 -6 -4 +10 +35 30 -5 6 -31 -4 -35 -10 -11 -36 -4 -5 -10 11 -6 -4 -30 -35 36 31 6 -4 -31 -6 -7 -32 -4 -31 -36 -37 -32 -4 -6 -11 -12 -7 -4 -32 -37 -12 -7 -4 -31 -36 11 -6 -4 36 -11 +31 +7 12 37 -6 -4 +32 7 12 -13 -8 -4 -32 37 -12 -7 -4 -33 -38 -13 -8 -4 32 -7 8 -33 -4 -37 -12 13 38 -4 -32 -37 -38 -33 -6 -4 33 -38 -39 -34 -4 8 13 -14 -9 -4 38 -13 -14 -39 -4 33 -8 9 -34 -4 -34 -39 14 -9 -4 -33 -38 -13 -8 -6 -4 -35 -40 -15 +39 +34 10 -4 -36 -41 -16 -11 -4 +15 +40 35 -10 11 -36 -4 -40 -15 16 41 -4 -10 -15 -16 -11 -4 -35 -40 -41 -36 -6 -4 36 -41 -42 -37 -4 11 16 -17 -12 -4 41 -16 -17 -42 -4 36 -11 12 -37 -4 -37 -42 17 -12 -4 -36 -41 -16 -11 -6 -4 -37 42 -17 -12 -4 -38 -43 -18 -13 -4 37 12 -13 -38 -4 -42 -17 -18 -43 -4 -12 17 -18 -13 -4 -37 42 -43 -38 -6 -4 -38 -43 -44 -39 -4 -13 -18 -19 -14 -4 -38 -13 -14 -39 -4 -39 -44 -19 -14 -4 -38 -43 -18 +37 13 -4 -43 18 -19 -44 -6 -4 -40 -45 -20 -15 -4 -41 -46 -21 -16 -4 -40 -15 -16 -41 -4 -45 -20 -21 -46 -4 +43 +38 +13 +18 +43 +38 +14 +19 +44 +39 15 20 -21 -16 -4 -40 45 +40 +16 +21 46 41 -6 -4 -42 -47 -22 -17 -4 -41 -46 -21 16 -4 +21 +46 41 -16 17 -42 -4 -46 -21 22 47 -4 -16 -21 -22 +42 17 -4 -41 -46 +22 47 42 -6 -4 -47 -22 +18 23 48 -4 -42 -17 -18 43 -4 -17 -22 -23 18 -4 -42 -47 -22 -17 -4 -43 -48 23 -18 -4 -42 -47 48 43 -6 -4 -43 -48 -49 -44 -4 -18 -23 -24 19 -4 -48 -23 24 49 -4 -43 -18 -19 -44 -4 44 -49 -24 -19 -4 -43 -48 -23 -18 -6 -4 -50 -55 -30 25 -4 -51 -56 -31 -26 -4 +30 +55 50 -25 26 -51 -4 -55 -30 -31 -56 -4 -25 -30 31 -26 -4 -50 -55 56 51 -6 -4 -51 -56 -57 -52 -4 26 31 -32 -27 -4 56 -31 -32 -57 -4 51 -26 27 -52 -4 -52 -57 32 -27 -4 -51 -56 -31 -26 -6 -4 -52 57 -32 -27 -4 -53 -58 -33 -28 -4 52 27 -28 -53 -4 -57 -32 -33 -58 -4 -27 32 -33 -28 -4 -52 57 +52 +28 +33 58 53 -6 -4 -53 -58 -59 -54 -4 28 33 -34 -29 -4 58 -33 -34 -59 -4 53 -28 29 -54 -4 -54 -59 34 -29 -4 -53 -58 -33 -28 -6 -4 -55 -60 -35 -30 -4 -56 -61 -36 -31 -4 -55 -30 -31 -56 -4 -60 -35 -36 -61 -4 +59 +54 30 35 -36 -31 -4 -55 -60 -61 -56 -6 -4 -56 -61 -62 -57 -4 -31 -36 -37 -32 -4 -61 -36 -37 -62 -4 -57 -62 -37 -32 -4 -56 -61 -36 +60 +55 31 -4 +36 +61 56 31 +36 +61 +56 32 -57 -6 -4 -57 -62 37 -32 -4 -58 -63 -38 -33 -4 -57 -32 -33 -58 -4 62 -37 -38 -63 -4 +57 32 37 -38 -33 -4 -57 62 -63 -58 -6 -4 +57 33 38 -39 -34 -4 -58 63 -64 -59 -4 58 33 -34 -59 -4 -59 -64 -39 -34 -4 -58 -63 38 -33 -4 63 -38 +58 +34 39 64 -6 -4 -60 -65 -40 +59 35 -4 -61 -66 -41 -36 -4 +40 +65 60 -35 36 -61 -4 -65 -40 41 66 -4 -35 -40 -41 -36 -4 -60 -65 -66 61 -6 -4 -66 +36 41 -42 -67 -4 -61 66 -41 -36 -4 -62 -67 -42 -37 -4 61 -36 37 -62 -4 -36 -41 42 -37 -4 -61 -66 67 62 -6 -4 37 42 -43 -38 -4 -67 -42 -43 -68 -4 -62 -67 -68 -63 -4 -63 -68 -43 -38 -4 -62 67 -42 -37 -4 62 -37 -38 -63 -6 -4 -63 -68 -69 -64 -4 38 43 -44 -39 -4 68 -43 -44 -69 -4 -64 -69 -44 -39 -4 63 -68 -43 38 -4 +43 +68 63 -38 39 +44 +69 64 -6 -4 -65 -70 -45 40 -4 -66 -71 -46 -41 -4 +45 +70 65 -40 41 -66 -4 -70 -45 -46 -71 -4 -40 -45 46 -41 -4 -65 -70 71 66 -6 -4 -66 -71 -46 41 -4 -67 -72 -47 -42 -4 +46 +71 66 -41 42 -67 -4 -71 -46 -47 -72 -4 -41 -46 47 -42 -4 -66 -71 -72 -67 -6 -4 72 -47 -48 -73 -4 67 42 -43 -68 -4 -42 47 -48 -43 -4 -67 72 -47 -42 -4 -68 -73 -48 -43 -4 67 -72 -73 -68 -6 -4 43 48 -49 -44 -4 -73 -48 -49 -74 -4 -68 73 -74 -69 -4 -69 -74 -49 -44 -4 68 43 +48 +73 +68 44 +49 +74 69 -4 -68 -73 -48 -43 -6 -4 -75 -80 -55 50 -4 -76 -81 -56 -51 -4 +55 +80 75 -50 51 -76 -4 -80 -55 56 81 -4 -50 -55 -56 -51 -4 -75 -80 -81 -76 -6 -4 76 -81 -82 -77 -4 51 56 -57 -52 -4 81 -56 +76 +52 57 82 -4 77 -82 -57 -52 -4 -76 -81 -56 -51 -4 -76 -51 52 -77 -6 -4 -77 -82 57 -52 -4 -78 -83 -58 -53 -4 +82 77 -52 53 -78 -4 -82 -57 -58 -83 -4 -52 -57 58 -53 -4 -77 -82 83 78 -6 -4 53 58 -59 -54 -4 -78 -83 -84 -79 -4 83 -58 -59 -84 -4 78 -53 54 -79 -4 -79 -84 59 -54 -4 -78 -83 -58 -53 -6 -4 -80 -85 -60 +84 +79 55 -4 -81 -86 -61 -56 -4 +60 +85 80 -55 56 -81 -4 -85 -60 -61 -86 -4 -55 -60 61 -56 -4 -80 -85 86 81 -6 -4 -81 -86 -87 -82 -4 56 61 -62 -57 -4 -86 -61 -62 -87 -4 -82 -87 -62 -57 -4 -81 86 -61 -56 -4 81 -56 57 -82 -6 -4 -82 -87 62 -57 -4 -83 -88 -63 -58 -4 -82 -57 -58 -83 -4 87 -62 -63 -88 -4 +82 57 62 -63 -58 -4 -82 87 -88 -83 -6 -4 -88 -63 -64 -89 -4 +82 58 63 -64 -59 -4 -83 88 -89 -84 -4 83 58 -59 -84 -4 -83 -88 63 -58 -4 -84 -89 -64 -59 -6 -4 -85 -90 -65 -60 -4 -86 -91 -66 -61 -4 -85 -60 -61 -86 -4 -90 -65 -66 -91 -4 +88 +83 +59 +64 +89 +84 60 65 -66 -61 -4 -85 90 +85 +61 +66 91 86 -6 -4 -86 -91 -92 -87 -4 -86 -61 -62 -87 -4 61 66 -67 -62 -4 -86 91 -66 -61 -4 -87 -92 -67 +86 62 -4 -91 -66 67 92 -6 -4 87 -92 -67 62 -4 -88 -93 -68 -63 -4 +67 +92 87 -62 63 -88 -4 -92 -67 68 93 -4 -62 -67 -68 -63 -4 -87 -92 -93 -88 -6 -4 -93 -68 -69 -94 -4 88 -93 -94 -89 -4 63 68 -69 -64 -4 +93 88 -63 64 -89 -4 -88 -93 -68 -63 -4 -89 -94 69 -64 -6 -4 -90 -95 -70 +94 +89 65 -4 -91 -96 -71 -66 -4 +70 +95 90 -65 66 -91 -4 -95 -70 71 96 -4 -65 -70 -71 +91 66 -4 -90 -95 +71 96 91 -6 -4 -91 -96 +67 +72 97 92 -4 -66 -71 -72 67 -4 -96 -71 72 97 -4 +92 +68 +73 +98 +93 +68 +73 +98 +93 +69 +74 +99 +94 +75 +80 +105 +100 +76 +81 +106 +101 +76 +81 +106 +101 +77 +82 +107 +102 +77 +82 +107 +102 +78 +83 +108 +103 +78 +83 +108 +103 +79 +84 +109 +104 +80 +85 +110 +105 +81 +86 +111 +106 +81 +86 +111 +106 +82 +87 +112 +107 +82 +87 +112 +107 +83 +88 +113 +108 +83 +88 +113 +108 +84 +89 +114 +109 +85 +90 +115 +110 +86 91 -66 -67 +116 +111 +86 +91 +116 +111 +87 92 -4 +117 +112 +87 +92 +117 +112 +88 +93 +118 +113 +88 +93 +118 +113 +89 +94 +119 +114 +90 +95 +120 +115 91 96 -71 -66 -4 +121 +116 +91 +96 +121 +116 92 97 -72 -67 -6 -4 +122 +117 92 97 -72 -67 -4 +122 +117 93 98 -73 -68 -4 -92 -67 -68 +123 +118 93 -4 -97 -72 -73 98 -4 -67 +123 +118 +94 +99 +124 +119 + + + +8 +16 +24 +32 +40 +48 +56 +64 72 -73 -68 +80 +88 +96 +104 +112 +120 +128 +136 +144 +152 +160 +168 +176 +184 +192 +200 +208 +216 +224 +232 +240 +248 +256 +264 +272 +280 +288 +296 +304 +312 +320 +328 +336 +344 +352 +360 +368 +376 +384 +392 +400 +408 +416 +424 +432 +440 +448 +456 +464 +472 +480 +488 +496 +504 +512 + + + +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 + + + + + +0 +1 +2 +3 4 -92 -97 -98 -93 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 + + + +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 + + + +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 + + + +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 + + + +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 + + + +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 + + + + + + +""" + + def _single_subdomain_3d_polytop_grid_vtu(self): + return """ + + + + + + +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +3.33333333333e-01 +0.00000000000e+00 +0.00000000000e+00 +6.66666666667e-01 +0.00000000000e+00 +0.00000000000e+00 +1.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +5.00000000000e-01 +0.00000000000e+00 +3.33333333333e-01 +5.00000000000e-01 +0.00000000000e+00 +6.66666666667e-01 +5.00000000000e-01 +0.00000000000e+00 +1.00000000000e+00 +5.00000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +1.00000000000e+00 +0.00000000000e+00 +3.33333333333e-01 +1.00000000000e+00 +0.00000000000e+00 +6.66666666667e-01 +1.00000000000e+00 +0.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +3.33333333333e-01 +3.33333333333e-01 +0.00000000000e+00 +3.33333333333e-01 +6.66666666667e-01 +0.00000000000e+00 +3.33333333333e-01 +1.00000000000e+00 +0.00000000000e+00 +3.33333333333e-01 +0.00000000000e+00 +5.00000000000e-01 +3.33333333333e-01 +3.33333333333e-01 +5.00000000000e-01 +3.33333333333e-01 +6.66666666667e-01 +5.00000000000e-01 +3.33333333333e-01 +1.00000000000e+00 +5.00000000000e-01 +3.33333333333e-01 +0.00000000000e+00 +1.00000000000e+00 +3.33333333333e-01 +3.33333333333e-01 +1.00000000000e+00 +3.33333333333e-01 +6.66666666667e-01 +1.00000000000e+00 +3.33333333333e-01 +1.00000000000e+00 +1.00000000000e+00 +3.33333333333e-01 +0.00000000000e+00 +0.00000000000e+00 +6.66666666667e-01 +3.33333333333e-01 +0.00000000000e+00 +6.66666666667e-01 +6.66666666667e-01 +0.00000000000e+00 +6.66666666667e-01 +1.00000000000e+00 +0.00000000000e+00 +6.66666666667e-01 +0.00000000000e+00 +5.00000000000e-01 +6.66666666667e-01 +3.33333333333e-01 +5.00000000000e-01 +6.66666666667e-01 +6.66666666667e-01 +5.00000000000e-01 +6.66666666667e-01 +1.00000000000e+00 +5.00000000000e-01 +6.66666666667e-01 +0.00000000000e+00 +1.00000000000e+00 +6.66666666667e-01 +3.33333333333e-01 +1.00000000000e+00 +6.66666666667e-01 +6.66666666667e-01 +1.00000000000e+00 +6.66666666667e-01 +1.00000000000e+00 +1.00000000000e+00 +6.66666666667e-01 +0.00000000000e+00 +0.00000000000e+00 +1.00000000000e+00 +3.33333333333e-01 +0.00000000000e+00 +1.00000000000e+00 +6.66666666667e-01 +0.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +0.00000000000e+00 +1.00000000000e+00 +0.00000000000e+00 +5.00000000000e-01 +1.00000000000e+00 +3.33333333333e-01 +5.00000000000e-01 +1.00000000000e+00 +6.66666666667e-01 +5.00000000000e-01 +1.00000000000e+00 +1.00000000000e+00 +5.00000000000e-01 +1.00000000000e+00 +0.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +3.33333333333e-01 +1.00000000000e+00 +1.00000000000e+00 +6.66666666667e-01 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 + + + + + +16 +17 +20 +21 +28 +29 +32 +33 +2 +3 +5 6 +7 +9 +10 +11 +14 +15 +17 +18 +19 +21 +22 +23 +13 +14 +15 +17 +18 +19 +21 +22 +23 +25 +26 +27 +29 +30 +31 +33 +34 +35 +0 +1 +2 4 -93 -98 -99 -94 -4 -98 -73 -74 -99 -4 -68 -73 -74 -69 -4 -94 -99 -74 -69 -4 -93 -68 -69 -94 -4 -93 -98 -73 -68 +5 6 -4 -100 -105 -80 -75 -4 -101 -106 -81 -76 -4 -100 -75 -76 -101 -4 -105 -80 -81 -106 -4 -75 -80 -81 -76 -4 -100 -105 -106 -101 +8 +9 +12 +13 +14 +16 +17 +18 +20 +21 +24 +25 +28 +29 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 + + + +8 +24 +42 +62 +86 + + + +42 +42 +42 +42 +42 + + + 6 4 -106 -81 -82 -107 -4 -101 -106 -107 -102 -4 -76 -81 -82 -77 -4 -101 -76 -77 -102 -4 -102 -107 -82 -77 -4 -101 -106 -81 -76 -6 +16 +20 +32 +28 4 -102 -107 -82 -77 +17 +21 +33 +29 4 -103 -108 -83 -78 +16 +28 +29 +17 4 -102 -77 -78 -103 +20 +32 +33 +21 4 -107 -82 -83 -108 +16 +17 +21 +20 4 -77 -82 -83 -78 +28 +29 +33 +32 +14 4 -102 -107 -108 -103 +2 6 +18 +14 4 -78 -83 -84 -79 -4 -108 -83 -84 -109 +3 +7 +19 +15 4 -103 -108 -109 -104 +5 +9 +21 +17 4 -103 -78 -79 -104 +7 +11 +23 +19 4 -103 -108 -83 -78 +2 +14 +15 +3 4 -104 -109 -84 -79 +5 +17 +18 6 4 -105 -110 -85 -80 -4 -106 -111 -86 -81 -4 -105 -80 -81 -106 -4 -110 -85 -86 -111 +9 +21 +22 +10 4 -80 -85 -86 -81 +10 +22 +23 +11 4 -105 -110 -111 -106 +2 +3 +7 6 4 -106 -111 -112 -107 -4 -81 -86 -87 -82 -4 -111 -86 -87 -112 -4 -106 -81 -82 -107 -4 -106 -111 -86 -81 -4 -107 -112 -87 -82 +5 6 +10 +9 4 -107 -112 -87 -82 -4 -108 -113 -88 -83 +6 +7 +11 +10 4 -107 -82 -83 -108 +14 +15 +19 +18 4 -112 -87 -88 -113 +17 +18 +22 +21 4 -82 -87 -88 -83 +18 +19 +23 +22 +16 4 -107 -112 -113 -108 -6 +13 +17 +29 +25 4 -108 -113 -114 -109 +15 +19 +31 +27 4 -113 -88 -89 -114 +17 +21 +33 +29 4 -83 -88 -89 -84 +19 +23 +35 +31 4 -109 -114 -89 -84 +13 +25 +26 +14 4 -108 -113 -88 -83 +14 +26 +27 +15 4 -108 -83 -84 -109 -6 +21 +33 +34 +22 4 -110 -115 -90 -85 +22 +34 +35 +23 4 -111 -116 -91 -86 +13 +14 +18 +17 4 -110 -85 -86 -111 +14 +15 +19 +18 4 -115 -90 -91 -116 +17 +18 +22 +21 4 -85 -90 -91 -86 +18 +19 +23 +22 4 -110 -115 -116 -111 -6 +25 +26 +30 +29 4 -111 -116 -117 -112 +26 +27 +31 +30 4 -86 -91 -92 -87 +29 +30 +34 +33 4 -116 -91 -92 -117 +30 +31 +35 +34 +18 4 -111 -86 -87 -112 +0 4 -111 -116 -91 -86 +16 +12 4 -112 -117 -92 -87 +2 6 +18 +14 4 -112 -117 -92 -87 4 -113 -118 -93 -88 +8 +20 +16 4 -112 -87 -88 -113 +5 +9 +21 +17 4 -117 -92 -93 -118 +12 +16 +28 +24 4 -87 -92 -93 -88 +13 +17 +29 +25 4 -112 -117 -118 -113 +0 +12 +13 +1 +4 +1 +13 +14 +2 +4 +5 +17 +18 6 4 -113 -118 -119 -114 +8 +20 +21 +9 4 -118 -93 -94 -119 +12 +24 +25 +13 4 -88 -93 -94 -89 +16 +28 +29 +17 4 -114 -119 -94 -89 +0 +1 +5 4 -113 -118 -93 -88 4 -113 -88 -89 -114 +1 +2 6 +5 4 -115 -120 -95 -90 4 -116 -121 -96 -91 +5 +9 +8 4 -115 -90 -91 -116 +13 +14 +18 +17 +4 +16 +17 +21 +20 +4 +24 +25 +29 +28 +22 4 -120 -95 -96 -121 +24 +28 +40 +36 4 -90 -95 -96 -91 +27 +31 +43 +39 4 -115 -120 -121 -116 -6 +28 +32 +44 +40 4 -116 -121 -122 -117 +31 +35 +47 +43 4 -121 -96 -97 -122 +24 +36 +37 +25 4 -91 -96 -97 -92 +25 +37 +38 +26 4 -117 -122 -97 -92 +26 +38 +39 +27 4 -116 -121 -96 -91 +32 +44 +45 +33 4 -116 -91 -92 -117 -6 +33 +45 +46 +34 4 -117 -122 -97 -92 +34 +46 +47 +35 4 -118 -123 -98 -93 +24 +25 +29 +28 4 -117 -92 -93 -118 +25 +26 +30 +29 4 -122 -97 -98 -123 +26 +27 +31 +30 4 -92 -97 -98 -93 +28 +29 +33 +32 4 -117 -122 -123 -118 -6 +29 +30 +34 +33 4 -93 -98 -99 -94 +30 +31 +35 +34 4 -118 -123 -98 -93 +36 +37 +41 +40 4 -119 -124 -99 -94 +37 +38 +42 +41 4 -118 -93 -94 -119 +38 +39 +43 +42 4 -123 -98 -99 -124 +40 +41 +45 +44 4 -118 -123 -124 -119 +41 +42 +46 +45 +4 +42 +43 +47 +46 31 -62 -93 -124 -155 -186 -217 -248 -279 -310 -341 -372 -403 -434 -465 -496 -527 -558 -589 -620 -651 -682 -713 -744 -775 -806 -837 -868 -899 -930 -961 -992 -1023 -1054 -1085 -1116 -1147 -1178 -1209 -1240 -1271 -1302 -1333 -1364 -1395 -1426 -1457 -1488 -1519 -1550 -1581 -1612 -1643 -1674 -1705 -1736 -1767 -1798 -1829 -1860 -1891 -1922 -1953 -1984 - - - - - -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 +102 +183 +274 +385 - -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 + + + +3 +1 +2 +0 +4 + + + 3.00000000000e+00 3.00000000000e+00 3.00000000000e+00 3.00000000000e+00 3.00000000000e+00 - - - -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 + + + +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 +3.00000000000e+00 + + + 3 3 3 3 3 + + + +0 +0 +0 +0 +0 + + + +0 +0 +0 +0 +0 + + + + + + +""" + + def _mdg_1_grid_1_vtu(self): + return """ + + + + + + +1.00000000000e+00 +5.00000000000e-01 +-5.55111512313e-17 +7.50000000000e-01 +5.00000000000e-01 +-2.77555756156e-17 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +5.00000000000e-01 +2.77555756156e-17 +0.00000000000e+00 +5.00000000000e-01 +5.55111512313e-17 + + + + + +0 +1 +1 +2 +2 3 3 +4 + + + +2 +4 +6 +8 + + + 3 3 3 3 + + 0 -1 -2 -3 -4 +1 +2 +3 + + + +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 + + + +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 + + + +1 +1 +1 +1 + + + +1 +1 +1 +1 + + + +0 +0 +0 +0 + + + +0 +0 +0 +0 + + + + + + +""" + + def _mdg_1_grid_2_vtu(self): + return """ + + + + + + +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +2.50000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +5.00000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +7.50000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +1.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +2.50000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +2.50000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +2.50000000000e-01 +0.00000000000e+00 +7.50000000000e-01 +2.50000000000e-01 +0.00000000000e+00 +1.00000000000e+00 +2.50000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +5.00000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +5.00000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +7.50000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +7.50000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +1.00000000000e+00 +5.00000000000e-01 +0.00000000000e+00 +1.00000000000e+00 +5.00000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +7.50000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +7.50000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +7.50000000000e-01 +0.00000000000e+00 +7.50000000000e-01 +7.50000000000e-01 +0.00000000000e+00 +1.00000000000e+00 +7.50000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +1.00000000000e+00 +0.00000000000e+00 +2.50000000000e-01 +1.00000000000e+00 +0.00000000000e+00 +5.00000000000e-01 +1.00000000000e+00 +0.00000000000e+00 +7.50000000000e-01 +1.00000000000e+00 +0.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +0.00000000000e+00 + + + + + +0 5 6 +1 +1 +6 +7 +2 +2 7 8 +3 +3 +8 9 +4 +5 10 -11 12 -13 +6 +6 +12 +14 +7 +7 14 -15 16 -17 +8 +8 +16 18 -19 +9 +11 20 21 +13 +13 +21 +22 +15 +15 22 23 +17 +17 +23 24 +19 +20 25 26 +21 +21 +26 +27 +22 +22 27 28 +23 +23 +28 29 -30 -31 +24 + + + +4 +8 +12 +16 +20 +24 +28 32 -33 -34 -35 36 -37 -38 -39 40 -41 -42 -43 44 -45 -46 -47 48 -49 -50 -51 52 -53 -54 -55 56 -57 -58 -59 60 -61 -62 -63 +64 + + + +9 +9 +9 +9 +9 +9 +9 +9 +9 +9 +9 +9 +9 +9 +9 +9 + + + + + +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 + + + +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 + + + +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 + + + +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 + + + +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 + + + +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 + + + +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 @@ -9622,692 +6581,818 @@ def _single_subdomain_3d_cart_grid_vtu(self): """ - def _single_subdomain_3d_polytop_grid_vtu(self): + def _mdg_1_mortar_grid_vtu(self): return """ - + -0.00000000000e+00 -0.00000000000e+00 -0.00000000000e+00 -3.33333333333e-01 -0.00000000000e+00 -0.00000000000e+00 -6.66666666667e-01 -0.00000000000e+00 -0.00000000000e+00 -1.00000000000e+00 -0.00000000000e+00 -0.00000000000e+00 -0.00000000000e+00 -5.00000000000e-01 -0.00000000000e+00 -3.33333333333e-01 -5.00000000000e-01 -0.00000000000e+00 -6.66666666667e-01 -5.00000000000e-01 -0.00000000000e+00 -1.00000000000e+00 -5.00000000000e-01 -0.00000000000e+00 -0.00000000000e+00 -1.00000000000e+00 -0.00000000000e+00 -3.33333333333e-01 -1.00000000000e+00 -0.00000000000e+00 -6.66666666667e-01 -1.00000000000e+00 -0.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -0.00000000000e+00 -0.00000000000e+00 -0.00000000000e+00 -3.33333333333e-01 -3.33333333333e-01 -0.00000000000e+00 -3.33333333333e-01 -6.66666666667e-01 -0.00000000000e+00 -3.33333333333e-01 -1.00000000000e+00 -0.00000000000e+00 -3.33333333333e-01 -0.00000000000e+00 -5.00000000000e-01 -3.33333333333e-01 -3.33333333333e-01 -5.00000000000e-01 -3.33333333333e-01 -6.66666666667e-01 -5.00000000000e-01 -3.33333333333e-01 -1.00000000000e+00 -5.00000000000e-01 -3.33333333333e-01 -0.00000000000e+00 -1.00000000000e+00 -3.33333333333e-01 -3.33333333333e-01 -1.00000000000e+00 -3.33333333333e-01 -6.66666666667e-01 -1.00000000000e+00 -3.33333333333e-01 -1.00000000000e+00 -1.00000000000e+00 -3.33333333333e-01 -0.00000000000e+00 -0.00000000000e+00 -6.66666666667e-01 -3.33333333333e-01 -0.00000000000e+00 -6.66666666667e-01 -6.66666666667e-01 -0.00000000000e+00 -6.66666666667e-01 -1.00000000000e+00 -0.00000000000e+00 -6.66666666667e-01 -0.00000000000e+00 -5.00000000000e-01 -6.66666666667e-01 -3.33333333333e-01 -5.00000000000e-01 -6.66666666667e-01 -6.66666666667e-01 -5.00000000000e-01 -6.66666666667e-01 -1.00000000000e+00 -5.00000000000e-01 -6.66666666667e-01 -0.00000000000e+00 -1.00000000000e+00 -6.66666666667e-01 -3.33333333333e-01 -1.00000000000e+00 -6.66666666667e-01 -6.66666666667e-01 -1.00000000000e+00 -6.66666666667e-01 1.00000000000e+00 -1.00000000000e+00 -6.66666666667e-01 -0.00000000000e+00 -0.00000000000e+00 -1.00000000000e+00 -3.33333333333e-01 -0.00000000000e+00 -1.00000000000e+00 -6.66666666667e-01 -0.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -0.00000000000e+00 -1.00000000000e+00 -0.00000000000e+00 5.00000000000e-01 -1.00000000000e+00 -3.33333333333e-01 +-5.55111512313e-17 +7.50000000000e-01 5.00000000000e-01 -1.00000000000e+00 -6.66666666667e-01 +-2.77555756156e-17 5.00000000000e-01 -1.00000000000e+00 -1.00000000000e+00 5.00000000000e-01 -1.00000000000e+00 0.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -3.33333333333e-01 -1.00000000000e+00 -1.00000000000e+00 -6.66666666667e-01 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 - - - - - -0 -1 -2 -4 -5 -6 -8 -9 -12 -13 -14 -16 -17 -18 -20 -21 -24 -25 -28 -29 -2 -3 -5 -6 -7 -9 -10 -11 -14 -15 -17 -18 -19 -21 -22 -23 -13 -14 -15 -17 -18 -19 -21 -22 -23 -25 -26 -27 -29 -30 -31 -33 -34 -35 -16 -17 -20 -21 -28 -29 -32 -33 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 +2.50000000000e-01 +5.00000000000e-01 +2.77555756156e-17 +0.00000000000e+00 +5.00000000000e-01 +5.55111512313e-17 +1.00000000000e+00 +5.00000000000e-01 +-5.55111512313e-17 +7.50000000000e-01 +5.00000000000e-01 +-2.77555756156e-17 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +5.00000000000e-01 +2.77555756156e-17 +0.00000000000e+00 +5.00000000000e-01 +5.55111512313e-17 + + + + + +0 +1 +1 +2 +2 +3 +3 +4 +5 +6 +6 +7 +7 +8 +8 +9 -20 -36 -54 -62 -86 +2 +4 +6 +8 +10 +12 +14 +16 -42 -42 -42 -42 -42 +3 +3 +3 +3 +3 +3 +3 +3 - -18 -4 -12 -16 -4 + + + 0 -4 -24 -28 -29 -25 -4 -16 -20 -21 -17 -4 -13 -17 -18 -14 -4 -4 -8 -9 -5 -4 1 -5 -6 2 -4 -28 -16 -17 -29 -4 -24 -12 -13 -25 -4 -20 -8 -9 -21 -4 -0 +3 4 5 +6 +7 + + + 1 -4 -13 1 -2 -14 -4 -12 +1 +1 +1 +1 +1 +1 + + + +0 +0 +0 +0 +0 +0 +0 0 + + + +1 +1 +1 +1 +1 +1 +1 +1 + + + +1 +1 +1 1 -13 -4 -25 -29 -17 -13 -4 -24 -28 -16 -12 -4 -17 -21 -9 -5 -4 -16 -20 -8 -4 -4 -14 -18 -6 2 -4 -17 -5 -6 -18 -14 -4 2 -6 -7 -3 -4 -18 -22 -23 -19 -4 -17 -21 -22 -18 +2 +2 + + + + + + +""" + + def _mdg_2_grid_1_vtu(self): + return """ + + + + + + +1.00000000000e+00 +5.00000000000e-01 +-5.55111512313e-17 +7.50000000000e-01 +5.00000000000e-01 +-2.77555756156e-17 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +5.00000000000e-01 +2.77555756156e-17 +0.00000000000e+00 +5.00000000000e-01 +5.55111512313e-17 +5.00000000000e-01 +7.50000000000e-01 +-2.77555756156e-17 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +2.50000000000e-01 +2.77555756156e-17 + + + + + +0 +1 +1 +2 +3 4 -14 -18 -19 -15 4 +5 6 -10 -11 7 -4 -5 +8 9 -10 -6 + + + +2 4 -22 +6 +8 10 -11 -23 -4 -15 -19 -7 +12 + + + 3 -4 -17 -5 -6 -18 -4 -14 +3 +3 +3 +3 +3 + + + + + +0 +1 2 3 -15 -4 -19 -23 -11 -7 -4 -17 -21 -9 -5 -4 -14 -18 +0 +1 + + + +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 + + + +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 + + + +1 +1 +1 +1 +1 +1 + + + +1 +1 +1 +1 +2 +2 + + + +0 +0 +0 +0 +0 +0 + + + +0 +0 +0 +0 +0 +0 + + + + + + +""" + + def _mdg_2_grid_2_vtu(self): + return """ + + + + + + +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +2.50000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +5.00000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +7.50000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +1.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +2.50000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +2.50000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +2.50000000000e-01 +0.00000000000e+00 +7.50000000000e-01 +2.50000000000e-01 +0.00000000000e+00 +1.00000000000e+00 +2.50000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +5.00000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +5.00000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +7.50000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +7.50000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +1.00000000000e+00 +5.00000000000e-01 +0.00000000000e+00 +1.00000000000e+00 +5.00000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +7.50000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +7.50000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +7.50000000000e-01 +0.00000000000e+00 +7.50000000000e-01 +7.50000000000e-01 +0.00000000000e+00 +1.00000000000e+00 +7.50000000000e-01 +0.00000000000e+00 +0.00000000000e+00 +1.00000000000e+00 +0.00000000000e+00 +2.50000000000e-01 +1.00000000000e+00 +0.00000000000e+00 +5.00000000000e-01 +1.00000000000e+00 +0.00000000000e+00 +7.50000000000e-01 +1.00000000000e+00 +0.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +0.00000000000e+00 + + + + + +0 +5 +6 +1 +1 6 +7 2 -4 -21 +2 +7 +8 +3 +3 +8 9 -10 -22 -16 4 +5 +10 +12 +6 +6 +12 14 -18 -19 +7 +7 15 -4 -30 -34 -35 -31 -4 -29 -33 -34 -30 -4 -26 -30 -31 -27 -4 -25 -29 -30 -26 -4 -18 -22 -23 -19 -4 -17 -21 -22 18 -4 -13 -17 +8 +8 18 -14 -4 -26 -14 -15 -27 -4 -33 -21 -22 -34 -4 -34 +20 +9 +11 22 23 -35 -4 -25 13 -14 -26 -4 -31 -35 +13 23 -19 -4 -29 -33 -21 +24 +16 17 -4 -27 -31 +24 +25 +19 19 -15 -4 25 -29 -17 -13 -6 -4 -28 -32 -33 -29 -4 -32 -20 -21 -33 -4 -16 -20 -21 -17 -4 -29 -33 +26 21 -17 -4 +22 +27 28 -32 -20 -16 -4 +23 +23 28 -16 -17 29 -22 -4 -25 +24 +24 29 30 -26 -4 -38 -42 -43 -39 -4 -37 -41 -42 -38 -4 -36 -40 -41 -37 -4 +25 +25 30 -34 -35 31 -4 -40 -44 -45 -41 -4 -29 -33 -34 -30 -4 -28 -32 -33 -29 -4 26 -30 -31 -27 + + + 4 +8 +12 +16 +20 24 28 -29 -25 -4 -36 -24 -25 -37 -4 -45 -33 -34 -46 -4 -44 -32 -33 -45 -4 -38 -26 -27 -39 -4 -37 -25 -26 -38 -4 -43 -47 -35 -31 -4 -40 -44 32 -28 -4 -39 -43 -31 -27 -4 36 40 -28 -24 -4 -41 -45 -46 -42 -4 -46 -34 -35 -47 +44 +48 +52 +56 +60 +64 + + + +9 +9 +9 +9 +9 +9 +9 +9 +9 +9 +9 +9 +9 +9 +9 +9 + + + + + +0 +1 +2 +3 4 -42 -46 -47 -43 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 - -91 -162 -243 -274 -385 + +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 + + + +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 + + + +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 + + + +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 + + + +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 + + + +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 + + + + + + +""" + + def _mdg_2_mortar_grid_1_vtu(self): + return """ + + + + + + +1.00000000000e+00 +5.00000000000e-01 +-5.55111512313e-17 +7.50000000000e-01 +5.00000000000e-01 +-2.77555756156e-17 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +5.00000000000e-01 +2.77555756156e-17 +0.00000000000e+00 +5.00000000000e-01 +5.55111512313e-17 +1.00000000000e+00 +5.00000000000e-01 +-5.55111512313e-17 +7.50000000000e-01 +5.00000000000e-01 +-2.77555756156e-17 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +2.50000000000e-01 +5.00000000000e-01 +2.77555756156e-17 +0.00000000000e+00 +5.00000000000e-01 +5.55111512313e-17 +5.00000000000e-01 +7.50000000000e-01 +-2.77555756156e-17 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +2.50000000000e-01 +2.77555756156e-17 +5.00000000000e-01 +7.50000000000e-01 +-2.77555756156e-17 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +2.50000000000e-01 +2.77555756156e-17 - - - -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 + + + +0 +1 +1 +2 +3 +4 +4 +5 +6 +7 +7 +8 +9 +10 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 - -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 -3.00000000000e+00 + +2 +4 +6 +8 +10 +12 +14 +16 +18 +20 +22 +24 - + +3 +3 +3 +3 +3 +3 +3 3 3 3 @@ -10315,12 +7400,111 @@ def _single_subdomain_3d_polytop_grid_vtu(self): 3 + + 0 1 2 3 4 +5 +6 +7 +0 +1 +2 +3 + + + +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 + + + +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 + + + +0 +0 +0 +0 +0 +0 +0 +0 +1 +1 +1 +1 + + + +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 + + + +1 +1 +1 +1 +2 +2 +2 +2 +1 +1 +2 +2 + + + +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 @@ -10329,12 +7513,12 @@ def _single_subdomain_3d_polytop_grid_vtu(self): """ - def _mdg_1_grid_1_vtu(self): + def _mdg_3_grid_1_vtu(self): return """ - + 1.00000000000e+00 @@ -10346,12 +7530,27 @@ def _mdg_1_grid_1_vtu(self): 5.00000000000e-01 5.00000000000e-01 0.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 2.50000000000e-01 5.00000000000e-01 2.77555756156e-17 0.00000000000e+00 5.00000000000e-01 5.55111512313e-17 +5.00000000000e-01 +7.50000000000e-01 +-2.77555756156e-17 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +2.50000000000e-01 +2.77555756156e-17 @@ -10361,10 +7560,14 @@ def _mdg_1_grid_1_vtu(self): 1 1 2 -2 -3 3 4 +4 +5 +6 +7 +8 +9 @@ -10372,6 +7575,8 @@ def _mdg_1_grid_1_vtu(self): 4 6 8 +10 +12 @@ -10379,15 +7584,28 @@ def _mdg_1_grid_1_vtu(self): 3 3 3 +3 +3 + +0 +1 +2 +3 +0 +1 + + 1.00000000000e+00 1.00000000000e+00 1.00000000000e+00 1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 @@ -10403,6 +7621,12 @@ def _mdg_1_grid_1_vtu(self): 1.00000000000e+00 1.00000000000e+00 1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 @@ -10410,6 +7634,8 @@ def _mdg_1_grid_1_vtu(self): 1 1 1 +1 +1 @@ -10417,6 +7643,8 @@ def _mdg_1_grid_1_vtu(self): 1 1 1 +2 +2 @@ -10424,6 +7652,8 @@ def _mdg_1_grid_1_vtu(self): 0 0 0 +0 +0 @@ -10431,13 +7661,8 @@ def _mdg_1_grid_1_vtu(self): 0 0 0 - - - 0 -1 -2 -3 +0 @@ -10446,12 +7671,12 @@ def _mdg_1_grid_1_vtu(self): """ - def _mdg_1_grid_2_vtu(self): + def _mdg_3_grid_2_vtu(self): return """ - + 0.00000000000e+00 @@ -10502,6 +7727,12 @@ def _mdg_1_grid_2_vtu(self): 5.00000000000e-01 5.00000000000e-01 0.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 7.50000000000e-01 5.00000000000e-01 0.00000000000e+00 @@ -10561,10 +7792,10 @@ def _mdg_1_grid_2_vtu(self): 7 8 3 +3 8 9 4 -3 5 10 12 @@ -10573,46 +7804,46 @@ def _mdg_1_grid_2_vtu(self): 12 14 7 -14 -16 -8 7 +15 +18 +8 8 -9 18 -16 -11 20 -21 +9 +11 +22 +23 13 13 -21 -22 -15 -15 -22 23 +24 +16 17 -23 24 +25 +19 19 -17 -20 25 26 21 -26 -27 -22 -21 22 27 28 23 23 +28 +29 +24 24 29 -28 +30 +25 +25 +30 +31 +26 @@ -10655,23 +7886,74 @@ def _mdg_1_grid_2_vtu(self): - -2.00000000000e+00 -2.00000000000e+00 -2.00000000000e+00 -2.00000000000e+00 -2.00000000000e+00 -2.00000000000e+00 -2.00000000000e+00 -2.00000000000e+00 -2.00000000000e+00 -2.00000000000e+00 -2.00000000000e+00 -2.00000000000e+00 -2.00000000000e+00 -2.00000000000e+00 -2.00000000000e+00 -2.00000000000e+00 + +1.25000000000e-01 +1.25000000000e-01 +0.00000000000e+00 +3.75000000000e-01 +1.25000000000e-01 +0.00000000000e+00 +6.25000000000e-01 +1.25000000000e-01 +0.00000000000e+00 +8.75000000000e-01 +1.25000000000e-01 +0.00000000000e+00 +1.25000000000e-01 +3.75000000000e-01 +0.00000000000e+00 +3.75000000000e-01 +3.75000000000e-01 +0.00000000000e+00 +6.25000000000e-01 +3.75000000000e-01 +0.00000000000e+00 +8.75000000000e-01 +3.75000000000e-01 +0.00000000000e+00 +1.25000000000e-01 +6.25000000000e-01 +0.00000000000e+00 +3.75000000000e-01 +6.25000000000e-01 +0.00000000000e+00 +6.25000000000e-01 +6.25000000000e-01 +0.00000000000e+00 +8.75000000000e-01 +6.25000000000e-01 +0.00000000000e+00 +1.25000000000e-01 +8.75000000000e-01 +0.00000000000e+00 +3.75000000000e-01 +8.75000000000e-01 +0.00000000000e+00 +6.25000000000e-01 +8.75000000000e-01 +0.00000000000e+00 +8.75000000000e-01 +8.75000000000e-01 +0.00000000000e+00 + + + +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 @@ -10800,25 +8082,6 @@ def _mdg_1_grid_2_vtu(self): 0 0 - - -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 - @@ -10826,12 +8089,12 @@ def _mdg_1_grid_2_vtu(self): """ - def _mdg_1_mortar_grid_vtu(self): + def _mdg_3_mortar_grid_1_vtu(self): return """ - + 1.00000000000e+00 @@ -10843,6 +8106,9 @@ def _mdg_1_mortar_grid_vtu(self): 5.00000000000e-01 5.00000000000e-01 0.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 2.50000000000e-01 5.00000000000e-01 2.77555756156e-17 @@ -10858,133 +8124,17 @@ def _mdg_1_mortar_grid_vtu(self): 5.00000000000e-01 5.00000000000e-01 0.00000000000e+00 +5.00000000000e-01 +5.00000000000e-01 +0.00000000000e+00 2.50000000000e-01 5.00000000000e-01 2.77555756156e-17 0.00000000000e+00 5.00000000000e-01 5.55111512313e-17 - - - - - -0 -1 -1 -2 -2 -3 -3 -4 -5 -6 -6 -7 -7 -8 -8 -9 - - - -2 -4 -6 -8 -10 -12 -14 -16 - - - -3 -3 -3 -3 -3 -3 -3 -3 - - - - - -1 -1 -1 -1 -1 -1 -1 -1 - - - -0 -0 -0 -0 -0 -0 -0 -0 - - - -1 -1 -1 -1 -1 -1 -1 -1 - - - -1 -1 -1 -1 -2 -2 -2 -2 - - - -0 -1 -2 -3 -4 -5 -6 -7 - - - - - - -""" - - def _mdg_2_grid_1_vtu(self): - return """ - - - - - - -1.00000000000e+00 5.00000000000e-01 --5.55111512313e-17 7.50000000000e-01 -5.00000000000e-01 -2.77555756156e-17 5.00000000000e-01 5.00000000000e-01 @@ -10992,12 +8142,9 @@ def _mdg_2_grid_1_vtu(self): 5.00000000000e-01 5.00000000000e-01 0.00000000000e+00 -2.50000000000e-01 5.00000000000e-01 +2.50000000000e-01 2.77555756156e-17 -0.00000000000e+00 -5.00000000000e-01 -5.55111512313e-17 5.00000000000e-01 7.50000000000e-01 -2.77555756156e-17 @@ -11025,8 +8172,20 @@ def _mdg_2_grid_1_vtu(self): 5 6 7 +7 8 9 +10 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 @@ -11036,6 +8195,12 @@ def _mdg_2_grid_1_vtu(self): 8 10 12 +14 +16 +18 +20 +22 +24 @@ -11045,38 +8210,29 @@ def _mdg_2_grid_1_vtu(self): 3 3 3 +3 +3 +3 +3 +3 +3 - -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 - - - -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 -1.00000000000e+00 + +0 +1 +2 +3 +4 +5 +6 +7 +0 +1 +2 +3 @@ -11086,42 +8242,72 @@ def _mdg_2_grid_1_vtu(self): 1 1 1 - - - 1 1 1 1 -2 -2 +1 +1 - -0 -0 -0 -0 + 0 0 - - - 0 0 0 0 0 0 +1 +1 +1 +1 - -0 + +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 + + + +1 +1 +1 1 2 -3 -4 -5 +2 +2 +2 +1 +1 +2 +2 + + + +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 @@ -11130,105 +8316,57 @@ def _mdg_2_grid_1_vtu(self): """ - def _mdg_2_grid_2_vtu(self): + def _nonconstant_data_grid_vtu(self): return """ - + 0.00000000000e+00 0.00000000000e+00 0.00000000000e+00 -2.50000000000e-01 -0.00000000000e+00 -0.00000000000e+00 -5.00000000000e-01 -0.00000000000e+00 -0.00000000000e+00 -7.50000000000e-01 -0.00000000000e+00 -0.00000000000e+00 -1.00000000000e+00 -0.00000000000e+00 -0.00000000000e+00 -0.00000000000e+00 -2.50000000000e-01 +3.33333333333e-01 0.00000000000e+00 -2.50000000000e-01 -2.50000000000e-01 0.00000000000e+00 -5.00000000000e-01 -2.50000000000e-01 +6.66666666667e-01 0.00000000000e+00 -7.50000000000e-01 -2.50000000000e-01 0.00000000000e+00 1.00000000000e+00 -2.50000000000e-01 -0.00000000000e+00 -0.00000000000e+00 -5.00000000000e-01 -0.00000000000e+00 -0.00000000000e+00 -5.00000000000e-01 -0.00000000000e+00 -2.50000000000e-01 -5.00000000000e-01 -0.00000000000e+00 -2.50000000000e-01 -5.00000000000e-01 -0.00000000000e+00 -5.00000000000e-01 -5.00000000000e-01 -0.00000000000e+00 -5.00000000000e-01 -5.00000000000e-01 0.00000000000e+00 -5.00000000000e-01 -5.00000000000e-01 0.00000000000e+00 -5.00000000000e-01 -5.00000000000e-01 0.00000000000e+00 -7.50000000000e-01 -5.00000000000e-01 +3.33333333333e-01 0.00000000000e+00 -7.50000000000e-01 -5.00000000000e-01 +3.33333333333e-01 +3.33333333333e-01 0.00000000000e+00 -1.00000000000e+00 -5.00000000000e-01 +6.66666666667e-01 +3.33333333333e-01 0.00000000000e+00 1.00000000000e+00 -5.00000000000e-01 -0.00000000000e+00 +3.33333333333e-01 0.00000000000e+00 -7.50000000000e-01 0.00000000000e+00 -2.50000000000e-01 -7.50000000000e-01 +6.66666666667e-01 0.00000000000e+00 -5.00000000000e-01 -7.50000000000e-01 +3.33333333333e-01 +6.66666666667e-01 0.00000000000e+00 -7.50000000000e-01 -7.50000000000e-01 +6.66666666667e-01 +6.66666666667e-01 0.00000000000e+00 1.00000000000e+00 -7.50000000000e-01 -0.00000000000e+00 +6.66666666667e-01 0.00000000000e+00 -1.00000000000e+00 0.00000000000e+00 -2.50000000000e-01 1.00000000000e+00 0.00000000000e+00 -5.00000000000e-01 +3.33333333333e-01 1.00000000000e+00 0.00000000000e+00 -7.50000000000e-01 +6.66666666667e-01 1.00000000000e+00 0.00000000000e+00 1.00000000000e+00 @@ -11240,107 +8378,101 @@ def _mdg_2_grid_2_vtu(self): 0 +1 +5 +0 +4 5 -6 1 +2 +6 1 +5 6 -7 2 +3 +7 2 +6 7 -8 -3 -8 -9 4 -3 5 -10 -12 -6 -6 -12 -14 -7 -15 -18 -8 -7 -8 9 -20 -18 -11 -22 -23 -13 -13 -23 -24 -16 -17 -24 -25 -19 -25 -26 -21 -19 -22 -27 -28 -23 -28 -29 -24 -23 -24 -29 -30 -25 -25 -26 -31 -30 +4 +8 +9 +5 +6 +10 +5 +9 +10 +6 +7 +11 +6 +10 +11 +8 +9 +13 +8 +12 +13 +9 +10 +14 +9 +13 +14 +10 +11 +15 +10 +14 +15 -4 -8 +3 +6 +9 12 -16 -20 +15 +18 +21 24 -28 -32 +27 +30 +33 36 -40 -44 +39 +42 +45 48 -52 -56 -60 -64 +51 +54 -9 -9 -9 -9 -9 -9 -9 -9 -9 -9 -9 -9 -9 -9 -9 -9 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 @@ -11362,6 +8494,8 @@ def _mdg_2_grid_2_vtu(self): 2.00000000000e+00 2.00000000000e+00 2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 @@ -11413,101 +8547,12 @@ def _mdg_2_grid_2_vtu(self): 2.00000000000e+00 2.00000000000e+00 2.00000000000e+00 - - - -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 - - - -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 - - - -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 - - - -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 -0 - - - -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 +2.00000000000e+00 @@ -11516,74 +8561,62 @@ def _mdg_2_grid_2_vtu(self): """ - def _mdg_2_mortar_grid_1_vtu(self): + def _constant_data_grid_vtu(self): return """ - + +0.00000000000e+00 +0.00000000000e+00 +0.00000000000e+00 +3.33333333333e-01 +0.00000000000e+00 +0.00000000000e+00 +6.66666666667e-01 +0.00000000000e+00 +0.00000000000e+00 1.00000000000e+00 -5.00000000000e-01 --5.55111512313e-17 -7.50000000000e-01 -5.00000000000e-01 --2.77555756156e-17 -5.00000000000e-01 -5.00000000000e-01 0.00000000000e+00 -5.00000000000e-01 -5.00000000000e-01 0.00000000000e+00 -2.50000000000e-01 -5.00000000000e-01 -2.77555756156e-17 0.00000000000e+00 -5.00000000000e-01 -5.55111512313e-17 +3.33333333333e-01 +0.00000000000e+00 +3.33333333333e-01 +3.33333333333e-01 +0.00000000000e+00 +6.66666666667e-01 +3.33333333333e-01 +0.00000000000e+00 1.00000000000e+00 -5.00000000000e-01 --5.55111512313e-17 -7.50000000000e-01 -5.00000000000e-01 --2.77555756156e-17 -5.00000000000e-01 -5.00000000000e-01 +3.33333333333e-01 0.00000000000e+00 -5.00000000000e-01 -5.00000000000e-01 0.00000000000e+00 -2.50000000000e-01 -5.00000000000e-01 -2.77555756156e-17 +6.66666666667e-01 0.00000000000e+00 -5.00000000000e-01 -5.55111512313e-17 -5.00000000000e-01 -7.50000000000e-01 --2.77555756156e-17 -5.00000000000e-01 -5.00000000000e-01 +3.33333333333e-01 +6.66666666667e-01 +0.00000000000e+00 +6.66666666667e-01 +6.66666666667e-01 +0.00000000000e+00 +1.00000000000e+00 +6.66666666667e-01 0.00000000000e+00 -5.00000000000e-01 -5.00000000000e-01 0.00000000000e+00 -5.00000000000e-01 -2.50000000000e-01 -2.77555756156e-17 -5.00000000000e-01 -7.50000000000e-01 --2.77555756156e-17 -5.00000000000e-01 -5.00000000000e-01 +1.00000000000e+00 0.00000000000e+00 -5.00000000000e-01 -5.00000000000e-01 +3.33333333333e-01 +1.00000000000e+00 +0.00000000000e+00 +6.66666666667e-01 +1.00000000000e+00 +0.00000000000e+00 +1.00000000000e+00 +1.00000000000e+00 0.00000000000e+00 -5.00000000000e-01 -2.50000000000e-01 -2.77555756156e-17 @@ -11591,108 +8624,214 @@ def _mdg_2_mortar_grid_1_vtu(self): 0 1 +5 +0 +4 +5 1 2 -3 -4 -4 +6 +1 5 6 +2 +3 7 +2 +6 7 +4 +5 +9 +4 8 9 +5 +6 +10 +5 +9 10 +6 +7 +11 +6 10 11 +8 +9 +13 +8 12 13 +9 +10 +14 +9 +13 +14 +10 +11 +15 +10 14 15 -16 -17 -18 -19 -2 -4 +3 6 -8 -10 +9 12 -14 -16 +15 18 -20 -22 +21 24 +27 +30 +33 +36 +39 +42 +45 +48 +51 +54 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 - -0.00000000000e+00 -0.00000000000e+00 -0.00000000000e+00 -0.00000000000e+00 -0.00000000000e+00 -0.00000000000e+00 + +2.22222222222e-01 +1.11111111111e-01 0.00000000000e+00 +1.11111111111e-01 +2.22222222222e-01 0.00000000000e+00 +5.55555555556e-01 +1.11111111111e-01 0.00000000000e+00 +4.44444444444e-01 +2.22222222222e-01 0.00000000000e+00 +8.88888888889e-01 +1.11111111111e-01 0.00000000000e+00 +7.77777777778e-01 +2.22222222222e-01 0.00000000000e+00 - - - +2.22222222222e-01 +4.44444444444e-01 0.00000000000e+00 +1.11111111111e-01 +5.55555555556e-01 0.00000000000e+00 +5.55555555556e-01 +4.44444444444e-01 0.00000000000e+00 +4.44444444444e-01 +5.55555555556e-01 0.00000000000e+00 +8.88888888889e-01 +4.44444444444e-01 0.00000000000e+00 +7.77777777778e-01 +5.55555555556e-01 0.00000000000e+00 +2.22222222222e-01 +7.77777777778e-01 0.00000000000e+00 +1.11111111111e-01 +8.88888888889e-01 0.00000000000e+00 +5.55555555556e-01 +7.77777777778e-01 0.00000000000e+00 +4.44444444444e-01 +8.88888888889e-01 0.00000000000e+00 +8.88888888889e-01 +7.77777777778e-01 0.00000000000e+00 +7.77777777778e-01 +8.88888888889e-01 0.00000000000e+00 - -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 + +0 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 - + +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 + + + +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 0 0 0 @@ -11701,55 +8840,27 @@ def _mdg_2_mortar_grid_1_vtu(self): 0 0 0 -1 -1 -1 -1 - - - -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2 -2 -2 -1 -1 -2 -2 - - - 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 diff --git a/tutorials/exporter.ipynb b/tutorials/exporter.ipynb new file mode 100644 index 0000000000..6fde366084 --- /dev/null +++ b/tutorials/exporter.ipynb @@ -0,0 +1,386 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Introduction\n", + "Currently, the standard procedure within PorePy is to export data to vtu and pvd format for visualization with ParaView. This tutorial explains how to use the `Exporter`. In particular, it will showcase different ways to address data, how constant-in-time data is handled, and how pvd-files are managed. \n", + "\n", + "First, an example data set is defined, then the actual exporter is defined, before all supported ways to export data are demonstrated.\n", + "\n", + "NOTE: Related but not necessary for this tutorial: it is highly recommended to read the ParaView documentation. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example contact mechanics model for a mixed-dimensional geometry\n", + "In order to illustrate the capability and explain the use of the Exporter, we consider a ContactMechanicsBiot model for a two-dimensional fractured geometry. The mixed-dimensional geometry consists of a 2D square and two crossing 1D fractures." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import porepy as pp\n", + "from porepy.grids.standard_grids.grid_buckets_2d import two_intersecting\n", + "\n", + "class BiotFractured(pp.ContactMechanicsBiot):\n", + "\n", + " def create_grid(self) -> None:\n", + " # Define domain\n", + " self.gb, self.box = two_intersecting()\n", + "\n", + " pp.contact_conditions.set_projections(self.gb)\n", + "\n", + "params = {\"use_ad\": True}\n", + "model = BiotFractured(params)\n", + "model.prepare_simulation()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The default data of the model is stored as pp.STATE in the grid bucket. Let's have a look:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Determine all keys of all states on all subdomains\n", + "subdomain_states = []\n", + "for g, d in model.gb.nodes():\n", + " subdomain_states += d[pp.STATE].keys()\n", + "# The key pp.ITERATE is no actual state.\n", + "subdomain_states = set(subdomain_states) - set([pp.ITERATE])\n", + "print(\"Keys of the states defined on subdomains:\", subdomain_states)\n", + "\n", + "# Determine all keys of all states on all interfaces\n", + "interface_states = []\n", + "for g, d in model.gb.edges():\n", + " interface_states += d[pp.STATE].keys()\n", + "# The key pp.ITERATE is no actual state.\n", + "interface_states = set(interface_states) - set([pp.ITERATE])\n", + "print(\"Keys of the states defined on interfaces:\", set(interface_states)) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Defining the exporter\n", + "Two arguments are required to define an object of type pp.Exporter: a grid bucket, and the target name of the output; optionally, one can add a directory name; and in fact, instead of a grid bucket, single grids can also be provided, see Example 7." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "exporter = pp.Exporter(model.gb, file_name=\"file\", folder_name=\"folder\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the following, we showcase how to use the main subroutines for exporting data:\n", + "- write_vtu()\n", + "- write_pvd()\n", + "\n", + "The first addresses the export of data for a specific time step, while the latter gathers the previous exports and collects them in a single file. This allows an easier analysis in ParaView." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 1: Exporting states\n", + "Data stored in the grid bucket under 'pp.STATE' can be simply exported by addressing their keys using the routine 'write_vtu()'. We define a dedicated exporter for this task." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "exporter_1 = pp.Exporter(model.gb, file_name=\"example-1\", folder_name=\"exporter-tutorial\")\n", + "exporter_1.write_vtu([\"p\", \"u\", \"mortar_p\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note, that here all available representation (i.e., on all dimensions) of the states will be exported." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 2: Exporting states on specified grids\n", + "Similar to Example 1, we will again export states by addressing their keys, but target only a subset of grids. For instance, we fetch the grids for the subdomains and interface.\n", + "\n", + "NOTE: For now, one has to make sure that subsets of the mixed-dimensional grid contain all grids of a particular dimension." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "grids_1d = model.gb.get_grids(lambda g: g.dim == 1).tolist()\n", + "grids_2d = model.gb.get_grids(lambda g: g.dim == 2).tolist()\n", + "edges_1d = [e for e, d in model.gb.edges() if d[\"mortar_grid\"].dim == 1]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And as a simple example extract the 2D subdomain:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "g_2d = grids_2d[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We export pressure on all 1D subdomains, displacements on all 2D subdomains, and the mortars on all interfaces. For this, we use tuples of grid(s) and keys. In order to not overwrite the previous data, we define a new exporter." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "exporter_2 = pp.Exporter(model.gb, \"example-2\", \"exporter-tutorial\")\n", + "exporter_2.write_vtu([(grids_1d, \"p\"), (grids_2d, \"u\"), (edges_1d, \"mortar_p\")])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 3: Exporting explicitly defined data\n", + "We can also export data which is not stored in the grid bucket under 'pp.STATE'. This capability requires defining tuples of (1) a single grid, (2) a key, and (3) the data vector. For example, let's export the cell centers of the 2D subdomain 'g_2d', as well as all interfaces (with different signs for the sake of the example). Again, we define a dedicated exporter for this task.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "subdomain_data = [(g_2d, \"cc\", g_2d.cell_centers)]\n", + "interface_data = [\n", + " (edges_1d[i], \"cc_e\", (-1) ** i * model.gb.edge_props(edges_1d[i])[\"mortar_grid\"].cell_centers)\n", + " for i in range(len(edges_1d))\n", + "]\n", + "\n", + "exporter_3 = pp.Exporter(model.gb, \"example-3\", \"exporter-tutorial\")\n", + "exporter_3.write_vtu(subdomain_data + interface_data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 4: Flexibility in the input arguments\n", + "The export allows for an arbitrary combination of all previous ways to export data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "exporter_4 = pp.Exporter(model.gb, \"example-4\", \"exporter-tutorial\")\n", + "exporter_4.write_vtu([(g_2d, \"cc\", g_2d.cell_centers), (grids_1d, \"p\"), \"u\"] + interface_data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 5: Exporting data in a time series\n", + "Data can also be exported in a time series, and the Exporter takes care of managing the file names. The user will only have to prescribe the time step number. Consider a time series consisting of 5 steps. For simplicity, we consider an analogous situation as in Example 1." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "exporter_5 = pp.Exporter(model.gb, \"example-5\", \"exporter-tutorial\")\n", + "for step in range(5):\n", + " # Data may change\n", + " exporter_5.write_vtu([\"p\", \"u\", \"mortar_p\"], time_step=step)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Alternatively, one can also let the Exporter internally manage the stepping and the appendix used when storing the data to file. This is triggered by the keyword 'time_dependent'." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "exporter_5 = pp.Exporter(model.gb, \"example-5\", \"exporter-tutorial\")\n", + "for step in range(5):\n", + " # Data may change\n", + " exporter_5.write_vtu([\"p\", \"u\", \"mortar_p\"], time_dependent=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 6: Exporting constant data\n", + "The export of both grid and geometry related data as well as heterogeneous material parameters may be of interest. However, these often do only change very seldomly in time or are even constant in time. In order to save storage space, constant data is stored separately. A multirate approach is used to address slowly changing \"constant\" data, which results in an extra set of output files. Every time constant data has to be updated (in a time series), the output files are updated as well. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "exporter_6_a = pp.Exporter(model.gb, \"example-6-a\", \"exporter-tutorial\")\n", + "# Define some 'constant' data\n", + "exporter_6_a.add_constant_data([(g_2d, \"cc\", g_2d.cell_centers)])\n", + "for step in range(5):\n", + " # Update the previously defined 'constant' data\n", + " if step == 2:\n", + " exporter_6_a.add_constant_data([(g_2d, \"cc\", -g_2d.cell_centers)])\n", + " # All constant data will be exported also if not specified\n", + " exporter_6_a.write_vtu([\"p\", \"u\", \"mortar_p\"], time_step=step)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The default is that constant data is always printed to extra files. Since the vtu format requires geometrical and topoligical information on the mesh (points, connectivity etc.), this type of constant data is exported to each vtu file. Depending on the situation, this overhead can be significant. Thus, one can also choose to print the constant data to the same files as the standard data, by setting a keyword when defining the exporter. With a similar setup as in part A, the same output is generated, but managed differently among files." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "exporter_6_b = pp.Exporter(model.gb, \"example-6-b\", \"exporter-tutorial\", export_constants_separately = False)\n", + "exporter_6_b.add_constant_data([(g_2d, \"cc\", g_2d.cell_centers)])\n", + "for step in range(5):\n", + " # Update the previously defined 'constant' data\n", + " if step == 2:\n", + " exporter_6_b.add_constant_data([(g_2d, \"cc\", -g_2d.cell_centers)])\n", + " # All constant data will be exported also if not specified\n", + " exporter_6_b.write_vtu([\"p\", \"u\", \"mortar_p\"], time_step=step)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 5 revisisted: PVD format\n", + "The pvd format collects previously exported data. At every application of 'write_vtu' a corresponding pvd file is generated, which gathers all 'vtu' files correpsonding to this time step. It is recommended to use the 'pvd' file for analyzing the data in ParaView.\n", + "\n", + "In addition, when considering a time series, it is possible to gather data connected to multiple time steps, and assign the actual time to each time step. Assume, Example 5 corresponds to an adaptive time stepping. We define the actual times, and collect the exported data from Example 5 in a single pvd file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "times_5 = [0., 0.1, 1., 2., 10.]\n", + "exporter_5.write_pvd(times_5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When providing no argument to write_pvd(), the time steps are used as actual times." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 7: Exporting data on a single grid\n", + "It is also possible to export data without prescribing a grid bucket, but a single grid. In this case, one has to assign the data when writing to vtu. For this, a key and a data array (with suitable size) have to be provided." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "exporter_7 = pp.Exporter(g_2d, \"example-7\", \"exporter-tutorial\")\n", + "exporter_7.write_vtu([(\"cc\", g_2d.cell_centers)])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}