pythonpython-3.xmacosfinite-element-analysisgmsh

Gmsh example t4.py unable to find t4_image.png, other examples run fine


Goal is to solve my previous question Generate a mesh from my polygon geometry to iterate FEM for geometry optimization? by myself using Gmsh

I've installed Gmsh (also) 4.12.2 using pip on macOS 10.15.7 Intel core i5 with Anaconda Python 3.7.

I ran the first five examples t1 through t5.py and all work nicely except t4.

https://gmsh.info/dev/doc/texinfo/gmsh.html#t4

I get

Exception: Could not open file `/Users/davido/Documents/Gmsn/../t4_image.png'

note: A friend ran t4.py under Windows and Python 3.11.5 and it worked fine. Right now I don't have the option to move to a new computer/OS, nor upgrade my Anaconda "just to see what happens".

I've added t4.py at the bottom of the question, it uses path specifications in a way I'm not familiar with (I'm more of a numerical person) and I don't know what /../ means.

I can't find any instance of t4_image.png on my computer except one from a few weeks ago within Anaconda packages when I first installed Gmsh.

Since making holes in a mesh is EXACTLY what I need to do to answer the linked question, I would like to get this example working.

Error:

(base) davido@Davidos-MacBook-Air Gmsn % python -i t4.py
Info    : Meshing 1D...
Info    : [  0%] Meshing curve 1 (Line)
Info    : [ 10%] Meshing curve 2 (Line)
[lines removed]
Info    : [ 90%] Meshing curve 19 (Circle)
Info    : [100%] Meshing curve 20 (Line)
Info    : Done meshing 1D (Wall 0.00703093s, CPU 0.024232s)
Info    : Meshing 2D...
Info    : [  0%] Meshing surface 22 (Plane, Frontal-Delaunay)
Info    : [ 50%] Meshing surface 24 (Plane, Frontal-Delaunay)
Info    : Done meshing 2D (Wall 0.0233768s, CPU 0.084051s)
Info    : 785 nodes 1629 elements
Info    : Writing 't4.msh'...
Info    : Done writing 't4.msh'
Error   : Could not open file `/Users/davido/Documents/Gmsn/../t4_image.png'
Traceback (most recent call last):
  File "t4.py", line 180, in <module>
    gmsh.fltk.run()
  File "/Users/davido/anaconda3/lib/python3.7/site-packages/gmsh.py", line 10032, in run
    raise Exception(logger.getLastError())
Exception: Could not open file `/Users/davido/Documents/Gmsn/../t4_image.png'

Original script t4.py

# ------------------------------------------------------------------------------
#
#  Gmsh Python tutorial 4
#
#  Holes in surfaces, annotations, entity colors
#
# ------------------------------------------------------------------------------

import gmsh
import math
import sys
import os

gmsh.initialize()

gmsh.model.add("t4")

cm = 1e-02
e1 = 4.5 * cm
e2 = 6 * cm / 2
e3 = 5 * cm / 2
h1 = 5 * cm
h2 = 10 * cm
h3 = 5 * cm
h4 = 2 * cm
h5 = 4.5 * cm
R1 = 1 * cm
R2 = 1.5 * cm
r = 1 * cm
Lc1 = 0.01
Lc2 = 0.003


def hypot(a, b):
    return math.sqrt(a * a + b * b)


ccos = (-h5 * R1 + e2 * hypot(h5, hypot(e2, R1))) / (h5 * h5 + e2 * e2)
ssin = math.sqrt(1 - ccos * ccos)

# We start by defining some points and some lines. To make the code shorter we
# can redefine a namespace:
factory = gmsh.model.geo
factory.addPoint(-e1 - e2, 0, 0, Lc1, 1)
factory.addPoint(-e1 - e2, h1, 0, Lc1, 2)
factory.addPoint(-e3 - r, h1, 0, Lc2, 3)
factory.addPoint(-e3 - r, h1 + r, 0, Lc2, 4)
factory.addPoint(-e3, h1 + r, 0, Lc2, 5)
factory.addPoint(-e3, h1 + h2, 0, Lc1, 6)
factory.addPoint(e3, h1 + h2, 0, Lc1, 7)
factory.addPoint(e3, h1 + r, 0, Lc2, 8)
factory.addPoint(e3 + r, h1 + r, 0, Lc2, 9)
factory.addPoint(e3 + r, h1, 0, Lc2, 10)
factory.addPoint(e1 + e2, h1, 0, Lc1, 11)
factory.addPoint(e1 + e2, 0, 0, Lc1, 12)
factory.addPoint(e2, 0, 0, Lc1, 13)

factory.addPoint(R1 / ssin, h5 + R1 * ccos, 0, Lc2, 14)
factory.addPoint(0, h5, 0, Lc2, 15)
factory.addPoint(-R1 / ssin, h5 + R1 * ccos, 0, Lc2, 16)
factory.addPoint(-e2, 0.0, 0, Lc1, 17)

factory.addPoint(-R2, h1 + h3, 0, Lc2, 18)
factory.addPoint(-R2, h1 + h3 + h4, 0, Lc2, 19)
factory.addPoint(0, h1 + h3 + h4, 0, Lc2, 20)
factory.addPoint(R2, h1 + h3 + h4, 0, Lc2, 21)
factory.addPoint(R2, h1 + h3, 0, Lc2, 22)
factory.addPoint(0, h1 + h3, 0, Lc2, 23)

factory.addPoint(0, h1 + h3 + h4 + R2, 0, Lc2, 24)
factory.addPoint(0, h1 + h3 - R2, 0, Lc2, 25)

factory.addLine(1, 17, 1)
factory.addLine(17, 16, 2)

# Gmsh provides other curve primitives than straight lines: splines, B-splines,
# circle arcs, ellipse arcs, etc. Here we define a new circle arc, starting at
# point 14 and ending at point 16, with the circle's center being the point 15:
factory.addCircleArc(14, 15, 16, 3)

# Note that, in Gmsh, circle arcs should always be smaller than Pi. The
# OpenCASCADE geometry kernel does not have this limitation.

# We can then define additional lines and circles, as well as a new surface:
factory.addLine(14, 13, 4)
factory.addLine(13, 12, 5)
factory.addLine(12, 11, 6)
factory.addLine(11, 10, 7)
factory.addCircleArc(8, 9, 10, 8)
factory.addLine(8, 7, 9)
factory.addLine(7, 6, 10)
factory.addLine(6, 5, 11)
factory.addCircleArc(3, 4, 5, 12)
factory.addLine(3, 2, 13)
factory.addLine(2, 1, 14)
factory.addLine(18, 19, 15)
factory.addCircleArc(21, 20, 24, 16)
factory.addCircleArc(24, 20, 19, 17)
factory.addCircleArc(18, 23, 25, 18)
factory.addCircleArc(25, 23, 22, 19)
factory.addLine(21, 22, 20)

factory.addCurveLoop([17, -15, 18, 19, -20, 16], 21)
factory.addPlaneSurface([21], 22)

# But we still need to define the exterior surface. Since this surface has a
# hole, its definition now requires two curves loops:
factory.addCurveLoop([11, -12, 13, 14, 1, 2, -3, 4, 5, 6, 7, -8, 9, 10], 23)
factory.addPlaneSurface([23, 21], 24)

# As a general rule, if a surface has N holes, it is defined by N+1 curve loops:
# the first loop defines the exterior boundary; the other loops define the
# boundaries of the holes.

factory.synchronize()

# Finally, we can add some comments by creating a post-processing view
# containing some strings:
v = gmsh.view.add("comments")

# Add a text string in window coordinates, 10 pixels from the left and 10 pixels
# from the bottom:
gmsh.view.addListDataString(v, [10, -10], ["Created with Gmsh"])

# Add a text string in model coordinates centered at (X,Y,Z) = (0, 0.11, 0),
# with some style attributes:
gmsh.view.addListDataString(v, [0, 0.11, 0], ["Hole"],
                            ["Align", "Center", "Font", "Helvetica"])

# If a string starts with `file://', the rest is interpreted as an image
# file. For 3D annotations, the size in model coordinates can be specified after
# a `@' symbol in the form `widthxheight' (if one of `width' or `height' is
# zero, natural scaling is used; if both are zero, original image dimensions in
# pixels are used):
png = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir,
                   't4_image.png')
print('Hey! png = ', png)
gmsh.view.addListDataString(v, [0, 0.09, 0], ["file://" + png + "@0.01x0"],
                            ["Align", "Center"])

# The 3D orientation of the image can be specified by proving the direction
# of the bottom and left edge of the image in model space:
gmsh.view.addListDataString(v, [-0.01, 0.09, 0],
                            ["file://" + png + "@0.01x0,0,0,1,0,1,0"])

# The image can also be drawn in "billboard" mode, i.e. always parallel to
# the camera, by using the `#' symbol:
gmsh.view.addListDataString(v, [0, 0.12, 0], ["file://" + png + "@0.01x0#"],
                            ["Align", "Center"])

# The size of 2D annotations is given directly in pixels:
gmsh.view.addListDataString(v, [150, -7], ["file://" + png + "@20x0"])

# These annotations are handled by a list-based post-processing view. For
# large post-processing datasets, that contain actual field values defined on
# a mesh, you should use model-based post-processing views instead, which
# allow to efficiently store continuous or discontinuous scalar, vector and
# tensor fields, or arbitrary polynomial order.

# Views and geometrical entities can be made to respond to double-click
# events, here to print some messages to the console:
gmsh.view.option.setString(v, "DoubleClickedCommand",
                           "Printf('View[0] has been double-clicked!');")
gmsh.option.setString(
    "Geometry.DoubleClickedLineCommand",
    "Printf('Curve %g has been double-clicked!', "
    "Geometry.DoubleClickedEntityTag);")

# We can also change the color of some entities:
gmsh.model.setColor([(2, 22)], 127, 127, 127)  # Gray50
gmsh.model.setColor([(2, 24)], 160, 32, 240)  # Purple
gmsh.model.setColor([(1, i) for i in range(1, 15)], 255, 0, 0)  # Red
gmsh.model.setColor([(1, i) for i in range(15, 21)], 255, 255, 0)  # Yellow

gmsh.model.mesh.generate(2)

gmsh.write("t4.msh")

# Launch the GUI to see the results:
if '-nopopup' not in sys.argv:
    gmsh.fltk.run()

gmsh.finalize()

Solution

  • Since making holes in a mesh is EXACTLY what I need to do to answer the linked question, I would like to get this example working.

    First, I found that if I disabeled gmsh.fltk.run() the script would end without error and the mesh would be written but not displayed.

    I could then open t4.msh in the GUI later either started from command line or from a script and view it.

    Then I discovered that the whole t4_image.png part of the example was superfluous to the example's main function, so simply choosing not to generate this optional static image solves my problem.

    if False:
        # If a string starts with `file://', the rest is interpreted as an image
        # file. For 3D annotations, the size in model coordinates can be specified after
        # a `@' symbol in the form `widthxheight' (if one of `width' or `height' is
        # zero, natural scaling is used; if both are zero, original image dimensions in
        # pixels are used):
        gmsh.view.addListDataString(v, [0, 0.09, 0], ["file://../t4_image.png@0.01x0"],
                                    ["Align", "Center"])
    
        # The 3D orientation of the image can be specified by proving the direction
        # of the bottom and left edge of the image in model space:
        gmsh.view.addListDataString(v, [-0.01, 0.09, 0],
                                    ["file://../t4_image.png@0.01x0,0,0,1,0,1,0"])
    
        # The image can also be drawn in "billboard" mode, i.e. always parallel to
        # the camera, by using the `#' symbol:
        gmsh.view.addListDataString(v, [0, 0.12, 0],
                                    ["file://../t4_image.png@0.01x0#"],
                                    ["Align", "Center"])
    
        # The size of 2D annotations is given directly in pixels:
        gmsh.view.addListDataString(v, [150, -7], ["file://../t4_image.png@20x0"])
    

    Gmsh t4.py example with .png stuff supressed