ndgrid/
lib.rs

1//! Data structures to work with n-dimensional grids.
2//!
3//! `ndgrid` builds upen [ndelement] to provide data structures for grids on either a single node or distributed via MPI.
4//!
5//! ## Creating a grid with `ndgrid`
6//!
7//! To demonstrate the library, we use an example grid consisting of two triangles that together form the unit square.
8//! We introduce the following points. As we will make a grid of second oder elements, we include points at the midpoint
9//! of each edge as well as at the vertices of the square
10//! - Point 0: (0, 0)
11//! - Point 1: (1, 0)
12//! - Point 2: (0, 1)
13//! - Point 3: (1, 1)
14//! - Point 4: (0.5, 0.5)
15//! - Point 5: (0.0, 0.5)
16//! - Point 6: (0.5, 0.0)
17//! - Point 7: (0.5, 1.0)
18//! - Point 8: (1.0, 0.5)
19//!
20//! The order of points for each cell follows the point ordering of the
21//! [Lagrange element on DefElement](https://defelement.org/elements/lagrange.html).
22//! For second order triangles we use
23//! [this ordering](https://defelement.org/elements/examples/triangle-lagrange-equispaced-2.html).
24//! Following this ordering, the two cells of our example grid are:
25//!
26//! - Cell 1: 0, 1, 2, 4, 5, 6
27//! - Cell 2: 1, 3, 2, 7, 4, 8
28//!
29//! In order to create our grid using ndgrid, we first create a grid builder.
30//! ```
31//! use ndgrid::traits::Builder;
32//! use ndelement::types::ReferenceCellType;
33//! let mut builder = ndgrid::SingleElementGridBuilder::<f64>::new_with_capacity(
34//!     2,
35//!     9,
36//!     2,
37//!     (ReferenceCellType::Triangle, 2),
38//! );
39//! ```
40//!
41//! The [SingleElementGridBuilder] is for grids that only use a single element type. The parameters passed when
42//! initialising the build are:
43//!
44//! - The geometric dimension: our example grid lives in two-dimensional space, so we use 2.
45//! - The number of points: 9 for our example grid.
46//! - The number of cells: 2 for our example grid.
47//! - The cell type and element degree: for our example, this is ([ReferenceCellType::Triangle](ndelement::types::ReferenceCellType::Triangle), 2)
48//!   as our geometry cells are triangles and we use quadratic geometry for each triangle.
49//!
50//! If we did not know the number of points and cells that we will include in out grid when creating ths builder,
51//! we could instead use the function [SingleElementGridBuilder::new] when initialising the grid.
52//!
53//! Now that we have created a grid builder, we can add the points and cells:
54//!
55//! ```
56//! # use ndgrid::traits::Builder;
57//! # use ndelement::types::ReferenceCellType;
58//! # let mut builder = ndgrid::SingleElementGridBuilder::<f64>::new_with_capacity(
59//! #     2,
60//! #     9,
61//! #     2,
62//! #     (ReferenceCellType::Triangle, 2),
63//! # );
64//! builder.add_point(0, &[0.0, 0.0]);
65//! builder.add_point(1, &[1.0, 0.0]);
66//! builder.add_point(2, &[0.0, 1.0]);
67//! builder.add_point(3, &[1.0, 1.0]);
68//! builder.add_point(4, &[0.5, 0.5]);
69//! builder.add_point(5, &[0.0, 0.5]);
70//! builder.add_point(6, &[0.5, 0.0]);
71//! builder.add_point(7, &[0.5, 1.0]);
72//! builder.add_point(8, &[1.0, 0.5]);
73//!
74//! builder.add_cell(1, &[0, 1, 2, 4, 5, 6]);
75//! builder.add_cell(2, &[1, 3, 2, 7, 4, 8]);
76//! ```
77//! Finally, we generate the grid.
78//! ```
79//! # use ndgrid::traits::Builder;
80//! # use ndelement::types::ReferenceCellType;
81//! # let mut builder = ndgrid::SingleElementGridBuilder::<f64>::new_with_capacity(
82//! #     2,
83//! #     9,
84//! #     2,
85//! #     (ReferenceCellType::Triangle, 2),
86//! # );
87//! # builder.add_point(0, &[0.0, 0.0]);
88//! # builder.add_point(1, &[1.0, 0.0]);
89//! # builder.add_point(2, &[0.0, 1.0]);
90//! # builder.add_point(3, &[1.0, 1.0]);
91//! # builder.add_point(4, &[0.5, 0.5]);
92//! # builder.add_point(5, &[0.0, 0.5]);
93//! # builder.add_point(6, &[0.5, 0.0]);
94//! # builder.add_point(7, &[0.5, 1.0]);
95//! # builder.add_point(8, &[1.0, 0.5]);
96//! #
97//! # builder.add_cell(1, &[0, 1, 2, 4, 5, 6]);
98//! # builder.add_cell(2, &[1, 3, 2, 7, 4, 8]);
99//! let grid = builder.create_grid();
100//! ```
101//!
102//! ## Querying the grid
103//!
104//! A grid is a hierarchy of entities. We follow the standard name conventions for entities of a given topological dimension:
105//! 0-, 1-, 2- and 3-dimensional entities and called vertices, edges, faces and volumes (respectively).
106//! The highest dimensional entities are called cells. If $d$ the (topological) dimension of the cells,
107//! then $d-1$-, $d-2$- and $d-3$-dimensional entities are called facets, ridges and peaks (respectively).
108//!
109//! For each entity there are two types of information: the topology and the geometry.
110//! The topology describes how entities are connected. The geometry describes how entities are positioned in physical space.
111//! As the topology is only concerned with the connectivity between entities, it only includes the cell's points that are at
112//! the vertices of a cell (eg for the triangle cells in our grid, the topology onle includes the first three points for each cell).
113//! In the geometry, all the points that define the cell are stored.
114//!
115//! Each entity has an associated `index`. Indices are unique within entities of a given type:
116//! there is a vertex with index 0 and a cell with index 0 but there cannot be two vertices with index 0. Points and
117//! cells may also have an associated `id`: these are the values provided by the user when using the `add_point` or `add_cell`
118//! methods in the grid builder. These ids are not guaranteed to be equal to the indices of the entities.
119//!
120//! The following code extracts the vertices of each cell and prints their corresponding physical coordinates.
121//! ```
122//! # use ndgrid::traits::Builder;
123//! use ndgrid::traits::{Grid, Entity, Topology, Geometry, Point};
124//! # use ndelement::types::ReferenceCellType;
125//! # let mut builder = ndgrid::SingleElementGridBuilder::<f64>::new_with_capacity(
126//! #     2,
127//! #     9,
128//! #     2,
129//! #     (ReferenceCellType::Triangle, 2),
130//! # );
131//! # builder.add_point(0, &[0.0, 0.0]);
132//! # builder.add_point(1, &[1.0, 0.0]);
133//! # builder.add_point(2, &[0.0, 1.0]);
134//! # builder.add_point(3, &[1.0, 1.0]);
135//! # builder.add_point(4, &[0.5, 0.5]);
136//! # builder.add_point(5, &[0.0, 0.5]);
137//! # builder.add_point(6, &[0.5, 0.0]);
138//! # builder.add_point(7, &[0.5, 1.0]);
139//! # builder.add_point(8, &[1.0, 0.5]);
140//! #
141//! # builder.add_cell(1, &[0, 1, 2, 4, 5, 6]);
142//! # builder.add_cell(2, &[1, 3, 2, 7, 4, 8]);
143//! # let grid = builder.create_grid();
144//!
145//! for cell in grid.entity_iter(ReferenceCellType::Triangle) {
146//!     for vertex in cell.topology().sub_entity_iter(ReferenceCellType::Point) {
147//!         let vertex = grid.entity(ReferenceCellType::Point, vertex).unwrap();
148//!         let mut coords = [0.0; 2];
149//!         vertex
150//!             .geometry()
151//!             .points()
152//!             .next()
153//!             .unwrap()
154//!             .coords(&mut coords);
155//!         println!(
156//!             "Cell {} has vertex {} with coordinate [{}, {}]",
157//!             cell.id().unwrap(),
158//!             vertex.id().unwrap(),
159//!             coords[0],
160//!             coords[1]
161//!         )
162//!     }
163//! }
164//! ```
165//!
166//! This snippets starts by using [Grid::entity_iter](crate::traits::Grid::entity_iter) to iterate through each
167//! cell (ie each entity that is a triangle).
168//! For each cell, we then access the topology information via [Entity::topology](crate::traits::Entity::topology)
169//! and iterate through the vertices (ie the subentities that are points) using [Topology::sub_entity_iter](crate::traits::Topology::sub_entity_iter).
170//! This iterators gives us the index of each vertex: to convert an entity index to an entity, we use [Grid::entity](crate::traits::Grid::entity).
171//! We now want to get the actual physical coordinate of a vertex.
172//! Since the geometric dimension is 2 we instantiate an array `[f64; 2]` for this. We use
173//! [Entity::geometry](crate::traits::Entity::geometry) to obtain the geometry for the vertex, then use
174//! [Geometry::points](crate::traits::Geometry::points) to get an iterator over the physical points
175//! associated with the vertex. Since a vertex has only one associated physical point, we
176//! call `next` once on this iterator to get the point. Finally, we call [Point::coords](crate::traits::Point::coords)
177//! to get the values of the physical coordinate.
178
179#![cfg_attr(feature = "strict", deny(warnings), deny(unused_crate_dependencies))]
180#![warn(missing_docs)]
181
182pub mod geometry;
183pub mod grid;
184mod io;
185pub mod shapes;
186pub mod topology;
187pub mod traits;
188pub mod types;
189
190#[cfg(feature = "mpi")]
191pub use grid::ParallelGridImpl;
192pub use grid::{MixedGrid, MixedGridBuilder, SingleElementGrid, SingleElementGridBuilder};
193pub use ndelement;
194
195// Hack to avoid unused dependency errors if partitioner features are used without the mpi feature
196#[cfg(not(feature = "mpi"))]
197#[cfg(feature = "coupe")]
198use coupe as _;
199#[cfg(not(feature = "mpi"))]
200#[cfg(feature = "scotch")]
201use scotch as _;