From dc9e2780fee243ccdebb64b9f1649d26395f683b Mon Sep 17 00:00:00 2001
From: Michiel Cottaar <michiel.cottaar@ndcn.ox.ac.uk>
Date: Mon, 12 Feb 2024 12:50:31 +0000
Subject: [PATCH] Define instant gradient component

---
 .../gradient_waveforms/instant_gradients.jl   | 83 -------------------
 src/components/instant_gradients.jl           | 82 ++++++++++++++++++
 2 files changed, 82 insertions(+), 83 deletions(-)
 delete mode 100644 src/components/gradient_waveforms/instant_gradients.jl
 create mode 100644 src/components/instant_gradients.jl

diff --git a/src/components/gradient_waveforms/instant_gradients.jl b/src/components/gradient_waveforms/instant_gradients.jl
deleted file mode 100644
index 08026bd..0000000
--- a/src/components/gradient_waveforms/instant_gradients.jl
+++ /dev/null
@@ -1,83 +0,0 @@
-module InstantGradients
-import StaticArrays: SVector
-import JuMP: @constraint, @variable, AbstractJuMPScalar
-import ...Variables: qvec, bmat_gradient, duration, variables, get_free_variable, VariableType
-import ...BuildingBlocks: GradientBlock, fixed
-import ...BuildSequences: global_model
-
-"""
-    InstantGradientBlock(; orientation=nothing, qval=nothing, qvec=nothing, rotate=nothing, scale=nothing)
-
-Defines an instantaneous gradient.
-
-## Parameters
-- `orientation` sets the gradient orienation (ignored if `qvec` is set). Can be set to a vector for a fixed orientation. Otherwise the orientation will be aligned with the `rotate` (if set) or fully free (if `rotate` is nothing). Set to :flip to point in the inverse of the user-provided `rotate`.
-- `rotate`: with which user-set parameter will this gradient be rotated (e.g., :bvec). Default is no rotation.
-- `scale`: with which user-set parameter will this gradient be scaled (e.g., :bval). Default is no scaling.
-
-## Variables
-- [`qvec`](@ref): Spatial scale and direction on which spins will be dephased due to this pulsed gradient in rad/um.
-- [`qval`](@ref): Spatial scale on which spins will be dephased due to this pulsed gradient in rad/um.
-"""
-struct InstantGradientBlock <: GradientBlock
-    qvec :: SVector{3, VariableType}
-    rotate :: Union{Nothing, Symbol}
-    scale :: Union{Nothing, Symbol}
-end
-
-function interpret_orientation(orientation, qval, qvec, will_rotate)
-    if !isnothing(qvec)
-        return (false, qvec)
-    end
-    if orientation == :flip
-        @assert will_rotate "setting `orientation=:flip` only makes sense if the `rotate` is set as well."
-        return (true, SVector{3}(
-            -get_free_variable(qval),
-            0.,
-            0.,
-        ))
-    end
-    if isnothing(orientation)
-        if !will_rotate
-            return (false, SVector{3}(
-                get_free_variable(nothing),
-                get_free_variable(nothing),
-                get_free_variable(nothing),
-            ))
-        else
-            return (true, SVector{3}(
-                get_free_variable(qval),
-                0.,
-                0.,
-            ))
-        end
-    else
-        qval = get_free_variable(qval)
-        return (true, (orientation ./ norm(orientation)) * qval)
-    end
-end
-
-function InstantGradientBlock(; orientation=nothing, qval=nothing, qvec=nothing, rotate=nothing, scale=nothing)
-    model = global_model()
-    (used_qval, qvec) = interpret_orientation(orientation, qval, qvec, !isnothing(rotate))
-    res = InstantGradientBlock(
-        qvec,
-        rotate,
-        scale
-    )
-    if !used_qval && !isnothing(qval)
-        @constraint model qval_sqr(res) == qval^2
-    end
-    if qval isa AbstractJuMPScalar
-        @constraint model qval >= 0
-    end
-    return res
-end
-
-
-qvec(instant::InstantGradientBlock) = instant.qvec
-bmat_gradient(::InstantGradientBlock, qstart=nothing) = zeros(3, 3)
-duration(instant::InstantGradientBlock) = 0.
-
-
-end
\ No newline at end of file
diff --git a/src/components/instant_gradients.jl b/src/components/instant_gradients.jl
new file mode 100644
index 0000000..a0a726d
--- /dev/null
+++ b/src/components/instant_gradients.jl
@@ -0,0 +1,82 @@
+module InstantGradients
+import StaticArrays: SVector, SMatrix
+import ...Variables: VariableType, duration, qvec, bmat_gradient, get_free_variable, set_simple_constraints, qval, effective_time
+import ..AbstractTypes: EventComponent, GradientWaveform
+
+"""
+Parent type for [`InstantGradient1D`](@ref) and [`InstantGradient3D`].
+
+This is an [`EventComponent`](@ref) rather than part of the [`GradientWaveform`](@ref),
+because it is more easily thought of as interrupting a finite gradient waveform rather than being part of it.
+"""
+abstract type InstantGradient <: EventComponent end
+
+"""
+    InstantGradient1D(; orientation=[1, 0, 0], group=nothing, variables...)
+
+Defines an instantaneous gradient with given fixed `orientation` (default: x-direction).
+
+To have a variable `orientation`, see [`InstantGradient3D`](@ref).
+
+## Parameters
+- `orientation` sets the gradient orienation as a length-3 vector (default: x-direction).
+- `group`: name of the group to which this gradient belongs (used for scaling and rotating).
+
+## Variables
+- [`qval`](@ref): Spatial frequency on which spins will be dephased due to this pulsed gradient in rad/um.
+- [`spoiler_scale`](@ref): Length-scale on which spins will be dephased by exactly 2Ï€ in mm.
+"""
+struct InstantGradient1D <: GradientBlock
+    qval :: VariableType
+    orientation :: SVector{3, Number}
+    group :: Union{Nothing, Symbol}
+end
+
+function InstantGradient1D(; orientation=[1, 0, 0], group=nothing, qval=nothing, variables...)
+    res = InstantGradient1D(qval, orientation, group)
+    set_simple_constraints(res, variables)
+    return res
+end
+
+qval(ig::InstantGradient1D) = ig.qval
+qvec(ig::InstantGradient1D) = qval(ig) .* ig.orientation
+
+
+"""
+    InstantGradient3D(; group=nothing, variables...)
+
+Defines an instantaneous gradient without a fixed orientation.
+
+To have a fixed `orientation`, see [`InstantGradient1D`](@ref).
+
+## Parameters
+- `group`: name of the group to which this gradient belongs (used for scaling and rotating).
+
+## Variables
+- [`qvec`](@ref): Vector with spatial frequency on which spins will be dephased due to this pulsed gradient in rad/um.
+- [`qval`](@ref): Norm of spatial frequency on which spins will be dephased due to this pulsed gradient in rad/um.
+- [`spoiler_scale`](@ref): Vector with length-scale on which spins will be dephased by exactly 2Ï€ in mm.
+"""
+struct InstantGradient3D <: GradientBlock
+    qvec :: SVector{3, VariableType}
+    group :: Union{Nothing, Symbol}
+end
+
+function InstantGradient3D(; qvec=[nothing, nothing, nothing], group=nothing, variables...)
+    if isnothing(qvec)
+        qvec = [nothing, nothing, nothing]
+    end
+    res = InstantGradient3D(get_free_variable.(qvec), group)
+    set_simple_constraints(res, variables)
+    return res
+end
+
+qvec(ig::InstantGradient3D) = ig.qvec
+
+
+duration(::InstantGradient) = 0.
+effective_time(::InstantGradient) = 0.
+bmat_gradient(::InstantGradient, qstart=nothing) = zero(SMatrix{3, 3, Float64, 3})
+
+
+end
\ No newline at end of file
-- 
GitLab