diff --git a/src/build_sequences.jl b/src/build_sequences.jl index 8ae15d65c340943515cc6e45c636cf88a6e425df..13421ba542ed5459b716f9681de13fa954fa5bdc 100644 --- a/src/build_sequences.jl +++ b/src/build_sequences.jl @@ -140,6 +140,7 @@ function optimise_with_cost_func!(jump_model::Model, cost_func, n_attempts) for _ in num_variables(jump_model):number_equality_constraints(jump_model) @variable(jump_model) end + consistent_minimum = false for attempt in 1:n_attempts if attempt != 1 new_values = rand(length(all_variables(jump_model))) @@ -160,6 +161,9 @@ function optimise_with_cost_func!(jump_model::Model, cost_func, n_attempts) #println(solution_summary(jump_model)) if termination_status(jump_model) in (LOCALLY_SOLVED, OPTIMAL) nsuccess += 1 + if isapprox(objective_value(jump_model), min_objective, rtol=1e-3) + consistent_minimum = true + end if objective_value(jump_model) < min_objective min_objective = objective_value(jump_model) min_values = copy(get_inner_state(jump_model)) @@ -170,11 +174,11 @@ function optimise_with_cost_func!(jump_model::Model, cost_func, n_attempts) sample_error[termination_status(jump_model)] = solution_summary(jump_model) end end - if nsuccess > 2 + if consistent_minimum break end end - if iszero(nsuccess) + if !consistent_minimum err_string = join(["$(String(Symbol(err))) (x$(sum([e == err for e in errors])))" for err in unique(errors)], ", ", ", and ") for msg in values(sample_error) println(msg) diff --git a/src/components/gradient_waveforms/changing_gradient_blocks.jl b/src/components/gradient_waveforms/changing_gradient_blocks.jl index f670fff3e5dfb65af46f0ed38e9530aafee8e7eb..80b029b7ba12c5c0f07bbc815206e320ba6aef5e 100644 --- a/src/components/gradient_waveforms/changing_gradient_blocks.jl +++ b/src/components/gradient_waveforms/changing_gradient_blocks.jl @@ -1,7 +1,7 @@ module ChangingGradientBlocks import StaticArrays: SVector import Rotations: RotMatrix3 -import LinearAlgebra: I +import LinearAlgebra: I, norm import ....Variables: @defvar, VariableType, variables, get_free_variable, adjust_internal import ...AbstractTypes: GradientWaveform @@ -42,7 +42,14 @@ end slew_rate(cgb::ChangingGradient1D) = cgb.slew_rate .* cgb.orientation slew_rate(cgb::ChangingGradient3D) = cgb.slew_rate grad_end(cgb::ChangingGradient) = variables.grad_start(cgb) .+ variables.slew_rate(cgb) .* variables.duration(cgb) - gradient_strength(cgb::ChangingGradient) = max.(variables.grad_start(cgb), variables.grad_end(cgb)) + gradient_strength_norm(cgb::ChangingGradient1D) = max( + abs(cgb.gradient_strength_start), + abs(cgb.gradient_strength_start + cgb.slew_rate * cgb.duration) + ) + gradient_strength_norm(cgb::ChangingGradient3D) = max( + norm(cgb.grad_start), + norm(cgb.grad_end) + ) qvec(cgb::ChangingGradient) = (variables.grad_start(cgb) .+ variables.grad_end(cgb)) .* (variables.duration(cgb) * π) gradient_strength(cgb::ChangingGradient, time::Number) = variables.slew_rate(cgb) .* time .+ variables.grad_start(cgb) diff --git a/src/containers/building_blocks.jl b/src/containers/building_blocks.jl index bac1a8205048f1b704aff00ec3f28879aa7b7d7d..0e18d00efcda77647e2f8252597951737a9a6a72 100644 --- a/src/containers/building_blocks.jl +++ b/src/containers/building_blocks.jl @@ -265,8 +265,9 @@ end function get_pulse(bb::BaseBuildingBlock, time::Number) for (key, component) in events(bb) - if component isa RFPulseComponent && (start_time(bb, key) <= time <= end_time(bb, key)) - return (component, time - start_time(bb, key)) + start = start_time(bb, key) + if component isa RFPulseComponent && (start <= time <= (start + variables.duration(component))) + return (component, time - start) end end return nothing @@ -284,10 +285,16 @@ end function get_gradient(bb::BaseBuildingBlock, time::Number) - for (key, block) in waveform_sequence(bb) - if (start_time(bb, key) <= time <= end_time(bb, key)) || isapprox(time, end_time(bb, key), atol=1e-6) - return (block, time - start_time(bb, key)) + current_time = 0. + if time < -1e-6 + error("$bb does not define a gradient at negative times.") + end + for (_, block) in waveform_sequence(bb) + duration = block.duration + if (time - current_time) <= duration || isapprox(time - current_time, duration, atol=1e-6) + return (block, time - current_time) end + current_time = current_time + duration end error("$bb with duration $(variables.duration(bb)) does not define a gradient at time $time.") end diff --git a/src/variables.jl b/src/variables.jl index 9f6f0f1b8e716830361ec966172aa4ace88ed174..6973a69131b56baada62aadd02efd14999231c6e 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -163,6 +163,8 @@ Assigns getters to specific variables. """ const getters = Dict{Symbol, Function}() +const SkipGeneric = Ref{Bool}(false) + """ _get_variable(name, tried_names, args...; kwargs...) @@ -172,14 +174,21 @@ The route through `tried_names` has already been attempted. This function returns the route to get to the value and the value itself. """ -function _get_variable(name::Symbol, tried_names::Set{Symbol}, args...; kwargs...) - if hasproperty(variables, name) - func = getproperty(variables, name) - if variable_defined_for(func, args...; kwargs...) - return Any[], func(args...; kwargs...) +function _get_variable(name::Symbol, tried_names::Set{Symbol}, args...; _is_first_call=false, kwargs...) + if !(_is_first_call) + try + func = getproperty(variables, name) + if variable_defined_for(func, args...; kwargs...) + return Any[], func(args...; kwargs...) + end + catch e + if !(e isa UndefVarError) + rethrow() + end end end + # alternative functions if name in keys(alternative_variables) for (new_name, converter, _) in alternative_variables[name] @@ -240,8 +249,11 @@ function add_new_variable!(name::Symbol) @eval variables function $(name) end @eval variables $(Expr(:public, name)) @eval function variables.$(name)(ab::AbstractBlock, args...; kwargs...) + if :_is_first_call in keys(kwargs) + error("Variables function definitions cannot use the keyword `_is_first_call`") + end try - return _get_variable($(QuoteNode(name)), Set{Symbol}(), ab, args...; kwargs...)[2] + return _get_variable($(QuoteNode(name)), Set{Symbol}(), ab, args...; _is_first_call=true, kwargs...)[2] catch e if e isa InvalidRoute string_name = String($(QuoteNode(name))) diff --git a/test/test_sequences.jl b/test/test_sequences.jl index b6f6102a6f6e5e39373743593b9c830bcb7ff3d6..c9e924d5d735dd4447458826d6ce3e3fad33aac4 100644 --- a/test/test_sequences.jl +++ b/test/test_sequences.jl @@ -59,7 +59,6 @@ end @testset "Set diffusion time Δ" begin seq = DiffusionSpinEcho(TE=80., Δ=70., qval=:max) - @show seq @test all(isapprox.(variables.duration.(seq), [0., 0., 10., 30., 0., 30., 10., 0., 0., 0.], atol=1e-4, rtol=1e-4)) @test variables.Δ(seq) ≈ 70. @test variables.TE(seq) ≈ 80. @@ -67,7 +66,6 @@ @test 0.72 < variables.bval(seq) < 0.73 @test variables.readout_times(seq)[1] ≈ variables.TE(seq) @test variables.rise_time(seq[:gradient]) ≈ min_rise_time rtol=1e-4 - @show edge_times(seq) @test all(isapprox.(edge_times(seq), [0., min_rise_time, 10. - min_rise_time, 10., 40, 70, 70 + min_rise_time, 80 - min_rise_time, 80.], atol=1e-4)) end @testset "Set gradient duration" begin