planetmodel.mesh1d package¶
Submodules¶
planetmodel.mesh1d.gll module¶
Gauss-Lobatto-Legendre nodes, weights and Lagrange machinery.
Everything here lives on the reference element [-1, 1] and knows nothing of models or physics: the GLL quadrature rule, the differentiation matrix of the associated Lagrange basis, and evaluation of that basis at arbitrary points.
- planetmodel.mesh1d.gll.gll_points_weights(ngll: int) tuple[ndarray, ndarray][source]¶
Gauss-Lobatto-Legendre points and weights on [-1, 1].
The ngll >= 2 nodes are the endpoints plus the ngll - 2 zeros of P’_N with N = ngll - 1, computed stably as the zeros of the Jacobi polynomial P^(1,1)_(N-1); the weights are 2 / (N (N+1) P_N(x)^2). The quadrature integrates polynomials of degree <= 2N - 1 exactly.
- planetmodel.mesh1d.gll.lagrange_basis(nodes: ArrayLike, x: ArrayLike) ndarray[source]¶
Values L[j, i] = l_i(x_j) of the Lagrange basis at points x.
Uses the second barycentric form for stability; points coinciding with a node, or so near one that the barycentric quotient would overflow, take that node’s basis exactly. Useful for interpolating a spectral-element solution off its nodes.
- planetmodel.mesh1d.gll.lagrange_derivative_matrix(x: ArrayLike) ndarray[source]¶
Derivative matrix D[k, i] = l_i’(x_k) on the GLL nodes x.
Closed form for GLL nodes (e.g. Canuto et al. 1988): off the diagonal D[k, i] = P_N(x_k) / (P_N(x_i) (x_k - x_i)); the diagonal vanishes except for D[0, 0] = -N(N+1)/4 and D[N, N] = +N(N+1)/4. The index convention (derivative of basis function i evaluated at node k) matches the ‘hp(knode, inode)’ arrays of standard SEM codes, so quadrature sums read sum_k w_k f(x_k) D[k, i] D[k, j].
planetmodel.mesh1d.gravity module¶
Gravity and mass of a spherically symmetric model, through its fields.
For a spherically symmetric density the acceleration is
g(r) = G M(r) / r^2 with M(r) = 4 pi int_0^r rho(s) s^2 ds, the mass
inside r accumulated layer by layer. Each layer’s integral is asked of
the field algebra: rho * s^2 is a field whose layer function
integrates itself, exactly when rho is a polynomial and by quadrature
otherwise. This is the gravity of the reference body: rho must be a
radial field on every layer, a density that depends on direction is
refused by name, and the geometry’s mapping does not enter, so a model
placed in the physical world by a non-trivial mapping is answered with
the gravity of its spherical reference. Nothing here is a number with
a unit: G is the model’s, the radii are in the model’s length, and a
hollow model has no mass inside its inner boundary.
- planetmodel.mesh1d.gravity.gravity(model: Model, radii: ArrayLike) np.ndarray[source]¶
g(r) = G M(r) / r^2 at
radii, in the model’s units.radiibroadcast to any shape and must lie in the model, to the geometry’s tolerance; g is zero at the centre and on the inner boundary of a hollow model. M(r) is the antiderivative of each layer’srho s^2, asgravity_fieldshas it.
- planetmodel.mesh1d.gravity.gravity_fields(model: Model) tuple[RadialField, ...][source]¶
g(r) = G M(r) / r^2 as one radial field per layer, named
g.Each field is exact in value and in its first derivative where the layer’s density is polynomial: M(r) is the polynomial antiderivative of 4 pi rho r^2 plus the mass below the layer, and dg/dr = G (4 pi rho - 2 M / r^3). The fields are continuous across every boundary and vanish at the centre; a model attaches them by
with_field(i, "g", field).
planetmodel.mesh1d.mesh module¶
Conforming GLL meshes over an interval and over a skeleton.
Mesh1D is pure geometry on an interval: pinned breakpoints, each span
between them cut uniformly into elements no wider than drmax, ngll
Gauss-Lobatto-Legendre nodes per element, a global node numbering, the
reference-element data a weak form assembles with, and the radial rules
between nodes: polynomial, the Lagrange polynomial through each
element’s GLL nodes, and linear, straight lines between consecutive
nodes, which agree when an element has two nodes. Each rule is offered
two ways that agree, as an exact piecewise-polynomial view of nodal
values (to_ppoly) and as the weights of an element’s nodes at any
point (interpolation_weights). Nodal arrays are stored per element,
shape (nspec, ngll), so a shared node carries one value from each side,
and at a shared boundary both views take the element above.
RadialMesh lays such a mesh over a Skeleton so that every skeleton
boundary is an element boundary, and records the layer each element
lies in. Given a model, nodal evaluates the field of each element’s
own layer at the element’s nodes, so a shared node carries both
one-sided values, and nodal_gravity the model’s gravity there.
Everything is numbers: the mesh knows nothing about units.
- class planetmodel.mesh1d.mesh.Mesh1D(breakpoints: ArrayLike, *, ngll: int = 5, drmax: float | None = None)[source]¶
Bases:
objectA conforming GLL mesh over an interval with pinned breakpoints.
Each span between consecutive breakpoints is subdivided uniformly into elements no wider than
drmax(one element per span whendrmaxis None). Attributes:nspecelements withleft,rightandjac(half-widths) of shape (nspec,); node coordinatesrof shape (nspec, ngll); the global numberinggmapof shape (nspec, ngll) intonglob = nspec (ngll - 1) + 1shared nodes with coordinatesrglob; and the reference-element dataxi,w,deriv(seelagrange_derivative_matrixfor the index convention).- element_of(x: float) int[source]¶
The element whose interval contains x.
A coordinate on a shared boundary resolves to the element above it; a coordinate outside the mesh clips to the first or last element.
- interpolation_weights(x: ArrayLike, *, method: str = 'polynomial', nu: int = 0) tuple[ndarray, ndarray][source]¶
The element of each point, and the weights of that element’s nodes.
Returns
(element, weights),elementof the shape ofxandweightsof that shape followed by (ngll,), so that nodal values u of shape (nspec, ngll) take the valuesum_i weights[..., i] * u[element, i]at x, the valueto_ppoly(u, method=method)gives. The element iselement_of’s, the upper one at a shared boundary and the end one beyond the mesh, whose piece continues there. Under"polynomial"the weights are the Lagrange basis of the element’s GLL nodes; under"linear"the two hat functions of the nodes bracketing x, zero elsewhere. Withnu=1they are the weights of the first derivative in x instead: the derivatives of the Lagrange basis, a polynomial of lower degree written in the same basis, or the slope of the bracketing segment.
- to_ppoly(nodal: ArrayLike, *, elements: tuple[int, int] | None = None, method: str = 'polynomial') PPoly[source]¶
The exact piecewise-polynomial (scipy PPoly) view of nodal values.
nodalhas shape (nspec, ngll), or that followed by any trailing shape, in which case the values of the result have the trailing shape;elements, when given, is a half-open (start, stop) element range restricting the view. Undermethod="polynomial"the result within each element is the interpolating polynomial through the GLL nodes, so its derivative and integral are those of the spectral-element function; under"linear"it is the straight line between each pair of consecutive nodes. Discontinuous data is representable; at a shared breakpoint the upper element’s piece is evaluated, and beyond the ends the end pieces continue. Complex values stay complex. The pieces are in the local monomial form PPoly holds, which loses digits as the order grows on data that is rough on the element (about 1e-11 of the data at ngll 9, 1e-8 at ngll 13), whereinterpolation_weightsstays at rounding; smooth data is held to rounding at every order used.
- planetmodel.mesh1d.mesh.RADIAL_METHODS = ('polynomial', 'linear')¶
the Lagrange polynomial through its GLL nodes, or straight lines between consecutive nodes.
- Type:
The radial rules between the nodes of an element
- class planetmodel.mesh1d.mesh.RadialMesh(skeleton: Skeleton | Geometry, *, ngll: int = 5, drmax: float | None = None, lmax: int | None = None, rmin: float | None = None, rmax: float | None = None, edges: ArrayLike | None = None, rtol: float = 1e-09)[source]¶
Bases:
Mesh1DA GLL mesh over [rmin, rmax] of a skeleton.
Exactly one of
drmax,lmaxandedgessizes the mesh. Withdrmaxorlmaxthe element boundaries are every skeleton boundary strictly inside the range, and every knot a Geometry’s mapping declares there, plus a uniform subdivision no coarser thandrmax, wherelmaxsetsdrmax = 0.1 rmax / (lmax + 1);rminandrmaxdefault to the skeleton’s ends. Withedgesthe element boundaries are taken as given, must lie inside the skeleton, and no element may straddle a skeleton boundary (tortolof the range). A pin within that tolerance of a skeleton boundary is the boundary.layerrecords the skeleton layer of every element. A Geometry or a Model is accepted in place of a skeleton, and the knots of its mapping are pinned.- element_at(r: float) int[source]¶
The element whose interval contains r; a boundary resolves upward.
- nodal(model: Model, name: str, *, nu: int = 0, missing: str = 'refuse') np.ndarray[source]¶
A radial field of a model at the nodes, per element: (nspec, ngll) for rank 0, with the stored components appended for higher rank.
Each element takes the field of its own layer, so a node on a skeleton boundary carries both one-sided values;
nuasks for the nu-th radial derivative. An element whose layer lacks the name is refused by name, or filled with NaN whenmissing="nan". Complex where any layer’s field is complex. A new array each call.
- nodal_gravity(model: Model) np.ndarray[source]¶
The model’s gravity at the nodes, per element, over the whole model.
Module contents¶
Radial spectral-element meshes: GLL nodes per element over a skeleton.
Mesh1D is the mesh of an interval; RadialMesh lays one over a
skeleton with every boundary an element boundary and evaluates a
model’s fields and gravity on its nodes. Both read nodal values
between the nodes by one of the RADIAL_METHODS. The GLL quadrature, basis and
differentiation matrix of the reference element are exported beside
them.