Relative between FLAC3D zone indexes and zonearray index

Hi,

I have questions about the relation between the ids of zones and faces within FLAC and the indexing in the python numpy interface:

  • What is the relation between the index of zones and the indexing within zonearray?

    • For instance is the ordering of it.zonearray.ids() the same as the ordering of the array that is accessible through the numpy interface outside of the fact that FLAC is 1 indexed and numpy is 0 indexed?
    • It seems the case to me but I would like to know under which condition this relation is broken.
  • What is the relation between a zone face ordering and the ordering when accesses through zonearray?

    • For example if I access the faces of a zone individually using Zone.faces() while it be in the same order as when access through it.zonearray.faces().
    • Is the ordering of faces defined in the documentation (Zone — Itasca Software 9.0 documentation) respected in the python zonearray interface? the documentation say say that “the ordering is relative to the ordering of the itasca.gridpointarray return values." and frankly I don’t understand what that means.

If I am asking these questions it is because I want to select faces by their face group but the interface is not accessible in zonearray. Therefore, I plan to find the face group by loop through the zones and using Zone.face_in_group(). But to perform my calculations using python loop is very inefficient so I would to be able to access the face informations through the numpy interface zonearray and therefore I need to make sure that the indexing between the two interface is consistent.

Thank you in advance

Hi!

Not an Itasca employee, so I cannot answer all of your questions.
However, from my experience :


For instance is the ordering of it.zonearray.ids() the same as the ordering of the array that is accessible through the numpy interface outside of the fact that FLAC is 1 indexed and numpy is 0 indexed?

Yes, ordering is consistant between Fish zone.list and both Python it.zonearray.ids() and it.zone.list().

If you want to be sure, you can run the script below in a Flac3D session (feel free to test on other geometries).

import itasca as it
# Create geometry
it.command("zone create tetrahedron size 1 2 3")
# Getting the ID list from Fish
it.command("""fish define get_zone_ids
    global zone_ids=zone.id(::zone.list)
end
""")
it.fish.call_function('get_zone_ids')
zone_ids_fish = it.fish.get('zone_ids')
# ID list from Python API
zone_ids_py = np.array([zone.id() for zone in it.zone.list()])
zone_ids_py_array = it.zonearray.ids()
assert (zone_ids_py ==zone_ids_py_array ).all()
assert (zone_ids_py ==zone_ids_fish ).all()

It seems the case to me but I would like to know under which condition this relation is broken.

Unfortunately, I cannot help on this one.


For example if I access the faces of a zone individually using Zone.faces() while it be in the same order as when access through it.zonearray.faces()

Yes!
However, you have to take care that it.zonearray.faces() contains ghost point indices to be able to fit all faces in a single fixed shape array.
In the example geometry I used, we have a mix of bricks and wedges:

Some zones will thus have both rectangle and triangle faces.
it.zonearray.ids() returns a harmonized (Nzones, Nfaces, Npoints)-shaped array and will fill in the missing points for the triangle faces with a -1 index:

To illustrate this point, we can detail the geometry of the two lower zones (gridpoint marked with their indices in NumPy, for simplicity):

The it.zonarray.faces() contains several types of zones: regular 6-sided ones with rectangular faces (in yellow below), and degenerated ones with triangular faces (in green) and with “missing” faces (in blue).

On the other side, zone.faces() will give strictly the gridpoints forming the zone faces.
Running this Python code, we keep the exact same structure for 6-sided zones (yellow), but -1 ghost points are not present anymore (green), neither are the “missing” faces (no blue).

for zone in it.zone.list():
    for face in zone.faces():
        print([gp.id()-1 for gp in face])
    print()


Is the ordering of faces defined in the documentation (Zone — Itasca Software 9.0 documentation) respected in the python zonearray interface?

Yes !
You can see in the example above that the gridpoint ordering is preserved (see references for bricks and wedges).

the documentation say say that “the ordering is relative to the ordering of the itasca.gridpointarray return values." and frankly I don’t understand what that means.

From my understanding, this specifies that faces are not identified as such (i.e., no Face object) but through the gridpoints they are made of. A face can be seen as a set of ordered points. In Flac3D, a face is identified by its gridpoints ordered clockwise when looking at the face from outside its zone.

Hope it helped, cheers!

Theophile

Thank you for the reply Theophile, that clarify a lot of things!

To add to your extensive explanation, I did some check with by comparing the face normales with ones I calculated using a cross product which is depending of ordering.

if you get the gridpoints from Zone.faces() the increasing index correspond to an anticlockwise ordering to be consistent with the FLAC face orientation (i.e. faces normal are pointing outward).

For example assuming a tetrahedral face, the indexes of the gridpoint that are given by Zone.faces() should be considered like this:

3---<--2
|      |    -> normal pointing toward the inside of the zone
|      |
0--->--1

1--->--2
|      |    -> normal pointing toward the outside of the zone (FLAC convention)
|      |
0---<--3

Glad it helped!

I agree with you: gridpoints are indeed ordered clockwise, giving normal vectors pointing outwards.

Cheers

Theophile

After some check I can complement the answer of Theophile one of my question:

For instance is the ordering of it.zonearray.ids() the same as the ordering of the array that is accessible through the numpy interface outside of the fact that FLAC is 1 indexed and numpy is 0 indexed?

This remains true as long as you don’t start deleting zones from example with zone delete command. In this case the counter of id for zone does not get reset and new zone will have in id that following still the counter as if the zone were not delete and therefore deviate from the indexing in numpy array.

Gridpoints follows the same rules.

Thanks for the feedback, @stephaneBeaussier !

Theophile