Closed kurtenkera closed 3 months ago
Did you try addressing the error, say by editing the file in gmsh
?
I don't understand how to edit the file in gmsh
to solve the problem (I don't really understand what the problem even is, as I'm new to meshing). Here is the code that I used in Python to parametrically design the hollow cylinder:
import gmsh
def create_hollow_cyl_msh_binary(outer_radius = 0.5, inner_radius = 0.4, height = 1):
"""
Returns:
NULL : generates the base hollow cylinder design as a MSH file (Binary) (via Gmsh). The resultant
file hollow_cylinder_binary.msh is written to the directory 'mesh_files'.
"""
# Initialize Gmsh - inputs used for a binary file
gmsh.initialize(argv=["","-bin"])
# Create a new model
model = gmsh.model
model.add("hollow_cylinder")
# Parameters for the hollow cylinder
radius_outer = outer_radius # Outer radius of the cylinder
radius_inner = inner_radius # Inner radius of the cylinder
height = height # Height of the cylinder
mesh_size = 1e-1
# Add points for the outer circle
p1_outer = model.geo.addPoint(radius_outer, 0, 0, mesh_size, 1)
p2_outer = model.geo.addPoint(0, radius_outer, 0, mesh_size, 2)
p3_outer = model.geo.addPoint(-radius_outer, 0, 0, mesh_size, 3)
p4_outer = model.geo.addPoint(0, -radius_outer, 0, mesh_size, 4)
# Add points for the inner circle
p1_inner = model.geo.addPoint(radius_inner, 0, 0, mesh_size, 5)
p2_inner = model.geo.addPoint(0, radius_inner, 0, mesh_size, 6)
p3_inner = model.geo.addPoint(-radius_inner, 0, 0, mesh_size, 7)
p4_inner = model.geo.addPoint(0, -radius_inner, 0, mesh_size, 8)
# Add centre point for circle arcs
centre = model.geo.addPoint(0, 0, 0, mesh_size, 9)
# Add the circles for the outer and inner boundaries
arc_outer1 = model.geo.addCircleArc(p1_outer, centre, p2_outer, 1)
arc_outer2 = model.geo.addCircleArc(p2_outer, centre, p3_outer, 2)
arc_outer3 = model.geo.addCircleArc(p3_outer, centre, p4_outer, 3)
arc_outer4 = model.geo.addCircleArc(p4_outer, centre, p1_outer, 4)
arc_inner1 = model.geo.addCircleArc(p1_inner, centre, p2_inner, 5)
arc_inner2 = model.geo.addCircleArc(p2_inner, centre, p3_inner, 6)
arc_inner3 = model.geo.addCircleArc(p3_inner, centre, p4_inner, 7)
arc_inner4 = model.geo.addCircleArc(p4_inner, centre, p1_inner, 8)
# Create a curve loop for the outer boundary
cl_outer = model.geo.addCurveLoop([arc_outer1, arc_outer2, arc_outer3, arc_outer4], 1)
# Create a curve loop for the inner boundary
cl_inner = model.geo.addCurveLoop([arc_inner1, arc_inner2, arc_inner3, arc_inner4], 2)
# Create a 2D plane surface for the doughnut
surface = model.geo.addPlaneSurface([cl_outer, cl_inner], 1)
# Extrude into 3D Hollow Cylinder
gmsh.model.geo.extrude([(2, surface)], 0, 0, height)
# Synchronize the geometry
model.geo.synchronize()
# Generate the mesh
model.mesh.generate(3)
# Save the mesh and the geometry
# ... and save it to disk
gmsh.write("mesh_files/hollow_cylinder_binary.msh")
# To visualize the model we can run the graphical user interface with
# `gmsh.fltk.run()'. Here we run it only if "-nopopup" is not provided in the
# command line arguments:
if '-nopopup' not in sys.argv:
gmsh.fltk.run()
# Finalize Gmsh
gmsh.finalize()
Is there a way I can alter this code to ensure all nodes are in a single block? It's hard for me to understand what 'ensuring all nodes are in a single block' even means, especially since the outputted binary .msh file is not human-readable...
@kurtenkera I have written a Converter https://github.com/mohammad200h/GMSHConverter This script takes a .msh file throws away surfaces, points, curves and only keeps volume entity. You can find your converted file in mujoco model folder. simply load the xml file into mujoco.
If you want to better understand what is going on have a look at hollow_cylinder_ascii.msh and volume.msh
Let me know how it goes :)
@mohammad200h This is brilliant thankyou very much! I have some questions below about converter.py
Q1: I want to understand the three functions below a little better. I understand that mesh
is a meshio
object, but currently I cannot find information on the attributes mesh.cells
, cellblock.type
and cellblock.data
in the Meshio source code (link: https://github.com/nschloe/meshio/blob/main/README.md)? Can you point me to the correct Meshio source code that explains these attributes?
def get_num_elements(mesh):
# find tetra cells
Elements = None
for cellblock in mesh.cells:
if cellblock.type == "tetra":
Elements = cellblock.data
return len(Elements)
def get_elements(mesh):
# find tetra cells
Elements = None
for cellblock in mesh.cells:
if cellblock.type == "tetra":
Elements = cellblock.data
return Elements
def get_num_points(mesh):
points = mesh.points
return len(points)
Q2: def get_elements(mesh)
outputs None
when I attempt to use the code to convert the file in this zip folder (
hollow_cylinder_ascii.zip), and hence does not output a valid volume.msh
file. The file that I've attached is the same as hollow_cylinder_ascii.msh
in your repo, however, it has 2 physical groups included in the file (see below):
$MeshFormat
4.1 0 8
$EndMeshFormat
$PhysicalNames
2
2 1 "bottom"
2 2 "top"
$EndPhysicalNames
$Entities
18 24 10 1
...
Thus, currently converter.py
can convert an ASCII or binary .msh file into the correct format needed for modelling in Mujoco when physical names aren't included. To get it working for files with physical names, I can imagine just deleting:
$PhysicalNames
...
$EndPhysicalNames
and then using converter.py
would work. Is there another perhaps more efficient way to get the code working for these files too (perhaps using the meshio
methods and/or attributes as you have above)? Thanks again!
Q1. I used print statements to better understand meshio
print(f"mesh::{mesh}")
print(f"mesh::cells:: {mesh.cells}")
I used the information in mesh::cells to understand how i can work with cellblock
mesh::cells:: [<meshio CellBlock, type: vertex, num cells: 1, tags: []>, <meshio CellBlock, type: vertex, num cells: 1, tags: []>,...]
Q2. if you view your model in Gmsh App https://gmsh.info/ (Download the app, import your model and look at it) you see the walls are gone. The walls where tetra therefore cellblock.type == "tetra" returns false hence you get None for get_elements.
@mohammad200h I have a very strange problem. There are three files in this attached zip folder hollow_cylinder.zip.
(1) hollow_cylinder_groups.msh (2) hollow_cylinder_nogroups.msh (3) hollow_cylinder_delgroups.msh
File (2) is an ASCII file of the same hollow cylinder that we've been talking about above. I can use the code that you provided to convert file (2) into a .msh file with only a single volume entity with no problems.
HOWEVER, file (1) is a file that was created with 2 physical groups ascribed. Effectively, it is the same as file (2) but with the following additional lines of text:
$PhysicalNames
2
2 1 "bottom"
2 2 "top"
$EndPhysicalNames
I can't convert file (1) using your code (but ideally I'd like to). File (3) is literally file (1) but where I manually deleted the lines of text seen above. I'd really expect file (3) to convert using your code, however, it doesn't? It's surprising to me because file (3) and file (2) appear to be identical to me? Why is file (2) converting but file (3) isn't?
Separate side question: Is this form of flexcomp modelling used for soft-bodies, whereas the mesh/asset approach is for simulating rigid bodies?
Q1. Thank you for providing the meshes. The issue seems to be with meshio library. I am trying to rewrite the code with gmsh library which is the official library but harder to work with.
Q2. Not exactly <flexcomp><\flexcomp>
is used for soft bodies and <geom><\geom>
on the other hand is used for rigid bodies. if you load a mesh with flexcomp it will create a softbody. if you load it with geom it will create a rigid body.
Okay thanks @mohammad200h. So then if I want to model a rigid body via reading in a binary MSH file - the binary MSH file would have to have the format seen below (link: https://mujoco.readthedocs.io/en/stable/XMLreference.html#asset-mesh):
Obviously you have shared code with me that can be used to convert a binary MSH file to an acceptable format for modelling soft bodies - is there a similar converter.py
module being built to convert a binary MSH file (like my hollow_cylinder_binary.msh) into the format described in the image above? Just because I may be interested in modelling both soft and rigid bodies, and I primarily deal with MSH files.
If not, are there simple ways to convert a binary MSH file to a binary STL or OBJ file (which could then be used to model a rigid body)?
@mohammad200h Just following up about the above comment?
@kurtenkera I have finished implementing the code using gmsh instead of meshio. I also tested it with the files you sent and it works now. Please give it a go. Regarding the format conversion you can use GMSH app to convert your file to stl. from there you can use blender to convert to any format you would like.
Here is how i created the stl file.
Let me know how it goes :)
Hi @mohammad200h - I've tested your code and it works with all the files as you say. However, I can't currently get it to work with the .msh file called vtu_converted_msh41_cyl.msh
attached in the following zip file:
vtu_converted_msh41_cyl.zip
For reference, there is a .vtu file in that zip folder of the hollow cylinder that we've been discussing. It is called cyl_diffusion_out_000_0.vtu
. In my project, it is a non-negotiable requirement that I start with that .vtu file, and I must convert it to a .msh file that can be read into MuJoCo as a soft entity via flexcomp. I converted the .vtu file into a .msh file using the following lines of code that use meshio
:
mesh = meshio.read("cyl_diffusion_out_000_0.vtu")
meshio.write("vtu_converted_msh41_cyl.msh", mesh)
How can I input the vtu_converted_msh41_cyl.msh
file into your code so that I get a resulting .msh file that has a single volume and/or surface entity? Is it not working because meshio
converted the .vtu file incorrectly? If this is the case, do you know a way to reliably convert a .vtu file into a .msh file that is then readable into converter.py
?
Q1. How can I input the vtu_converted_msh41_cyl.msh file into your code so that I get a resulting .msh file that has a single volume and/or surface entity? Is it not working because meshio converted the .vtu file incorrectly?
If you try to open vtu_converted_msh41_cyl.msh in GMSHapp you get an error. which tells me it is not a valid file format. That is why the converter fails.
Q2. If this is the case, do you know a way to reliably convert a .vtu file into a .msh file that is then readable into converter.py? I am not familiar with the VTK format. But I tried opening it using the following script
import vtk
print(vtk.__version__)
# Create a reader for polygonal data
reader = vtk.vtkPolyDataReader()
# Set the file name
reader.SetFileName("cyl_diffusion_out_000_0.vtu")
# Read the file
reader.Update()
and i got this output:
9.3.0
2024-04-05 11:17:57.798 ( 0.204s) [ 7D296AC70740] vtkDataReader.cxx:555 ERR| vtkPolyDataReader (0x2bb7b40): Unrecognized file type: <?xml version="1.0"?> for file: cyl_diffusion_out_000_0.vtu
I also tried opening it with this app: https://www.paraview.org/desktop/ I did not have any luck Can you tell me more about how this file was generated? Was it generated via a script? meshio might have failed because the .vtu file is not valid. Having looked at the content of the file I can see the node indexes:
<DataArray type="Int32" Name="libmesh_node_id" format="ascii" RangeMin="0" RangeMax="795">
</DataArray>
nodes
<Points>
<DataArray type="Float64" Name="Points" NumberOfComponents="3" format="ascii"
RangeMin="0.3999999999999999" RangeMax="1.118033988749895">
</DataArray>
</Points>
elements_indexes
<DataArray type="Int32" Name="libmesh_elem_id" format="ascii" RangeMin="0" RangeMax="2256">
</DataArray>
connectivity which might be how nodes are connected
<DataArray type="Int64" Name="connectivity" format="ascii" RangeMin="0" RangeMax="795">
</DataArray>
offset. Looking at the numbers each number seems to be incremented by 4 which is exact number of nodes you need to create a tetrahedral. Therefore my best guess is that this contains information on how to slice connectivity data.
<DataArray type="Int64" Name="offsets" format="ascii" RangeMin="4" RangeMax="9028">
</DataArray>
type. My best guess is this describes the element type (tetrahedral, triangle ... )
<DataArray type="UInt8" Name="types" format="ascii" RangeMin="10" RangeMax="10">
</DataArray>
If you help me better understand how this file was created I might be able to write a converter.
The .vtu file was the output of a finite element solve performed with MOOSE (an open-source finite-element framework).
Assuming you are familiar with MOOSE/have downloaded the repository (with download instructions detailed here), I generated the file by using the example which can be seen here (link: https://github.com/idaholab/moose/tree/next/examples/ex01_inputfile). The image below shows the relevant files in this simple MOOSE example:
Importantly, instead of using the input file ex01.i
, I used a modified input file called cyl_diffusion.i
. Within the input file, it reads in the hollow_cylinder_groups.msh
file as an input mesh, and outputs the cyl_diffusion_out_000_0.vtu
file. Below is the complete contents of the cyl_diffusion.i
file that I used:
[Mesh]
# We use a pre-generated mesh file (in msh format).
# This mesh file has 'top' and 'bottom' named boundaries defined inside it.
file = hollow_cylinder_groups.msh
[]
[Variables]
[./diffused]
order = FIRST
family = LAGRANGE
[../]
[]
[Kernels]
[./diff]
type = Diffusion
variable = diffused
[../]
[]
[BCs]
[./bottom] # arbitrary user-chosen name
type = DirichletBC
variable = diffused
boundary = 'bottom' # This must match a named boundary in the mesh file
value = 1
[../]
[./top] # arbitrary user-chosen name
type = DirichletBC
variable = diffused
boundary = 'top' # This must match a named boundary in the mesh file
value = 0
[../]
[]
[Executioner]
type = Steady
solve_type = 'PJFNK'
[]
[Outputs]
execute_on = 'timestep_end'
vtk = true
[]
If you are unfamiliar with finite element analysis, then the above might not make any sense to you! Note that I think I have found a rather inefficient, makeshift solution - though I'd be much interested in a better solution if you think there is one? What I did was converted the .vtu file to a .msh file (MSH 2.2) using Meshio, and then I used Gmsh to convert the MSH 2.2 file to MSH 4.1, returning a suitable file. This meshio discussion thread (link: https://github.com/nschloe/meshio/issues/865) talks about how writing various file types to MSH 4.1 is a longstanding issue in meshio. It says that converting to MSH 2.2 with meshio before converting to MSH 4.1 with Gmsh is the current best makeshift solution?
FYI, I was able to open the .vtu file in Paraview? See below:
This link (https://docs.vtk.org/en/latest/design_documents/VTKFileFormats.html#unstructuredgrid) has more information on the explicit .vtu file format that I have above if it helps!
Can you provide the MSH2.2 and MSH4.1 for the vtu file please.
Sure - msh22_and_msh41.zip contains the MSH2.2 and MSH4.1 files. The MSH2.2 file is called vtu_converted_msh22_cyl.msh
(since it was created by converting a .vtu file to MSH2.2), and the MSH4.1 file is called msh22_converted_msh41_cyl.msh
.
@mohammad200h Do you think there is a more efficient solution than the one I used?
I am not familiar with vtu format. So I can not comment on that.
We might look into it and add it to the converter that we are building.
No problems - do you know roughly when the converter will be released?
Can not give an accurate estimate. But hopefully soon!
Hi @mohammad200h - I know how to convert a MSH file to an STL file using Gmsh (simply open the .msh file and export it to an .stl file). However, I am unable to use this method to convert an STL file to a MSH file.
The following zip folder (3d_mbb_STL2MSH.zip) contains an STL file called 3d_mbb_ascii.stl
which I would like to convert to a .msh file. When I opened the STL file in GMSH and wrote it to a file called converted_ascii_stl3dmbb.msh
, the resulting MSH file only contained a single surface entity (and no volume entities). Hence, when I then attempted to convert converted_ascii_stl3dmbb.msh
to a MSH file with a single volume entity using your provided code, the file conversion failed. The file 3dmbb_nogroups_mujoco_vol.msh
contains the attempted file conversion, where I used your code to convert converted_ascii_stl3dmbb.msh
to 3dmbb_nogroups_mujoco_vol.msh
.
The root of the problem is converting an STL file to a MSH file - do you know a feasible way of doing this? This is important as I want to model the STL file (visualised below) as a soft body using flexcomp
, and hence I must convert the STL file to a MSH file with a single volume entity?
Hey @kurtenkera You can run the following python script:
import gmsh
import math
#https://gitlab.onelab.info/gmsh/gmsh/-/blob/master/examples/api/remesh_stl.py?ref_type=heads
# Initialize Gmsh
gmsh.initialize()
# Merge the STL file
gmsh.merge("3d_mbb_ascii.stl")
# get all surfaces
s = gmsh.model.getEntities(2)
# create a surface loop from all the surfaces
l = gmsh.model.geo.addSurfaceLoop([e[1] for e in s])
# add a volume bounded by that surface loop
gmsh.model.geo.addVolume([l])
gmsh.model.geo.synchronize()
gmsh.model.mesh.generate(3)
# Save the mesh
gmsh.write("3d_mbb_ascii.msh")
# Finalize Gmsh
gmsh.finalize()
which will lead to this error. Because there is an issue with your mesh:
Info : Reading '3d_mbb_ascii.stl'...
Info : 17409 facets in solid 0 Visualization Toolkit generated SLA File
Warning : 0 duplicate/64 degenerate triangles in STL file
Info : Done reading '3d_mbb_ascii.stl'
Info : Meshing 1D...
Info : Done meshing 1D (Wall 7.12508e-06s, CPU 6e-06s)
Info : Meshing 2D...
Info : Done meshing 2D (Wall 1.5042e-05s, CPU 8e-06s)
Info : Meshing 3D...
Info : 3D Meshing 1 volume with 1 connected component
Info : Tetrahedrizing 10456 nodes...
Info : Done tetrahedrizing 10464 nodes (Wall 0.117862s, CPU 0.239193s)
Info : Reconstructing mesh...
Info : - Creating surface mesh
Info : - Identifying boundary edges
Info : - Recovering boundary
Error : PLC Error: A segment and a facet intersect at point
or you can follow the following tutorial: https://www.youtube.com/watch?v=RlZ6hPIo9F8
which will lead to the same error.
You need to clean the mesh and try the above methods or just try ftetwild: https://github.com/wildmeshing/fTetWild
which cleans and generate a volumetric mesh: 3d_mbbascii.stl.msh.zip
which you need to clean to be useable by mujoco using the converter: 3d_mbb_ascii.stl_out_vol.msh.zip
Thanks @mohammad200h very helpful as always - I will look into mesh cleaning/ftetwild. By the way, the cleaned volumetric mesh and also the mesh useable by mujoco were in Gmsh 2.2 format, whereas we need Gmsh 4.1? Is there any chance you can link me to the tutorials that you followed to use ftetmesh and/or the code that was used for mesh cleaning?
1. Mujoco should support 2.2 and 4.1 as long as you have a single entity in the file. Latest cleaner code allows for generation of both 4.1 and 2.2. it is still under progress. The latest code can be found here: https://github.com/mohammad200h/GMSHConverter/tree/tree 2. There is not a tutorial. After you build ftetwild. You can use the following command to clean and generate a volumetric mesh. assuming you are in the ftetwild folder:
./build/FloatTetwild_bin -i <path to where the file is>/3d_mbb_ascii.stl
@mohammad200h I have MuJoCo version 3.1.2 and I could not import the file 3d_mbb_ascii.stl_out_vol.msh
as a softcomp
soft-body. When I tried to execute the following code in a Jupyter Notebook code-cell:
import mujoco
import mediapy as media
xml = r"""
<mujoco model="3DMBB">
<worldbody>
<flexcomp name="3DMBB" type="gmsh" dim="3" file="3d_mbb_ascii.stl_out_vol.msh">
<edge equality="true"/>
</flexcomp>
</worldbody>
</mujoco>
"""
# Make model and data
model = mujoco.MjModel.from_xml_string(xml)
data = mujoco.MjData(model)
# Make renderer, render and show the pixels
renderer = mujoco.Renderer(model)
mujoco.mj_forward(model, data)
renderer.update_scene(data)
media.show_image(renderer.render())
I got the following error:
ValueError: XML Error: Error: Only GMSH file format 4.1 supported
Element 'flexcomp', line 4
(1) So which versions of MuJoCo support both Gmsh 2.2 and 4.1?
Also, I converted 3d_mbb_ascii.stl_out_vol.msh
to a MSH 4.1 file (attached:
MSH41_exceed_memory.zip), but I couldn't even load in the mesh as I exceeded memory constraints? I tried following the advice here (https://mujoco.readthedocs.io/en/2.3.7/XMLreference.html#size-memory) but even still, MuJoCo couldn't load in the file (which has ~10k nodes, ~40k elements). After including the line <size memory = "8G"/>
in my XML file, I got the following error:
ValueError: Error: engine error: mj_stackAlloc: insufficient memory: max = 8573357512, available = 107779936, requested = 8461964232 (ne = 57810, nf = 0, nefc = 57810, ncon = 0)
(2) How do I load in .msh files with a large amount of nodes and elements like this one? Do I need to try run the model on a different machine with more RAM (my machine has 64GB of memory)? Even when I try to load the model with the following setting <size memory = "20G"/>
to increase the memory allocation I get the following output:
ValueError: XML Error: unsigned integer with an optional suffix {K,M,G,T,P,E} is expected in attribute 'memory' (or the size specified is too big)
Element 'size', line 3
This indicates that I can't allocate 20GB of memory even though I should be able to on my machine?
See item 3 in the 3.1.4 changelog
Hi, I'm opening an issue that extends on the discussion related to issue #1492 (as this issue was closed and I'm not a repo collaborator, I cannot re-open the issue).
I'm trying to import a binary .msh file of a hollow cylinder into MuJoCo. Below is the code that I'm trying to execute in a Jupyter notebook environment:
When I run the code above, I get the following error message:
Here is the .msh file that I'm trying to use: hollow_cylinder_binary.zip
I desperately need to import this binary .msh file into MuJoCo for this project that I'm doing. I've been able to import a binary STL file successfully, however, the binary msh file approach is not working!
Thanks in advance!