From 20011efdd776fc3bfa4751d4785f15a0c4938f46 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 22 May 2026 11:21:25 +0300 Subject: [PATCH 1/6] docs: add more docs to LinearPDESystemOperator --- sumpy/expansion/diff_op.py | 84 +++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 20 deletions(-) diff --git a/sumpy/expansion/diff_op.py b/sumpy/expansion/diff_op.py index b71d88b5..4f886673 100644 --- a/sumpy/expansion/diff_op.py +++ b/sumpy/expansion/diff_op.py @@ -25,6 +25,7 @@ import logging from dataclasses import dataclass +from functools import cached_property from itertools import accumulate from typing import TYPE_CHECKING, TypeAlias @@ -92,23 +93,32 @@ class DerivativeIdentifier: class LinearPDESystemOperator: r""" Represents a constant-coefficient linear differential operator of a - vector-valued function with `dim` spatial variables. + vector-valued function with :attr:`dim` spatial variables. - It is represented by a tuple of immutable dictionaries. The dictionary maps a - :class:`DerivativeIdentifier` to the coefficient. Optionally supports a - time variable as the last variable in the multi-index of the - :class:`DerivativeIdentifier`. + The operator is given by a tuple of immutable dictionaries. The dictionary + maps a :class:`DerivativeIdentifier` to a (time- and space-independent) + coefficient. It optionally supports a time variable as the last element in + the multi-index of the :class:`DerivativeIdentifier`. + + The class also supports basic arithmetic, i.e. multiplication and addition + with other operators and constants. .. autoattribute:: dim .. autoattribute:: eqs + .. autoproperty:: is_time_dependent .. autoproperty:: order .. autoproperty:: total_dims .. automethod:: to_sym """ dim: int + """The total number of spatial dimensions of the PDE (use :attr:`total_dims` + to include time). + """ + eqs: tuple[Mapping[DerivativeIdentifier, sym.Expr], ...] + """A tuple of all the equations in the system.""" if __debug__: @@ -116,13 +126,7 @@ def __post_init__(self) -> None: # NOTE: this will raise a TypeError if it's not hashable _ = hash(self) - @property - def order(self) -> int: - deg = 0 - for eq in self.eqs: - deg = max(deg, max(sum(ident.mi) for ident in eq)) - - return deg + # {{{ arithmetic def __mul__(self, other: Number | sym.Expr) -> LinearPDESystemOperator: import numbers @@ -170,6 +174,8 @@ def __sub__(self, other: LinearPDESystemOperator) -> LinearPDESystemOperator: def __neg__(self) -> LinearPDESystemOperator: return (-1) * self + # }}} + @override def __repr__(self) -> str: return f"LinearPDESystemOperator({self.dim}, {self.eqs!r})" @@ -180,29 +186,67 @@ def __getitem__(self, idx: int | slice) -> LinearPDESystemOperator: return LinearPDESystemOperator(self.dim, eqs) @property + def is_time_dependent(self) -> bool: + """Is *True* if the PDE operator has a time component.""" + return self.dim != self.total_dims + + @cached_property + def order(self) -> int: + """The order of the PDE operator (maximum order of all derivatives).""" + deg = 0 + for eq in self.eqs: + deg = max(deg, max(sum(ident.mi) for ident in eq)) + + return deg + + @cached_property def total_dims(self) -> int: - """ - Returns the total number of dimensions including time - """ - did = next(iter(self.eqs[0].keys())) + """The total number of dimensions (including time).""" + did = next(iter(self.eqs[0])) return len(did.mi) - def to_sym(self, fnames: Sequence[str] | None = None) -> list[sym.Expr]: - x: list[sym.Expr] = list(sym.make_sym_vector("x", self.dim)) - x.extend(sym.make_sym_vector("t", self.total_dims - self.dim)) + @cached_property + def nvariables(self) -> int: + """Number of variables in the system.""" + max_vec_idx = max((did.vec_idx for eq in self.eqs for did in eq), default=-1) + return max_vec_idx + 1 + + def to_sym( + self, + fnames: Sequence[str] | None = None, + *, + x_var_name: str = "x", + t_var_name: str = "t", + ) -> list[sym.Expr]: + """Transform the system to a list of :mod:`sympy` expressions. + + :arg fnames: the names of the variables in the system. + (defaults to `["f0", "f1", ....]`) + :arg x_var_name: the name of the spatial variables. + (defaults to `["x0", "x1", ....]`) + :arg t_var_name: the name of the temporal variables. + """ + x: list[sym.Expr] = list(sym.make_sym_vector(x_var_name, self.dim)) + x.extend(sym.make_sym_vector(t_var_name, self.total_dims - self.dim)) if fnames is None: noutputs = 0 for eq in self.eqs: for deriv_ident in eq: noutputs = max(noutputs, deriv_ident.vec_idx) + fnames = [f"f{i}" for i in range(noutputs+1)] funcs = [sym.Function(fname)(*x) for fname in fnames] + if len(funcs) < self.nvariables: + raise ValueError( + f"'fnames' does not match system: {len(fnames)} names " + f"(for a system of {self.nvariables} variables)" + ) res: list[sym.Expr] = [] for eq in self.eqs: - sym_eq: sym.Expr = sym.sympify(0) + sym_eq: sym.Expr = sym.Integer(0) for deriv_ident, coeff in eq.items(): expr = funcs[deriv_ident.vec_idx] for i, val in enumerate(deriv_ident.mi): From 13f4c0179f9d5a34e593e39cd004edbb7d30c7af Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 22 May 2026 11:22:27 +0300 Subject: [PATCH 2/6] feat: add a helper function to create Fourier symbol --- sumpy/expansion/diff_op.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/sumpy/expansion/diff_op.py b/sumpy/expansion/diff_op.py index 4f886673..6ce92145 100644 --- a/sumpy/expansion/diff_op.py +++ b/sumpy/expansion/diff_op.py @@ -63,6 +63,7 @@ .. autoclass:: DerivativeIdentifier .. autofunction:: make_identity_diff_op .. autofunction:: as_scalar_pde +.. autofunction:: to_fourier_matrix """ @@ -426,6 +427,42 @@ def as_scalar_pde( return _get_all_scalar_pdes(pde)[comp_idx] +def to_fourier_matrix( + pde: LinearPDESystemOperator, + ks: sym.Matrix, + ) -> sym.Matrix: + r"""Return the Fourier (symbol) matrix of a constant-coefficient PDE system. + + Each spatial derivative :math:`\partial / \partial x_j` is replaced by + multiplication by :math:`i\,k_j`. The result is a + :obj:`sympy.matrices.dense.Matrix` whose ``(row, col)`` entry is the + polynomial in the frequency variables contributed by equation *row* acting + on component *col*. + + :returns: a matrix of size ``(len(pde.eqs), nvariables)``. + """ + if pde.is_time_dependent: + raise ValueError("cannot compute Fourier symbol for time-dependent PDEs") + + ncols = pde.nvariables + + mat = [] + for eq in pde.eqs: + row = [sym.Integer(0)] * ncols + + for deriv_ident, coeff in eq.items(): + factor: sym.Expr = sym.Integer(1) + for j, power in enumerate(deriv_ident.mi): + factor *= (sym.I * ks[j]) ** power + + row[deriv_ident.vec_idx] += coeff * factor + + assert len(row) == ncols + mat.append(row) + + return sym.Matrix(mat) + + def laplacian(diff_op: LinearPDESystemOperator) -> LinearPDESystemOperator: dim = diff_op.dim empty: tuple[Mapping[DerivativeIdentifier, sym.Expr], ...] = ( From f58a6636dae5b54e26feff88852be66261075572 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 22 May 2026 15:45:32 +0300 Subject: [PATCH 3/6] feat: add some simple tests for the Fourier symbols --- sumpy/test/test_misc.py | 111 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 107 insertions(+), 4 deletions(-) diff --git a/sumpy/test/test_misc.py b/sumpy/test/test_misc.py index df3fc208..da84d7b1 100644 --- a/sumpy/test/test_misc.py +++ b/sumpy/test/test_misc.py @@ -54,6 +54,7 @@ gradient, laplacian, make_identity_diff_op, + to_fourier_matrix, ) from sumpy.kernel import ( BiharmonicKernel, @@ -475,7 +476,6 @@ def test_as_scalar_pde_maxwell(): # {{{ test_as_scalar_pde_elasticity def test_as_scalar_pde_elasticity(): - # Ref: https://doi.org/10.1006/jcph.1996.0102 diff_op = make_identity_diff_op(2, 5) @@ -492,21 +492,29 @@ def test_as_scalar_pde_elasticity(): x = (1, 0) y = (0, 1) - exprs = [ + pde = concat(*[ diff(sigma_x, x) + diff(tau, y), diff(tau, x) + diff(sigma_y, y), sigma_x - (lam + 2*mu)*diff(u, x) - lam*diff(v, y), sigma_y - (lam + 2*mu)*diff(v, y) - lam*diff(u, x), tau - mu*(diff(u, y) + diff(v, x)), - ] + ]) - pde = concat(*exprs) assert pde.order == 1 for i in range(5): scalar_pde = as_scalar_pde(pde, i) assert scalar_pde == laplacian(laplacian(diff_op[0])) assert scalar_pde.order == 4 + # Wikipedia: displacement formulation + + u = make_identity_diff_op(3, 3) + pde = mu * laplacian(u) + (mu + lam) * gradient(divergence(u)) + + for i in range(3): + scalar_pde = as_scalar_pde(pde, i) + assert scalar_pde == laplacian(laplacian(u[0])) + # }}} @@ -530,6 +538,101 @@ def test_elasticity_new(): # }}} +# {{{ test_to_fourier_matrix_laplace + +def test_to_fourier_matrix_scalar() -> None: + ks = sym.make_sym_vector("k", 3) + lam = sym.Symbol("lam") + + # LaplaceKernel + kernel = LaplaceKernel(2) + mat = to_fourier_matrix(kernel.get_pde_as_diff_op(), ks) + assert mat == sym.Matrix([[-ks[0]**2 - ks[1]**2]]) + + kernel = LaplaceKernel(3) + mat = to_fourier_matrix(kernel.get_pde_as_diff_op(), ks) + assert mat == sym.Matrix([[-ks[0]**2 - ks[1]**2 - ks[2]**2]]) + + # YukawaKernel + kernel = YukawaKernel(2, yukawa_lambda_name=lam.name) + mat = to_fourier_matrix(kernel.get_pde_as_diff_op(), ks) + assert mat == sym.Matrix([[-ks[0]**2 - ks[1]**2 - lam**2]]) + + kernel = YukawaKernel(3, yukawa_lambda_name=lam.name) + mat = to_fourier_matrix(kernel.get_pde_as_diff_op(), ks) + assert mat == sym.Matrix([[-ks[0]**2 - ks[1]**2 - ks[2]**2 - lam**2]]) + +# }}} + + +# {{{ test_to_fourier_matrix_stokes + +@pytest.mark.parametrize("dim", [2, 3]) +def test_to_fourier_matrix_stokes(dim: int) -> None: + ks = sym.make_sym_vector("k", dim) + mu = sym.Symbol("mu") + + diff_op = make_identity_diff_op(dim, dim + 1) + u = diff_op[:dim] + p = diff_op[dim] + pde = concat(mu * laplacian(u) - gradient(p), divergence(u)) + + k_sqr = sum(k**2 for k in ks) + if dim == 2: + expected = sym.Matrix([ + [-mu * k_sqr, 0, -sym.I*ks[0]], + [0, -mu * k_sqr, -sym.I*ks[1]], + [sym.I*ks[0], sym.I*ks[1], 0], + ]) + elif dim == 3: + expected = sym.Matrix([ + [-mu * k_sqr, 0, 0, -sym.I*ks[0]], + [0, -mu * k_sqr, 0, -sym.I*ks[1]], + [0, 0, -mu * k_sqr, -sym.I*ks[2]], + [sym.I*ks[0], sym.I*ks[1], sym.I*ks[2], 0], + ]) + else: + raise ValueError(f"unsupported dimension: {dim}") + + mat = to_fourier_matrix(pde, ks) + assert mat.expand() == expected.expand() + +# }}} + + +# {{{ test_to_fourier_matrix_elasticity + +@pytest.mark.parametrize("dim", [2, 3]) +def test_to_fourier_matrix_elasticity(dim: int) -> None: + ks = sym.make_sym_vector("k", dim) + mu = sym.Symbol("mu") + nu = sym.Symbol("nu") + mn = mu / (1 - 2 * nu) + + u = make_identity_diff_op(dim, dim) + pde = mu * laplacian(u) + mn * gradient(divergence(u)) + + k_sqr = sum(k**2 for k in ks) + if dim == 2: + expected = sym.Matrix([ + [-mu * k_sqr - mn * ks[0]**2, -mn * ks[0] * ks[1]], + [-mn * ks[1] * ks[0], -mu * k_sqr - mn * ks[1]**2], + ]) + elif dim == 3: + expected = sym.Matrix([ + [-mu * k_sqr - mn * ks[0]**2, -mn * ks[0] * ks[1], -mn * ks[0] * ks[2]], + [-mn * ks[1] * ks[0], -mu * k_sqr - mn * ks[1]**2, -mn * ks[1] * ks[2]], + [-mn * ks[2] * ks[0], -mn * ks[2] * ks[1], -mu * k_sqr - mn * ks[2]**2], + ]) + else: + raise ValueError(f"unsupported dimension: {dim}") + + mat = to_fourier_matrix(pde, ks) + assert mat.expand() == expected.expand() + +# }}} + + # {{{ test_weird_kernel w = make_identity_diff_op(2) From c7fb930b65df24addfcc9745ba261d111aa935a2 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 22 May 2026 15:56:43 +0300 Subject: [PATCH 4/6] feat: add the brinkman kernel to the tests --- sumpy/test/test_misc.py | 63 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/sumpy/test/test_misc.py b/sumpy/test/test_misc.py index da84d7b1..7affcf89 100644 --- a/sumpy/test/test_misc.py +++ b/sumpy/test/test_misc.py @@ -518,6 +518,33 @@ def test_as_scalar_pde_elasticity(): # }}} +# {{{ test_as_scalar_pde_brinkman + +def test_as_scalar_pde_brinkman(): + dim = 3 + mu = sym.Symbol("mu") + k = sym.Symbol("k") + + # NOTE: momentum + incompressibility equations + diff_op = make_identity_diff_op(dim, dim + 1) + u = diff_op[:3] + p = diff_op[3] + pde = concat(mu * (laplacian(u) - k**2 * u) - gradient(p), divergence(u)) + + # velocity components in Brinkman should satisfy Yukawa + for i in range(3): + scalar_pde = as_scalar_pde(pde, i) + + logger.info("pde\n%s", scalar_pde) + logger.info("\n%s", laplacian(laplacian(u[i]))) + assert scalar_pde == laplacian(laplacian(u[0]) - k**2 * u[0]) + + # pressure should satisfy Laplace + assert as_scalar_pde(pde, 3) == laplacian(u[0]) + +# }}} + + # {{{ test_elasticity_new def test_elasticity_new(): @@ -633,6 +660,42 @@ def test_to_fourier_matrix_elasticity(dim: int) -> None: # }}} +# {{{ test_to_fourier_matrix_brinkman + +@pytest.mark.parametrize("dim", [2, 3]) +def test_to_fourier_matrix_brinkman(dim: int) -> None: + ks = sym.make_sym_vector("k", dim) + mu = sym.Symbol("mu") + kappa = sym.Symbol("k") + + diff_op = make_identity_diff_op(dim, dim + 1) + u = diff_op[:dim] + p = diff_op[dim] + pde = concat(mu * (laplacian(u) - kappa**2 * u) - gradient(p), divergence(u)) + + k_sqr = sum(k**2 for k in ks) + if dim == 2: + expected = sym.Matrix([ + [mu * (-k_sqr - kappa**2), 0, -sym.I*ks[0]], + [0, mu * (-k_sqr - kappa**2), -sym.I*ks[1]], + [sym.I*ks[0], sym.I*ks[1], 0], + ]) + elif dim == 3: + expected = sym.Matrix([ + [mu * (-k_sqr - kappa**2), 0, 0, -sym.I*ks[0]], + [0, mu * (-k_sqr - kappa**2), 0, -sym.I*ks[1]], + [0, 0, mu * (-k_sqr - kappa**2), -sym.I*ks[2]], + [sym.I*ks[0], sym.I*ks[1], sym.I*ks[2], 0], + ]) + else: + raise ValueError(f"unsupported dimension: {dim}") + + mat = to_fourier_matrix(pde, ks) + assert mat.expand() == expected.expand() + +# }}} + + # {{{ test_weird_kernel w = make_identity_diff_op(2) From 2f4a99b658ba1625c7d027d2f92b73497cc0ff2e Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 22 May 2026 21:38:41 +0300 Subject: [PATCH 5/6] feat: rename LinearPDESystemOperator.dim to spatial_dim --- sumpy/expansion/diff_op.py | 72 ++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/sumpy/expansion/diff_op.py b/sumpy/expansion/diff_op.py index 6ce92145..c17129f0 100644 --- a/sumpy/expansion/diff_op.py +++ b/sumpy/expansion/diff_op.py @@ -94,30 +94,33 @@ class DerivativeIdentifier: class LinearPDESystemOperator: r""" Represents a constant-coefficient linear differential operator of a - vector-valued function with :attr:`dim` spatial variables. + vector-valued function with :attr:`spatial_dim` spatial variables and + additional temporal variables. The operator is given by a tuple of immutable dictionaries. The dictionary maps a :class:`DerivativeIdentifier` to a (time- and space-independent) - coefficient. It optionally supports a time variable as the last element in - the multi-index of the :class:`DerivativeIdentifier`. + coefficient. In the :class:`DerivativeIdentifier`, each multi-index has + :attr:`spatial_dim` indices for the spatial variables and the remaining + ones represent temporal variables. The class also supports basic arithmetic, i.e. multiplication and addition with other operators and constants. - .. autoattribute:: dim .. autoattribute:: eqs + .. autoattribute:: spatial_dim + .. autoproperty:: total_dims - .. autoproperty:: is_time_dependent .. autoproperty:: order - .. autoproperty:: total_dims + .. autoproperty:: nvariables + .. autoproperty:: is_time_dependent + .. automethod:: to_sym """ - dim: int - """The total number of spatial dimensions of the PDE (use :attr:`total_dims` + spatial_dim: int + """The number of spatial dimensions of the PDE (use :attr:`total_dims` to include time). """ - eqs: tuple[Mapping[DerivativeIdentifier, sym.Expr], ...] """A tuple of all the equations in the system.""" @@ -142,7 +145,7 @@ def __mul__(self, other: Number | sym.Expr) -> LinearPDESystemOperator: eqs.append(constantdict(deriv_ident_to_coeff)) - return LinearPDESystemOperator(self.dim, tuple(eqs)) + return LinearPDESystemOperator(self.spatial_dim, tuple(eqs)) def __rmul__(self, param: Number | sym.Expr) -> LinearPDESystemOperator: return self.__mul__(param) @@ -151,7 +154,7 @@ def __add__(self, other: LinearPDESystemOperator) -> LinearPDESystemOperator: if not isinstance(other, LinearPDESystemOperator): return NotImplemented - assert self.dim == other.dim + assert self.spatial_dim == other.spatial_dim assert len(self.eqs) == len(other.eqs) eqs: list[Mapping[DerivativeIdentifier, sym.Expr]] = [] @@ -164,7 +167,7 @@ def __add__(self, other: LinearPDESystemOperator) -> LinearPDESystemOperator: res[k] = v eqs.append(constantdict(res)) - return LinearPDESystemOperator(self.dim, tuple(eqs)) + return LinearPDESystemOperator(self.spatial_dim, tuple(eqs)) def __radd__(self, other: LinearPDESystemOperator) -> LinearPDESystemOperator: return self.__add__(other) @@ -179,17 +182,17 @@ def __neg__(self) -> LinearPDESystemOperator: @override def __repr__(self) -> str: - return f"LinearPDESystemOperator({self.dim}, {self.eqs!r})" + return f"LinearPDESystemOperator({self.spatial_dim}, {self.eqs!r})" def __getitem__(self, idx: int | slice) -> LinearPDESystemOperator: item = self.eqs.__getitem__(idx) eqs = item if isinstance(item, tuple) else (item,) - return LinearPDESystemOperator(self.dim, eqs) + return LinearPDESystemOperator(self.spatial_dim, eqs) @property def is_time_dependent(self) -> bool: """Is *True* if the PDE operator has a time component.""" - return self.dim != self.total_dims + return self.spatial_dim != self.total_dims @cached_property def order(self) -> int: @@ -227,8 +230,8 @@ def to_sym( (defaults to `["x0", "x1", ....]`) :arg t_var_name: the name of the temporal variables. """ - x: list[sym.Expr] = list(sym.make_sym_vector(x_var_name, self.dim)) - x.extend(sym.make_sym_vector(t_var_name, self.total_dims - self.dim)) + x: list[sym.Expr] = list(sym.make_sym_vector(x_var_name, self.spatial_dim)) + x.extend(sym.make_sym_vector(t_var_name, self.total_dims - self.spatial_dim)) if fnames is None: noutputs = 0 @@ -282,8 +285,8 @@ def _get_all_scalar_pdes(pde: LinearPDESystemOperator) -> list[LinearPDESystemOp import sympy as sp from sympy.polys.orderings import grevlex - gens = [sp.Symbol(f"_x{i}") for i in range(pde.dim)] - gens += [sp.Symbol(f"_t{i}") for i in range(pde.total_dims - pde.dim)] + gens = [sp.Symbol(f"_x{i}") for i in range(pde.spatial_dim)] + gens += [sp.Symbol(f"_t{i}") for i in range(pde.total_dims - pde.spatial_dim)] max_vec_idx = max(deriv_ident.vec_idx for eq in pde.eqs for deriv_ident in eq) @@ -348,7 +351,8 @@ def intersect(a: SubModulePolyRing, b: SubModulePolyRing) -> SubModulePolyRing: for (mi, coeff) in zip(scalar_pde.monoms(), scalar_pde.coeffs(), strict=True) } - results.append(LinearPDESystemOperator(pde.dim, (constantdict(pde_dict),))) + results.append(LinearPDESystemOperator(pde.spatial_dim, + (constantdict(pde_dict),))) return results @@ -464,7 +468,7 @@ def to_fourier_matrix( def laplacian(diff_op: LinearPDESystemOperator) -> LinearPDESystemOperator: - dim = diff_op.dim + dim = diff_op.spatial_dim empty: tuple[Mapping[DerivativeIdentifier, sym.Expr], ...] = ( (constantdict(),) * len(diff_op.eqs)) @@ -490,17 +494,17 @@ def diff( eqs.append(constantdict(res)) - return LinearPDESystemOperator(diff_op.dim, tuple(eqs)) + return LinearPDESystemOperator(diff_op.spatial_dim, tuple(eqs)) def divergence(diff_op: LinearPDESystemOperator) -> LinearPDESystemOperator: - if len(diff_op.eqs) != diff_op.dim: + if len(diff_op.eqs) != diff_op.spatial_dim: raise ValueError( "number of equations does not match system dimension: " - f"got {len(diff_op.eqs)} equations for {diff_op.dim}d system") + f"got {len(diff_op.eqs)} equations for {diff_op.spatial_dim}d system") - res = LinearPDESystemOperator(diff_op.dim, (constantdict(),)) - for i in range(diff_op.dim): + res = LinearPDESystemOperator(diff_op.spatial_dim, (constantdict(),)) + for i in range(diff_op.spatial_dim): mi = [0]*diff_op.total_dims mi[i] = 1 res += diff(diff_op[i], tuple(mi)) @@ -514,7 +518,7 @@ def gradient(diff_op: LinearPDESystemOperator) -> LinearPDESystemOperator: f"can only take gradient of scalar system: got {len(diff_op.eqs)}d") eqs: list[Mapping[DerivativeIdentifier, sym.Expr]] = [] - dim = diff_op.dim + dim = diff_op.spatial_dim for i in range(dim): mi = [0]*diff_op.total_dims mi[i] = 1 @@ -524,13 +528,13 @@ def gradient(diff_op: LinearPDESystemOperator) -> LinearPDESystemOperator: def curl(diff_op: LinearPDESystemOperator) -> LinearPDESystemOperator: - if len(diff_op.eqs) != diff_op.dim: + if len(diff_op.eqs) != diff_op.spatial_dim: raise ValueError( "number of equations does not match system dimension: " - f"got {len(diff_op.eqs)} equations for {diff_op.dim}d system") + f"got {len(diff_op.eqs)} equations for {diff_op.spatial_dim}d system") - if diff_op.dim != 3: - raise ValueError(f"can only take curl of 3d system: got {diff_op.dim}d") + if diff_op.spatial_dim != 3: + raise ValueError(f"can only take curl of 3d system: got {diff_op.spatial_dim}d") eqs: list[Mapping[DerivativeIdentifier, sym.Expr]] = [] mis: list[MultiIndex] = [] @@ -545,7 +549,7 @@ def curl(diff_op: LinearPDESystemOperator) -> LinearPDESystemOperator: - diff(diff_op[(i+1) % 3], mis[(i+2) % 3])) eqs.append(new_pde.eqs[0]) - return LinearPDESystemOperator(diff_op.dim, tuple(eqs)) + return LinearPDESystemOperator(diff_op.spatial_dim, tuple(eqs)) def concat(*ops: LinearPDESystemOperator) -> LinearPDESystemOperator: @@ -555,8 +559,8 @@ def concat(*ops: LinearPDESystemOperator) -> LinearPDESystemOperator: if len(ops) == 1: return ops[0] - dim = ops[0].dim - if not all(op.dim == dim for op in ops): + dim = ops[0].spatial_dim + if not all(op.spatial_dim == dim for op in ops): raise ValueError(f"operators must have the same dimension (expected {dim}d)") eqs = list(ops[0].eqs) From 6fae83c3cf56de7f392eae86eaac43f8f7a34b87 Mon Sep 17 00:00:00 2001 From: Alexandru Fikl Date: Fri, 22 May 2026 16:16:15 +0300 Subject: [PATCH 6/6] chore: update baseline --- .basedpyright/baseline.json | 308 +++++++++++++++++++++++++++++++++++- 1 file changed, 302 insertions(+), 6 deletions(-) diff --git a/.basedpyright/baseline.json b/.basedpyright/baseline.json index f474cceb..cb82cdc4 100644 --- a/.basedpyright/baseline.json +++ b/.basedpyright/baseline.json @@ -6509,7 +6509,7 @@ "code": "reportArgumentType", "range": { "startColumn": 33, - "endColumn": 67, + "endColumn": 82, "lineCount": 1 } }, @@ -6517,7 +6517,7 @@ "code": "reportArgumentType", "range": { "startColumn": 17, - "endColumn": 69, + "endColumn": 84, "lineCount": 1 } }, @@ -6788,16 +6788,40 @@ { "code": "reportUnknownArgumentType", "range": { - "startColumn": 56, - "endColumn": 81, + "startColumn": 47, + "endColumn": 72, "lineCount": 1 } }, { "code": "reportUnknownArgumentType", "range": { - "startColumn": 70, - "endColumn": 78, + "startColumn": 61, + "endColumn": 69, + "lineCount": 1 + } + }, + { + "code": "reportCallIssue", + "range": { + "startColumn": 12, + "endColumn": 36, + "lineCount": 1 + } + }, + { + "code": "reportArgumentType", + "range": { + "startColumn": 12, + "endColumn": 36, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 8, + "endColumn": 18, "lineCount": 1 } }, @@ -23417,6 +23441,278 @@ "lineCount": 1 } }, + { + "code": "reportAny", + "range": { + "startColumn": 4, + "endColumn": 7, + "lineCount": 1 + } + }, + { + "code": "reportAny", + "range": { + "startColumn": 35, + "endColumn": 38, + "lineCount": 1 + } + }, + { + "code": "reportArgumentType", + "range": { + "startColumn": 17, + "endColumn": 61, + "lineCount": 1 + } + }, + { + "code": "reportArgumentType", + "range": { + "startColumn": 39, + "endColumn": 68, + "lineCount": 1 + } + }, + { + "code": "reportOperatorIssue", + "range": { + "startColumn": 32, + "endColumn": 40, + "lineCount": 1 + } + }, + { + "code": "reportOperatorIssue", + "range": { + "startColumn": 43, + "endColumn": 51, + "lineCount": 1 + } + }, + { + "code": "reportOperatorIssue", + "range": { + "startColumn": 32, + "endColumn": 40, + "lineCount": 1 + } + }, + { + "code": "reportOperatorIssue", + "range": { + "startColumn": 43, + "endColumn": 51, + "lineCount": 1 + } + }, + { + "code": "reportOperatorIssue", + "range": { + "startColumn": 54, + "endColumn": 62, + "lineCount": 1 + } + }, + { + "code": "reportOperatorIssue", + "range": { + "startColumn": 32, + "endColumn": 40, + "lineCount": 1 + } + }, + { + "code": "reportOperatorIssue", + "range": { + "startColumn": 43, + "endColumn": 51, + "lineCount": 1 + } + }, + { + "code": "reportOperatorIssue", + "range": { + "startColumn": 32, + "endColumn": 40, + "lineCount": 1 + } + }, + { + "code": "reportOperatorIssue", + "range": { + "startColumn": 43, + "endColumn": 51, + "lineCount": 1 + } + }, + { + "code": "reportOperatorIssue", + "range": { + "startColumn": 54, + "endColumn": 62, + "lineCount": 1 + } + }, + { + "code": "reportArgumentType", + "range": { + "startColumn": 17, + "endColumn": 48, + "lineCount": 1 + } + }, + { + "code": "reportOperatorIssue", + "range": { + "startColumn": 16, + "endColumn": 20, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 16, + "endColumn": 32, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 11, + "endColumn": 21, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 27, + "endColumn": 42, + "lineCount": 1 + } + }, + { + "code": "reportOperatorIssue", + "range": { + "startColumn": 16, + "endColumn": 20, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 16, + "endColumn": 32, + "lineCount": 1 + } + }, + { + "code": "reportOperatorIssue", + "range": { + "startColumn": 32, + "endColumn": 40, + "lineCount": 1 + } + }, + { + "code": "reportOperatorIssue", + "range": { + "startColumn": 53, + "endColumn": 61, + "lineCount": 1 + } + }, + { + "code": "reportOperatorIssue", + "range": { + "startColumn": 32, + "endColumn": 40, + "lineCount": 1 + } + }, + { + "code": "reportOperatorIssue", + "range": { + "startColumn": 53, + "endColumn": 61, + "lineCount": 1 + } + }, + { + "code": "reportOperatorIssue", + "range": { + "startColumn": 74, + "endColumn": 82, + "lineCount": 1 + } + }, + { + "code": "reportArgumentType", + "range": { + "startColumn": 28, + "endColumn": 31, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 11, + "endColumn": 21, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 27, + "endColumn": 42, + "lineCount": 1 + } + }, + { + "code": "reportArgumentType", + "range": { + "startColumn": 17, + "endColumn": 65, + "lineCount": 1 + } + }, + { + "code": "reportOperatorIssue", + "range": { + "startColumn": 16, + "endColumn": 20, + "lineCount": 1 + } + }, + { + "code": "reportUnknownArgumentType", + "range": { + "startColumn": 16, + "endColumn": 32, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 11, + "endColumn": 21, + "lineCount": 1 + } + }, + { + "code": "reportUnknownMemberType", + "range": { + "startColumn": 27, + "endColumn": 42, + "lineCount": 1 + } + }, { "code": "reportUnknownParameterType", "range": {