Skip to content
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
128 changes: 72 additions & 56 deletions .basedpyright/baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -1207,54 +1207,6 @@
"lineCount": 1
}
},
{
"code": "reportUnknownVariableType",
"range": {
"startColumn": 8,
"endColumn": 20,
"lineCount": 1
}
},
{
"code": "reportUnknownArgumentType",
"range": {
"startColumn": 32,
"endColumn": 13,
"lineCount": 5
}
},
{
"code": "reportCallIssue",
"range": {
"startColumn": 12,
"endColumn": 24,
"lineCount": 1
}
},
{
"code": "reportArgumentType",
"range": {
"startColumn": 16,
"endColumn": 23,
"lineCount": 1
}
},
{
"code": "reportAny",
"range": {
"startColumn": 4,
"endColumn": 13,
"lineCount": 1
}
},
{
"code": "reportUnknownArgumentType",
"range": {
"startColumn": 23,
"endColumn": 35,
"lineCount": 1
}
},
{
"code": "reportAny",
"range": {
Expand Down Expand Up @@ -2481,14 +2433,6 @@
"lineCount": 1
}
},
{
"code": "reportImplicitAbstractClass",
"range": {
"startColumn": 6,
"endColumn": 19,
"lineCount": 1
}
},
{
"code": "reportUnknownParameterType",
"range": {
Expand Down Expand Up @@ -6273,6 +6217,78 @@
"lineCount": 1
}
},
{
"code": "reportAny",
"range": {
"startColumn": 23,
"endColumn": 82,
"lineCount": 1
}
},
{
"code": "reportAny",
"range": {
"startColumn": 27,
"endColumn": 54,
"lineCount": 1
}
},
{
"code": "reportAny",
"range": {
"startColumn": 34,
"endColumn": 46,
"lineCount": 1
}
},
{
"code": "reportAny",
"range": {
"startColumn": 27,
"endColumn": 54,
"lineCount": 1
}
},
{
"code": "reportAny",
"range": {
"startColumn": 34,
"endColumn": 46,
"lineCount": 1
}
},
{
"code": "reportAny",
"range": {
"startColumn": 27,
"endColumn": 54,
"lineCount": 1
}
},
{
"code": "reportAny",
"range": {
"startColumn": 34,
"endColumn": 46,
"lineCount": 1
}
},
{
"code": "reportAny",
"range": {
"startColumn": 33,
"endColumn": 47,
"lineCount": 1
}
},
{
"code": "reportAny",
"range": {
"startColumn": 40,
"endColumn": 46,
"lineCount": 1
}
},
{
"code": "reportAny",
"range": {
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
python-version: '3.x'
- name: "Main Script"
run: |
EXTRA_INSTALL="matplotlib pytest scipy scipy-stubs basedpyright"
EXTRA_INSTALL="matplotlib pytest scipy scipy-stubs basedpyright optype"
curl -L -O https://tiker.net/ci-support-v0
. ./ci-support-v0
build_py_project_in_venv
Expand Down
5 changes: 5 additions & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@

autodoc_member_order = "bysource"

nitpick_ignore_regex = [
# NOTE: optype does not have Sphinx compatible documentation
["py:class", r"onp.*"],
]

sphinxconfig_missing_reference_aliases = {
# numpy
"NDArray": "obj:numpy.typing.NDArray",
Expand Down
2 changes: 2 additions & 0 deletions modepy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
from modepy.modes import (
Basis,
BasisNotOrthonormal,
SimplexBasis,
TensorProductBasis,
basis_for_space,
grad_jacobi,
Expand Down Expand Up @@ -133,6 +134,7 @@
"QuadratureRuleUnavailable",
"Shape",
"Simplex",
"SimplexBasis",
"TensorProductBasis",
"TensorProductQuadrature",
"TensorProductShape",
Expand Down
97 changes: 71 additions & 26 deletions modepy/modal_decay.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,18 @@
THE SOFTWARE.
"""


from functools import singledispatch
from typing import TYPE_CHECKING

import numpy as np
import numpy.linalg as la
from typing_extensions import deprecated

from modepy.modes import Basis, SimplexBasis, TensorProductBasis


if TYPE_CHECKING:
import optype.numpy as onp
from numpy.typing import NDArray

from modepy.typing import ArrayF
Expand All @@ -49,25 +53,82 @@

.. versionadded:: 2013.2

.. autofunction:: simplex_interp_error_coefficient_estimator_matrix
.. autofunction:: degree_vector
.. autofunction:: interp_error_coefficient_estimator_matrix
.. autofunction:: fit_modal_decay
.. autofunction:: estimate_relative_expansion_residual
"""


def simplex_interp_error_coefficient_estimator_matrix(
unit_nodes: ArrayF,
order: int,
n_tail_orders: int) -> ArrayF:
@singledispatch
def degree_vector(basis: Basis) -> onp.Array1D[np.integer]:
"""Return a vector of integers indicating the 'overall order' of each basis
function in the basis, in order. For simplex discretizations, consider
the total degree. For tensor product discretizations, the max degree.

.. versionadded:: 2026.1.1
"""
raise TypeError(f"unsupported basis type: {type(basis)}")


@degree_vector.register(SimplexBasis)
def degree_vector_simplex(basis: SimplexBasis) -> onp.Array1D[np.integer]:
return np.array([
sum(mid_tuple)
for mid_tuple in basis.mode_ids
])


@degree_vector.register(TensorProductBasis)
def degree_vector_tp(basis: TensorProductBasis) -> onp.Array1D[np.integer]:
degree_vectors = [degree_vector(b) for b in basis.bases]
return np.array([
max(
ov[mode_id]
for mode_id, ov
in zip(mid_tuple, degree_vectors, strict=True)
)
for mid_tuple in basis._mode_index_tuples # pyright: ignore[reportPrivateUsage]
])


def interp_error_coefficient_estimator_matrix(
unit_nodes: ArrayF,
n_tail_orders: int,
basis: Basis,
) -> ArrayF:
"""Return a matrix :math:`C` that, when multiplied by a vector of nodal values,
yields the coeffiicients belonging to the basis functions of the *n_tail_orders*
yields the coefficients belonging to the basis functions of the *n_tail_orders*
highest orders.

The 2-norm of the resulting coefficients can be used as an estimate of the
interpolation error.

.. versionadded:: 2018.1
.. versionadded:: 2026.1.1
"""
from modepy.matrices import vandermonde
vdm = vandermonde(basis.functions, unit_nodes)
vdm_inv = la.inv(vdm)

degree_vec = degree_vector(basis)

max_order = np.max(degree_vec)
return vdm_inv[degree_vec > max_order-n_tail_orders]


@deprecated("Use interp_error_coefficient_estimator_matrix instead")
def simplex_interp_error_coefficient_estimator_matrix(
unit_nodes: ArrayF,
order: int,
n_tail_orders: int,
) -> ArrayF:
from warnings import warn
warn("simplex_interp_error_coefficient_estimator_matrix is deprecated, "
"use interp_error_coefficient_estimator_matrix instead. "
"This will stop working in 2027.x.",
DeprecationWarning, stacklevel=2,
)

dim, _nunit_nodes = unit_nodes.shape

from modepy.shapes import Simplex
Expand All @@ -77,24 +138,8 @@ def simplex_interp_error_coefficient_estimator_matrix(

from modepy.modes import orthonormal_basis_for_space
basis = orthonormal_basis_for_space(space, shape)

from modepy.matrices import vandermonde
vdm = vandermonde(basis.functions, unit_nodes)
vdm_inv = la.inv(vdm)

if dim > 1:
order_vector = np.array([
# NOTE: basis.mode_ids are declared as `Tuple[Hashable, ...]`, which
# cannot be summed
sum(mode_id) for mode_id in basis.mode_ids
])
else:
order_vector = np.array(basis.mode_ids)

max_order = np.max(order_vector)
assert max_order == order

return vdm_inv[order_vector > max_order-n_tail_orders]
return interp_error_coefficient_estimator_matrix(
unit_nodes, n_tail_orders, basis)


def make_mode_number_vector(
Expand Down
10 changes: 7 additions & 3 deletions modepy/modes.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@

.. autofunction:: symbolicize_function

Base classes
------------
.. autoclass:: SimplexBasis

Tensor product adapter
----------------------

Expand Down Expand Up @@ -873,7 +877,7 @@ def _grad_pkdo_1d(order: tuple[int], r: ArrayF) -> tuple[ArrayF]:
return (grad_jacobi(0, 0, i, r0),)


class _SimplexBasis(Basis):
class SimplexBasis(Basis, ABC):
def __init__(self, dim, order):
self._dim = dim
self._order = order
Expand All @@ -889,7 +893,7 @@ def mode_ids(self):
return tuple(gnitsam(self._order, self._dim))


class _SimplexONB(_SimplexBasis):
class _SimplexONB(SimplexBasis):
is_orthonormal = True

def orthonormality_weight(self):
Expand Down Expand Up @@ -920,7 +924,7 @@ def gradients(self):
raise NotImplementedError(f"gradient in {self._dim} dimensions")


class _SimplexMonomialBasis(_SimplexBasis):
class _SimplexMonomialBasis(SimplexBasis):
def orthonormality_weight(self) -> float:
raise BasisNotOrthonormal

Expand Down
Loading
Loading