landlab.graph.quasi_spherical package#

Subpackages#

Submodules#

landlab.graph.quasi_spherical.dual_icosphere module#

icosphere_global_grid: Create a Landlab global (i.e., quasi-spherical) grid based on an IcoSphere, which is formed by iteratively densifying an icosahedron.

Greg Tucker, University of Colorado Boulder, September 2023

class DualIcosphereGraph(radius=1.0, mesh_densification_level=0)[source]#

Bases: object

A Landlab global (quasi-spherical) graph type based on an IcoSphere.

Parameters:
  • radius (float, optional) – Radius for the icosphere, m (default 1)

  • mesh_densification_level (int, optional) – Number of iterative subdivisions of initial triangles (default 0)

Initialize DualIcosphereGraph

Parameters:
  • radius (float, optional) – Radius of the sphere (default 1.0)

  • mesh_densification_level (int, optional) – Number of times to subdivide the mesh (default 0)

Notes

Data structures set up include the following (note that n_cells = n_nodes and n_faces = n_links): - coords_of_node : ndarray of float (n_nodes, 3) - x_of_node, y_of_node, z_of_node : ((n_nodes, ) views of coords_of_node) - coords_of_corner : ndarray of float (n_corners, 3) - x_of_corner, y_of_corner, z_of_corner : ndarray of float (n_corners, ) - length_of_link : ndarray of float (n_links, ) - nodes_at_link : ndarray of int (n_links, 2) - node_at_link_tail, node_at_link_head : (n_links, ) views of nodes_at_link - links_at_node : ndarray of int (n_nodes, 6) - cell_at_node, node_at_cell : ndarray of int (n_nodes, ) - corners_at_face : ndarray of int (n_links, 2) - corners_at_node, corners_at_cell : ndarray of int (n_nodes, 6) - area_of_cell : ndarray of float (n_nodes, ) - length_of_face : ndarray of float (n_links, ) - link_at_face, face_at_link : ndarray of int (n_links, ) - faces_at_cell : ndarray of int (n_nodes, 6) - adjacent_nodes_at_node : ndarray of int (n_nodes, 6)

Examples

>>> import numpy as np

Basic example: dodecahedron

>>> ico = DualIcosphereGraph()
>>> np.round(ico.coords_of_node[0], 3)
array([-0.526,  0.851,  0.   ])
>>> ico.r_of_node[0]
1.0
>>> int(ico.phi_of_node[0] * 100), int(ico.theta_of_node[0] * 100)
(212, 157)
>>> np.round(ico.coords_of_corner[1], 3)
array([-0.   ,  0.934,  0.357])
>>> round(ico.length_of_link[0], 3)
1.107
>>> ico.nodes_at_link[0]
array([ 0, 11])
>>> ico.links_at_node[0]
array([ 0,  2,  4,  6,  8, -1])
>>> ico.cell_at_node[0]
0
>>> ico.node_at_cell[1]
1
>>> ico.corners_at_face[0]
array([0, 4])
>>> int(10000 * ico.length_of_face[0])
7297
>>> ico.corners_at_node[0]
array([ 3,  4,  0,  1,  2, -1])
>>> ico.corners_at_cell[0]
array([ 3,  4,  0,  1,  2, -1])
>>> int(1e6 * ico.area_of_cell[0])
1047197
>>> ico.link_at_face[2]
2
>>> ico.face_at_link[3]
3
>>> ico.faces_at_cell[0]
array([ 0,  2,  4,  6,  8, -1])
>>> ico.adjacent_nodes_at_node[0]
array([11,  5,  1,  7, 10, -1])

Icosphere with 1 level of subdivision

>>> ico = DualIcosphereGraph(mesh_densification_level=1)
>>> ico.number_of_patches
80
__init__(radius=1.0, mesh_densification_level=0)[source]#

Initialize DualIcosphereGraph

Parameters:
  • radius (float, optional) – Radius of the sphere (default 1.0)

  • mesh_densification_level (int, optional) – Number of times to subdivide the mesh (default 0)

Notes

Data structures set up include the following (note that n_cells = n_nodes and n_faces = n_links): - coords_of_node : ndarray of float (n_nodes, 3) - x_of_node, y_of_node, z_of_node : ((n_nodes, ) views of coords_of_node) - coords_of_corner : ndarray of float (n_corners, 3) - x_of_corner, y_of_corner, z_of_corner : ndarray of float (n_corners, ) - length_of_link : ndarray of float (n_links, ) - nodes_at_link : ndarray of int (n_links, 2) - node_at_link_tail, node_at_link_head : (n_links, ) views of nodes_at_link - links_at_node : ndarray of int (n_nodes, 6) - cell_at_node, node_at_cell : ndarray of int (n_nodes, ) - corners_at_face : ndarray of int (n_links, 2) - corners_at_node, corners_at_cell : ndarray of int (n_nodes, 6) - area_of_cell : ndarray of float (n_nodes, ) - length_of_face : ndarray of float (n_links, ) - link_at_face, face_at_link : ndarray of int (n_links, ) - faces_at_cell : ndarray of int (n_nodes, 6) - adjacent_nodes_at_node : ndarray of int (n_nodes, 6)

Examples

>>> import numpy as np

Basic example: dodecahedron

>>> ico = DualIcosphereGraph()
>>> np.round(ico.coords_of_node[0], 3)
array([-0.526,  0.851,  0.   ])
>>> ico.r_of_node[0]
1.0
>>> int(ico.phi_of_node[0] * 100), int(ico.theta_of_node[0] * 100)
(212, 157)
>>> np.round(ico.coords_of_corner[1], 3)
array([-0.   ,  0.934,  0.357])
>>> round(ico.length_of_link[0], 3)
1.107
>>> ico.nodes_at_link[0]
array([ 0, 11])
>>> ico.links_at_node[0]
array([ 0,  2,  4,  6,  8, -1])
>>> ico.cell_at_node[0]
0
>>> ico.node_at_cell[1]
1
>>> ico.corners_at_face[0]
array([0, 4])
>>> int(10000 * ico.length_of_face[0])
7297
>>> ico.corners_at_node[0]
array([ 3,  4,  0,  1,  2, -1])
>>> ico.corners_at_cell[0]
array([ 3,  4,  0,  1,  2, -1])
>>> int(1e6 * ico.area_of_cell[0])
1047197
>>> ico.link_at_face[2]
2
>>> ico.face_at_link[3]
3
>>> ico.faces_at_cell[0]
array([ 0,  2,  4,  6,  8, -1])
>>> ico.adjacent_nodes_at_node[0]
array([11,  5,  1,  7, 10, -1])

Icosphere with 1 level of subdivision

>>> ico = DualIcosphereGraph(mesh_densification_level=1)
>>> ico.number_of_patches
80
property area_of_patch#
property cell_at_node#
property corners_at_cell#
property faces_at_cell#

Face-cell and node-link numbering are the same!

property node_at_cell#
property patches_at_node#
property phi_of_node#
property r_of_node#
property theta_of_node#

landlab.graph.quasi_spherical.refinable_icosahedron module#

RefinableIcosahedron: class that creates the vertices and triangular faces of an icosahedron (20-sided polygonal solid) that can be iteratively refined by replacing each triangle with four smaller triangles. Designed to provide the starting point for a Landlab icosphere grid.

Greg Tucker, University of Colorado Boulder, 2023 Adapted from a blog post and code snippet by Andreas Kahler, and translated into Python (plus function to output in vtk format)

class RefinableIcosahedron(radius=1.0)[source]#

Bases: object

An icosahedron with faces that can be iteratively subdivided.

Class includes Cartesian coordinates of vertices (12 if not subdivided) and IDs of vertices composing each face (20 faces if not subdivided). Adapted from a blog post by Andreas Kahler.

Parameters:

radius (float) – Radius for the RefinableIcosahedron, length units (default 1)

Examples

Basic icosahedron

>>> ico = RefinableIcosahedron()
>>> len(ico.faces)
20
>>> len(ico.vertices)
12

Icosphere with two iterations of refinement

>>> ico.refine_triangles(recursion_level=2)
>>> len(ico.faces)
320
>>> len(ico.vertices)
162

Icosahedron with radius != 1

>>> ico = RefinableIcosahedron(radius=2.0)
>>> round(ico.vertices[1][0], 2)
1.05
>>> round(ico.vertices[0][1], 2)
1.7

Initialize RefinableIcosahedron

__init__(radius=1.0)[source]#

Initialize RefinableIcosahedron

add_vertex(vtx)[source]#

Add a vertex, scaling its coordinates to fit the given radius, and return its index in the vertices list.

Parameters:

vtx (3-element tuple of float) – x, y, and z coordinates of vertex

Returns:

int

Return type:

index number of the new vertex

create_faces()[source]#

Create the 20 triangular faces of the icosahedron.

Faces are stored in a list of 3-element tuples with the vertex IDs.

Note that “face” here means a triangle on the surface of the object, and is different from the Landlab definition of face as the edge shared by two neighboring cells.

create_vertices()[source]#

Create the 12 vertices of an icosahedron.

Note that the vertex coordinates will be scaled to match the given radius.

get_middle_point(p1, p2)[source]#

Identify and add a new point between two existing points.

Parameters:
  • p1 (int) – IDs of two points (vertices)

  • p2 (int) – IDs of two points (vertices)

Returns:

int

Return type:

index of the point (vertex) added

Notes

This function is used to refine the icosphere, and is called to place new vertices in the middle of the edges of triangles. Because each edge is shared by two triangles, we have to avoid adding the same point twice. To do this, we use a dictionary (middle_point_index_cache) to keep track of points already added. Each point has a key made of the two vertex IDs (one is bit-shifted, then they are added together). We only add a point if it isn’t already in the dict.

refine_triangles(recursion_level=1)[source]#

Subdivide each triangle into four, and add corresponding vertices.

Parameters:

recursion_level (int, optional) – Number of subdivisions to apply (default 1)

write_to_vtk(path, clobber=False)[source]#

Save the geometry in a vtk-format text file.

Note: this function is intended to test the RefinableIcosahedron. To write vtk for a Landlab IcosphereGlobalGrid, use landlab.io.legacy_vtk.dump to capture the full set of geometric primitives.

Parameters:
  • path (str, path-like , or file-like) – Target for output.

  • clobber (bool, optional) – Whether to allow overwriting of existing file.

Returns:

path – The given output path

Return type:

same as input above

Examples

>>> import io
>>> import os
>>> ico = RefinableIcosahedron()
>>> output = ico.write_to_vtk(io.StringIO())
>>> lines = output.getvalue().splitlines()
>>> print(lines[0])
# vtk DataFile Version 2.0
>>> print(os.linesep.join(lines[5:18]))
POINTS 12 float
-0.5257311121191336 0.85065080835204 0.0
0.5257311121191336 0.85065080835204 0.0
-0.5257311121191336 -0.85065080835204 0.0
0.5257311121191336 -0.85065080835204 0.0
0.0 -0.5257311121191336 0.85065080835204
0.0 0.5257311121191336 0.85065080835204
0.0 -0.5257311121191336 -0.85065080835204
0.0 0.5257311121191336 -0.85065080835204
0.85065080835204 0.0 -0.5257311121191336
0.85065080835204 0.0 0.5257311121191336
-0.85065080835204 0.0 -0.5257311121191336
-0.85065080835204 0.0 0.5257311121191336
>>> print(os.linesep.join(lines[18:40]))
CELLS 20 80
3 0 11 5
3 0 5 1
3 0 1 7
3 0 7 10
3 0 10 11
3 1 5 9
3 5 11 4
3 11 10 2
3 10 7 6
3 7 1 8
3 3 9 4
3 3 4 2
3 3 2 6
3 3 6 8
3 3 8 9
3 4 9 5
3 2 4 11
3 6 2 10
3 8 6 7
3 9 8 1
>>> print(os.linesep.join(lines[41:45]))
CELL_TYPES 20
5
5
5

Module contents#