Skip to content
58 changes: 58 additions & 0 deletions examples/gpu_backend_selection.py
Comment thread
cncastillo marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""Simulate MRI acquisition with optional GPU acceleration.

This example demonstrates GPU backend selection:
- Uncomment km.load_cuda() (or load_metal/load_amdgpu/load_oneapi) to enable GPU
- Once a backend is loaded, gpu=true is used by default
- Without a backend, simulation runs on CPU automatically

The simulation produces identical results on CPU and GPU (within numerical precision).
"""

import numpy as np

import komamripy as km

# Optional: Load a GPU backend to enable GPU acceleration.
# Uncomment ONE of these (backend must be installed):
# km.load_cuda() # NVIDIA GPUs
# km.load_metal() # Apple Silicon
# km.load_amdgpu() # AMD GPUs
# km.load_oneapi() # Intel GPUs (experimental)

# If no backend is loaded, simulation runs on CPU automatically.

print("Creating phantom...")
coords = np.linspace(-40e-3, 40e-3, 64)
xx, yy = np.meshgrid(coords, coords)
radius = np.sqrt(xx**2 + yy**2)
mask = radius <= 32e-3

x = xx[mask]
y = yy[mask]
z = np.zeros_like(x)

phantom = km.Phantom(
name="circle",
x=x,
y=y,
z=z,
ρ=np.ones_like(x),
T1=np.ones_like(x),
T2=0.1 * np.ones_like(x),
T2s=0.1 * np.ones_like(x),
)

print("Setting up acquisition...")
sys = km.Scanner()
seq = km.PulseDesigner.EPI_example()

print("Simulating...")
sim_params = {"return_type": "mat"} # gpu=true by default if backend loaded
signal = km.simulate(phantom, seq, sys, sim_params=sim_params)
signal = np.asarray(signal).reshape(-1)

print("Simulation complete!")
print(f"Signal shape: {signal.shape}")
signal_min = np.abs(signal).min()
signal_max = np.abs(signal).max()
print(f"Signal magnitude range: [{signal_min:.2e}, {signal_max:.2e}]")
13 changes: 13 additions & 0 deletions src/komamripy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
Julia::

using KomaMRI
using CUDA # Load GPU backend
Comment thread
anvika-singhal marked this conversation as resolved.
sys = Scanner()
obj = brain_phantom2D()
seq = PulseDesigner.EPI_example()
Expand All @@ -18,6 +19,7 @@
import komamripy as km
import numpy as np

km.load_cuda() # Load GPU backend
sys = km.Scanner()
obj = km.brain_phantom2D()
seq = km.PulseDesigner.EPI_example()
Expand All @@ -26,10 +28,21 @@

Simulation results are returned as Julia objects; use ``numpy.asarray`` to
convert array-like results (such as a ``"mat"`` signal) into NumPy arrays.

GPU backends can be loaded with:
- km.load_cuda() # NVIDIA GPUs
- km.load_metal() # Apple Silicon
- km.load_amdgpu() # AMD GPUs
- km.load_oneapi() # Intel GPUs (experimental)

Once a backend is loaded, gpu=true is used by default in simulations.
"""

from ._backends import load_amdgpu, load_cuda, load_metal, load_oneapi
from ._session import get_julia

__all__ = ["load_cuda", "load_metal", "load_amdgpu", "load_oneapi"]

_JULIA_MODULE_ALIASES = {
"base": "KomaMRIBase",
"core": "KomaMRICore",
Expand Down
36 changes: 36 additions & 0 deletions src/komamripy/_backends.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""GPU backend selection for komamripy.

Maps Python functions to Julia backend loading:
- load_cuda() → using CUDA
- load_metal() → using Metal
- load_amdgpu() → using AMDGPU
- load_oneapi() → using oneAPI

Once a backend is loaded, KomaMRI.jl automatically uses it with gpu=true by default.
"""

from ._session import get_julia


def load_cuda() -> None:
"""Load CUDA backend: using CUDA"""
jl = get_julia()
jl.seval("using CUDA")


def load_metal() -> None:
"""Load Metal backend: using Metal"""
jl = get_julia()
jl.seval("using Metal")


def load_amdgpu() -> None:
"""Load AMDGPU backend: using AMDGPU"""
jl = get_julia()
jl.seval("using AMDGPU")


def load_oneapi() -> None:
"""Load oneAPI backend: using oneAPI (experimental)"""
jl = get_julia()
jl.seval("using oneAPI")