Coder Social home page Coder Social logo

krober10nd / seismicmesh Goto Github PK

View Code? Open in Web Editor NEW
125.0 9.0 32.0 31.23 MB

2D/3D serial and parallel triangular mesh generation tool for finite element methods.

Home Page: https://seismicmesh.readthedocs.io/

License: GNU General Public License v3.0

C++ 29.46% Python 65.63% CMake 0.59% TeX 3.88% Makefile 0.44%
meshing seismic-velocity-models parallel distmesh

seismicmesh's Introduction

SeismicMesh

Create high-quality, simulation-ready 2D/3D meshes.

status CircleCI ReadTheDocs CodeCov Code style: black PyPI pyversions PyPi GPL

SeismicMesh: Triangular Mesh generation in Python

SeismicMesh is a Python package for simplex mesh generation in two or three dimensions. As an implementation of DistMesh, it produces high-geometric quality meshes at the expense of speed. For increased efficiency, the core package is written in C++, works in parallel, and uses the Computational Geometry Algorithms Library. SeismicMesh can also produce mesh-density functions from seismological data to be used in the mesh generator.

SeismicMesh is distributed under the GPL3 license and more details can be found in our short paper.

Table of contents

Installation

For installation, SeismicMesh needs CGAL:

sudo apt install libcgal-dev

After that, SeismicMesh can be installed from the Python Package Index (pypi), so with:

pip install -U SeismicMesh

If you'd like to read and write velocity models from segy/h5 format, you can install like:

pip install -U SeismicMesh[io]

For more detailed information about installation and requirements see:

Install - How to install SeismicMesh.

Contributing

All contributions are welcome!

To contribute to the software:

  1. Fork the repository.
  2. Clone the forked repository, add your contributions and push the changes to your fork.
  3. Create a Pull request

Before creating the pull request, make sure that the tests pass by running

tox

Some things that will increase the chance that your pull request is accepted:

  • Write tests.
  • Add Python docstrings that follow the Sphinx.
  • Write good commit and pull request messages.

Codebase

Here is a visual overview of the repository. An interactive version of this image can be found here: https://octo-repo-visualization.vercel.app/?repo=krober10nd%2FSeismicMesh

Visualization of this repo

Citing

You may use the following BibTeX entry:

@article{Roberts2021,
  doi = {10.21105/joss.02687},
  url = {https://doi.org/10.21105/joss.02687},
  year = {2021},
  publisher = {The Open Journal},
  volume = {6},
  number = {57},
  pages = {2687},
  author = {Keith J. Roberts and Rafael dos Santos Gioria and William J. Pringle},
  title = {SeismicMesh: Triangular meshing for seismology},
  journal = {Journal of Open Source Software}
}

Problems?

If something isn't working as it should or you'd like to recommend a new addition/feature to the software, please let me know by starting an issue through the issues tab. I'll try to get to it as soon as possible.

Examples

The user can quickly build quality 2D/3D meshes from seismic velocity models in serial/parallel.

BP2004

WARNING: To run the code snippet below you must download the 2D BP2004 seismic velocity model and then you must uncompress it (e.g., gunzip). This file can be downloaded from here

Above shows the mesh in ParaView that results from running the code below

from mpi4py import MPI
import meshio

from SeismicMesh import get_sizing_function_from_segy, generate_mesh, Rectangle

comm = MPI.COMM_WORLD

"""
Build a mesh of the BP2004 benchmark velocity model in serial or parallel
Takes roughly 1 minute with 2 processors and less than 1 GB of RAM.
"""

# Name of SEG-Y file containg velocity model.
fname = "vel_z6.25m_x12.5m_exact.segy"

# Bounding box describing domain extents (corner coordinates)
bbox = (-12000.0, 0.0, 0.0, 67000.0)

# Desired minimum mesh size in domain
hmin = 75.0

rectangle = Rectangle(bbox)

# Construct mesh sizing object from velocity model
ef = get_sizing_function_from_segy(
    fname,
    bbox,
    hmin=hmin,
    wl=10,
    freq=2,
    dt=0.001,
    grade=0.15,
    domain_pad=1e3,
    pad_style="edge",
)

points, cells = generate_mesh(domain=rectangle, edge_length=ef)

if comm.rank == 0:
    # Write the mesh in a vtk format for visualization in ParaView
    # NOTE: SeismicMesh outputs assumes the domain is (z,x) so for visualization
    # in ParaView, we swap the axes so it appears as in the (x,z) plane.
    meshio.write_points_cells(
        "BP2004.vtk",
        points[:, [1, 0]] / 1000,
        [("triangle", cells)],
        file_format="vtk",
    )

Note SeismicMesh can also be used to write velocity models to disk in a hdf5 format using the function write_velocity_model. Following the previous example above with the BP2004 velocity model, we create an hdf5 file with a domain pad of 1000 m.

from SeismicMesh import write_velocity_model

# Name of SEG-Y file containg velocity model.
fname = "vel_z6.25m_x12.5m_exact.segy"

# Bounding box describing domain extents (corner coordinates)
bbox = (-12000.0, 0.0, 0.0, 67000.0)

write_velocity_model(
     fname,
     ofname="bp2004_velocity_model",  # how the file will be called (with a .hdf5 extension)
     bbox=bbox,
     domain_pad=500,  # the width of the domain pad in meters
     pad_style="edge",  # how the velocity data will be extended into the layer
     units="m-s",  # the units that the velocity model is in.
 )

EAGE

WARNING: To run the code snippet below you must download (and uncompress) the 3D EAGE seismic velocity model from (WARNING: File is ~500 MB) here

WARNING: Computationaly demanding! Running this example takes around 3 minutes in serial and requires around 2 GB of RAM due to the 3D nature of the problem and the domain size.

Above shows the mesh in ParaView that results from running the code below.

from mpi4py import MPI
import zipfile
import meshio

from SeismicMesh import (
    get_sizing_function_from_segy,
    generate_mesh,
    sliver_removal,
    Cube,
)

comm = MPI.COMM_WORLD

# Bounding box describing domain extents (corner coordinates)
bbox = (-4200.0, 0.0, 0.0, 13520.0, 0.0, 13520.0)

# Desired minimum mesh size in domain.
hmin = 150.0

# This file is in a big Endian binary format, so we must tell the program the shape of the velocity model.
path = "Salt_Model_3D/3-D_Salt_Model/VEL_GRIDS/"
if comm.rank == 0:
    # Extract binary file Saltf@@ from SALTF.ZIP
    zipfile.ZipFile(path + "SALTF.ZIP", "r").extract("Saltf@@", path=path)

fname = path + "Saltf@@"

# Dimensions of model (number of grid points in z, x, and y)
nx, ny, nz = 676, 676, 210

cube = Cube(bbox)

# A graded sizing function is created from the velocity model along with a signed distance function by passing
# the velocity grid that we created above.
# More details can be found here: https://seismicmesh.readthedocs.io/en/master/api.html

ef = get_sizing_function_from_segy(
    fname,
    bbox,
    hmin=hmin,
    dt=0.001,
    freq=2,
    wl=5,
    grade=0.15,
    hmax=5e3,
    domain_pad=250,
    pad_style="linear_ramp",
    nz=nz,
    nx=nx,
    ny=ny,
    byte_order="big",
    axes_order=(2, 0, 1),  # order for EAGE (x, y, z) to default order (z,x,y)
    axes_order_sort="F",  # binary is packed in a FORTRAN-style
)

points, cells = generate_mesh(domain=cube, edge_length=ef, max_iter=75)

# For 3D mesh generation, we provide an implementation to bound the minimum dihedral angle::
# We use the preserve kwarg to ensure the level-set is very accurately preserved.
points, cells = sliver_removal(
    points=points, bbox=bbox, domain=cube, edge_length=ef, preserve=True
)

# Meshes can be written quickly to disk using meshio and visualized with ParaView::
if comm.rank == 0:

    # NOTE: SeismicMesh outputs assumes the domain is (z,x,y) so for visualization
    # in ParaView, we swap the axes so it appears as in the (x,y,z) plane.
    meshio.write_points_cells(
        "EAGE_Salt.vtk",
        points[:, [1, 2, 0]] / 1000.0,
        [("tetra", cells)],
    )

The user can still specify their own signed distance functions and sizing functions to generate_mesh (in serial or parallel) just like the original DistMesh algorithm but now with quality bounds in 3D. Try the codes below!

Cylinder

Cylinder

# Mesh a cylinder
from mpi4py import MPI
import meshio

import SeismicMesh

comm = MPI.COMM_WORLD

hmin = 0.10

cylinder = SeismicMesh.Cylinder(h=1.0, r=0.5)

points, cells = SeismicMesh.generate_mesh(
    domain=cylinder,
    edge_length=hmin,
)

points, cells = SeismicMesh.sliver_removal(
    points=points,
    domain=cylinder,
    edge_length=hmin,
)

if comm.rank == 0:
    meshio.write_points_cells(
        "Cylinder.vtk",
        points,
        [("tetra", cells)],
        file_format="vtk",
    )

Disk

Disk

# mesh a disk
import meshio
import SeismicMesh

disk = SeismicMesh.Disk([0.0, 0.0], 1.0)
points, cells = SeismicMesh.generate_mesh(domain=disk, edge_length=0.1)
meshio.write_points_cells(
    "disk.vtk",
    points,
    [("triangle", cells)],
    file_format="vtk",
)

Square

Square

# mesh a square/rectangle
import meshio
import SeismicMesh

bbox = (0.0, 1.0, 0.0, 1.0)
square = SeismicMesh.Rectangle(bbox)
points, cells = SeismicMesh.generate_mesh(domain=square, edge_length=0.05)
meshio.write_points_cells(
    "square.vtk",
    points,
    [("triangle", cells)],
    file_format="vtk",
)

Cube

Cube

# mesh a cuboid/cube
import meshio
import SeismicMesh

bbox = (0.0, 1.0, 0.0, 1.0, 0.0, 1.0)
cube = SeismicMesh.Cube(bbox)
points, cells = SeismicMesh.generate_mesh(domain=cube, edge_length=0.05)
points, cells = SeismicMesh.sliver_removal(points=points, domain=cube, edge_length=0.05)
meshio.write_points_cells(
    "cube.vtk",
    points,
    [("tetra", cells)],
    file_format="vtk",
)

Torus

Torus

# mesh a torus
import meshio
import SeismicMesh

hmin = 0.10

torus = SeismicMesh.Torus(r1=1.0, r2=0.5)
points, cells = SeismicMesh.generate_mesh(
    domain=torus,
    edge_length=hmin,
)
points, cells = SeismicMesh.sliver_removal(
    points=points, domain=torus, edge_length=hmin
)
meshio.write_points_cells(
    "torus.vtk",
    points,
    [("tetra", cells)],
)

Torus

Prism

# mesh a prism
import meshio

import SeismicMesh

hmin = 0.05
prism = SeismicMesh.Prism(b=0.5, h=0.5)

points, cells = SeismicMesh.generate_mesh(
    domain=prism,
    edge_length=hmin,
)
points, cells = SeismicMesh.sliver_removal(
    points=points, domain=prism, edge_length=hmin
)
meshio.write_points_cells(
    "prism.vtk",
    points,
    [("tetra", cells)],
    file_format="vtk",
)

Union

Union

# Compute the union of several SDFs to create more complex geometries
import meshio
import SeismicMesh

h = 0.10
rect0 = SeismicMesh.Rectangle((0.0, 1.0, 0.0, 0.5))
rect1 = SeismicMesh.Rectangle((0.0, 0.5, 0.0, 1.0))
disk0 = SeismicMesh.Disk([0.5, 0.5], 0.5)
union = SeismicMesh.Union([rect0, rect1, disk0])
# Visualize the signed distance function
union.show()
points, cells = SeismicMesh.generate_mesh(domain=union, edge_length=h)
meshio.write_points_cells(
    "Lshape_wDisk.vtk",
    points,
    [("triangle", cells)],
    file_format="vtk",
)

Intersection

Leaf

# Compute the intersection of several SDFs to create more complex geometries
import meshio
import SeismicMesh

h = 0.05
rect0 = SeismicMesh.Rectangle((0.0, 1.0, 0.0, 1.0))
disk0 = SeismicMesh.Disk([0.25, 0.25], 0.5)
disk1 = SeismicMesh.Disk([0.75, 0.75], 0.5)
intersection = SeismicMesh.Intersection([rect0, disk0, disk1])
points, cells = SeismicMesh.generate_mesh(domain=intersection, edge_length=h)
meshio.write_points_cells(
    "Leaf.vtk",
    points,
    [("triangle", cells)],
    file_format="vtk",
)

Difference

Hole

# Compute the difference of two SDFs to create more complex geometries.
import meshio
import SeismicMesh

h = 0.05
rect0 = SeismicMesh.Rectangle((0.0, 1.0, 0.0, 1.0))
disk0 = SeismicMesh.Disk([0.5, 0.5], 0.1)
disk1 = SeismicMesh.Disk([0.75, 0.75], 0.20)
difference = SeismicMesh.Difference([rect0, disk0, disk1])
points, cells = SeismicMesh.generate_mesh(domain=difference, edge_length=h)
meshio.write_points_cells(
    "Hole.vtk",
    points,
    [("triangle", cells)],
    file_format="vtk",
)

Difference of Signed Distance Functions in 3-D

Cube wHoles

# Compute the difference of several SDFs in 3D
import meshio
import SeismicMesh

h = 0.10
cube0 = SeismicMesh.Cube((0.0, 1.0, 0.0, 1.0, 0.0, 1.0))
ball1 = SeismicMesh.Ball([0.5, 0.0, 0.5], 0.30)
ball2 = SeismicMesh.Ball([0.5, 0.5, 0.0], 0.30)
ball3 = SeismicMesh.Ball([0.0, 0.5, 0.5], 0.30)
ball4 = SeismicMesh.Ball([0.5, 0.5, 0.5], 0.45)
difference = SeismicMesh.Difference([cube0, ball1, ball2, ball3, ball4])
points, cells = SeismicMesh.generate_mesh(domain=difference, edge_length=h, verbose=1)
points, cells = SeismicMesh.sliver_removal(
    points=points, domain=difference, edge_length=h, verbose=1
)
meshio.write_points_cells(
    "Cube_wHoles.vtk",
    points,
    [("tetra", cells)],
    file_format="vtk",
)

Immersion

Immersed disk

# Immerse a subdomain so that it's boundary is conforming in the mesh.
import numpy as np

import meshio

import SeismicMesh

box0 = SeismicMesh.Rectangle((-1.25, 0.0, -0.250, 1.250))
disk0 = SeismicMesh.Disk([-0.5, 0.5], 0.25)

hmin = 0.10


fh = lambda p: 0.05 * np.abs(disk0.eval(p)) + hmin

points, cells = SeismicMesh.generate_mesh(
    domain=box0,
    edge_length=fh,
    h0=hmin,
    subdomains=[disk0],
    max_iter=100,
)
meshio.write_points_cells(
    "Square_wsubdomain.vtk",
    points,
    [("triangle", cells)],
    file_format="vtk",
)

Boundaries

Boundary conditions can also be prescribed and written to gmsh compatible files using mehsio. In the following example, we immerse a disk into the connectivity and then prescribe boundary conditions around the circle and each wall of the domain for later usage inside a finite element solver.

Screen Shot 2021-02-12 at 12 04 03 PM

import numpy as np
import meshio
import SeismicMesh as sm

bbox = (0.0, 10.0, 0.0, 1.0)
channel = sm.Rectangle(bbox)
suspension = sm.Disk([0.5, 0.5], 0.25)

hmin = 0.10
fh = lambda p: 0.05 * np.abs(suspension.eval(p)) + hmin
points, cells = sm.generate_mesh(
    domain=channel,
    edge_length=fh,
    h0=hmin,
    subdomains=[suspension],
    max_iter=1000,
 )
# This gets the edges of the mesh in a winding order (clockwise or counterclockwise).
ordered_bnde = sm.geometry.get_winded_boundary_edges(cells)
# We use the midpoint of the edge to determine its boundary label
mdpt = points[ordered_bnde].sum(1) / 2
infl = ordered_bnde[mdpt[:, 0] < 1e-6, :]  # x=0.0
outfl = ordered_bnde[mdpt[:, 0] > 9.9 + 1e-6, :]  # x=10.0
walls = ordered_bnde[
    (mdpt[:, 1] < 1e-6) | (mdpt[:, 1] > 0.99 + 1e-6), :
]  # y=0.0 or y=1.0
cells_prune = cells[suspension.eval(sm.geometry.get_centroids(points, cells)) < 0]
circle = sm.geometry.get_winded_boundary_edges(cells_prune)

# Write to gmsh22 format with boundary conditions for the walls and disk/circle.
meshio.write_points_cells(
    "example.msh",
    points,
    cells=[
        ("triangle", cells),
        ("line", np.array(infl)),
        ("line", np.array(outfl)),
        ("line", np.array(walls)),
        ("line", np.array(circle)),
    ],
    field_data={
        "InFlow": np.array([11, 1]),
        "OutFlow": np.array([12, 1]),
        "Walls": np.array([13, 1]),
        "Circle": np.array([14, 1]),
    },
    cell_data={
        "gmsh:physical": [
            np.repeat(3, len(cells)),
            np.repeat(11, len(infl)),
            np.repeat(12, len(outfl)),
            np.repeat(13, len(walls)),
            np.repeat(14, len(circle)),
        ],
        "gmsh:geometrical": [
            np.repeat(1, len(cells)),
            np.repeat(1, len(infl)),
            np.repeat(1, len(outfl)),
            np.repeat(1, len(walls)),
            np.repeat(1, len(circle)),
        ],
    },
    file_format="gmsh22",
    binary=False,
)

Periodic

Periodic torus

# Repeat primitives to create more complex domains/shapes.
import SeismicMesh
import meshio

hmin = 0.30
bbox = (0.0, 10.0, 0.0, 10.0, 0.0, 10.0)
torus = SeismicMesh.Torus(r1=1.0, r2=0.5)
# the Repeat function takes a list specifying the repetition period in each dim
periodic_torus = SeismicMesh.Repeat(bbox, torus, [2.0, 2.0, 2.0])
points, cells = SeismicMesh.generate_mesh(domain=periodic_torus, edge_length=hmin)
points, cells = SeismicMesh.sliver_removal(
    points=points, domain=periodic_torus, edge_length=hmin
)
meshio.write_points_cells(
    "periodic_torus.vtk",
    points,
    [("tetra", cells)],
    file_format="vtk",
)

Rotations

Rotated squares

# Rotate squares in 2D
import numpy as np

import meshio
import SeismicMesh

bbox = (0.0, 1.0, 0.0, 1.0)
rotations = np.linspace(-3.14, 3.14, 40)
squares = []
for _, rotate in enumerate(rotations):
    squares.append(SeismicMesh.Rectangle(bbox, rotate=[rotate,0,0]))

rotated_squares = SeismicMesh.Union(squares)

points, cells = SeismicMesh.generate_mesh(domain=rotated_squares, edge_length=0.05)
meshio.write_points_cells(
    "rotated_squares" + str(rotate) + ".vtk",
    points,
    [("triangle", cells)],
    file_format="vtk",
)

Rotated cubes

# Same as above but for cubes
import numpy as np

import meshio
import SeismicMesh

bbox = (0.0, 1.0, 0.0, 1.0, 0.0, 1.0)
rotations = np.linspace(-3.14, 3.14, 40)
cubes = []
for _, rotate in enumerate(rotations):
    cubes.append(SeismicMesh.Cube(bbox, rotate=[rotate,0,0]))

rotated_cubes = SeismicMesh.Union(cubes)

points, cells = SeismicMesh.generate_mesh(domain=rotated_cubes, edge_length=0.10)
meshio.write_points_cells(
    "rotated_cubes.vtk",
    points,
    [("tetra", cells)],
    file_format="vtk",
)

Stretching

Stretched squares

# Geometric primitives can be stretched (while being rotated)
import meshio

from SeismicMesh import *

domain = Rectangle((0.0, 1.0, 0.0, 1.0), stretch=[0.5, 2.0], rotate=0.1*3.14)

points, cells = generate_mesh(domain=domain, edge_length=0.1, verbose=2)

meshio.write_points_cells(
    "stretched_square.vtk",
    points,
    [("triangle", cells)],
    file_format="vtk",
)

Translation

A translated cuboid

# Geometric primitives can be translated (while being rotated and stretched)
import meshio

from SeismicMesh import *

cuboid = Cube(
    (0.0, 1.0, 0.0, 1.0, 0.1, 1.0),
    stretch=[1.5, 1.5, 1.5],
    translate=[0.5, 4.0, 1.0],
    rotate=4.5 * 3.14,
)
points, cells = generate_mesh(domain=cuboid, edge_length=0.10, max_iter=200)
points, cells = sliver_removal(points=points, domain=cuboid, edge_length=0.10, preserve=True)


meshio.write_points_cells(
    "stretched_square.vtk",
    points,
    [("tetra", cells)],
    file_format="vtk",
)

Checking

Example of checking

SeismicMesh's mesh generator is sensitive to poor geometry definitions and thus you should probably check it prior to complex expensive meshing. We enable all signed distance functions to be visualized via the domain.show() method where domain is an instance of a signed distance function primitive from SeismicMesh.geometry. Note: you can increase the number of samples to visualize the signed distance function by increasing the kwarg samples to the show method, which is by default set to 10000.

Parallelism

A simplified version of the parallel Delaunay algorithm proposed by Peterka et. al 2014 is implemented inside the DistMesh algorithm, which does not consider sophisticated domain decomposition or load balancing yet. A peak speed-up of approximately 6 times using 11 cores when performing 50 meshing iterations is observed to generate the 33M cell mesh of the EAGE P-wave velocity model. Parallel performance in 2D is better with peak speedups around 8 times using 11 cores. While the parallel performance is not perfect at this stage of development, the capability reduces the generation time of this relatively large example (e.g., 33 M cells) from 91.0 minutes to approximately 15.6 minutes. Results indicate that the simple domain decomposition approach inhibit perfect scalability. The machine used for this experiment was an Intel Xeon Gold 6148 machine clocked at 2.4 GHz with 192 GB of RAM connected together with a 100 Gb/s InfiniBand network.

To use parallelism see the docs

See the paper/paper.md and associated figures for more details.

Performance

**How does performance and cell quality compare to Gmsh and CGAL mesh generators?

Here we use SeismicMesh 3.1.4, pygalmesh 0.8.2, and pygmsh 7.0.0 (more details in the benchmarks folder).

Some key findings:

  • Mesh generation in 2D and 3D using analytical sizing functions is quickest when using Gmsh but a closer competition for CGAL and SeismicMesh.
  • However, using mesh sizing functions defined on gridded interpolants significantly slow down both Gmsh and CGAL. In these cases, SeismicMesh and Gmsh perform similarly both outperforming CGAL's 3D mesh generator in terms of mesh generation time.
  • All methods produce 3D triangulations that have a minimum dihedral angle > 10 degrees enabling stable numerical simulation (not shown)
  • Head over to the benchmarks folder for more detailed information on these experiments.

Summary of the benchmarks

  • In the figure for the panels that show cell quality, solid lines indicate the mean and dashed lines indicate the minimum cell quality in the mesh.

  • Note: it's important to point out here that a significant speed-up can be achieved for moderate to large problems using the parallel capabilities provided in SeismicMesh.

**For an additional comparison of SeismicMesh against several other popular mesh generators head over to meshgen-comparison.

Changelog

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Unreleased

Added

  • Short blurb about using write_velocity_model
  • Adding read_velocity_model to public API

Fixed

  • Bug fix to gradient limiting of mesh size functions

[3.6.1]-2021-05-22

Added

  • Smoothed sets (e.g., intersections, differences, and unions)
  • Conversion of velocity data from feet-second to meters-second
  • Support for fixed points in iterative Laplacian mesh smoother.

Improved

  • Simplified pybind11 build system.
  • Now using pytest-codeblocks instead of exdown

[3.5.0]-2021-03-09

Added

  • Rotations for all geometric primitives
  • Stretching for all geometric primitives
  • Visuzlization of signed distance functions

Fixed

  • Support for Python 3.9

Improved

  • Fixed points in iterative Laplacian smooth

[3.4.0]-2021-02-14

Added

  • Mesh improvement now solves Lapl. smoothing as a fixed-point problem using AMG solver.
  • User can now mesh user-defined sizing functions in parallel (not from :class:SizeFunction)
  • Ability to specify data type dtype of floating point number inside binary files.
  • Example how to specify and write boundary conditions.

Improved

  • Faster unique edge calculation.

[3.3.0]-2021-01-08

Added

  • Ability to improve accuracy of level-set when performing 3d sliver removal.

Improved

  • Marginally faster parallel speedup at scale in 2d/3d

[3.2.0] -2020-12-14

Added

  • Adding basic periodic domains with the Repeat SDF.
  • sliver_removal has optional variable step size when perturbing vertices. Helps to remove the "last sliver".

Improved

  • Faster rectangle and cube primitives.
  • Reworking CPP code and bottlenecks...20-30% faster generate_mesh in parallel for 2D/3D from previous versions.

[3.1.7] - 2020-11-27

Improved

  • Table of contents in README

Added

  • More testing of sliver removal and 2d mesh generation qualities.

Fixed

  • Disabled bug when doing Newton boundary projection at the end of 3d sliver_removal.

[3.1.6] - 2020-11-26

Bug present with sliver removal. Recommend to not use.

Added

  • Unit testing three versions of Python (3.6.1, 3.7.4, 3.8.1)

[3.1.5] - 2020-11-24

  • Support for constraining/immersing subdomains represented as signed distance functions.
  • Faster cell manipulation operations for ~5-10% better speedups in parallel.
  • Projection of points back onto level set.

[3.1.4] - 2020-11-15

  • Laplacian smoothing at termination for 2D meshing...significantly improves minimum cell quality.
  • Made hmin a field of the SizeFunction class, which implies the user no longer needs to pass h0 to generate_mesh or sliver_removal.

[3.1.3] - 2020-11-06

Fixed

  • Cylinder radius and height are now correct.
  • Torus, Prism, and Cylinder now have dim tag.

Improved

  • More control over the grad option in the mesh sizing function.

[3.1.2] - 2020-11-04

Improved

  • Faster calculation of boundary vertices.
  • More robust sliver removal in 3D.

Fixed

  • Corners are only constrained for constant resolution meshes

[3.1.0] - 2020-10-28

Added

  • New geometric primitives--torus, wedge/prism, and cylinder.
  • Updated images on README.

Fixed

  • Only constrain corners near 0-level set.
  • Bug fix to 3D binary velocity reading.

[3.0.6] - 2020-10-21

Fixed

  • Silence messages about pfix when verbose=0

Added

  • Added more examples on README
  • New unions/intersections/differences with several SDF primivitives
  • Automatic corner constraints in serial

[3.0.5] - 2020-10-18

Fixed

  • Preserving fixed points in serial.
  • Units in km-s detection warning bug.
  • Docstring fixes to generate_mesh
  • Improved mesh quality in 3D

Added

  • Automatic corner point extraction for cubes and rectangles.
  • More support for reading binary files packed in a binary format.
  • Check to make sure bbox is composed of all floats.

[3.0.4] - 2020-10-12

Added

  • Improve conformity of level-set in final mesh through additional set of Newton boundary projection iterations.

More information

All other information is available at: https://seismicmesh.readthedocs.io

Getting started - Learn the basics about the program and the application domain.

Tutorials - Tutorials that will guide you through the main features.

seismicmesh's People

Contributors

dependabot[bot] avatar krober10nd avatar matteounimib avatar recovery avatar santos-td avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

seismicmesh's Issues

dependencies and test dependencies mixed up

Some of the dependencies listed in setup.cfg are not necessary for running SeismicMesh, but really are test dependencies, for example exdown. They should be listed separately, for example in a test_requirements.txt file, in the circleci config, or in a tox.ini (should you ever use that).

`pfix` not preserved

  • CGAL's 2d/3d Delaunay triangulation routine appears to scramble the outputted points of the triangulation leading to a result where the first nfix points does not correspond topfix.

still too verbose

Even though I set verbose=False, I'm still getting

Commencing mesh generation with 4 vertices on rank 0.
Termination reached...maximum number of iterations reached.

Failure of sliver removal

  • MWE
 import meshio
 import numpy
 
 
 import SeismicMesh
 
 h = 0.01
 
 
 def box_with_refinement(h):
     bbox = (-1.0, 1.0, -1.0, 1.0, -1.0, 1.0)
     cube = SeismicMesh.geometry.Cube([-1.0, 1.0, -1.0, 1.0, -1.0, 1.0])
 
     def edge_length(x):
         return h + 0.1 * numpy.sqrt(x[:, 0] ** 2 + x[:, 1] ** 2 + x[:, 2] ** 2)
 
     points, cells = SeismicMesh.generate_mesh(
         bbox=bbox, h0=h, domain=cube, edge_length=edge_length, verbose=2
     )
     points, cells = SeismicMesh.sliver_removal(
         points=points,
         bbox=bbox,
         h0=h,
         domain=cube,
         edge_length=edge_length,
         verbose=2,
     )
     return points, cells
 
 
 points, cells = box_with_refinement(h)
 
 meshio.write_points_cells(
     "cube.vtk",
     points,
     [("tetra", cells)],
     file_format="vtk",
 )

generalize WriteSegyToNetcdf.m

Hi, for your consideration..

this patch against the 3dmeshing branch reads from a .segy as per script's filename, allows 2D arrays to be written, and follows given data's array size instead of hardcoding it.

Many nc*() functions don't return any output so don't need to end in ';', thus dropped those.

diff --git a/utilities/WriteSegyToNetcdf.m b/utilities/WriteSegyToNetcdf.m
index 98b86b6..1dd1847 100644
--- a/utilities/WriteSegyToNetcdf.m
+++ b/utilities/WriteSegyToNetcdf.m
@@ -1,7 +1,7 @@
 clearvars; close all; clc; 
 %%
 OFNAME = 'SeismicUnitCubeVp_V2.nc';
-data=ncread('SeismicUnitCubeVp.nc','vp'); 
+data = ReadSegy('SeismicUnitCubeVp.segy');
 dims = size(data);
 [nx,ny,nz]=size(data);
 
@@ -40,7 +40,7 @@ ncwriteatt(OFNAME, 'x0y0z0', 'long_name', 'position of bottom left front corner'
 ncwriteatt(OFNAME,'x0y0z0','units','cartesian coordinates'); 
 
 
-nccreate(OFNAME,'vp','Dimensions',{'x',50,'y',50,'z',50});
+nccreate(OFNAME,'vp','Dimensions',{'x',nx,'y',ny,'z',nz})
 ncwriteatt(OFNAME, 'vp', 'long_name', 'P-wave velocity');
 ncwriteatt(OFNAME,'vp','units','m/s')
 
@@ -50,14 +50,21 @@ ncwrite(OFNAME,'dim',length(dims));
 
 ncwrite(OFNAME,'gridspace',100);
 
-ncwrite(OFNAME,'x0y0z0',[0,0,0]);
+if(length(dims) > 2)
+   ncwrite(OFNAME, 'x0y0z0', [0,0,0])
+else
+   ncwrite(OFNAME, 'x0y0z0', [0,0])
+end
 
 ncwrite(OFNAME,'nx',dims(1));
 
 ncwrite(OFNAME,'ny',dims(2));
 
-ncwrite(OFNAME,'nz',dims(3));
-
+if(length(dims) > 2)
+   ncwrite(OFNAME,'nz',dims(3))
+else
+   ncwrite(OFNAME,'nz',1)
+end
 
 ncwrite(OFNAME,'vp',data);
 

thanks,
Hamish

Update `pygalmesh` and redo disk benchmark

Bug fixes occurred in pygalmesh which remove the degenerate cells that occurred when meshing a uniform disk. The benchmark needs to be done and the figure in the paper and on the README updated accordingly.

SeismicMesh.__version__, circle -> disk?

Please provide

SeismicMesh.__version__

Also, Circle should perhaps be renamed to Disk (since it's the geometry with the interior, not just the circular boundary).

JOSS: Review notes

  • Fix typo in affiliations: Currently: Reearch Center for Gas Innovation, should be Research
  • Update install instructions. On ubuntu 20.04, there is no python-pybind11, only python3-pybind11. This should be clarified in the installation instructions.
  • Suggestion (not required) Using Eigen in C++ would remove quite a plot of the code converting back and forth in pybind11. See: https://pybind11.readthedocs.io/en/stable/advanced/cast/eigen.html
  • Suggestion (not required): Add a Dockerfile with installation based. For example base it on the ubuntu20.04 image, to make installation easier.
    More notes to come over the following days.

Distmesh initialization in 3D

In 2D, distmesh initializes with a triangular space-filling pattern. How do you initialize tets in 3D? Link is fine.

"cell_size"?

The main controlling variable for cell size is cell_size. Does this represent the cell area? Or the edge length? (I can't find it from the code.) If the latter, the variable should probably be renamed. Documentation should also be clear on this.

Installation error: ModuleNotFoundError: No module named 'skbuild'

pip install -e .
Defaulting to user installation because normal site-packages is not writeable
Obtaining file:///home/nschloe/software/seismicmesh/source-upstream
Requirement already satisfied: numpy in /home/nschloe/.local/lib/python3.9/site-packages (from SeismicMesh==1.0.0) (1.19.1)
Collecting segyio
  Downloading segyio-1.9.1.tar.gz (1.1 MB)
     |████████████████████████████████| 1.1 MB 1.0 MB/s
    ERROR: Command errored out with exit status 1:
     command: /usr/bin/python3.9 -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-h2xsxm3l/segyio/setup.py'"'"'; __file__='"'"'/tmp/pip-install-h2xsxm3l/segyio/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /tmp/pip-pip-egg-info-64qhrrex
         cwd: /tmp/pip-install-h2xsxm3l/segyio/
    Complete output (5 lines):
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-install-h2xsxm3l/segyio/setup.py", line 3, in <module>
        import skbuild
    ModuleNotFoundError: No module named 'skbuild'
    ----------------------------------------
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.

The segyio dep needs

pip install scikit-build

to be installed. Probably worth mentioning in the the instructions, and probably worth filing a bug about upstream.

verbose=0 prints something

Despite setting verbose=0, I'm getting

Constraining 24 fixed points..

Let me know if you need an MWE.

Inconsistent use of HMIN

As both your mesh generator, and and the build sizing function has hmin as an input argument, is there
any way of making a check that the input argument into these two functions matches each other?
Could you add hmin as a variable in the SizeFunction class, such that you do not need hmin as an input to generate mesh when supplying a SizingFunction?

make purpose of SeismicMesh clearer

This is about the README. The premise of SeismicMesh is unclear:

2D/3D triangular meshing for a slab of Earth based on modifications to the DistMesh algorithm. SeismicMesh is distributed under GPL3.

First off, I think it's tetrahedral meshing in 3D, right? What does is "meshing for a slab of Earth" mean, how is it different from meshing for the air around a wing? What exactly does SeismicMesh do differently than gmsh, or what's it's specialty? I'm guessing it has to do with the segy files. How is this useful for seismic computations?

consider using code formats

There's the common Python formatter black; from what I see your code is already it. Also: isort for sorting the imports, and: flake8. In my project Makefile, I usually have

format:
>~isort .
>~black .
>~blacken-docs README.md

lint:
>~isort --check .
>~black --check .
>~flake8 .

Perhaps one of them is useful to you, perhaps not. Perhaps there's something similar for the C++ part of your code.

readme examples

The readme still lists many examples which take a function for a domain. I think it'd be nicer if those were converted to domains, too, e.g.,

class Torus:
    def __init__(self, x0, x1, r):
        bbox = (-r, r, -r, r, x0, x1)

    def eval(x):
        xz = np.column_stack((p[:, 0], p[:, 2]))
        q = np.column_stack((length(xz) - t[0], p[:, 1]))
        return length(q) - t[1]

Other than that I think the readme is great. SM looks quite capable now.

Also a tip for the figures: You can turn off the orientation axis in ParaView and trim the pngs afterwards. This way, you don't waste precious vertical room. On unix, you can trim on the command line via

convert -trim in.png out.png

Minimal mesh quality of DistMesh algorithm

As I've mentioned in previous comments, it seems like the DistMesh algorithm produces cells where the average mesh quality is very high. However, it seems like the minimal quality of cells in a mesh is quite alot worse than for GMSH. In the setting of 3D you have shown that the sliver removal algorithm can increase the mesh quality of these cells quite efficiently.

However, in the 2D setting, how can one control the minimum mesh quality?
Take for instance the BP2004 benchmark. Running the benchmark produces the following minimal, avg and max mesh quality:

1.466166516027528066e-01
9.593859657712772160e-01
9.999999866475057786e-01

while gmsh produces:

5.564115344181080891e-01
9.413313063736762354e-01
9.999983294113985455e-01

I've attached the figures of the worst cell in gmsh and seismic mesh below, where the color indicate the radius ratio (Seismic Mesh 6.82 on the scale [1,infty), Gmsh 1.79 on the same scale). Allthough the cell is far from collapsed, I think it is very interesting in the perspective of FEM and multigrid solvers.

GMSH:
bp2004_gmsh_worst

SeismicMesh:
bp2004_sm_worst

cost of improving level set accuracy

  • doing the boundary projection at the end can be costly for huge meshes (> 10 million cells). Do we really need 10 of these project iterations or can we get away with fewer?

constant cell_size not working

Much to my surprise, a simple example like

import SeismicMesh

disk = SeismicMesh.Circle(0.0, 0.0, 1.0)
points, cells = SeismicMesh.generate_mesh(domain=disk, cell_size=0.1)

fails with

ValueError: `cell_size` must either be a function or a `cell_size` object

This should probably be fixed.

test coverage

With under 60%, SeismicMesh has a very low code coverage. In my experience, code that isn't tested doesn't work (or at least doesn't work much of the time).

13e

You could cover much ground for example by testing (or removing of course) your plot functions (see https://codecov.io/gh/krober10nd/SeismicMesh/src/par3d/SeismicMesh/sizing/mesh_size_function.py). Since your tests run headless, you best set the env variable MPLBACKEND=Agg. Locally, I always run my tests with

MPLBACKEND=Agg pytest --maxfail=1

Perhaps that's useful for you as well.

misleading license

SeismicMesh uses the BSD-2 license. This is probably misleading. In my experience, users will be surprised to learn that they cannot use SeismicMesh under the BSD-2 only, but have to consider the licences of the dependencies too. And there you'll find CGAL with ships under the (L)GPL which restricts users much more.

I'd either

  • change SeismicMesh license's to GPL, or
  • put a BIG FAT warning in the readme/licensing etc., or
  • remove the CGAL dependency.

Array manipulation improvement

  • Using cgal /c++ perhaps some operations can be rewritten and be made perhaps faster than the array manipulation which occurs in Python (e.g., incident faces, dblock(?), fix_mesh(?))

constraining shapes leave some global state

When trying to mesh the L-shape and something else one after another, SM will error out.

MWE:

import numpy
import SeismicMesh


def l_shape():
    h = 0.1
    bbox = (0.0, 1.0, 0.0, 1.0)

    bbox0 = (0.0, 1.0, 0.0, 0.5)
    rect0 = SeismicMesh.Rectangle(bbox0)

    bbox1 = (0.0, 0.5, 0.0, 1.0)
    rect1 = SeismicMesh.Rectangle(bbox1)

    corners = SeismicMesh.geometry.corners

    def union(x):
        return numpy.minimum.reduce([rect0.eval(x), rect1.eval(x)])

    pfix = numpy.vstack((corners(bbox0), corners(bbox1)))

    points, cells = SeismicMesh.generate_mesh(
        bbox=bbox,
        domain=union,
        edge_length=h,
        pfix=pfix,
        verbose=0,
    )


def ball():
    h = 0.1
    bbox = (-1.0, 1.0, -1.0, 1.0, -1.0, 1.0)

    def ball(x):
        return numpy.sqrt(x[:, 0] ** 2 + x[:, 1] ** 2 + x[:, 2] ** 2) - 1.0

    points, cells = SeismicMesh.generate_mesh(
        bbox=bbox, h0=h, domain=ball, edge_length=h, verbose=False
    )
    points, cells = SeismicMesh.sliver_removal(
        points=points, bbox=bbox, h0=h, domain=ball, edge_length=h, verbose=False
    )


l_shape()
ball()
Constraining 8 fixed points..
Constraining 8 fixed points..
Traceback (most recent call last):
  File "y.py", line 47, in <module>
    ball()
  File "y.py", line 38, in ball
    points, cells = SeismicMesh.generate_mesh(
  File "/home/nschloe/.local/lib/python3.8/site-packages/SeismicMesh/generation/mesh_generator.py", line 399, in generate_mesh
    fh, p, extents = _initialize_points(dim, geps, bbox, fh, fd, h0, opts, pfix, comm)
  File "/home/nschloe/.local/lib/python3.8/site-packages/SeismicMesh/generation/mesh_generator.py", line 756, in _initialize_points
    fh, p, extents = _generate_initial_points(
  File "/home/nschloe/.local/lib/python3.8/site-packages/SeismicMesh/generation/mesh_generator.py", line 741, in _generate_initial_points
    p = np.vstack(
  File "<__array_function__ internals>", line 5, in vstack
  File "/home/nschloe/.local/lib/python3.8/site-packages/numpy/core/shape_base.py", line 283, in vstack
    return _nx.concatenate(arrs, 0)
  File "<__array_function__ internals>", line 5, in concatenate
ValueError: all the input array dimensions for the concatenation axis must match exactly, but along dimension 1, the array at index 0 has size 2 and the array at index 1 has size 3

API improvement suggestions

The user interface of SM is more complicated than it needs to be when it comes to bounding boxes, corners etc. Consider the following code for the L-shape:

  def l_shape(h):
      bbox = (0.0, 1.0, 0.0, 1.0)

      bbox0 = (0.0, 1.0, 0.0, 0.5)
      rect0 = SeismicMesh.Rectangle(bbox0)

      bbox1 = (0.0, 0.5, 0.0, 1.0)
      rect1 = SeismicMesh.Rectangle(bbox1)

      corners = SeismicMesh.geometry.corners

      def union(x):
          return numpy.minimum.reduce([rect0.eval(x), rect1.eval(x)])

      pfix = numpy.vstack((corners(bbox0), corners(bbox1)))

      points, cells = SeismicMesh.generate_mesh(
          bbox=bbox,
          domain=union,
          edge_length=h,
          pfix=pfix,
          verbose=0,
      )
      return points, cells

Suggestion: Define Rectangle objects like

class Rectangle:
     def __init__(self, bbox):
        self.corners = corners
        self.bbox = # ... compute the bbox

     def eval(self, x):
        return # ...

The unions, intersections etc. can compute their bounding boxes and corners from the bounding boxes and corners of the input arguments, e.g.,

class Union:
    def __init__(self, domains: list):
        self.bbox = [
            max(d.bbox[0] for d in domains),
            # ...
        ]

One can check which corners are at the boundary of the union by evaluating the eval() for every domain.

The above code would then look like

  def l_shape(h):
      rect0 = SeismicMesh.Rectangle(0.0, 1.0, 0.0, 0.5)
      rect1 = SeismicMesh.Rectangle(0.0, 0.5, 0.0, 1.0)
      union = SeismicMesh.Union([rect0, rect1])
      points, cells = SeismicMesh.generate_mesh(domain=union, edge_length=h)
      return points, cells

The arguments to Rectangle could be better organized as [0.0, 1.0], [0.0, 0.5].

Defining a new domain would then always require writing a class with eval, a bbox, and (perhaps optionally) corners.

What do you think?

generate_mesh: verbose=False

By default, generate_mesh prints lots of intermediate stats,

[...]
Iteration #43, max movement is 0.010014, there are 367 vertices and 678 cells
     Elapsed wall-clock time 0.001311 :
Iteration #44, max movement is 0.010025, there are 367 vertices and 678 cells
     Elapsed wall-clock time 0.001292 :
Iteration #45, max movement is 0.010035, there are 367 vertices and 678 cells
     Elapsed wall-clock time 0.001268 :
Iteration #46, max movement is 0.010043, there are 367 vertices and 678 cells
     Elapsed wall-clock time 0.001271 :
[...]

It'd be very useful if it was possible to disable this. This is typically done by a verbose argument. (Usually, this defaults to False, too.)

[joss] comparison with gmsh, cgal

SeismicMesh consists of two parts:

  • A converter that takes a seismic velocity model and constructs a mesh size function from it.
  • A parallel and 3D distmesh implementation.

Those two tasks can be neatly separated, and I find it unfortunate that those two things are tied together in one package.

  • A parallel 3D distmesh implementation could be useful for everyone trying to build a mesh, not only seismologists.
  • Seismologists might want to use a method other than distmesh to build a mesh from the size size function.

Anyway, here we are.

What's currently missing from the README and the draft is a comparison with other commonly used mesh generators. I would count CGAL and Gmsh to those (perhaps there are more you can think of). I would like to see

  • a comparison in mesh creation speed, and
  • a comparison in cell quality

with CGAL and Gmsh. A good measure for cell quality is, for example, d * circumcircle_radius / incircle_radius (where d is 2 for triangles and 3 for tetrahedra). The value is between 0 and 1, where 1 is a perfectly symmetrical simplex. CGAL has mesh size function input for 3D meshes only. Since you're using Python, it may be useful to use pygalmesh or to access the CGAL methods directly from C++. (You may be familiar enough with the API.) Gmsh supports mesh functions from its own Python interface in master (and 4.7.0, to be released). It may be useful to take a look at pygmsh for simplicity here.

CONTRIBUTING.md

Perhaps a CONTRIBUTING.md file can be added to the repo. This could serve as a template.

domain + bounding box, Disk arguments.

For many domains and domain combinations the bounding box can be computed internally. This saves the bounding_box argument. For example, instead of

bbox = (-1.0, 1.0, -1.0, 1.0)
disk = SeismicMesh.geometry.Disk(0, 0, 1)
 points, cells = SeismicMesh.generate_mesh(
     bbox=bbox,
     h0=h,
     domain=disk,
     edge_length=h
 )

one could have

disk = SeismicMesh.geometry.Disk(0, 0, 1)
 points, cells = SeismicMesh.generate_mesh(
     h0=h,
     domain=disk,
     edge_length=h
 )

This requires the Disk object to have a member bbox which can be computed easily from the input values.

Also, I would probably make the arguments of Disk and array of length 2 and a scalar, not three scalars. With Disk([0.2, 0.1], 1.5), it's clear at first glance what the arguments mean. With Disk(0.2, 0.1, 1.5) not so much.

more hints for the article

Important:

  • You speak of "modifications of distmesh". What modifications are these, exactly? Please go into more detail in the article. (If the mods aren't too small.) If the mods are small, don't mention them.
  • I think you need to make clearer that SeismicMesh is a parellel distmesh implementation plus a mesh density function generator. I would like to hear why distmesh_ and not some other algorithm? Easy to implement, known to produce high-quality cells, easily paralellizable, but can be slow.
  • Why don't you simply use Gmsh, right? Aha, you find that Gmsh is slow for your use case! Why? Because your density function is so complicated and it heavily benefits from vectorization. Gmsh's algorithm doesn't allow for vectorization, distmesh does. ==> For complicated density functions, distmesh is better.

Less important:

  • You're using Gmsh and CGAL (perhaps others) in texttt as if it was on a command line. The names are Gmsh and CGAL, really, no particular style needed.

dents in 3D boundary

I'm trying to generate the mesh of an ordinary ball. I get a ball with lots of dents. Looks like not all boundary nodes are correctly projected to the boundary. MWE:

import meshio
import numpy
import SeismicMesh

h = 0.1
bbox = (-1.0, 1.0, -1.0, 1.0, -1.0, 1.0)


def ball(x):
    return numpy.sqrt(x[:, 0] ** 2 + x[:, 1] ** 2 + x[:, 2] ** 2) - 1.0


points, cells = SeismicMesh.generate_mesh(
    bbox=bbox, h0=h, domain=ball, edge_length=h, verbose=False
)

meshio.Mesh(points, {"tetra": cells}).write("out.vtk")

out

throttle point movement in sliver_removal

  • Sometimes degenerate 3d elements can create very large vertex perturbations in the sliver_removal method leading to expensive move operations as the point moves far affecting many tetrahedral.

  • Need to think about how to bound this point movement to prevent this.

CGAL/cgal#5137

Immersed subdomains

  • Support for domain embedding i.e., 3d geometry embedded within a larger 3d geometry (ball inside of a ball, etc.)

  • Define a new geometry signed distance class Embed which has an additional field called label. At initialization of the points for generation, points are given an integer label depending on which SDF they belong in.

  • During mesh generation each SDF of the domain is evaluated but the point can only return back to its initial domain where it was seeded.

bounding box from files?

In the README examples, the bounding boxes are specified explicitly. Can those not be read from the segy files?

more extensive readme

In my experience, it improves the onboarding of your software package if the landing page (usually the github readme) contains all the some information to get started, in particular:

  • Has an install section, short and crispy, e.g.,

On Linux, install the dependencies with

sudo apt install libcgal-dev

and then simply install SeismicMesh from pypi,

pip install SeismicMesh

An upload to pypi is also strongly advised, this way the user doesn't have to check out the development repo.

  • Has a small example that can be copy-pasted.

PS: I was assigned as reviewer of your JOSS paper, hence the flood of suggestions. :)

Some minor comments on the paper, hopefully final revision

Some comments from me ;) The first one is more of a general question than a suggestion to changing the text.

Some packages have been created to script mesh generation from geophysical datasets suchas in coastal ocean modeling (e.g.,K. J. Roberts et al., 2019; GMT and TerrenoGormanet al., 2008) and reservoir modeling (e.g., MeshITCacace & Blöcher, 2015).

What distinguishes SeismicMesh from these? For a non expert, how does the geophysical datasets differ between these methods?
You mention in the Ongoing and future applications that you are developing a version of OceanMesh2D. Could you elaborate (here, and not in the paper) on what changes this would imply.

Then to some comments/corrections:

Our mesh density functions can be used with other mesh generators however the usage of a particular sizing function can have significant implications on mesh generation performance.

My suggestion:
The mesh density function can be used as input other mesh generators. However, the usage of a particular sizing function can have impact the mesh generation performance.

For example, Gmsh’s advancing front and Delaunay refinement methods construct the mesh incrementally and do not permit vectorization which leads to slow downs at scale in 2D/3D

For example, Gmsh’s advancing front and Delaunay refinement methods construct the mesh incrementally and do not permit vectorization, which leads to reduced performance at scale in 2D/3D.

In contrast, the DistMesh algorithm takes advantage of vectorization when querying a complex mesh density function making it efficient and competitive to Gmsh forthis kind of meshing problem.

Does the original DistMesh algorithm take this into account, or is this a feature of your implementation of the algorithm?

Similar to other meshing programs such as Gmsh, SeismicMesh (K. Roberts, 2020) enables both generation of simplex meshes through a Python scripting-based application programming interface

Remove both from enables both

The 2D/3D serial performance against Gmsh (Geuzaine & Remacle, 2009) and CGAL (Alliezet al., 2020;Rineau, 2020) in terms of cell quality and creation time where cell quality isdefined as dimension (2 or 3) multiplied by the circumcircle radius divided by the incircleradius. This cell quality is between 0 and 1, where 1 is a perfectly symmetrical simplex.

We present a comparison of the 2D/3D serial performance in terms of cell quality and mesh creation time. We compare SeismicMesh with Gmsh (Geuzaine & Remacle, 2009) and CGAL (Alliezet al., 2020;Rineau, 2020). The cell quality is defined as the product of the topological dimension of the mesh (2 or 3) and the circumcircle radius divided by the incircle radius. This metric is between 0 and 1, where 1 is a perfectly symmetrical simplex.

After this sentence I would start a new paragraph before For the analytical geometries .

For the analytical geometries (e.g. disk and ball) Gmsh is the fastest to generate a mesh and then performance is approximately similar for both SeismicMesh and CGAL with CGAL outperforming SeismicMesh for the disk and vice versa for the ball.

I would suggest you split this sentence into multiple sentences.

However,SeismicMesh in general produces very higher mean cell qualities which are about 5-10% greaterthan either Gmsh or CGAL

Remove very from very higher

For the two seismic domains (e.g., BP2004 and EAGE), SeismicMesh is faster than Gmsh for the 2D BP2004 benchmark but slightly slower for the 3D EAGE benchmark at scale, while CGAL is not competitive for the 3D benchmark and does not support user-defined mesh sizing functions and therefore is shown.

Split into multiple sentences.

... Gmsh increasing its mesh generation time by a factor of around 3x

Gmsh increasing its mesh generation time by a factor of $\sim 3$.

Figure 1:The mesh creation time (left columns) and resulting cell quality (right columns) for the four benchmark problems studied over a range of problem sizes.

I think you can replace for the four benchmark problems with for the four benchmarks.

each inversion iteration, updates to an objective functional produce modifications to the 0-level set

each inversion iteration, updates to an objective functional and produce modifications to the 0-level set.

MeshSizeFunction: eval?

The name MeshSizeFunction implies that it's an object that can be evaluated. I don't see an eval() method or something like that. Am I missing something?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.