Related
My aim is to create a mesh in Python and setting markers on some 1-dimensional subset.
Up until now, I have always creates a set, for example a rectangle in gmsh, then put for example a circle in it. Then gmsh puts a mesh on my structure and I can mark the boundary of the rectangle and of the circle as facets (as xdmf files). I can then let Python read my mesh and boundary facets and using it, f.e. to solve PDEs.
What I want to do now is the following: I still want to have my rectangle with a mesh, but instead of defining the facet markes in gmsh, I want to define them using the image of a function.
More precicely: Instead of creating a circle in gmsh, I want to consider, for example, the function
Then I want to use as my facet and mark it in my mesh.
Is there a way to do this? I feel kind of lost here.
I am not sure if I understood everything in your question correctly, but here is some code that might help you. One thing I do not know is how to add element edges to physical groups in gmsh. But maybe you can figure that out.
So here is the code:
import gmsh
import numpy as np
def mapping(point):
x = point[0]
y = point[1]
z = point[2]
result = [2*x,3*y,z]
return result
def inverseMapping(point):
x = point[0]
y = point[1]
z = point[2]
result = [(1/2)*x,(1/3)*y,z]
return result
def getNodes():
nodeTags, nodeCoord, _ = gmsh.model.mesh.getNodes()
nodeCoord = np.reshape(nodeCoord,(len(nodeTags),3))
return nodeTags, nodeCoord
def getEdgeNodeCoordinates():
edgeTags, edgeNodes = gmsh.model.mesh.getAllEdges()
edgeNodes = np.reshape(edgeNodes,(len(edgeTags),2))
nodeTags, nodeCoord = getNodes()
coord = []
for i in range(0,len(edgeTags)):
tagNode1 = edgeNodes[i][0]
tagNode2 = edgeNodes[i][1]
nodeIndex1 = list(nodeTags).index(tagNode1)
nodeIndex2 = list(nodeTags).index(tagNode2)
nodeCoord1 = nodeCoord[nodeIndex1]
nodeCoord2 = nodeCoord[nodeIndex2]
coord.append([nodeCoord1,nodeCoord2])
return edgeTags, edgeNodes, nodeTags, coord
def getInverseNodeCoordinates(edgeNodeCoordinates):
coord = []
for edgeNodes in edgeNodeCoordinates:
nodeCoord1 = edgeNodes[0]
nodeCoord2 = edgeNodes[1]
newCoord1 = inverseMapping(nodeCoord1)
newCoord2 = inverseMapping(nodeCoord2)
coord.append([newCoord1, newCoord2])
return coord
def checkIntersection(edgeTags, edgeNodeCoordinates, inverseCoordinates):
intersectingEdgeTags = []
intersectingEdgeNodeCoord = []
# 1 = inside, 0 = outside
for i in range(0,len(inverseCoordinates)):
pair = inverseCoordinates[i]
coord1 = pair[0]
coord2 = pair[1]
e1 = 1 if np.linalg.norm(coord1) <= 1 else 0
e2 = 1 if np.linalg.norm(coord2) <= 1 else 0
s = e1 + e2 # s = 0 --> both nodes outside of manifold
# s = 1 --> one node inside and one node outside of manifold
# s = 2 --> both nodes inside of manifold
if s == 1:
intersectingEdgeTags.append(edgeTags[i])
intersectingEdgeNodeCoord.append(edgeNodeCoordinates[i])
return intersectingEdgeTags, intersectingEdgeNodeCoord
def visualizeEdges(intersectingEdgeNodeCoord):
for pair in intersectingEdgeNodeCoord:
p1 = pair[0]
p2 = pair[1]
t1 = gmsh.model.occ.addPoint(p1[0],p1[1],p1[2])
t2 = gmsh.model.occ.addPoint(p2[0],p2[1],p2[2])
line = gmsh.model.occ.addLine(t1, t2)
gmsh.model.occ.synchronize()
gmsh.model.setColor([(1,line)], 255, 0, 0)
gmsh.model.occ.synchronize()
gmsh.initialize()
# Create a rectangle which will be meshed later.
tag_vol_1 = gmsh.model.occ.addRectangle(-3, -4, 0, 6, 8)
# Sample the S1 manifold with n_points
S1_sampling_points = []
n_points = 100
maxAngle = 2*np.pi
angle = maxAngle/n_points
z = 0
for i in range(0,n_points):
x = np.cos(i*angle)
y = np.sin(i*angle)
S1_sampling_points.append([x,y,z])
# Map the sampled S1 points to 2*x, 3*y, z.
# This is only for "visualization" via a spline.
mappedPoints = []
mappedPointTags = []
for point in S1_sampling_points:
mappedPoint = mapping(point)
tag = gmsh.model.occ.addPoint(mappedPoint[0], mappedPoint[1], mappedPoint[2])
mappedPointTags.append(tag)
# Here the spline fitting is performed
# You will see it visualized when gmsh opens.
tagMappedS1 = gmsh.model.occ.addSpline(mappedPointTags + [mappedPointTags[0]]) # make the spline periodic by setting the last point equal to the first one
gmsh.model.occ.synchronize()
# Mesh the rectangle and tell gmsh to create edges which we can access.
gmsh.model.mesh.generate(2)
gmsh.model.mesh.createEdges() # You need to call this before using gmsh.model.mesh.getAllEdges()
# Get all these self-explanatory things
edgeTags, edgeNodes, nodeTags, edgeNodeCoordinates = getEdgeNodeCoordinates()
# Calculate the inverse-mapped coordinates of all nodes.
# With this we can just check if the transformed nodes are inside a circle of radius 1 or outside.
# If for every egde one node is inside, and the other node is outside the circle, then the edge is
# intersected by the mapped manifold f(S1) --> (2*x, 3*y, z). We then save the tag of such an edge.
inverseCoordinates = getInverseNodeCoordinates(edgeNodeCoordinates)
intersectingEdgeTags, intersectingEdgeNodeCoord = checkIntersection(edgeTags, edgeNodeCoordinates, inverseCoordinates)
# Here all intersecting edges are created within gmsh so you can see it.
# This is for visualization only. A lot of nodes with the same coordinates are created here twice.
visualizeEdges(intersectingEdgeNodeCoord)
gmsh.fltk.run()
gmsh.finalize()
And the result in gmsh looks like this:
i am trying to generate geometries and meshes with the Python api of Gmsh, planning to use it in FEniCS.
I started creating my geometry following the steps reported here: https://jsdokken.com/src/tutorial_gmsh.html
The author first create the volume and then retrieve the surfaces with the command:
surfaces = gmsh.model.occ.getEntities(dim=2)
Finally, he is able to relate the surface to the tag simply by finding the center of mass (com). He uses the command gmsh.model.occ.getCenterOfMass(dim,tag) and compares it with the know com position of his surfaces, like this:
inlet_marker, outlet_marker, wall_marker, obstacle_marker = 1, 3, 5, 7
walls = []
obstacles = []
for surface in surfaces:
com = gmsh.model.occ.getCenterOfMass(surface[0], surface[1])
if np.allclose(com, [0, B/2, H/2]):
gmsh.model.addPhysicalGroup(surface[0], [surface[1]], inlet_marker)
inlet = surface[1]
gmsh.model.setPhysicalName(surface[0], inlet_marker, "Fluid inlet")
elif np.allclose(com, [L, B/2, H/2]):
gmsh.model.addPhysicalGroup(surface[0], [surface[1]], outlet_marker)
gmsh.model.setPhysicalName(surface[0], outlet_marker, "Fluid outlet")
elif np.isclose(com[2], 0) or np.isclose(com[1], B) or np.isclose(com[2], H) or np.isclose(com[1],0):
walls.append(surface[1])
else:
obstacles.append(surface[1])
Now, my problem is that this cannot work if two or more surfaces share the same com, such as two concentric cylinders.
How can i discriminate between them in such situation?
For example in case of an hollow cylinder, i would like to have a tag for each surface in order to apply different boundary conditions in FEniCS.
Thanks in advance!
You can make use of gmsh.model.getAdjacencies(dim,tag) where dim and tag are the dimension and tag of your entity of interest. This functions returns two lists up, down. The first one gives you the tags of all entities adjacent (=neighbouring) to the entity of interest with dimension dim + 1. The second list gives you the tags of all entities adjacent to the entity of interest with dimension dim - 1.
In 3D (i.e. dim = 3) the up list will be empty because there are no 4D structures in gmsh. The down list will contain all surface tags the boundary of the volume is made of.
Below is an example code. Part 1 is straight forward and in Part 2 I added a functions that sorts the surface tags by their x-coordinate.
import gmsh
gmsh.initialize()
## PART 1:
tag_cylinder_1 = gmsh.model.occ.addCylinder(0, 0, 0, 1, 0, 0, 0.1)
tag_cylinder_2 = gmsh.model.occ.addCylinder(0, 0, 0, 1, 0, 0, 0.2)
gmsh.model.occ.synchronize()
up_cyl_1, down_cyl_1 = gmsh.model.getAdjacencies(3,tag_cylinder_1)
up_cyl_2, down_cyl_2 = gmsh.model.getAdjacencies(3,tag_cylinder_2)
com_1 = gmsh.model.occ.getCenterOfMass(2, down_cyl_1[0])
com_2 = gmsh.model.occ.getCenterOfMass(2, down_cyl_1[1])
com_3 = gmsh.model.occ.getCenterOfMass(2, down_cyl_1[2])
## PART 2:
def calcBoxVolume(box):
dx = box[3] - box[0]
dy = box[4] - box[1]
dz = box[5] - box[2]
return dx*dy*dz
def getOrderedTags(cylTag):
up, down = gmsh.model.getAdjacencies(3,cylTag)
surf_COM = []
for surface in down:
com = [surface] + list(gmsh.model.occ.getCenterOfMass(2, surface))
surf_COM.append(com)
orderedSurfaces = sorted(surf_COM,key = lambda x: x[1])
orderedSurfaceTags = [item[0] for item in orderedSurfaces]
return orderedSurfaceTags
def setPhysicalTags(name,cylTag):
orderedSurfaces = getOrderedTags(cylTag)
gmsh.model.addPhysicalGroup(2, [orderedSurfaces[0]],name="inlet_"+name)
gmsh.model.addPhysicalGroup(2, [orderedSurfaces[1]],name="tube_"+name)
gmsh.model.addPhysicalGroup(2, [orderedSurfaces[3]],name="outlet_"+name)
def setPhysicalTagsCylDiff(name,cylTag):
orderedSurfaces = getOrderedTags(cylTag)
tag_A = orderedSurfaces[1]
tag_B = orderedSurfaces[2]
box_tube_A = gmsh.model.getBoundingBox(2,tag_A)
box_tube_B = gmsh.model.getBoundingBox(2,tag_B)
volBoxA = calcBoxVolume(box_tube_A)
volBoxB = calcBoxVolume(box_tube_B)
if volBoxA > volBoxB:
innerTag = tag_B
outerTag = tag_A
else:
innerTag = tag_A
outerTag = tag_B
gmsh.model.addPhysicalGroup(2, [orderedSurfaces[0]],name="inlet_"+name)
gmsh.model.addPhysicalGroup(2, [innerTag],name="tube_inner_"+name)
gmsh.model.addPhysicalGroup(2, [outerTag],name="tube_outer_"+name)
gmsh.model.addPhysicalGroup(2, [orderedSurfaces[3]],name="outlet_"+name)
# setPhysicalTags("Cylinder_1",tag_cylinder_1)
# setPhysicalTags("Cylinder_2",tag_cylinder_2)
outDimTags, outDimTagsMap = gmsh.model.occ.cut([(3,tag_cylinder_2)],[(3,tag_cylinder_1)])
cylDiffTag = outDimTags[0][1]
gmsh.model.occ.synchronize()
setPhysicalTagsCylDiff("CylDiff",cylDiffTag)
gmsh.model.mesh.generate(2)
gmsh.fltk.run()
gmsh.finalize()
So I'm new to both pathfinding and and pygame but I'm trying to visualize the matrix into a grid in pygame. However, when I try the boxes get further and further away and it infinitely loops. So my question is how can i Get this to display a 4x4 grind like my matrix, with each square properly spaced? The code was split into 2 files. the main problem is in the second, I put the first here just for context. Also just theory is fine too I don't necessarily need a code solution just wann wrap my head around this.
P.s. To any familiar with pygame is there anyway to get the Grid/grid.node() information from the pathfinder? thatd make this a bit easier i think
from pathfinding.core.grid import Grid
from pathfinding.finder.a_star import AStarFinder
matrix = [
[1,1,0,1],
[1,0,0,1],
[1,1,0,1],
[1,1,1,1]
]
#creates a grid from the matrix
grid = Grid(matrix=matrix)
start = grid.node(0,0)
end = grid.node(3,0)
class setup:
def createFinder(self):
#create a new instance of a finder
self.finder = AStarFinder(diagonal_movement=DiagonalMovement.always)
#The find_path function returns 2 values, the amounrs of times it runs to --
#-- find a path(runs) and the length of the finished path(path)
self.path, self.runs = self.finder.find_path(start, end, grid)
print(self.path)
def showPath(self):
print('operations', self.runs, 'path length:', len(self.path))
print(grid.grid_str(path=self.path, start=start, end=end))
from Pathfinder import *
import pygame
#creating a pygame screen
white = (255, 255, 255)
black = (198,238,78)
class pygameSetup():
def createDisplay(self):
self.gridDisplay = pygame.display.set_mode((800,600))
self.gridDisplay.fill(white)
def createSquare(self,x,y):
pygame.draw.rect(self.gridDisplay, black, [x,y,10,10])
def visualizeGrid(self):
x = 0
y = 0
z = 0
w = 0
v = 0
while w <=3:
pygame.display.update()
for i in matrix[z]:
print("The matrix is ", matrix[z],"and Z is: ", i)
v += 1
x += x + 11
if x >= 700:
x = 0
y += 11
if v == 4:
i += 1
z+=1
if z >= 4:
z = 0
v = 0
self.createSquare(x,y)
w+1
pS = pygameSetup()
pS.createDisplay()
pS.visualizeGrid()
Your visualizeGrid function is a lot more than you actually need. I am not sure why you have all those variables (v,w,x,y,z), you only need an x and a y coordiante. If you only want to display the grid, here is a simple working solution:
import pygame
gridDisplay = pygame.display.set_mode((200, 200))
pygame.display.get_surface().fill((200, 200, 200)) # background
matrix = [[1 ,1 ,0 ,1],
[1 ,0 ,0 ,1],
[1 ,1 ,0 ,1],
[1 ,1 ,1 ,1]]
# we use the sizes to draw as well as to do our "steps" in the loops.
grid_node_width = 10
grid_node_height = 10
def createSquare(x, y, color):
pygame.draw.rect(gridDisplay, color, [x, y, grid_node_width, grid_node_height ])
def visualizeGrid():
y = 0 # we start at the top of the screen
for row in matrix:
x = 0 # for every row we start at the left of the screen again
for item in row:
if item == 0:
createSquare(x, y, (255, 255, 255))
else:
createSquare(x, y, (0, 0, 0))
x += grid_node_width # for ever item/number in that row we move one "step" to the right
y += grid_node_height # for every new row we move one "step" downwards
pygame.display.update()
visualizeGrid() # call the function
while True:
pass # keeps the window open so you can see the result.
Concerning
get the Grid/grid.node() information from the pathfinder
the docs mention that you can get a node from your grid with grid.node(index_x, index_y):
matrix = [
[1, 1, 1],
[1, 0, 1],
[1, 1, 1]
]
grid = Grid(matrix=matrix)
start = grid.node(0, 0) # getting the first node in the 3x3 Matrix
end = grid.node(2, 2) # getting the last node in the 3x3 Matrix
I have an oriented cylinder generated with vtkCylinderSource and some transformations are applied on it to get the orientation that i want. Here is the code for creating this oriented-cylinder:
def cylinder_object(startPoint, endPoint, radius, my_color="DarkRed", opacity=1):
colors = vtk.vtkNamedColors()
# Create a cylinder.
# Cylinder height vector is (0,1,0).
# Cylinder center is in the middle of the cylinder
cylinderSource = vtk.vtkCylinderSource()
cylinderSource.SetRadius(radius)
cylinderSource.SetResolution(50)
# Generate a random start and end point
# startPoint = [0] * 3
# endPoint = [0] * 3
rng = vtk.vtkMinimalStandardRandomSequence()
rng.SetSeed(8775070) # For testing.8775070
# Compute a basis
normalizedX = [0] * 3
normalizedY = [0] * 3
normalizedZ = [0] * 3
# The X axis is a vector from start to end
vtk.vtkMath.Subtract(endPoint, startPoint, normalizedX)
length = vtk.vtkMath.Norm(normalizedX)
vtk.vtkMath.Normalize(normalizedX)
# The Z axis is an arbitrary vector cross X
arbitrary = [0] * 3
for i in range(0, 3):
rng.Next()
arbitrary[i] = rng.GetRangeValue(-10, 10)
vtk.vtkMath.Cross(normalizedX, arbitrary, normalizedZ)
vtk.vtkMath.Normalize(normalizedZ)
# The Y axis is Z cross X
vtk.vtkMath.Cross(normalizedZ, normalizedX, normalizedY)
matrix = vtk.vtkMatrix4x4()
# Create the direction cosine matrix
matrix.Identity()
for i in range(0, 3):
matrix.SetElement(i, 0, normalizedX[i])
matrix.SetElement(i, 1, normalizedY[i])
matrix.SetElement(i, 2, normalizedZ[i])
# Apply the transforms
transform = vtk.vtkTransform()
transform.Translate(startPoint) # translate to starting point
transform.Concatenate(matrix) # apply direction cosines
transform.RotateZ(-90.0) # align cylinder to x axis
transform.Scale(1.0, length, 1.0) # scale along the height vector
transform.Translate(0, .5, 0) # translate to start of cylinder
# Transform the polydata
transformPD = vtk.vtkTransformPolyDataFilter()
transformPD.SetTransform(transform)
transformPD.SetInputConnection(cylinderSource.GetOutputPort())
cylinderSource.Update()
# Create a mapper and actor for the arrow
mapper = vtk.vtkPolyDataMapper()
actor = vtk.vtkActor()
if USER_MATRIX:
mapper.SetInputConnection(cylinderSource.GetOutputPort())
actor.SetUserMatrix(transform.GetMatrix())
else:
mapper.SetInputConnection(transformPD.GetOutputPort())
actor.SetMapper(mapper)
actor.GetProperty().SetColor(colors.GetColor3d(my_color))
actor.GetProperty().SetOpacity(opacity)
return actor, transformPD
Now i want to ray cast a line with this oriented cylinder. unfortunately, using the vtkCylinderSource as the dataset for vtkOBBTree produces the wrong points as the result. how can i use ray-casting with a PolyDataFilter?
I came up with a solution where i export my oriented-cylinder to a .stl file and then read it again to implement the ray-casting algorithm using IntersectWithLine. The problem is i have thousands of these oriented-cylinders and this method (exporting and reading) makes my code extremely slow.
def ray_cast(filename, p_source, p_target):
'''
:param filename: STL file to perform ray casting on.
:param p_source: first point
:param p_target: second point
:return: code --> 0 : No intersection.
:return: code --> +1 : p_source lies OUTSIDE the closed surface.
:return; code --> -1 : p_source lies INSIDE closed surface
'''
reader = vtk.vtkSTLReader()
reader.SetFileName(filename)
reader.Update()
mesh = reader.GetOutput()
obbtree = vtk.vtkOBBTree()
obbtree.SetDataSet(mesh)
obbtree.BuildLocator()
pointsVTKIntersection = vtk.vtkPoints()
code = obbtree.IntersectWithLine(p_source, p_target, pointsVTKIntersection, None)
# Extracting data
pointsVTKIntersectionData = pointsVTKIntersection.GetData()
noPointsVTKIntersection = pointsVTKIntersectionData.GetNumberOfTuples()
pointsIntersection = []
for idx in range(noPointsVTKIntersection):
_tup = pointsVTKIntersectionData.GetTuple3(idx)
pointsIntersection.append(_tup)
return code, pointsIntersection, noPointsVTKIntersection
Below image shows the desired result using export-stl method. (the green spheres are intersection points)
I would appreciate any suggestion and help..
With vedo:
from vedo import *
cyl = Cylinder() # vtkActor
cyl.alpha(0.5).pos(3,3,3).orientation([2,1,1])
p1, p2 = (0,0,0), (4,4,5)
ipts_coords = cyl.intersectWithLine(p1, p2)
print('hit coords are', ipts_coords)
pts = Points(ipts_coords, r=10).color("yellow")
# print(pts.polydata()) # is the vtkPolyData object
origin = Point()
ln = Line(p1,p2)
show(origin, cyl, ln, pts, axes=True)
How do I make it so that the circles in this list can be modified or removed later on? Isn't the list different from the actual objects?
def drawAllBubbles(window,numOfBubbles):
bublist=list()
for x in range(numOfBubbles):
p1= random.randrange(1000)
p2= random.randrange(1000)
center= graphics.Point(p1,p2)
bubx = center.getX()
buby = center.getY()
r = random.randint(1, 255)#randomize rgb values
g = random.randint(1, 255)
b = random.randint(1, 255)
circle = graphics.Circle(center, 5)
circle.setFill(color_rgb(r, g, b))
circle.setOutline("black")
circle.draw(window)
bublist.append(circle)
return bublist
window.getMouse()
This part of the script essentially draws
And then returns a list of circles.
The objects are contained in bublist
If you iterate over the list, you can change, remove, or redraw the circles. For example:
for bubble in bublist:
bubble.setOutline("green")
bubble.draw(window)