PyCDFt 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pycdft/__init__.py +59 -0
- pycdft/api.py +199 -0
- pycdft/core.py +489 -0
- pycdft/gpu/__init__.py +42 -0
- pycdft/gpu/_torch.py +29 -0
- pycdft/gpu/api.py +634 -0
- pycdft/gpu/devices.py +160 -0
- pycdft/gpu/kernels.py +554 -0
- pycdft/math.py +561 -0
- pycdft/py.typed +0 -0
- pycdft-0.1.0.dist-info/METADATA +289 -0
- pycdft-0.1.0.dist-info/RECORD +15 -0
- pycdft-0.1.0.dist-info/WHEEL +5 -0
- pycdft-0.1.0.dist-info/licenses/LICENSE +201 -0
- pycdft-0.1.0.dist-info/top_level.txt +1 -0
pycdft/gpu/devices.py
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""Device detection and resolution for :mod:`pycdft.gpu`.
|
|
2
|
+
|
|
3
|
+
The torch backend is device-agnostic: it runs wherever torch exposes a
|
|
4
|
+
device. Detection order (first available wins for the automatic default):
|
|
5
|
+
|
|
6
|
+
- ``cuda`` -- NVIDIA CUDA, and also AMD ROCm builds of torch, which expose
|
|
7
|
+
the ROCm devices through the same ``cuda`` API (a ROCm build is
|
|
8
|
+
identified by ``torch.version.hip`` being set and
|
|
9
|
+
``torch.cuda.is_available()`` being true);
|
|
10
|
+
- ``rocm`` -- reported (as an alias of the same ``cuda`` device) when a
|
|
11
|
+
ROCm build of torch is detected;
|
|
12
|
+
- ``mps`` -- Apple Metal (MacOS);
|
|
13
|
+
- ``cpu`` -- always available.
|
|
14
|
+
|
|
15
|
+
A specific device can be requested (``"cuda"``, ``"cuda:0"``, ``"mps"``,
|
|
16
|
+
``"cpu"``, or ``"rocm"`` as an alias for the CUDA device of a ROCm build);
|
|
17
|
+
``resolve_device`` raises ``ValueError`` when the requested device is not
|
|
18
|
+
available, so explicit requests never silently fall back to CPU.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from typing import TYPE_CHECKING
|
|
24
|
+
|
|
25
|
+
from ._torch import TORCH_AVAILABLE, require_torch
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING: # annotations only, never runtime
|
|
28
|
+
import torch
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"TORCH_AVAILABLE",
|
|
32
|
+
"available_devices",
|
|
33
|
+
"rocm_available",
|
|
34
|
+
"is_rocm_build",
|
|
35
|
+
"resolve_device",
|
|
36
|
+
"describe",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
#: Preferred order for the automatic default device.
|
|
40
|
+
_DEFAULT_ORDER = ("cuda", "mps", "cpu")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def is_rocm_build() -> bool:
|
|
44
|
+
"""True when the installed torch was built for AMD ROCm."""
|
|
45
|
+
if not TORCH_AVAILABLE:
|
|
46
|
+
return False
|
|
47
|
+
torch = require_torch()
|
|
48
|
+
return getattr(torch.version, "hip", None) is not None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def rocm_available() -> bool:
|
|
52
|
+
"""True when torch exposes an AMD ROCm device.
|
|
53
|
+
|
|
54
|
+
A ROCm build of torch is identified by ``torch.version.hip`` (set) and
|
|
55
|
+
exposes the AMD devices through the ``cuda`` API. ``torch.backends.rocm``
|
|
56
|
+
exists only in some builds; the HIP build string plus
|
|
57
|
+
``torch.cuda.is_available()`` is the reliable marker.
|
|
58
|
+
"""
|
|
59
|
+
if not TORCH_AVAILABLE:
|
|
60
|
+
return False
|
|
61
|
+
torch = require_torch()
|
|
62
|
+
backend = getattr(torch.backends, "rocm", None)
|
|
63
|
+
if backend is not None and getattr(backend, "is_available", lambda: False)():
|
|
64
|
+
return True
|
|
65
|
+
return bool(is_rocm_build() and torch.cuda.is_available())
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def available_devices() -> list:
|
|
69
|
+
"""Names of the devices torch can use, best first (``["cuda", "cpu"]``,
|
|
70
|
+
``["cuda", "rocm", "cpu"]`` for a ROCm build, ``["mps", "cpu"]``, ...).
|
|
71
|
+
|
|
72
|
+
An empty list means torch is not installed.
|
|
73
|
+
"""
|
|
74
|
+
if not TORCH_AVAILABLE:
|
|
75
|
+
return []
|
|
76
|
+
torch = require_torch()
|
|
77
|
+
devices = []
|
|
78
|
+
if torch.cuda.is_available():
|
|
79
|
+
devices.append("cuda")
|
|
80
|
+
if rocm_available():
|
|
81
|
+
devices.append("rocm") # same physical device, ROCm flavour
|
|
82
|
+
if torch.backends.mps.is_available():
|
|
83
|
+
devices.append("mps")
|
|
84
|
+
devices.append("cpu")
|
|
85
|
+
return devices
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def resolve_device(preferred=None) -> "torch.device":
|
|
89
|
+
"""Resolve a device name (or ``None`` for the best available device).
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
preferred : str, torch.device or None
|
|
94
|
+
``"cuda"`` / ``"cuda:0"``, ``"mps"``, ``"cpu"``, or ``"rocm"``
|
|
95
|
+
(alias for the CUDA device of a ROCm build). ``None`` picks the
|
|
96
|
+
best available device automatically.
|
|
97
|
+
|
|
98
|
+
Returns
|
|
99
|
+
-------
|
|
100
|
+
torch.device
|
|
101
|
+
|
|
102
|
+
Raises
|
|
103
|
+
------
|
|
104
|
+
ImportError
|
|
105
|
+
If torch is not installed.
|
|
106
|
+
ValueError
|
|
107
|
+
If the requested device is not available on this machine (an
|
|
108
|
+
explicit request never silently falls back).
|
|
109
|
+
"""
|
|
110
|
+
torch = require_torch()
|
|
111
|
+
available = available_devices()
|
|
112
|
+
if preferred is None:
|
|
113
|
+
for name in _DEFAULT_ORDER:
|
|
114
|
+
if name in available:
|
|
115
|
+
return torch.device(name)
|
|
116
|
+
raise RuntimeError("torch is installed but exposes no usable device")
|
|
117
|
+
if isinstance(preferred, torch.device):
|
|
118
|
+
name = preferred.type
|
|
119
|
+
index = preferred.index
|
|
120
|
+
else:
|
|
121
|
+
name, _, index_part = str(preferred).partition(":")
|
|
122
|
+
index = int(index_part) if index_part else None
|
|
123
|
+
if name == "rocm":
|
|
124
|
+
if "rocm" not in available:
|
|
125
|
+
raise ValueError(
|
|
126
|
+
f"torch device 'rocm' is not available on this machine "
|
|
127
|
+
f"(available: {available}); a ROCm build of torch is "
|
|
128
|
+
f"required on AMD GPUs")
|
|
129
|
+
return torch.device("cuda" if index is None else f"cuda:{index}")
|
|
130
|
+
if name not in available:
|
|
131
|
+
raise ValueError(
|
|
132
|
+
f"torch device {preferred!r} is not available on this machine "
|
|
133
|
+
f"(available: {available})")
|
|
134
|
+
return torch.device(name if index is None else f"{name}:{index}")
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def describe() -> dict:
|
|
138
|
+
"""Human-readable description of the torch installation and devices.
|
|
139
|
+
|
|
140
|
+
Returns a dict with the torch version, the CUDA / ROCm build versions,
|
|
141
|
+
the available devices and, for CUDA devices, ``torch.cuda.get_device_name``
|
|
142
|
+
of each device. Useful for diagnostics (e.g. on a compute node).
|
|
143
|
+
"""
|
|
144
|
+
if not TORCH_AVAILABLE:
|
|
145
|
+
return {"torch_available": False, "devices": []}
|
|
146
|
+
torch = require_torch()
|
|
147
|
+
info = {
|
|
148
|
+
"torch_available": True,
|
|
149
|
+
"torch_version": torch.__version__,
|
|
150
|
+
"cuda_build": getattr(torch.version, "cuda", None),
|
|
151
|
+
"hip_build": getattr(torch.version, "hip", None),
|
|
152
|
+
"rocm_backend": rocm_available(),
|
|
153
|
+
"devices": available_devices(),
|
|
154
|
+
}
|
|
155
|
+
if torch.cuda.is_available():
|
|
156
|
+
info["cuda_device_count"] = torch.cuda.device_count()
|
|
157
|
+
info["cuda_device_names"] = [
|
|
158
|
+
torch.cuda.get_device_name(i) for i in range(torch.cuda.device_count())
|
|
159
|
+
]
|
|
160
|
+
return info
|