diff --git a/KomaMRICore/Project.toml b/KomaMRICore/Project.toml index f64f605d6..f3be72d0a 100644 --- a/KomaMRICore/Project.toml +++ b/KomaMRICore/Project.toml @@ -5,19 +5,35 @@ version = "0.8.3" [deps] Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" -CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" Functors = "d9f16b24-f501-4c13-a1f2-28368ffc5196" +KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" KomaMRIBase = "d0bc0b20-b151-4d03-b2a4-6ca51751cb9c" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" ThreadsX = "ac1d9e8a-700a-412c-b207-f0111f4b6c0d" +[weakdeps] +AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" +CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" +Metal = "dde4c033-4e86-420c-a63e-0dd931031962" +oneAPI = "8f75cd03-7ff8-4ecb-9b8f-daf728133b1b" + +[extensions] +KomaAMDGPUExt = "AMDGPU" +KomaCUDAExt = "CUDA" +KomaMetalExt = "Metal" +KomaoneAPIExt = "oneAPI" + [compat] Adapt = "3, 4" +AMDGPU = "0.9" CUDA = "3, 4, 5" Functors = "0.4" +KernelAbstractions = "0.9" KomaMRIBase = "0.8" +Metal = "1" +oneAPI = "1" Pkg = "1.4" ProgressMeter = "1" Reexport = "1" diff --git a/KomaMRICore/ext/KomaAMDGPUExt.jl b/KomaMRICore/ext/KomaAMDGPUExt.jl new file mode 100644 index 000000000..7a507cf90 --- /dev/null +++ b/KomaMRICore/ext/KomaAMDGPUExt.jl @@ -0,0 +1,24 @@ +module KomaAMDGPUExt + +using AMDGPU +import KomaMRICore + +KomaMRICore.name(::ROCBackend) = "AMDGPU" +KomaMRICore.isfunctional(::ROCBackend) = AMDGPU.functional() +KomaMRICore.set_device!(::ROCBackend, dev_idx::Integer) = AMDGPU.device_id!(dev_idx) +KomaMRICore.set_device!(::ROCBackend, dev::AMDGPU.HIPDevice) = AMDGPU.device!(dev) +KomaMRICore.device_name(::ROCBackend) = AMDGPU.device().name + +function KomaMRICore._print_devices(::ROCBackend) + devices = [ + Symbol("($(i-1)$(i == 1 ? "*" : " "))") => d.name for + (i, d) in enumerate(AMDGPU.devices()) + ] + @info "$(length(AMDGPU.devices())) AMD capable device(s)." devices... +end + +function __init__() + push!(KomaMRICore.LOADED_BACKENDS[], ROCBackend()) +end + +end \ No newline at end of file diff --git a/KomaMRICore/ext/KomaCUDAExt.jl b/KomaMRICore/ext/KomaCUDAExt.jl new file mode 100644 index 000000000..32d275d07 --- /dev/null +++ b/KomaMRICore/ext/KomaCUDAExt.jl @@ -0,0 +1,23 @@ +module KomaCUDAExt + +using CUDA +import KomaMRICore + +KomaMRICore.name(::CUDABackend) = "CUDA" +KomaMRICore.isfunctional(::CUDABackend) = CUDA.functional() +KomaMRICore.set_device!(::CUDABackend, val) = CUDA.device!(val) +KomaMRICore.device_name(::CUDABackend) = CUDA.name(CUDA.device()) + +function KomaMRICore._print_devices(::CUDABackend) + devices = [ + Symbol("($(i-1)$(i == 1 ? "*" : " "))") => CUDA.name(d) for + (i, d) in enumerate(CUDA.devices()) + ] + @info "$(length(CUDA.devices())) CUDA capable device(s)." devices... +end + +function __init__() + push!(KomaMRICore.LOADED_BACKENDS[], CUDABackend()) +end + +end \ No newline at end of file diff --git a/KomaMRICore/ext/KomaMetalExt.jl b/KomaMRICore/ext/KomaMetalExt.jl new file mode 100644 index 000000000..8aa59f2c2 --- /dev/null +++ b/KomaMRICore/ext/KomaMetalExt.jl @@ -0,0 +1,28 @@ +module KomaMetalExt + +using Metal +import KomaMRICore + +KomaMRICore.name(::MetalBackend) = "Metal" +KomaMRICore.isfunctional(::MetalBackend) = Metal.functional() +KomaMRICore.set_device!(::MetalBackend, device_index::Integer) = device_index == 1 || @warn "Metal does not support multiple gpu devices. Ignoring the device setting." +KomaMRICore.set_device!(::MetalBackend, dev::Metal.MTLDevice) = Metal.device!(dev) +KomaMRICore.device_name(::MetalBackend) = String(Metal.current_device().name) + +function KomaMRICore._print_devices(::MetalBackend) + @info "Metal device type: $(KomaMRICore.device_name(MetalBackend()))" +end + +#Temporary workaround for https://github.com/JuliaGPU/Metal.jl/issues/348 +#Once run_spin_excitation! and run_spin_precession! are kernel-based, this code +#can be removed +Base.cumsum(x::MtlVector) = convert(MtlVector, cumsum(KomaMRICore.cpu(x))) +Base.cumsum(x::MtlArray{T}; dims) where T = convert(MtlArray{T}, cumsum(KomaMRICore.cpu(x), dims=dims)) +Base.findall(x::MtlVector{Bool}) = convert(MtlVector, findall(KomaMRICore.cpu(x))) + +function __init__() + push!(KomaMRICore.LOADED_BACKENDS[], MetalBackend()) + @warn "Due to https://github.com/JuliaGPU/Metal.jl/issues/348, some functions may need to run on the CPU. Performance may be impacted as a result." +end + +end \ No newline at end of file diff --git a/KomaMRICore/ext/KomaoneAPIExt.jl b/KomaMRICore/ext/KomaoneAPIExt.jl new file mode 100644 index 000000000..5f97619e0 --- /dev/null +++ b/KomaMRICore/ext/KomaoneAPIExt.jl @@ -0,0 +1,23 @@ +module KomaoneAPIExt + +using oneAPI +import KomaMRICore + +KomaMRICore.name(::oneAPIBackend) = "oneAPI" +KomaMRICore.isfunctional(::oneAPIBackend) = oneAPI.functional() +KomaMRICore.set_device!(::oneAPIBackend, val) = oneAPI.device!(val) +KomaMRICore.device_name(::oneAPIBackend) = oneAPI.properties(oneAPI.device()).name + +function KomaMRICore._print_devices(::oneAPIBackend) + devices = [ + Symbol("($(i-1)$(i == 1 ? "*" : " "))") => oneAPI.properties(d).name for + (i, d) in enumerate(oneAPI.devices()) + ] + @info "$(length(oneAPI.devices())) oneAPI capable device(s)." devices... +end + +function __init__() + push!(KomaMRICore.LOADED_BACKENDS[], oneAPIBackend()) +end + +end \ No newline at end of file diff --git a/KomaMRICore/src/KomaMRICore.jl b/KomaMRICore/src/KomaMRICore.jl index b61f4b125..1a8450642 100644 --- a/KomaMRICore/src/KomaMRICore.jl +++ b/KomaMRICore/src/KomaMRICore.jl @@ -2,12 +2,11 @@ module KomaMRICore # General import Base.*, Base.abs +import KernelAbstractions as KA using Reexport using ThreadsX # Printing using ProgressMeter -# Simulation -using CUDA # KomaMRIBase @reexport using KomaMRIBase @@ -18,6 +17,7 @@ include("rawdata/ISMRMRD.jl") include("datatypes/Spinor.jl") include("other/DiffusionModel.jl") # Simulator +include("simulation/Functors.jl") include("simulation/GPUFunctions.jl") include("simulation/SimulatorCore.jl") diff --git a/KomaMRICore/src/simulation/Functors.jl b/KomaMRICore/src/simulation/Functors.jl new file mode 100644 index 000000000..83066aebe --- /dev/null +++ b/KomaMRICore/src/simulation/Functors.jl @@ -0,0 +1,94 @@ +import Adapt: adapt, adapt_storage +import Functors: @functor, functor, fmap, isleaf + +#Aux. funcitons to check if the variable we want to convert to CuArray is numeric +_isleaf(x) = isleaf(x) +_isleaf(::AbstractArray{<:Number}) = true +_isleaf(::AbstractArray{T}) where T = isbitstype(T) +_isleaf(::AbstractRange) = true + +""" + gpu(x) + +Tries to move `x` to the GPU backend specified in the 'backend' parameter. + +This works for functions, and any struct marked with `@functor`. + +Use [`cpu`](@ref) to copy back to ordinary `Array`s. + +See also [`f32`](@ref) and [`f64`](@ref) to change element type only. + +# Examples +```julia +x = gpu(x, CUDABackend()) +``` +""" +function gpu(x, backend::KA.GPU) + return fmap(x -> adapt(backend, x), x; exclude=_isleaf) +end + +# To CPU +""" + cpu(x) + +Tries to move object to CPU. This works for functions, and any struct marked with `@functor`. + +See also [`gpu`](@ref). + +# Examples +```julia +x = x |> cpu +``` +""" +cpu(x) = fmap(x -> adapt(KA.CPU(), x), x, exclude=_isleaf) + +#Precision +paramtype(T::Type{<:Real}, m) = fmap(x -> adapt(T, x), m) +adapt_storage(T::Type{<:Real}, xs::Real) = convert(T, xs) +adapt_storage(T::Type{<:Real}, xs::AbstractArray{<:Real}) = convert.(T, xs) +adapt_storage(T::Type{<:Real}, xs::AbstractArray{<:Complex}) = convert.(Complex{T}, xs) +adapt_storage(T::Type{<:Real}, xs::AbstractArray{<:Bool}) = xs +adapt_storage(T::Type{<:Real}, xs::SimpleMotion) = SimpleMotion(paramtype(T, xs.types)) +adapt_storage(T::Type{<:Real}, xs::NoMotion) = NoMotion{T}() +function adapt_storage(T::Type{<:Real}, xs::ArbitraryMotion) + fields = [] + for field in fieldnames(ArbitraryMotion) + push!(fields, paramtype(T, getfield(xs, field))) + end + return ArbitraryMotion(fields...) +end + +""" + f32(m) + +Converts the `eltype` of model's parameters to `Float32` +Recurses into structs marked with `@functor`. + +See also [`f64`](@ref). +""" +f32(m) = paramtype(Float32, m) + +""" + f64(m) + +Converts the `eltype` of model's parameters to `Float64` (which is Koma's default).. +Recurses into structs marked with `@functor`. + +See also [`f32`](@ref). +""" +f64(m) = paramtype(Float64, m) + +#The functor macro makes it easier to call a function in all the parameters +@functor Phantom + +@functor Translation +@functor Rotation +@functor HeartBeat +@functor PeriodicTranslation +@functor PeriodicRotation +@functor PeriodicHeartBeat + +@functor Spinor +@functor DiscreteSequence + +export gpu, cpu, f32, f64 \ No newline at end of file diff --git a/KomaMRICore/src/simulation/GPUFunctions.jl b/KomaMRICore/src/simulation/GPUFunctions.jl index dff9725b4..6d6200678 100644 --- a/KomaMRICore/src/simulation/GPUFunctions.jl +++ b/KomaMRICore/src/simulation/GPUFunctions.jl @@ -1,160 +1,95 @@ -import Functors: @functor, functor, fmap, isleaf -import Adapt: adapt, adapt_storage -#Checks if CUDA is available for the session -const use_cuda = Ref{Union{Nothing,Bool}}(nothing) +const LOADED_BACKENDS = Ref{Vector{KA.GPU}}([]) +const BACKEND = Ref{Union{KA.Backend,Nothing}}(nothing) + +device_name(backend) = @error "device_name called with invalid backend type $(typeof(backend))" +isfunctional(::KA.CPU) = true +isfunctional(x) = false +_print_devices(backend) = @error "_print_devices called with invalid backend type $(typeof(backend))" +_print_devices(::KA.CPU) = @info "CPU: $(length(Sys.cpu_info())) x $(Sys.cpu_info()[1].model)" +name(::KA.CPU) = "CPU" +set_device!(backend, val) = @error "set_device! called with invalid parameter types: '$(typeof(backend))', '$(typeof(val))'" """ - print_gpus() + get_backend(use_gpu) + +Gets the simulation backend to use. If use_gpu=false or there are no available GPU backends, +returns CPU(), else, returns the GPU backend (currently either CUDABackend(), MetalBackend(), +ROCBackend(), or oneAPIBackend()). + +The GPU package for the corresponding backend (CUDA.jl, Metal.jl, AMDGPU.jl, or oneAPI.jl) must be +loaded and functional, otherwise KomaMRI will default to using the CPU. -Simple function to print the CUDA devices available in the host. +# Arguments +- 'use_gpu': ('::Bool') If true, attempt to use GPU and check for available backends + +# Returns +- 'backend': (::KernelAbstractions.backend) The backend to use """ -function print_gpus() - check_use_cuda() - if use_cuda[] - cuda_devices = [ - Symbol("($(i-1)$(i == 1 ? "*" : " "))") => name(d) for - (i, d) in enumerate(devices()) - ] - @info "$(length(devices())) CUDA capable device(s)." cuda_devices... - else - @info "0 CUDA capable devices(s)." +function get_backend(use_gpu::Bool) + if !isnothing(BACKEND[]) + # The backend can be set and still need to change based on the value of + # use_gpu, e.g. if switching between CPU and GPU while running tests + ((BACKEND[] isa KA.GPU) == use_gpu) && return BACKEND[] end - return nothing -end -""" -Checks if the PC has a functional CUDA installation. Inspired by Flux's `check_use_cuda` funciton. -""" -function check_use_cuda() - if use_cuda[] === nothing - use_cuda[] = CUDA.functional() - if !(use_cuda[]) - @info """The GPU function is being called but the GPU is not accessible. - Defaulting back to the CPU. (No action is required if you want to run on the CPU).""" maxlog = - 1 - end + if !use_gpu + BACKEND[] = KA.CPU() + return BACKEND[] end -end -#Aux. funcitons to check if the variable we want to convert to CuArray is numeric -_isbitsarray(::AbstractArray{<:Real}) = true -_isbitsarray(::AbstractArray{T}) where {T} = isbitstype(T) -_isbitsarray(x) = false -_isleaf(x) = _isbitsarray(x) || isleaf(x) + if isempty(LOADED_BACKENDS[]) + @info """ + The GPU functionality is being called but no GPU backend is loaded + to access it. Add 'using CUDA / Metal / AMDGPU / oneAPI' to your + code. Defaulting back to the CPU. (No action is required if you want + to run on the CPU). + """ maxlog=1 + BACKEND[] = KA.CPU() + return BACKEND[] + end -# GPU adaptor -struct KomaCUDAAdaptor end -adapt_storage(to::KomaCUDAAdaptor, x) = CUDA.cu(x) -adapt_storage(to::KomaCUDAAdaptor, x::NoMotion) = NoMotion{Float32}() -adapt_storage(to::KomaCUDAAdaptor, x::SimpleMotion) = f32(x) -function adapt_storage(to::KomaCUDAAdaptor, x::ArbitraryMotion) - fields = [] - for field in fieldnames(ArbitraryMotion) - if field in (:ux, :uy, :uz) - push!(fields, adapt(KomaCUDAAdaptor(), getfield(x, field))) + functional_gpu_backends = [] + for backend in LOADED_BACKENDS[] + if isfunctional(backend) + push!(functional_gpu_backends, backend) else - push!(fields, f32(getfield(x, field))) + @warn "Loaded backend $(name(backend)) is not functional" maxlog=1 end end - return ArbitraryMotion(fields...) -end -function adapt_storage( - to::KomaCUDAAdaptor, x::Vector{LinearInterpolator{T,V}} -) where {T<:Real,V<:AbstractVector{T}} - return CUDA.cu.(x) -end - -""" - gpu(x) - -Tries to move `x` to the current GPU device. Inspired by Flux's `gpu` function. - -This works for functions, and any struct marked with `@functor`. - -Use [`cpu`](@ref) to copy back to ordinary `Array`s. - -See also [`f32`](@ref) and [`f64`](@ref) to change element type only. - -# Examples -```julia -x = x |> gpu -``` -""" -function gpu(x) - check_use_cuda() - return use_cuda[] ? fmap(x -> adapt(KomaCUDAAdaptor(), x), x; exclude=_isleaf) : x -end - -#CPU adaptor -struct KomaCPUAdaptor end -adapt_storage(to::KomaCPUAdaptor, x::AbstractArray) = adapt(Array, x) -adapt_storage(to::KomaCPUAdaptor, x::AbstractRange) = x - -# To CPU -""" - cpu(x) - -Tries to move object to CPU. Inspired by Flux's `cpu` function. - -This works for functions, and any struct marked with `@functor`. - -See also [`gpu`](@ref). - -# Examples -```julia -x = x |> cpu -``` -""" -cpu(x) = fmap(x -> adapt(KomaCPUAdaptor(), x), x) - -#Precision -paramtype(T::Type{<:Real}, m) = fmap(x -> adapt(T, x), m) -adapt_storage(T::Type{<:Real}, xs::Real) = convert(T, xs) -adapt_storage(T::Type{<:Real}, xs::AbstractArray{<:Real}) = convert.(T, xs) -adapt_storage(T::Type{<:Real}, xs::AbstractArray{<:Complex}) = convert.(Complex{T}, xs) -adapt_storage(T::Type{<:Real}, xs::AbstractArray{<:Bool}) = xs -adapt_storage(T::Type{<:Real}, xs::SimpleMotion) = SimpleMotion(paramtype(T, xs.types)) -adapt_storage(T::Type{<:Real}, xs::NoMotion) = NoMotion{T}() -function adapt_storage(T::Type{<:Real}, xs::ArbitraryMotion) - fields = [] - for field in fieldnames(ArbitraryMotion) - push!(fields, paramtype(T, getfield(xs, field))) + + if length(functional_gpu_backends) == 1 + BACKEND[] = functional_gpu_backends[1] + @info """Using backend: '$(name(BACKEND[]))'""" maxlog = 1 + return BACKEND[] + elseif length(functional_gpu_backends) == 0 + @info """Defaulting back to the CPU. (No action is required if you want to run on the CPU). """ maxlog = 1 + BACKEND[] = KA.CPU() + return BACKEND[] + else + # Will probably never get here + @info """ + Multiple functional backends have been loaded and KomaMRI does not + know which one to use. Ensure that your code contains only one 'using' + statement for the GPU backend you wish to use. Defaulting back to the + CPU. (No action is required if you want to run on the CPU). + """ maxlog = 1 + BACKEND[] = KA.CPU() + return BACKEND[] end - return ArbitraryMotion(fields...) end """ - f32(m) - -Converts the `eltype` of model's parameters to `Float32` -Recurses into structs marked with `@functor`. - -See also [`f64`](@ref). -""" -f32(m) = paramtype(Float32, m) - -""" - f64(m) + print_devices() -Converts the `eltype` of model's parameters to `Float64` (which is Koma's default).. -Recurses into structs marked with `@functor`. +Simple function to print available devices. Calls internal get_backend() function to +get the appropriate GPU / CPU backend and prints device information. -See also [`f32`](@ref). +# Arguments +- 'use_gpu': ('::Bool') If true, check for loaded / functional GPU backends and print appropriate warnings if no GPU backends have been loaded """ -f64(m) = paramtype(Float64, m) - -#The functor macro makes it easier to call a function in all the parameters -@functor Phantom - -@functor Translation -@functor Rotation -@functor HeartBeat -@functor PeriodicTranslation -@functor PeriodicRotation -@functor PeriodicHeartBeat - -@functor Spinor -@functor DiscreteSequence +function print_devices(use_gpu = true) + backend = get_backend(use_gpu) + _print_devices(backend) +end -#Exporting functions -export gpu, cpu, f32, f64 +export print_devices \ No newline at end of file diff --git a/KomaMRICore/src/simulation/SimulatorCore.jl b/KomaMRICore/src/simulation/SimulatorCore.jl index 31c6b0029..4315e4c37 100644 --- a/KomaMRICore/src/simulation/SimulatorCore.jl +++ b/KomaMRICore/src/simulation/SimulatorCore.jl @@ -36,7 +36,9 @@ allowing the user to define some of them. parallel threads, speeding up the execution time * "gpu": is a boolean that determines whether to use GPU or CPU hardware resources, as long as they are available on the host computer - * "gpu_device": sets the index ID of the available GPU in the host computer + * "gpu_device": default value is 'nothing'. If set to integer or device instance, calls the + corresponding function to set the device of the available GPU in the host computer + (e.g. CUDA.device!) # Returns - `sim_params`: (`::Dict{String,Any}`) dictionary with simulation parameters @@ -44,11 +46,7 @@ allowing the user to define some of them. function default_sim_params(sim_params=Dict{String,Any}()) sampling_params = KomaMRIBase.default_sampling_params() get!(sim_params, "gpu", true) - if sim_params["gpu"] - check_use_cuda() - sim_params["gpu"] &= use_cuda[] - end - get!(sim_params, "gpu_device", 0) + get!(sim_params, "gpu_device", nothing) get!(sim_params, "Nthreads", sim_params["gpu"] ? 1 : Threads.nthreads()) get!(sim_params, "Nblocks", 20) get!(sim_params, "Δt", sampling_params["Δt"]) @@ -334,29 +332,39 @@ function simulate( # Signal init Ndims = sim_output_dim(obj, seq, sys, sim_params["sim_method"]) sig = zeros(ComplexF64, Ndims..., sim_params["Nthreads"]) - # Objects to GPU - if sim_params["gpu"] #Default - device!(sim_params["gpu_device"]) - gpu_name = name.(devices())[sim_params["gpu_device"] + 1] - obj = obj |> gpu #Phantom - seqd = seqd |> gpu #DiscreteSequence - Xt = Xt |> gpu #SpinStateRepresentation - sig = sig |> gpu #Signal + backend = get_backend(sim_params["gpu"]) + sim_params["gpu"] &= backend isa KA.GPU + if !KA.supports_float64(backend) && sim_params["precision"] == "f64" + sim_params[precision] = "f32" + @info """ Backend: '$(name(backend))' does not support 64-bit precision + floating point operations. Automatically converting to type Float32. + (set sim_param["precision"] = "f32" to avoid seeing this message). + """ maxlog=1 end - if sim_params["precision"] == "f32" #Default - obj = obj |> f32 #Phantom - seqd = seqd |> f32 #DiscreteSequence - Xt = Xt |> f32 #SpinStateRepresentation - sig = sig |> f32 #Signal - elseif sim_params["precision"] == "f64" + if sim_params["precision"] == "f64" && KA.supports_float64(backend) obj = obj |> f64 #Phantom seqd = seqd |> f64 #DiscreteSequence Xt = Xt |> f64 #SpinStateRepresentation sig = sig |> f64 #Signal + else + #Precision #Default + obj = obj |> f32 #Phantom + seqd = seqd |> f32 #DiscreteSequence + Xt = Xt |> f32 #SpinStateRepresentation + sig = sig |> f32 #Signal + end + # Objects to GPU + if backend isa KA.GPU + isnothing(sim_params["gpu_device"]) || set_device!(backend, sim_params["gpu_device"]) + gpu_name = device_name(backend) + obj = gpu(obj, backend) #Phantom + seqd = gpu(seqd, backend) #DiscreteSequence + Xt = gpu(Xt, backend) #SpinStateRepresentation + sig = gpu(sig, backend) #Signal end # Simulation - @info "Running simulation in the $(sim_params["gpu"] ? "GPU ($gpu_name)" : "CPU with $(sim_params["Nthreads"]) thread(s)")" koma_version = + @info "Running simulation in the $(backend isa KA.GPU ? "GPU ($gpu_name)" : "CPU with $(sim_params["Nthreads"]) thread(s)")" koma_version = __VERSION__ sim_method = sim_params["sim_method"] spins = length(obj) time_points = length( seqd.t ) adc_points = Ndims[1] @@ -376,10 +384,6 @@ function simulate( sig = sum(sig; dims=length(Ndims) + 1) |> cpu #Sum over threads sig .*= get_adc_phase_compensation(seq) Xt = Xt |> cpu - if sim_params["gpu"] - GC.gc(true) - CUDA.reclaim() - end # Output if sim_params["return_type"] == "state" out = Xt @@ -390,6 +394,7 @@ function simulate( sim_params_raw = copy(sim_params) sim_params_raw["sim_method"] = string(sim_params["sim_method"]) sim_params_raw["gpu"] = sim_params["gpu"] + sim_params_raw["gpu_device"] = backend isa KA.GPU ? gpu_name : "nothing" sim_params_raw["Nthreads"] = sim_params["Nthreads"] sim_params_raw["t_sim_parts"] = t_sim_parts sim_params_raw["type_sim_parts"] = excitation_bool diff --git a/KomaMRICore/test/Project.toml b/KomaMRICore/test/Project.toml index 283ab5dd9..a87a0b7f3 100644 --- a/KomaMRICore/test/Project.toml +++ b/KomaMRICore/test/Project.toml @@ -1,7 +1,11 @@ [deps] +AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" +CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" KomaMRIBase = "d0bc0b20-b151-4d03-b2a4-6ca51751cb9c" +Metal = "dde4c033-4e86-420c-a63e-0dd931031962" +oneAPI = "8f75cd03-7ff8-4ecb-9b8f-daf728133b1b" Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" TestItemRunner = "f8b46487-2199-4994-9208-9a1283c18c0a" -TestItems = "1c621080-faea-4a02-84b6-bbd5e436b8fe" +TestItems = "1c621080-faea-4a02-84b6-bbd5e436b8fe" \ No newline at end of file diff --git a/KomaMRICore/test/runtests.jl b/KomaMRICore/test/runtests.jl index 9306533e0..d6b553394 100644 --- a/KomaMRICore/test/runtests.jl +++ b/KomaMRICore/test/runtests.jl @@ -209,6 +209,7 @@ end @testitem "Bloch_GPU" tags=[:important, :skipci, :core, :gpu] begin + using AMDGPU, CUDA, Metal, oneAPI using Suppressor include(joinpath(@__DIR__, "test_files", "utils.jl")) @@ -325,6 +326,7 @@ end end @testitem "Bloch_GPU_RF_accuracy" tags=[:important, :core, :skipci, :gpu] begin + using AMDGPU, CUDA, Metal, oneAPI using Suppressor Tadc = 1e-3 @@ -501,6 +503,7 @@ end end @testitem "Bloch GPU SimpleMotion" tags=[:important, :core, :skipci, :gpu] begin + using AMDGPU, CUDA, Metal, oneAPI using Suppressor include(joinpath(@__DIR__, "test_files", "utils.jl")) @@ -522,6 +525,7 @@ end end @testitem "Bloch GPU ArbitraryMotion" tags=[:important, :core, :skipci, :gpu] begin + using AMDGPU, CUDA, Metal, oneAPI using Suppressor include(joinpath(@__DIR__, "test_files", "utils.jl")) diff --git a/docs/src/reference/3-koma-core.md b/docs/src/reference/3-koma-core.md index 6faeb4526..5675091c7 100644 --- a/docs/src/reference/3-koma-core.md +++ b/docs/src/reference/3-koma-core.md @@ -12,9 +12,11 @@ simulate_slice_profile default_sim_params ``` -## Adapt converters for GPU +## GPU helper functions ```@docs +get_backend +print_devices gpu cpu f32 @@ -42,4 +44,4 @@ Un Rx Ry Rz -``` \ No newline at end of file +``` diff --git a/src/KomaUI.jl b/src/KomaUI.jl index f4bd93759..3fa163cc0 100644 --- a/src/KomaUI.jl +++ b/src/KomaUI.jl @@ -60,13 +60,15 @@ function KomaUI(; darkmode=true, frame=true, phantom_mode="2D", sim=Dict{String, rec_params = merge(Dict{Symbol,Any}(:reco=>"direct"), rec) mat_folder = tempdir() + # Print gpu information + if !(haskey(sim_params, "gpu") && sim_params["gpu"] == false) + KomaMRICore.print_devices() + end + # Boleans to indicate first time for precompilation is_first_sim = true is_first_rec = true - # Print GPU information - KomaMRICore.print_gpus() - # Handle "View" sidebar buttons handle(w, "index") do _ content!(w, "div#content", index)