Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bump min Python to 3.10 #243

Merged
merged 5 commits into from
Nov 3, 2024
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
3 changes: 3 additions & 0 deletions .pylintrc-local.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
- arg: py-version
val: '3.10'

- arg: ignore
val:
- old_diffop_primitives.py
Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ requires = [

[project]
name = "pytential"
version = "2020.2"
version = "2024.0"
description = "Evaluate layer and volume potentials accurately. Solve integral equations."
readme = "README.rst"
authors = [
{ name = "Andreas Kloeckner", email = "[email protected]" },
]
requires-python = ">=3.8"
requires-python = ">=3.10"
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
Expand Down Expand Up @@ -140,7 +140,7 @@ extend-ignore-re = [
]

[tool.mypy]
python_version = "3.8"
python_version = "3.10"
warn_unused_ignores = true
exclude = [
"pytential/symbolic/old_diffop_primitives.py",
Expand Down
60 changes: 31 additions & 29 deletions pytential/collection.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

__copyright__ = """
Copyright (C) 2013 Andreas Kloeckner
Copyright (C) 2018 Alexandru Fikl
Expand All @@ -23,7 +25,8 @@
THE SOFTWARE.
"""

from typing import Any, Dict, Hashable, Mapping, Optional, Tuple, Union
from collections.abc import Hashable, Mapping
from typing import Any

import immutables

Expand All @@ -37,20 +40,21 @@
from meshmode.discretization import Discretization

__doc__ = """
.. class:: AutoWhereLike

Types accepted for ``auto_where`` arguments to aid in determining where an
expression is evaluated.

.. class:: GeometryLike

Types accepted by the :class:`GeometryCollection`.

.. autoclass:: GeometryCollection

.. autofunction:: add_geometry_to_collection
"""

GeometryLike = Union[TargetBase, PotentialSource, Discretization]
AutoWhereLike = Union[
"DOFDescriptorLike",
Tuple["DOFDescriptorLike", "DOFDescriptorLike"]
]
GeometryLike = TargetBase | PotentialSource | Discretization
AutoWhereLike = DOFDescriptorLike | tuple[DOFDescriptorLike, DOFDescriptorLike]


def _is_valid_identifier(name: str) -> bool:
Expand Down Expand Up @@ -113,12 +117,12 @@ class GeometryCollection:
"""

def __init__(self,
places: Union[
"GeometryLike",
Tuple["GeometryLike", "GeometryLike"],
Mapping[Hashable, "GeometryLike"]
],
auto_where: Optional[AutoWhereLike] = None) -> None:
places: (
GeometryLike
| tuple[GeometryLike, GeometryLike]
| Mapping[Hashable, GeometryLike]
),
auto_where: AutoWhereLike | None = None) -> None:
r"""
:arg places: a scalar, tuple of or mapping of symbolic names to
geometry objects. Supported objects are
Expand Down Expand Up @@ -151,7 +155,7 @@ def __init__(self,
elif isinstance(places, TargetBase):
places_dict = {auto_target.geometry: places}
auto_source = auto_target
if isinstance(places, (Discretization, PotentialSource)):
if isinstance(places, Discretization | PotentialSource):
places_dict = {
auto_source.geometry: places,
auto_target.geometry: places
Expand All @@ -169,7 +173,7 @@ def __init__(self,
self.places = immutables.Map(places_dict)
self.auto_where = (auto_source, auto_target)

self._caches: Dict[str, Any] = {}
self._caches: dict[str, Any] = {}

# }}}

Expand All @@ -193,7 +197,7 @@ def __init__(self,

# check allowed types
for p in self.places.values():
if not isinstance(p, (PotentialSource, TargetBase, Discretization)):
if not isinstance(p, PotentialSource | TargetBase | Discretization):
raise TypeError(
"Values in 'places' must be discretization, targets "
f"or layer potential sources, got '{type(p).__name__}'")
Expand Down Expand Up @@ -282,9 +286,7 @@ def _get_qbx_discretization(self, geometry, discr_stage):

# }}}

def get_connection(self,
from_dd: "DOFDescriptorLike",
to_dd: "DOFDescriptorLike"):
def get_connection(self, from_dd: DOFDescriptorLike, to_dd: DOFDescriptorLike):
"""Construct a connection from *from_dd* to *to_dd* geometries.

:returns: an object compatible with the
Expand All @@ -297,8 +299,8 @@ def get_connection(self,

def get_discretization(
self, geometry: Hashable,
discr_stage: Optional["DiscretizationStages"] = None
) -> "GeometryLike":
discr_stage: DiscretizationStages | None = None
) -> GeometryLike:
"""Get the geometry or discretization in the collection.

If a specific QBX stage discretization is requested, refinement is
Expand Down Expand Up @@ -327,7 +329,7 @@ def get_discretization(
else:
return discr

def get_geometry(self, geometry: Hashable) -> "GeometryLike":
def get_geometry(self, geometry: Hashable) -> GeometryLike:
"""
:arg geometry: the identifier of the geometry in the collection.
"""
Expand All @@ -339,18 +341,18 @@ def get_geometry(self, geometry: Hashable) -> "GeometryLike":

def copy(
self,
places: Optional[Mapping[Hashable, "GeometryLike"]] = None,
auto_where: Optional[AutoWhereLike] = None,
) -> "GeometryCollection":
places: Mapping[Hashable, GeometryLike] | None = None,
auto_where: AutoWhereLike | None = None,
) -> GeometryCollection:
"""Get a shallow copy of the geometry collection."""
return type(self)(
places=self.places if places is None else places,
auto_where=self.auto_where if auto_where is None else auto_where)

def merge(
self,
places: Union["GeometryCollection", Mapping[Hashable, "GeometryLike"]],
) -> "GeometryCollection":
places: GeometryCollection | Mapping[Hashable, GeometryLike],
) -> GeometryCollection:
"""Merges two geometry collections and returns the new collection.

:arg places: a mapping or :class:`GeometryCollection` to
Expand Down Expand Up @@ -380,7 +382,7 @@ def __str__(self):

def add_geometry_to_collection(
places: GeometryCollection,
geometries: Mapping[Hashable, "GeometryLike"]) -> GeometryCollection:
geometries: Mapping[Hashable, GeometryLike]) -> GeometryCollection:
"""Adds a mapping of geometries to an existing collection.

This function is similar to :meth:`GeometryCollection.merge`, but it makes
Expand All @@ -399,7 +401,7 @@ def add_geometry_to_collection(
if key in places.places:
raise ValueError(f"geometry '{key}' already in the collection")

if not isinstance(geometry, (PointsTarget, PointPotentialSource)):
if not isinstance(geometry, PointsTarget | PointPotentialSource):
raise TypeError(
f"Cannot add a geometry of type '{type(geometry).__name__}' "
"to the existing collection. Construct a new collection "
Expand Down
24 changes: 12 additions & 12 deletions pytential/linalg/gmres.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@
.. autoclass:: ResidualPrinter
"""

from collections.abc import Callable, Sequence
from dataclasses import dataclass
from functools import partial
from typing import Callable, Optional, Sequence

import numpy as np

Expand Down Expand Up @@ -193,7 +193,7 @@ def norm(x):
if (stall_iterations
and len(residual_norms) > stall_iterations
and norm_r > (
residual_norms[-stall_iterations] # pylint:disable=invalid-unary-operand-type
residual_norms[-stall_iterations]
/ no_progress_factor)):

state = "stalled"
Expand Down Expand Up @@ -278,16 +278,16 @@ def __call__(self, resid):
def gmres(
op: Callable[[ArrayOrContainerT], ArrayOrContainerT],
rhs: ArrayOrContainerT,
restart: Optional[int] = None,
tol: Optional[float] = None,
x0: Optional[ArrayOrContainerT] = None,
inner_product: Optional[
Callable[[ArrayOrContainerT, ArrayOrContainerT], float]] = None,
maxiter: Optional[int] = None,
hard_failure: Optional[bool] = None,
no_progress_factor: Optional[float] = None,
stall_iterations: Optional[int] = None,
callback: Optional[Callable[[ArrayOrContainerT], None]] = None,
restart: int | None = None,
tol: float | None = None,
x0: ArrayOrContainerT | None = None,
inner_product: (
Callable[[ArrayOrContainerT, ArrayOrContainerT], float] | None) = None,
maxiter: int | None = None,
hard_failure: bool | None = None,
no_progress_factor: float | None = None,
stall_iterations: int | None = None,
callback: Callable[[ArrayOrContainerT], None] | None = None,
progress: bool = False,
require_monotonicity: bool = True) -> GMRESResult:
"""Solve a linear system :math:`Ax = b` using GMRES with restarts.
Expand Down
28 changes: 15 additions & 13 deletions pytential/linalg/proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
THE SOFTWARE.
"""

from collections.abc import Callable
from dataclasses import dataclass
from typing import Any, Callable, Optional
from typing import Any

import numpy as np
import numpy.linalg as la
Expand Down Expand Up @@ -68,9 +69,9 @@

def partition_by_nodes(
actx: PyOpenCLArrayContext, places: GeometryCollection, *,
dofdesc: Optional["DOFDescriptorLike"] = None,
tree_kind: Optional[str] = "adaptive-level-restricted",
max_particles_in_box: Optional[int] = None) -> IndexList:
dofdesc: DOFDescriptorLike | None = None,
tree_kind: str | None = "adaptive-level-restricted",
max_particles_in_box: int | None = None) -> IndexList:
"""Generate equally sized ranges of nodes. The partition is created at the
lowest level of granularity, i.e. nodes. This results in balanced ranges
of points, but will split elements across different ranges.
Expand Down Expand Up @@ -116,7 +117,7 @@ def partition_by_nodes(
).nonzero()

indices: np.ndarray = np.empty(len(leaf_boxes), dtype=object)
starts: Optional[np.ndarray] = None
starts: np.ndarray | None = None

for i, ibox in enumerate(leaf_boxes):
box_start = tree.box_source_starts[ibox]
Expand Down Expand Up @@ -230,7 +231,7 @@ class ProxyClusterGeometryData:
centers: np.ndarray
radii: np.ndarray

_cluster_radii: Optional[np.ndarray] = None
_cluster_radii: np.ndarray | None = None

@property
def nclusters(self) -> int:
Expand Down Expand Up @@ -361,11 +362,11 @@ class ProxyGeneratorBase:

def __init__(
self, places: GeometryCollection,
approx_nproxy: Optional[int] = None,
radius_factor: Optional[float] = None,
approx_nproxy: int | None = None,
radius_factor: float | None = None,
norm_type: str = "linf",

_generate_ref_proxies: Optional[Callable[[int], np.ndarray]] = None,
_generate_ref_proxies: Callable[[int], np.ndarray] | None = None,
) -> None:
"""
:param approx_nproxy: desired number of proxy points. In higher
Expand Down Expand Up @@ -422,7 +423,7 @@ def get_radii_kernel_ex(self, actx: PyOpenCLArrayContext) -> lp.ExecutorBase:

def __call__(self,
actx: PyOpenCLArrayContext,
source_dd: Optional["DOFDescriptorLike"],
source_dd: DOFDescriptorLike | None,
dof_index: IndexList,
**kwargs: Any) -> ProxyClusterGeometryData:
"""Generate proxy points for each cluster in *dof_index_set* with nodes in
Expand Down Expand Up @@ -625,7 +626,7 @@ def get_radii_kernel_ex(self, actx: PyOpenCLArrayContext) -> lp.ExecutorBase:

def __call__(self,
actx: PyOpenCLArrayContext,
source_dd: Optional["DOFDescriptorLike"],
source_dd: DOFDescriptorLike | None,
dof_index: IndexList, **kwargs) -> ProxyClusterGeometryData:
if source_dd is None:
source_dd = self.places.auto_source
Expand All @@ -651,7 +652,7 @@ def __call__(self,

def gather_cluster_neighbor_points(
actx: PyOpenCLArrayContext, pxy: ProxyClusterGeometryData, *,
max_particles_in_box: Optional[int] = None) -> IndexList:
max_particles_in_box: int | None = None) -> IndexList:
"""Generate a set of neighboring points for each cluster of points in
*pxy*. Neighboring points of a cluster :math:`i` are defined
as all the points inside the proxy ball :math:`i` that do not also
Expand Down Expand Up @@ -735,7 +736,8 @@ def prg() -> lp.ExecutorBase:
# get nodes inside the boxes
istart = tree.box_source_starts[iboxes]
iend = istart + tree.box_source_counts_cumul[iboxes]
isources = np.hstack([np.arange(s, e) for s, e in zip(istart, iend)])
isources = np.hstack([
np.arange(s, e) for s, e in zip(istart, iend, strict=True)])
nodes = np.vstack([s[isources] for s in tree.sources])
isources = tree.user_source_ids[isources]

Expand Down
Loading
Loading