Skip to content

Commit

Permalink
Merge pull request #165 from neutrinoceros/lint_docstrings
Browse files Browse the repository at this point in the history
  • Loading branch information
neutrinoceros authored Nov 3, 2023
2 parents a269ff0 + b74b633 commit b93cd3b
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 62 deletions.
12 changes: 10 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,12 @@ exclude_lines = [
]

[tool.ruff]
exclude = ["*__init__.py"]
ignore = ["E501"]
ignore = ["E501", "D101"]
select = [
"E",
"F",
"W",
"D", # pydocstyle
"C4", # flake8-comprehensions
"B", # flake8-bugbear
"YTT", # flake8-2020
Expand All @@ -95,6 +95,14 @@ select = [
combine-as-imports = true
known-first-party = ["gpgi"]

[tool.ruff.pydocstyle]
convention = "numpy"

[tool.ruff.per-file-ignores]
"tests/**" = ["D"]
"setup.py" = ["D"]
"_backports.py" = ["D"]

[tool.mypy]
python_version = "3.9"
show_error_codes = true
Expand Down
65 changes: 64 additions & 1 deletion src/gpgi/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,68 @@
"""gpgi: Fast particle deposition at post-processing time."""
from __future__ import annotations

from .api import load
from typing import Any

from .types import Dataset, FieldMap, Geometry, Grid, ParticleSet

__version__ = "0.14.0"


def load(
*,
geometry: str = "cartesian",
grid: dict[str, FieldMap] | None = None,
particles: dict[str, FieldMap] | None = None,
metadata: dict[str, Any] | None = None,
) -> Dataset:
r"""
Load a Dataset.
Parameters
----------
geometry: Literal["cartesian", "polar", "cylindrical", "spherical", "equatorial"]
This flag is used for validation of axis names, order and domain limits.
grid: dict[str, FieldMap] (optional)
A dictionary representing the grid coordinates as 1D arrays of cell left edges,
and on-grid fields as ND arrays (fields are assumed to be defined on cell
centers)
particles: dict[str, FieldMap] (optional)
A dictionary representing particle coordinates and associated fields as 1D
arrays
metadata: dict[str, Any] (optional)
A dictionnary representing arbitrary additional data, that will be attached to
the returned Dataset as an attribute (namely, ds.metadata). This special
attribute is accessible from boundary condition methods as the argument of the
same name.
"""
try:
_geometry = Geometry(geometry)
except ValueError:
raise ValueError(
f"unknown geometry {geometry!r}, expected any of {tuple(_.value for _ in Geometry)}"
) from None

_grid: Grid | None = None
if grid is not None:
if "cell_edges" not in grid:
raise ValueError("grid dictionary missing required key 'cell_edges'")
_grid = Grid(
_geometry, cell_edges=grid["cell_edges"], fields=grid.get("fields", {})
)

_particles: ParticleSet | None = None
if particles is not None:
if "coordinates" not in particles:
raise ValueError("particles dictionary missing required key 'coordinates'")
_particles = ParticleSet(
_geometry,
coordinates=particles["coordinates"],
fields=particles.get("fields", {}),
)

return Dataset(
geometry=_geometry, grid=_grid, particles=_particles, metadata=metadata
)
42 changes: 0 additions & 42 deletions src/gpgi/api.py

This file was deleted.

1 change: 1 addition & 0 deletions src/gpgi/lib/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Python implementations of deposition algorithms."""
68 changes: 55 additions & 13 deletions src/gpgi/types.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
r"""Define the core data structures of the library: Grid, ParticleSet, and Dataset."""
from __future__ import annotations

import enum
Expand Down Expand Up @@ -48,10 +49,12 @@ class Geometry(StrEnum):
SPHERICAL = enum.auto()
EQUATORIAL = enum.auto()

def __str__(self) -> str:
if sys.version_info >= (3, 11):
return super().__str__()
else:
if sys.version_info >= (3, 11):
pass
else:

def __str__(self) -> str:
r"""Return str(self)."""
return self.name.lower()


Expand Down Expand Up @@ -202,7 +205,7 @@ def _validate_geometry(self) -> None:
}


class CoordinateValidatorMixin(ValidatorMixin, CoordinateData, ABC):
class _CoordinateValidatorMixin(ValidatorMixin, CoordinateData, ABC):
def __init__(self) -> None:
super().__init__()
dts = {
Expand Down Expand Up @@ -252,13 +255,26 @@ def _get_safe_datatype(self, reference: np.ndarray | None = None) -> np.dtype:
return np.dtype(dt)


class Grid(CoordinateValidatorMixin):
class Grid(_CoordinateValidatorMixin):
def __init__(
self,
geometry: Geometry,
cell_edges: FieldMap,
fields: FieldMap | None = None,
) -> None:
r"""
Define a Grid from cell left-edges and data fields.
Parameters
----------
geometry: gpgi.types.Geometry
cell_edges: gpgi.types.FieldMap
Left cell edges in each direction as 1D arrays, including the right edge
of the rightmost cell.
fields: gpgi.types.FieldMap (optional)
"""
self.geometry = geometry
self.coordinates = cell_edges

Expand Down Expand Up @@ -288,18 +304,22 @@ def _validate(self) -> None:

@property
def cell_edges(self) -> FieldMap:
r"""An alias for self.coordinates."""
return self.coordinates

@cached_property
def cell_centers(self) -> FieldMap:
r"""The positions of cell centers in each direction."""
return {ax: 0.5 * (arr[1:] + arr[:-1]) for ax, arr in self.coordinates.items()}

@cached_property
def cell_widths(self) -> FieldMap:
r"""The width of cells, expressed as the difference between consecutive left edges."""
return {ax: np.diff(arr) for ax, arr in self.coordinates.items()}

@cached_property
def shape(self) -> tuple[int, ...]:
r"""The shape of the grid, as a tuple (nx1, (nx2, (nx3)))."""
return tuple(len(_) - 1 for _ in self.cell_edges.values())

@cached_property
Expand All @@ -308,18 +328,22 @@ def _padded_shape(self) -> tuple[int, ...]:

@property
def size(self) -> int:
r"""The total number of cells in the grid."""
return reduce(int.__mul__, self.shape)

@property
def ndim(self) -> int:
r"""The number of spatial dimensions that coordinates are defined in."""
return len(self.axes)

@property
def cell_volumes(self) -> RealArray:
"""
r"""
The generalized ND-volume of grid cells.
3D: (nx, ny, nz) array representing volumes
2D: (nx, ny) array representing surface
1D: (nx,) array representing widths (redundant with cell_widths)
1D: (nx,) array representing widths (redundant with cell_widths).
"""
widths = list(self.cell_widths.values())
if self.geometry is Geometry.CARTESIAN:
Expand All @@ -331,7 +355,7 @@ def cell_volumes(self) -> RealArray:
)


class ParticleSet(CoordinateValidatorMixin):
class ParticleSet(_CoordinateValidatorMixin):
def __init__(
self,
geometry: Geometry,
Expand All @@ -357,10 +381,12 @@ def _validate(self) -> None:

@property
def count(self) -> int:
r"""The total number of particles in the set."""
return len(next(iter(self.coordinates.values())))

@property
def ndim(self) -> int:
r"""The number of spatial dimensions that coordinates are defined in."""
return len(self.axes)


Expand All @@ -373,6 +399,24 @@ def __init__(
particles: ParticleSet | None = None,
metadata: dict[str, Any] | None = None,
) -> None:
r"""
Compose a Dataset from a Grid, a ParticleSet, or both.
Parameters
----------
geometry: gpgi.types.Geometry
An enum member that represents the geometry.
grid: gpgi.types.Grid (optional)
particles: gpgi.types.ParticleSet (optional)
metadata: dict[str, Any] (optional)
A dictionnary representing arbitrary additional data, that will be attached to
the returned Dataset as an attribute (namely, ds.metadata). This special
attribute is accessible from boundary condition methods as the argument of the
same name.
"""
self.geometry = geometry

if grid is None:
Expand Down Expand Up @@ -487,8 +531,8 @@ def _setup_host_cell_index(self, verbose: bool = False) -> None:
@property
def host_cell_index(self) -> np.ndarray[Any, np.dtype[np.uint16]]:
r"""
The host cell index (HCI) represents the ndimensional index
of the host cell for each particle.
The ND index of the host cell for each particle.
It has shape (particles.count, grid.ndim).
Indices are 0-based and ghost layers are included.
"""
Expand Down Expand Up @@ -530,7 +574,6 @@ def is_sorted(self, *, axes: tuple[int, ...] | None = None) -> bool:
Parameters
----------
axes: tuple[int, ...]
specify in which order axes should be used for sorting.
"""
Expand All @@ -544,7 +587,6 @@ def sorted(self, axes: tuple[int, ...] | None = None) -> Self:
Parameters
----------
axes: tuple[int, ...]
specify in which order axes should be used for sorting.
"""
Expand Down
8 changes: 4 additions & 4 deletions tests/test_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
import numpy.testing as npt
import pytest

from gpgi.api import load
import gpgi


def test_cell_volumes_cartesian():
ds = load(
ds = gpgi.load(
geometry="cartesian",
grid={
"cell_edges": {
Expand All @@ -21,7 +21,7 @@ def test_cell_volumes_cartesian():


def test_cell_volumes_curvilinear():
ds = load(
ds = gpgi.load(
geometry="cylindrical",
grid={
"cell_edges": {
Expand All @@ -38,7 +38,7 @@ def test_cell_volumes_curvilinear():


def test_cell_volumes_shape():
ds = load(
ds = gpgi.load(
grid={
"cell_edges": {
"x": np.linspace(0, 1, 3),
Expand Down

0 comments on commit b93cd3b

Please sign in to comment.