diff --git a/.github/scripts/insert_doc_links.py b/.github/scripts/insert_doc_links.py new file mode 100644 index 0000000..7bd88ed --- /dev/null +++ b/.github/scripts/insert_doc_links.py @@ -0,0 +1,34 @@ +"""Replace the links to other crates in documentation strings. + +This can be run before building the docs with the `--no-deps` flag to make the links work +in the generated docs +""" + +import os +import re + + +def _insert_doc_links(content): + content = re.sub(r"(\s)\[ndelement\](\s|\n)", r"\1[ndelement](https://bempp.github.io/ndelement/rust/ndelement/)\1", content) + return content + + +def insert_doc_links(folder): + for file in os.listdir(folder): + if not file.startswith("."): + file_path = os.path.join(folder, file) + if os.path.isdir(file_path): + insert_doc_links(file_path) + elif os.path.isfile(file_path) and file.endswith(".rs"): + with open(file_path) as f: + content = f.read() + with open(file_path, "w") as f: + f.write(_insert_doc_links(content)) + + +root_dir = os.path.join(os.path.join( + os.path.dirname(os.path.realpath(__file__)), + ".."), "..") +insert_doc_links(os.path.join(root_dir, "src")) +insert_doc_links(os.path.join(root_dir, "examples")) +insert_doc_links(os.path.join(root_dir, "tests")) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index b6ec04d..2453fa3 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -24,8 +24,15 @@ jobs: mpi: "mpich" - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3.14 + - name: Insert doc links + run: python .github/scripts/insert_doc_links.py + - name: Build docs - run: cargo +nightly doc --no-deps -Zunstable-options -Zrustdoc-scrape-examples --all-features + run: RUSTDOCFLAGS="--html-in-header katex-header.html" cargo +nightly doc --no-deps -Zunstable-options -Zrustdoc-scrape-examples --all-features - name: Make index page run: echo "" > target/doc/index.html diff --git a/Cargo.toml b/Cargo.toml index 8240770..056da7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ cbindgen = "0.29.2" [package.metadata.docs.rs] cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] +rustdoc-args = [ "--html-in-header", "katex-header.html" ] [lints.clippy] wildcard_imports = "forbid" diff --git a/README.md b/README.md index 69f9d95..a2779e1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ # ndgrid [![crates.io](https://img.shields.io/crates/v/ndgrid?color=blue)](https://crates.io/crates/ndgrid) -[![docs.rs](https://img.shields.io/docsrs/ndgrid?label=docs.rs)](https://docs.rs/ndgrid/latest/ndgrid/) ndgrid is an open-source library written in Rust for n-dimensional grids/meshes. @@ -25,7 +24,6 @@ cargo test Examples of use can be found in the [examples folder](examples/). ## Getting help -Documentation of the latest release of ndgrid can be found on [docs.rs](https://docs.rs/ndgrid/latest/ndgrid/). Documentation of the latest development version of ndgrid can be found at [bempp.github.io/ndgrid/ndgrid](https://bempp.github.io/ndgrid/ndgrid). Errors in the library should be added to the [GitHub issue tracker](https://github.com/bempp/ndgrid/issues). diff --git a/katex-header.html b/katex-header.html new file mode 100644 index 0000000..da412ac --- /dev/null +++ b/katex-header.html @@ -0,0 +1,19 @@ + + + + diff --git a/src/geometry/single_element/entity_geometry.rs b/src/geometry/single_element/entity_geometry.rs index 1879426..7fe6efa 100644 --- a/src/geometry/single_element/entity_geometry.rs +++ b/src/geometry/single_element/entity_geometry.rs @@ -66,6 +66,7 @@ impl Geometry for SingleElementEntityGeometry<' fn point_count(&self) -> usize { self.geometry.cells().shape()[0] } + fn degree(&self) -> usize { self.geometry.element().lagrange_superdegree() } diff --git a/src/grid/local_grid/single_element/builder.rs b/src/grid/local_grid/single_element/builder.rs index 1fa6736..6e74f3f 100644 --- a/src/grid/local_grid/single_element/builder.rs +++ b/src/grid/local_grid/single_element/builder.rs @@ -18,6 +18,28 @@ use rlst::rlst_dynamic_array; use std::collections::{HashMap, HashSet}; /// Grid builder for a single element grid +/// +/// The following gives an example of creating a new grid consisting +/// of a single triangle. +/// +/// ``` +/// use ndgrid::traits::Builder; +/// use ndgrid::SingleElementGridBuilder; +/// use ndelement::types::ReferenceCellType; +/// +/// // The geometric dimension of our space is 3. +/// let gdim = 3; +/// +/// // We are building a two dimensional surface triangle grid within a three dimensional space. +/// // Our grid will have three points and one `Triangle` cell of order 1. +/// let mut builder = SingleElementGridBuilder::new_with_capacity(gdim, 3, 1, (ReferenceCellType::Triangle, 1)); +/// builder.add_point(0, &[0.0, 0.0, 0.0]); +/// builder.add_point(1, &[1.0, 0.0, 0.0]); +/// builder.add_point(2, &[0.0, 1.0, 0.0]); +/// builder.add_cell(0, &[0, 1, 2]); +/// +/// let grid = builder.create_grid(); +/// ``` #[derive(Debug)] pub struct SingleElementGridBuilder { gdim: usize, diff --git a/src/lib.rs b/src/lib.rs index 6585fd0..237ea1e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,38 +4,29 @@ //! //! ## Creating a grid with `ndgrid` //! -//! To explain the library we use the following example of a grid consisting of two triangles that together form the unit rectangle. -//! To that effect we introduce the following boundary vertices. +//! To demonstrate the library, we use an example grid consisting of two triangles that together form the unit square. +//! We introduce the following points. As we will make a grid of second oder elements, we include points at the midpoint +//! of each edge as well as at the vertices of the square //! - Point 0: (0, 0) //! - Point 1: (1, 0) //! - Point 2: (0, 1) //! - Point 3: (1, 1) -//! -//! To make matters more interesting we will define a grid of second order elements. -//! This means that each edge of the triangle also has a middle point. The corresponding points are -//! given as follows: -//! //! - Point 4: (0.5, 0.5) //! - Point 5: (0.0, 0.5) //! - Point 6: (0.5, 0.0) //! - Point 7: (0.5, 1.0) //! - Point 8: (1.0, 0.5) //! -//! The order of points for each element is the same as the one on [defelement.org](https://defelement.org). +//! The order of points for each cell follows the point ordering of the +//! [Lagrange element on DefElement](https://defelement.org/elements/lagrange.html). //! For second order triangles we use //! [this ordering](https://defelement.org/elements/examples/triangle-lagrange-equispaced-2.html). +//! Following this ordering, the two cells of our example grid are: //! -//! `ndgrid` does an important distinction between points and topological vertices. The boundary points -//! of the triangle are called vertices and determine the connectivity relationship of this triangle with other -//! triangles. Topologically, the two triangles are defined through the points 0, 1, 2 for the first triangle -//! and 1, 3, 2 for the second triangle. However, the full cell definition is //! - Cell 1: 0, 1, 2, 4, 5, 6 //! - Cell 2: 1, 3, 2, 7, 4, 8 //! -//! in terms of the points. -//! -//! Let us generate the corresponding data structures. -//! +//! In order to create our grid using ndgrid, we first create a grid builder. //! ``` //! use ndgrid::traits::Builder; //! use ndelement::types::ReferenceCellType; @@ -46,14 +37,21 @@ //! (ReferenceCellType::Triangle, 2), //! ); //! ``` -//! The [SingleElementGridBuilder] is for grids that -//! only use a single element type. The first parameter is the geometric dimension. Here we choose 2, -//! meaning the grid lives in two-dimensionals pace. The next parameter is the number of points, 9 in this case, -//! and the third parameter is the number of cells, which is also 2 here. -//! The element type is [ReferenceCellType::Triangle](ndelement::types::ReferenceCellType::Triangle). -//! The order of the triangles is 2. -//! -//! We now add the definitions of the points and cells. +//! +//! The [SingleElementGridBuilder] is for grids that only use a single element type. The parameters passed when +//! initialising the build are: +//! +//! - The geometric dimension: our example grid lives in two-dimensional space, so we use 2. +//! - The number of points: 9 for our example grid. +//! - The number of cells: 2 for our example grid. +//! - The cell type and element degree: for our example, this is ([ReferenceCellType::Triangle](ndelement::types::ReferenceCellType::Triangle), 2) +//! as our geometry cells are triangles and we use quadratic geometry for each triangle. +//! +//! If we did not know the number of points and cells that we will include in out grid when creating ths builder, +//! we could instead use the function [SingleElementGridBuilder::new] when initialising the grid. +//! +//! Now that we have created a grid builder, we can add the points and cells: +//! //! ``` //! # use ndgrid::traits::Builder; //! # use ndelement::types::ReferenceCellType; @@ -73,8 +71,8 @@ //! builder.add_point(7, &[0.5, 1.0]); //! builder.add_point(8, &[1.0, 0.5]); //! -//! builder.add_cell(0, &[0, 1, 2, 4, 5, 6]); -//! builder.add_cell(1, &[1, 3, 2, 7, 4, 8]); +//! builder.add_cell(1, &[0, 1, 2, 4, 5, 6]); +//! builder.add_cell(2, &[1, 3, 2, 7, 4, 8]); //! ``` //! Finally, we generate the grid. //! ``` @@ -95,24 +93,34 @@ //! # builder.add_point(6, &[0.5, 0.0]); //! # builder.add_point(7, &[0.5, 1.0]); //! # builder.add_point(8, &[1.0, 0.5]); -//! -//! # builder.add_cell(0, &[0, 1, 2, 4, 5, 6]); -//! # builder.add_cell(1, &[1, 3, 2, 7, 4, 8]); +//! # +//! # builder.add_cell(1, &[0, 1, 2, 4, 5, 6]); +//! # builder.add_cell(2, &[1, 3, 2, 7, 4, 8]); //! let grid = builder.create_grid(); //! ``` +//! //! ## Querying the grid //! -//! A grid is a hierarchy of entities. The highest dimension entities are the cells. Each cell consists of subentities, -//! which are faces, edges, etc. For each entity there are two types of information, the topology information and the -//! geometry information. The topology describes how entities are connected. The geometry describes how entities related -//! to their associated physical points. Each entity is associated with an `index`. Indices are unique within the class -//! of entities, that is there is a point with index 0 and a cell with index 0 but no two points with index 0. Points and -//! cells also have an associated `id`. `ids` are the indices provided by the user with the `add_point` or `add_cell` -//! methods in the grid builder. These ids will usually be different from the internal indices of entities. +//! A grid is a hierarchy of entities. We follow the standard name conventions for entities of a given topological dimension: +//! 0-, 1-, 2- and 3-dimensional entities and called vertices, edges, faces and volumes (respectively). +//! The highest dimensional entities are called cells. If $d$ the (topological) dimension of the cells, +//! then $d-1$-, $d-2$- and $d-3$-dimensional entities are called facets, ridges and peaks (respectively). +//! +//! For each entity there are two types of information: the topology and the geometry. +//! The topology describes how entities are connected. The geometry describes how entities are positioned in physical space. +//! As the topology is only concerned with the connectivity between entities, it only includes the cell's points that are at +//! the vertices of a cell (eg for the triangle cells in our grid, the topology onle includes the first three points for each cell). +//! In the geometry, all the points that define the cell are stored. //! -//! The following code extracts all topological vertices for each cell and prints the corresponding physical coordinates. +//! Each entity has an associated `index`. Indices are unique within entities of a given type: +//! there is a vertex with index 0 and a cell with index 0 but there cannot be two vertices with index 0. Points and +//! cells may also have an associated `id`: these are the values provided by the user when using the `add_point` or `add_cell` +//! methods in the grid builder. These ids are not guaranteed to be equal to the indices of the entities. +//! +//! The following code extracts the vertices of each cell and prints their corresponding physical coordinates. //! ``` //! # use ndgrid::traits::Builder; +//! use ndgrid::traits::{Grid, Entity, Topology, Geometry, Point}; //! # use ndelement::types::ReferenceCellType; //! # let mut builder = ndgrid::SingleElementGridBuilder::::new_with_capacity( //! # 2, @@ -129,11 +137,11 @@ //! # builder.add_point(6, &[0.5, 0.0]); //! # builder.add_point(7, &[0.5, 1.0]); //! # builder.add_point(8, &[1.0, 0.5]); -//! -//! # builder.add_cell(0, &[0, 1, 2, 4, 5, 6]); -//! # builder.add_cell(1, &[1, 3, 2, 7, 4, 8]); +//! # +//! # builder.add_cell(1, &[0, 1, 2, 4, 5, 6]); +//! # builder.add_cell(2, &[1, 3, 2, 7, 4, 8]); //! # let grid = builder.create_grid(); -//! use ndgrid::traits::{Grid, Entity, Topology, Geometry, Point}; +//! //! for cell in grid.entity_iter(ReferenceCellType::Triangle) { //! for vertex in cell.topology().sub_entity_iter(ReferenceCellType::Point) { //! let vertex = grid.entity(ReferenceCellType::Point, vertex).unwrap(); @@ -154,26 +162,19 @@ //! } //! } //! ``` -//! Let us dissect what is going on here. First, we iteratre through the cells of the grid. -//! For this we use the [Grid::entity_iter](crate::traits::Grid::entity_iter) function. -//! For each cell we then access the topology information via [Entity::topology](crate::traits::Entity::topology) -//! and iterate through the point subentities via [Topology::sub_entity_iter](crate::traits::Topology::sub_entity_iter). -//! This gives us the vertices of the triangles. The topology information only considers the points that define the topology. -//! So the middle points on each edge which are necessary for the order of the triangle, are not returned. Also, the iterator -//! returns integer indices of entities. To convert an entity index to an actual entity use the -//! [Grid::entity](crate::traits::Grid::entity) function. We now want to get the actual physical coordinate of a vertex. -//! Since the geometric dimension is 2 we instantiate an array `[f64; 2]` for this. We now call on the vertex the -//! [Entity::geometry](crate::traits::Entity::geometry) function to obtain its geometry information. We then -//! call [Geometry::points](crate::traits::Geometry::points) to get an iterator to all physical points -//! associated with the vertex. Since a vertex only has one associated physical point (namely the vertex itself) we just -//! call `next` once on this iterator to get the actual point. Finally, we call [Point::coords](crate::traits::Point::coords) -//! to get the values of the physical coordinate. -//! -//! -//! -//! -//! //! +//! This snippets starts by using [Grid::entity_iter](crate::traits::Grid::entity_iter) to iterate through each +//! cell (ie each entity that is a triangle). +//! For each cell, we then access the topology information via [Entity::topology](crate::traits::Entity::topology) +//! and iterate through the vertices (ie the subentities that are points) using [Topology::sub_entity_iter](crate::traits::Topology::sub_entity_iter). +//! 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). +//! We now want to get the actual physical coordinate of a vertex. +//! Since the geometric dimension is 2 we instantiate an array `[f64; 2]` for this. We use +//! [Entity::geometry](crate::traits::Entity::geometry) to obtain the geometry for the vertex, then use +//! [Geometry::points](crate::traits::Geometry::points) to get an iterator over the physical points +//! associated with the vertex. Since a vertex has only one associated physical point, we +//! call `next` once on this iterator to get the point. Finally, we call [Point::coords](crate::traits::Point::coords) +//! to get the values of the physical coordinate. #![cfg_attr(feature = "strict", deny(warnings), deny(unused_crate_dependencies))] #![warn(missing_docs)] diff --git a/src/traits/builder.rs b/src/traits/builder.rs index 5adb5cf..9e10a9b 100644 --- a/src/traits/builder.rs +++ b/src/traits/builder.rs @@ -11,28 +11,6 @@ use std::hash::Hash; /// /// After instantiation points and cells can be added. /// To build the actual grid call [Builder::create_grid]. -/// -/// The following gives an example of creating a new grid consisting -/// of a single triangle. -/// -/// ``` -/// use ndgrid::traits::Builder; -/// use ndgrid::SingleElementGridBuilder; -/// use ndelement::types::ReferenceCellType; -/// -/// // The geometric dimension of our space is 3. -/// let gdim = 3; -/// -/// // We are building a two dimensional surface triangle grid within a three dimensional space. -/// // Our grid will have three points and one `Triangle` cell of order 1. -/// let mut builder = SingleElementGridBuilder::new_with_capacity(gdim, 3, 1, (ReferenceCellType::Triangle, 1)); -/// builder.add_point(0, &[0.0, 0.0, 0.0]); -/// builder.add_point(1, &[1.0, 0.0, 0.0]); -/// builder.add_point(2, &[0.0, 1.0, 0.0]); -/// builder.add_cell(0, &[0, 1, 2]); -/// -/// let grid = builder.create_grid(); -/// ``` pub trait Builder { /// Type used as identifier of different entity types type EntityDescriptor: Debug + PartialEq + Eq + Clone + Copy + Hash;