porepy.utils.adtree module

Module containing the implementation of an alternating digital tree (ADT) for 3D geometric searching and intersection problems.

See the following works: 10.1002/nme.1620310102 and 10.1201/9781420050349 section 14.

Let [a, b) be the interval that contains the points that we want to insert in a binary tree. We then divide the interval into two equal parts: all the points in the interval [a, (a+b)/2) will be inserted in the left subtree, while the points in the interval [(a+b)/2, b ) will be placed in the right subtree. The reasoning is then iterated to the next level for each subtree.

[0, 1) / / / [0, 0.5) [0.5, 1)

/ | | / | | / | | [0, 0.25) [0.25, 0.5) [0.5, 0.75) [0.75, 1)

When inserting the following nodes A = 0.1, B = 0.6, C = 0.7, D = 0.8, E = 0.2 and F = 0.1 in the tree, we get the following

A

/ / E B

/ / / / F -1 C D

The first element A is added as the root. The second element B check if its coordinate (in this case is a single value) is smaller than 0.5. If so, it goes on the left part of the tree starting from the root otherwise on the right part. Now, since B = 0.6 it goes on the right part. Now are with the node C, we need to check as before if its coordinate is smaller than 0.5 (so it goes on the left) or bigger than 0.5 (so it goes on the right). Since it is 0.7 it goes on the right and being already taken by B we need to go one level down. We check now if its coordinate is smaller (left) or bigger (right) than 0.75. Since it’s smaller we proceed on the left part and being empty we add it. The insertion is not related to the parent but to which level and coordinate a node has. For the multi-dimension case we alternate the dimension by each level, so first we check the abscissa (again with left and right decided as before) and then the ordinate and so on. We detail a bit more here in the sequel.

In the multi-dimensional case, the ADT is organized in the same way, but the subdivision takes place alternately for the various coordinates: if the structure must contain n-dimensional points, at the i-th level of the tree the subdivision is carried out with respect to the j-th coordinate, where j is the remainder of the i / n division. We immediately observe that the n-dimensional “points”, the structure contains true points in 2 or 3 dimensions, and rectangles or parallelepipeds, which can be represented by “points” in 4 and 6 dimensions, with the writing (xmin, ymin, zmin, xmax, ymax, zmax). Other geometrical objects are represented by their bounding box. To avoid floating point problems, all the “points” are rescaled in [0, 1].

A search in the tree gives a list of all possible nodes that may intersect the given one.

class ADTNode(key, box)[source]

Bases: object

Simple bookkeeping class that contains the basic information of a tree node.

Parameters
key

any key related to the node

Type

Any

box

the bounding box associated to the node

Type

np.ndarray

child

list of identification of right and left children, if a children is not present is marked as -1.

Type

list

parent

identification of the parent node, marked -1 if not present (the root of a tree)

Type

int

class ADTree(tree_dim, phys_dim)[source]

Bases: object

ADT structure, it is possible to fill the tree by giving a PorePy grid and then search for possible intersections. The implementation does not include some features, like removing a node, that are not used so far. Possible extensions in the future.

Parameters
  • tree_dim (int) –

  • phys_dim (int) –

tree_dim

search dimension of the tree, typically (e.g., when a pp.Grid is given) the double of the phys_dim

Type

int

phys_dim

physical dimension of nodes in the tree, e.g., a 2d grid will have phys_dim = 2

Type

int

nodes

the list of nodes as ADTNode

Type

list

region_min

to scale the bounding box of all the elements in [0, 1]^phys_dim we need the minimum corner point of the all region

Type

float

delta

a parameter to scale and get all the bounding box of the elements in [0, 1]^phys_dim

Type

float

LEFT

define the index of the left child, being equal to 0

Type

int

RIGHT

define the index of the right child, being equal to 1

Type

int

add_node(node)[source]

Add a new node to the tree. We traverse the tree as previously specified and assign the new node accordingly.

Parameters

node (ADTNode) – the new node to be added.

Return type

None

from_grid(g, only_cells=None)[source]

Function that constructs the tree from a grid by adding one cell at a time.

Parameters
  • g (pp.Grid) – The grid to be used to construct the tree

  • only_cells (np.ndarray, optional) – Consider only a portion of the cells due to some a-priori estimates of the searching elements.

Return type

None

search(node, tol=2.0e-6)[source]

Search all possible nodes in the tree that might intersect with the input node. The node is not added to the tree.

Parameters
  • node (ADTNode) – Input node

  • tol (float, optional) – Geometrical tolerance to avoid floating point problems

Returns

Sorted, by id, list of nodes id that might intersect the node

Return type

nodes (np.ndarray)

LEFT: int
RIGHT: int