Tracking Geometric Entities in Python API

Hello!

I currently use Coreform Cubit with the Python API mostly via the command wrapper function cubit.cmd(), but tracking geometric entities can be troublesome if you are performing a lot of boolean operations to divide your geometry this way, so I want to use more functions directly from the Cubit Interface Namespace to take advantage of interacting with geometric entities as objects in Python.

I have a toy problem that I think illustrates many of the reasons why you would want to use an object approach rather than keeping track of geometric entities.

br = cubit.brick(10, 15, 10)
cyl = cubit.cylinder(10, 5.02, 5.02, 5.02)
cubit.move(cyl, (0,5,0))
cubit.subtract([cyl], [br], keep_old_in=True)
cubit.cmd("delete Volume 1")

So we have four volume geometric entities (2: cylinder, 3: upper left cut, 4: upper right cut, 5: lower cut) and only one named object within Python (cyl, as we removed the br object geometric entity).

Let’s say I need to mesh this geometry and wanted to minimally interact with the GUI. How would I go about identifying the new volumes and interacting with them in Python?

I can use cubit.get_last_id("volume") before and after the subtract operation to at least see how many volumes were generated and get their geometric IDs, but now I’m back to tracking geometric entities, and I do not have named objects in Python with which I can interact with the new entities (at least I don’t believe I do).

If I assign a variable name while performing subtract: cuts = cubit.subtract([cyl], [br], keep_old_in=True) I end up with a tuple: (<cubit3.Body; proxy of <Swig Object of type 'CubitInterface::Body *' at 0x10b06ee70> >,) which I’m not really sure what to do with… Is there a way to identify which volumes are contained within this object and potentially assign them to new names (e.g. bottom, left, right)?

In the past, I would look at the volumes of the shapes which does at least identify the large cut portion

ids = [3, 4, 5]
vols = [cubit.volume(3).volume(), cubit.volume(4).volume(), cubit.volume(5).volume()]
big_id = ids[vols.index(max(volumes))]

or I could alternatively use the centroids of all of the objects to identify them provided I knew enough of what to expect the geometry to look like.

Anyways, I wanted to bring this up to get some discussion going around this topic. How would you go about tracking entities after they have been generated here?

Hi,

I have typically used the last id and done computations from that. I would get the last id before the boolean and the last id after. The difference is the number of objects created.

I’m a little surprised that the result of the subtract only gives one object in the tuple. Getting all the elements in created was probably somewhat difficult. You can do print(cuts[0].id()) to see the last id generated in the boolean operation.

The other thing I do is name the objects before the boolean operation. There is a set_entity_name() method in the extended CubitInterface. This does not update the GUI and I can’t use the name in commands. I do this instead.

cubit.cmd('reset')
br = cubit.brick(10, 15, 10)
cubit.cmd( f"volume {br.id()} name '_brick")
cyl = cubit.cylinder(10, 5.02, 5.02, 5.02)
cubit.cmd( f"volume {cyl.id()} name '_cyl")
cubit.move(cyl, (0,5,0))
cuts = cubit.subtract([cyl], [br], keep_old_in=True)
cubit.cmd("delete _brick")

The newly created volumes are named _brick@A, _brick@B, _brick@C so that you know where they came from. I can issue the command draw volume with name "brick*", or group "old_brick" add volume with name "_brick*".

If I want to delete the slivers volumes, I would still have to go through the entities in the group and delete the ones with a volume less than some given tolerance.

Karl

Thanks karl, I like this approach since it effectively eliminates the need to track entity IDs. It would be really nice to eliminate the need for wrapped cubit commands as well. For example, why does the set_entity_name() method not update the entity name within cubit itself (necessitating the cubit.cmd( f"volume {br.id()} name '_brick") line) and why do we need to use a wrapped function to delete geometric entities (cubit.cmd("delete _brick") instead of just a cubit.delete_entity() method). Are there any plans to extend CubitInterface methods to accomplish these functions?

Also, the tuple generated shows that the CubitInterface was generated with Swig but it looks like this may just be an area that hasn’t been built out from the original as it’s referring to pointers here.

Overall, it sounds like it would be nice to have some more development on the python API, but thank you for letting me know about your current approach!