Skip to content
This repository was archived by the owner on Dec 2, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/scripts/insert_doc_links.py
Original file line number Diff line number Diff line change
@@ -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"))
9 changes: 8 additions & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 "<html><head><meta http-equiv='refresh' content='0; URL=ndgrid'></head></html>" > target/doc/index.html
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -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.

Expand All @@ -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).
Expand Down
19 changes: 19 additions & 0 deletions katex-header.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css" integrity="sha384-WcoG4HRXMzYzfCgiyfrySxx90XSl2rxY5mnVY5TwtWE6KLrArNKn0T/mOgNL0Mmi" crossorigin="anonymous">
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.js" integrity="sha384-J+9dG2KMoiR9hqcFao0IBLwxt6zpcyN68IgwzsCSkbreXUjmNVRhPFTssqdSGjwQ" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/contrib/auto-render.min.js" integrity="sha384-hCXGrW6PitJEwbkoStFjeJxv+fSOOQKOPbJxSfM6G5sWZjAyWhXiTIIAmQqnlLlh" crossorigin="anonymous"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
renderMathInElement(document.body, {
// customised options
// • auto-render specific keys, e.g.:
delimiters: [
{left: '$$', right: '$$', display: true},
{left: '$', right: '$', display: false},
{left: '\\(', right: '\\)', display: false},
{left: '\\[', right: '\\]', display: true}
],
// • rendering keys, e.g.:
throwOnError : false
});
});
</script>
1 change: 1 addition & 0 deletions src/geometry/single_element/entity_geometry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ impl<T: RealScalar, E: FiniteElement> Geometry for SingleElementEntityGeometry<'
fn point_count(&self) -> usize {
self.geometry.cells().shape()[0]
}

fn degree(&self) -> usize {
self.geometry.element().lagrange_superdegree()
}
Expand Down
22 changes: 22 additions & 0 deletions src/grid/local_grid/single_element/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T: RealScalar> {
gdim: usize,
Expand Down
121 changes: 61 additions & 60 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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.
//! ```
Expand All @@ -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::<f64>::new_with_capacity(
//! # 2,
Expand All @@ -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();
Expand All @@ -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)]
Expand Down
22 changes: 0 additions & 22 deletions src/traits/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down