-
Notifications
You must be signed in to change notification settings - Fork 92
Enhance the fracture generation feature of mesh_doctor
#2793
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 20 commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
e97aa3e
cell_to_faces for fracture generation.
TotoGaz 1b81668
First more or less working new version of the fracture generation.
TotoGaz 6d9e148
Little improvement of the testing.
TotoGaz 130bc4d
remove commented code
TotoGaz 3c87eca
More tests
TotoGaz 77c7c88
Another test
TotoGaz c5805e4
Comments
TotoGaz 84121d1
Add copy field
TotoGaz 8dac541
first version of check_fractures.py
TotoGaz bc25c93
Merge branch 'develop' into TotoGaz/feature/meshDoctorFracture
TotoGaz 5418a69
Replace _2 files and add polyhedron support.
TotoGaz bb9adb4
Discard fracture element with no duplicated node.
TotoGaz 7ad903e
Merge branch 'develop' into TotoGaz/feature/meshDoctorFracture
TotoGaz 4336a1d
Merge branch 'develop' into TotoGaz/feature/meshDoctorFracture
TotoGaz 774431d
Attempts to use mypy
TotoGaz e4deac9
Attempts to use mypy, again
TotoGaz 1229b7f
Merge branch 'develop' into TotoGaz/feature/meshDoctorFracture
TotoGaz 561ab13
Parsing mistake: int instead of float
TotoGaz 7a6f9a9
cleaning + mypy, and comments.
TotoGaz d55f8ef
Merge branch 'develop' into TotoGaz/feature/meshDoctorFracture
TotoGaz 79aaca7
Using Mesh doctor generate fractures with internal surfaces already d…
AntoineMazuyer fdc0caf
Small reorgs, using an enum to distinguish between fracture descripti…
TotoGaz 8468d4e
Add a unit tests for internal fracture description
TotoGaz 32b4267
Use a generator instead of clumsy maps, filters...
TotoGaz 618ce7f
Use a dataclass instead of a tuple
TotoGaz 26f155f
Use a dataclass instead of a tuple
TotoGaz f1a3159
Add CLI tests + remove useless split_on_domain_boundary option.
TotoGaz 215ca8f
Little refactoring
TotoGaz 0f3eaa2
Merge branch 'develop' into TotoGaz/feature/meshDoctorFracture
TotoGaz 22b29f2
Merge branch 'develop' into TotoGaz/feature/meshDoctorFracture
TotoGaz 400b74e
Merge branch 'develop' into TotoGaz/feature/meshDoctorFracture
TotoGaz ddae8f7
Merge branch 'develop' into TotoGaz/feature/meshDoctorFracture
TotoGaz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
177 changes: 177 additions & 0 deletions
177
src/coreComponents/python/modules/geosx_mesh_doctor/checks/check_fractures.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
from dataclasses import dataclass | ||
import logging | ||
|
||
from typing import ( | ||
Collection, | ||
FrozenSet, | ||
Iterable, | ||
Sequence, | ||
Set, | ||
Tuple, | ||
) | ||
|
||
from tqdm import tqdm | ||
import numpy | ||
|
||
from vtkmodules.vtkCommonDataModel import ( | ||
vtkUnstructuredGrid, | ||
vtkCell, | ||
) | ||
from vtkmodules.vtkCommonCore import ( | ||
vtkPoints, | ||
) | ||
from vtkmodules.vtkIOXML import ( | ||
vtkXMLMultiBlockDataReader, | ||
) | ||
from vtkmodules.util.numpy_support import ( | ||
vtk_to_numpy, | ||
) | ||
from vtk_utils import ( | ||
vtk_iter, | ||
) | ||
|
||
|
||
@dataclass(frozen=True) | ||
class Options: | ||
tolerance: float | ||
matrix_name: str | ||
fracture_name: str | ||
collocated_nodes_field_name: str | ||
|
||
|
||
@dataclass(frozen=True) | ||
class Result: | ||
# First index is the local index of the fracture mesh. | ||
# Second is the local index of the matrix mesh. | ||
# Third is the global index in the matrix mesh. | ||
errors: Sequence[tuple[int, int, int]] | ||
|
||
|
||
def __read_multiblock(vtk_input_file: str, matrix_name: str, fracture_name: str) -> Tuple[vtkUnstructuredGrid, vtkUnstructuredGrid]: | ||
reader = vtkXMLMultiBlockDataReader() | ||
reader.SetFileName(vtk_input_file) | ||
reader.Update() | ||
multi_block = reader.GetOutput() | ||
for b in range(multi_block.GetNumberOfBlocks()): | ||
block_name: str = multi_block.GetMetaData(b).Get(multi_block.NAME()) | ||
if block_name == matrix_name: | ||
matrix: vtkUnstructuredGrid = multi_block.GetBlock(b) | ||
if block_name == fracture_name: | ||
fracture: vtkUnstructuredGrid = multi_block.GetBlock(b) | ||
assert matrix and fracture | ||
return matrix, fracture | ||
|
||
|
||
def format_collocated_nodes(fracture_mesh: vtkUnstructuredGrid) -> Sequence[Iterable[int]]: | ||
""" | ||
Extract the collocated nodes information from the mesh and formats it in a python way. | ||
:param fracture_mesh: The mesh of the fracture (with 2d cells). | ||
:return: An iterable over all the buckets of collocated nodes. | ||
""" | ||
collocated_nodes: numpy.ndarray = vtk_to_numpy(fracture_mesh.GetPointData().GetArray("collocated_nodes")) | ||
if len(collocated_nodes.shape) == 1: | ||
collocated_nodes: numpy.ndarray = collocated_nodes.reshape((collocated_nodes.shape[0], 1)) | ||
return tuple(map(lambda bucket: tuple(sorted(filter(lambda i: i != -1, bucket))), collocated_nodes)) | ||
|
||
|
||
def __check_collocated_nodes_positions(matrix_points: Sequence[Tuple[float, float, float]], | ||
fracture_points: Sequence[Tuple[float, float, float]], | ||
g2l: Sequence[int], | ||
collocated_nodes: Iterable[Iterable[int]]) -> Collection[Tuple[int, Iterable[int], Iterable[Tuple[float, float, float]]]]: | ||
issues = [] | ||
for li, bucket in enumerate(collocated_nodes): | ||
matrix_nodes = (fracture_points[li], ) + tuple(map(lambda gi: matrix_points[g2l[gi]], bucket)) | ||
m = numpy.array(matrix_nodes) | ||
rank: int = numpy.linalg.matrix_rank(m) | ||
if rank > 1: | ||
issues.append((li, bucket, tuple(map(lambda gi: matrix_points[g2l[gi]], bucket)))) | ||
return issues | ||
|
||
|
||
def my_iter(ccc): | ||
car, cdr = ccc[0], ccc[1:] | ||
for i in car: | ||
if cdr: | ||
for j in my_iter(cdr): | ||
yield i, *j | ||
else: | ||
yield (i, ) | ||
|
||
|
||
def __check_neighbors(matrix: vtkUnstructuredGrid, | ||
fracture: vtkUnstructuredGrid, | ||
g2l: Sequence[int], | ||
collocated_nodes: Sequence[Iterable[int]]): | ||
fracture_nodes: Set[int] = set() | ||
for bucket in collocated_nodes: | ||
for gi in bucket: | ||
fracture_nodes.add(g2l[gi]) | ||
# For each face of each cell, | ||
# if all the points of the face are "made" of collocated nodes, | ||
# then this is a fracture face. | ||
fracture_faces: Set[FrozenSet[int]] = set() | ||
for c in range(matrix.GetNumberOfCells()): | ||
cell: vtkCell = matrix.GetCell(c) | ||
for f in range(cell.GetNumberOfFaces()): | ||
face: vtkCell = cell.GetFace(f) | ||
point_ids = frozenset(vtk_iter(face.GetPointIds())) | ||
if point_ids <= fracture_nodes: | ||
fracture_faces.add(point_ids) | ||
# Finding the cells | ||
for c in tqdm(range(fracture.GetNumberOfCells()), desc="Finding neighbor cell pairs"): | ||
cell: vtkCell = fracture.GetCell(c) | ||
cns: Set[FrozenSet[int]] = set() # subset of collocated_nodes | ||
point_ids = frozenset(vtk_iter(cell.GetPointIds())) | ||
for point_id in point_ids: | ||
bucket = collocated_nodes[point_id] | ||
local_bucket = frozenset(map(g2l.__getitem__, bucket)) | ||
cns.add(local_bucket) | ||
found = 0 | ||
tmp = tuple(map(tuple, cns)) | ||
for node_combinations in my_iter(tmp): | ||
f = frozenset(node_combinations) | ||
if f in fracture_faces: | ||
found += 1 | ||
if found != 2: | ||
logging.warning(f"Something went wrong since we should have found 2 fractures faces (we found {found}) for collocated nodes {cns}.") | ||
|
||
|
||
def __check(vtk_input_file: str, options: Options) -> Result: | ||
matrix, fracture = __read_multiblock(vtk_input_file, options.matrix_name, options.fracture_name) | ||
matrix_points: vtkPoints = matrix.GetPoints() | ||
fracture_points: vtkPoints = fracture.GetPoints() | ||
|
||
collocated_nodes: Sequence[Iterable[int]] = format_collocated_nodes(fracture) | ||
assert matrix.GetPointData().GetGlobalIds() and matrix.GetCellData().GetGlobalIds() and \ | ||
fracture.GetPointData().GetGlobalIds() and fracture.GetCellData().GetGlobalIds() | ||
|
||
point_ids = vtk_to_numpy(matrix.GetPointData().GetGlobalIds()) | ||
g2l = numpy.ones(len(point_ids), dtype=int) * -1 | ||
for loc, glo in enumerate(point_ids): | ||
g2l[glo] = loc | ||
g2l.flags.writeable = False | ||
|
||
issues = __check_collocated_nodes_positions(vtk_to_numpy(matrix.GetPoints().GetData()), | ||
vtk_to_numpy(fracture.GetPoints().GetData()), | ||
g2l, | ||
collocated_nodes) | ||
assert len(issues) == 0 | ||
|
||
__check_neighbors(matrix, fracture, g2l, collocated_nodes) | ||
|
||
errors = [] | ||
for i, duplicates in enumerate(collocated_nodes): | ||
for duplicate in filter(lambda i: i > -1, duplicates): | ||
p0 = matrix_points.GetPoint(g2l[duplicate]) | ||
p1 = fracture_points.GetPoint(i) | ||
if numpy.linalg.norm(numpy.array(p1) - numpy.array(p0)) > options.tolerance: | ||
errors.append((i, g2l[duplicate], duplicate)) | ||
return Result(errors=errors) | ||
|
||
|
||
def check(vtk_input_file: str, options: Options) -> Result: | ||
try: | ||
return __check(vtk_input_file, options) | ||
except BaseException as e: | ||
logging.error(e) | ||
return Result(errors=()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is completely fine (and maybe a bit faster?), but I find list comprehension+slicing to be much more readable/pythonic than lambdas+maps+filters:
tuple([tuple(sorted(bucket[bucket != -1])) for bucket in collected_nodes])
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, it's far better!