Source code for ogstools.meshlib.boundary_set
# Copyright (c) 2012-2024, OpenGeoSys Community (http://www.opengeosys.org)
# Distributed under a Modified BSD License.
# See accompanying file LICENSE.txt or
# http://www.opengeosys.org/project/license
#
import tempfile
from abc import ABC, abstractmethod
from collections import namedtuple
from pathlib import Path
import pandas as pd
from ogstools.meshlib.boundary import Layer, LocationFrame, Raster
from ogstools.meshlib.boundary_subset import Surface
[docs]
class BoundarySet(ABC):
"""
Abstract base class representing a collection of boundaries with constraints.
A BoundarySet is composed of multiple boundaries linked with constraints:
- Free of gaps and overlaps.
- Distinguished by markers to identify different boundaries.
- Adherence to rules of `piecewise linear complex (PLC)`.
"""
[docs]
@abstractmethod
def bounds(self) -> list:
return []
[docs]
@abstractmethod
def filenames(self) -> list[Path]:
return []
[docs]
class LayerSet(BoundarySet):
"""
Collection of geological layers stacked to represent subsurface arrangements.
In a geological information system, multiple layers can be stacked vertically to
represent the subsurface arrangement. This class provides methods to manage and
work with layered geological data.
"""
[docs]
def __init__(self, layers: list[Layer]):
"""
Initializes a LayerSet. It checks if the list of provided layers are given in a top to bottom order.
In neighboring layers, layers share the same surface (upper bottom == low top).
"""
for upper, lower in zip(layers, layers[1:]):
if upper.bottom != lower.top:
msg = "Layerset is not consistent."
raise ValueError(msg)
self.layers = layers
[docs]
def bounds(self) -> list:
return list(self.layers[0].top.mesh.bounds)
[docs]
def filenames(self) -> list[Path]:
layer_filenames = [layer.bottom.filename for layer in self.layers]
layer_filenames.insert(0, self.layers[0].top.filename) # file interface
return layer_filenames
[docs]
@classmethod
def from_pandas(cls, df: pd.DataFrame) -> "LayerSet":
"""Create a LayerSet from a Pandas DataFrame."""
Row = namedtuple("Row", ["material_id", "mesh", "resolution"])
surfaces = [
Row(
material_id=surface._asdict()["material_id"],
mesh=surface._asdict()["filename"],
resolution=surface._asdict()["resolution"],
)
for surface in df.itertuples(index=False)
]
base_layer = [
Layer(
top=Surface(top.mesh, material_id=top.material_id),
bottom=Surface(bottom.mesh, material_id=bottom.material_id),
num_subdivisions=bottom.resolution,
)
for top, bottom in zip(surfaces, surfaces[1:])
]
return cls(layers=base_layer)
[docs]
def create_raster(self, resolution: float) -> tuple[Path, Path]:
"""
Create raster representations for the LayerSet.
This method generates raster files at a specified resolution for each layer's
top and bottom boundaries and returns paths to the raster files.
"""
bounds = self.layers[0].top.mesh.bounds
raster_set = self.create_rasters(resolution=resolution)
locFrame = LocationFrame(
xmin=bounds[0], xmax=bounds[1], ymin=bounds[2], ymax=bounds[3]
)
# Raster needs to be finer then asc files. Otherwise Mesh2Raster fails
raster = Raster(locFrame, resolution=resolution * 0.95)
raster_vtu = Path(tempfile.mkstemp(".vtu", "raster")[1])
raster.as_vtu(raster_vtu)
rastered_layers_txt = Path(
tempfile.mkstemp(".txt", "rastered_layers")[1]
)
with rastered_layers_txt.open("w") as file:
file.write("\n".join(str(item) for item in raster_set))
return raster_vtu, rastered_layers_txt
[docs]
def create_rasters(self, resolution: float) -> list[Path]:
"""
For each surface a (temporary) raster file with given resolution is created.
"""
rasters = [self.layers[0].top.create_raster_file(resolution=resolution)]
for layer in self.layers:
r = layer.create_raster(resolution=resolution)
rasters.extend(r[1:])
return list(dict.fromkeys(rasters))