# mesh_fem { #tvbo.data.mesh_fem }

`data.mesh_fem`

P1 finite-element operators for a triangular mesh, flat or curved.

:func:`~tvbo.data.mesh_io.read_mesh` turns a mesh file into vertices and faces; this turns those into the two matrices a field equation is discretised with — the mass matrix ``int(u v)`` and the stiffness matrix ``int(grad(u).grad(v))``. Together they are the whole of a P1 discretisation, so ``a*laplacian(u)`` assembles as ``-a*K`` and ``a*u`` as ``a*M``.

The reason this exists rather than a call into a FEM library: a cortical surface is a 2-manifold embedded in 3-space, and the general-purpose libraries assemble on domains whose element dimension equals their coordinate dimension. Their affine mapping inverts a square Jacobian, which a triangle carrying three coordinates does not have — scikit-fem builds such a mesh and then fails inside the mapping. For P1 the manifold case needs no mapping at all:
the basis gradients are tangential and are written in closed form below, which makes the brain-surface case exact rather than unsupported.

Coefficients may vary per vertex, which is what lets a propagation scale differ across cortex. Both weighted forms are exact for a P1 coefficient, not a one-point approximation of one: a gradient product is constant on a triangle so the weighted stiffness needs only the coefficient's element mean, while the weighted mass integrates the full cubic.

## Functions

| Name | Description |
| --- | --- |
| [boundary_vertices](#tvbo.data.mesh_fem.boundary_vertices) | Vertices on the mesh boundary — empty for a closed surface. |
| [p1_mass](#tvbo.data.mesh_fem.p1_mass) | ``int(c u v)`` — the consistent mass matrix, weighted by ``c``. |
| [p1_stiffness](#tvbo.data.mesh_fem.p1_stiffness) | ``int(c grad(u) . grad(v))`` — the cotangent operator, weighted by ``c``. |
| [triangle_areas](#tvbo.data.mesh_fem.triangle_areas) | Area of every triangle, in the mesh's own length unit squared. |

### boundary_vertices { #tvbo.data.mesh_fem.boundary_vertices }

```python
data.mesh_fem.boundary_vertices(faces)
```

Vertices on the mesh boundary — empty for a closed surface.

An edge shared by one triangle is a boundary edge. A closed cortical surface has none, which is why a Dirichlet condition declared on one constrains nothing.

### p1_mass { #tvbo.data.mesh_fem.p1_mass }

```python
data.mesh_fem.p1_mass(vertices, faces, coefficient=None)
```

``int(c u v)`` — the consistent mass matrix, weighted by ``c``.

Consistent, not lumped: the row sums give the area each vertex carries, but the off-diagonal entries are kept. Lumping is a first-order approximation whose error grows with spatial frequency, so it distorts exactly the high modes a wave equation resolves.

### p1_stiffness { #tvbo.data.mesh_fem.p1_stiffness }

```python
data.mesh_fem.p1_stiffness(vertices, faces, coefficient=None)
```

``int(c grad(u) . grad(v))`` — the cotangent operator, weighted by ``c``.

Positive semi-definite with the constant vector in its kernel, so a Laplacian term enters an operator NEGATED. On a closed surface that kernel is exactly one-dimensional, which is the discrete statement that no flux leaves the domain.

#### Parameters {.doc-section .doc-section-parameters}

| Name        | Type   | Description                                                                                                                                  | Default    |
|-------------|--------|----------------------------------------------------------------------------------------------------------------------------------------------|------------|
| vertices    |        | ``(V, 2)`` or ``(V, 3)`` coordinates. Three columns is a surface in space.                                                                   | _required_ |
| faces       |        | ``(F, 3)`` triangles.                                                                                                                        | _required_ |
| coefficient |        | ``None``, a scalar, or a per-vertex array — a spatially varying diffusivity, assembled as ``div(c grad(u))`` rather than ``c laplacian(u)``. | `None`     |

### triangle_areas { #tvbo.data.mesh_fem.triangle_areas }

```python
data.mesh_fem.triangle_areas(vertices, faces)
```

Area of every triangle, in the mesh's own length unit squared.