From 2419cb629a4bf795c572f86e8b077f517ba49d39 Mon Sep 17 00:00:00 2001 From: Michiel Cottaar <michiel.cottaar@ndcn.ox.ac.uk> Date: Fri, 2 Feb 2024 10:37:46 +0000 Subject: [PATCH] Fix include order --- src/MRIBuilder.jl | 4 +-- src/build_sequences.jl | 10 +----- src/building_blocks.jl | 76 ++++++++++++++++++++++++++++++++++++------ src/scanners.jl | 45 ------------------------- src/variables.jl | 3 +- 5 files changed, 70 insertions(+), 68 deletions(-) diff --git a/src/MRIBuilder.jl b/src/MRIBuilder.jl index 11be175..63399d5 100644 --- a/src/MRIBuilder.jl +++ b/src/MRIBuilder.jl @@ -3,10 +3,10 @@ Builds and optimises NMR/MRI sequences. """ module MRIBuilder -include("variables.jl") -include("building_blocks.jl") include("scanners.jl") include("build_sequences.jl") +include("variables.jl") +include("building_blocks.jl") include("wait.jl") include("gradients/gradients.jl") include("pulses/pulses.jl") diff --git a/src/build_sequences.jl b/src/build_sequences.jl index 41deba6..5600724 100644 --- a/src/build_sequences.jl +++ b/src/build_sequences.jl @@ -2,8 +2,7 @@ module BuildSequences import JuMP: Model, optimizer_with_attributes, optimize! import Ipopt import Juniper -import ..Scanners: Scanner, scanner_constraints! -import ..BuildingBlocks: BuildingBlock, apply_simple_constraint!, match_blocks! +import ..Scanners: Scanner const GLOBAL_MODEL = Ref(Model()) const IGNORE_MODEL = GLOBAL_MODEL[] @@ -75,11 +74,4 @@ function global_scanner() end -scanner_constraints!(bb::BuildingBlock) = scanner_constraints!(bb, global_scanner()) - -apply_simple_constraint!(variable, value) = apply_simple_constraint!(global_model(), variable, value) -match_blocks!(block1::BuildingBlock, block2::BuildingBlock, property_list) = match_blocks!(global_model(), block1, block2, property_list) -scanner_constraints!(building_block::BuildingBlock, scanner::Scanner, func::Function) = scanner_constraints!(building_block, scanner, func) -scanner_constraints!(building_block::BuildingBlock) = scanner_constraints!(building_block, global_scanner()) - end \ No newline at end of file diff --git a/src/building_blocks.jl b/src/building_blocks.jl index 3c278dd..a2cab1d 100644 --- a/src/building_blocks.jl +++ b/src/building_blocks.jl @@ -2,6 +2,8 @@ module BuildingBlocks import JuMP: value, Model, @constraint, @objective, objective_function, AbstractJuMPScalar import Printf: @sprintf import ..Variables: variables, start_time, duration, end_time, gradient_strength, slew_rate, effective_time, VariableType, qval_square +import ..BuildSequences: global_model, global_scanner +import ..Scanners: Scanner """ Parent type for all individual components out of which a sequence can be built. @@ -234,17 +236,23 @@ function set_simple_constraints!(block::BuildingBlock, kwargs) end """ - apply_simple_constraint!([model, ]variable, value) + apply_simple_constraint!(variable, value) -Add a single constraint or objective to the JuMP `model`. -This is an internal function used by [`set_simple_constraints`](@ref). +Add a single constraint or objective to the `variable`. + +`value` can be one of: +- `nothing`: do nothing +- `:min`: minimise the variable +- `:max`: maximise the variable +- `number`: fix variable to this value +- `equation`: fix variable to the result of this equation """ -apply_simple_constraint!(model::Model, variable, ::Nothing) = nothing -apply_simple_constraint!(model::Model, variable, value::Symbol) = apply_simple_constraint!(model, variable, Val(value)) -apply_simple_constraint!(model::Model, variable, ::Val{:min}) = @objective model Min objective_function(model) + variable -apply_simple_constraint!(model::Model, variable, ::Val{:max}) = @objective model Min objective_function(model) - variable -apply_simple_constraint!(model::Model, variable, value::VariableType) = @constraint model variable == value -apply_simple_constraint!(model::Model, variable::AbstractVector, value::AbstractVector) = [apply_simple_constraint!(model, v1, v2) for (v1, v2) in zip(variable, value)] +apply_simple_constraint!(variable, ::Nothing) = nothing +apply_simple_constraint!(variable, value::Symbol) = apply_simple_constraint!(variable, Val(value)) +apply_simple_constraint!(variable, ::Val{:min}) = @objective global_model() Min objective_function(global_model()) + variable +apply_simple_constraint!(variable, ::Val{:max}) = @objective global_model() Min objective_function(global_model()) - variable +apply_simple_constraint!(variable, value::VariableType) = @constraint model variable == value +apply_simple_constraint!(variable::AbstractVector, value::AbstractVector) = [apply_simple_constraint!(v1, v2) for (v1, v2) in zip(variable, value)] """ @@ -253,9 +261,9 @@ apply_simple_constraint!(model::Model, variable::AbstractVector, value::Abstract Matches the listed variables between two [`BuildingBlock`](@ref) objects. By default all shared variables (i.e., those with the same name) are matched. """ -function match_blocks!(model::Model, block1::BuildingBlock, block2::BuildingBlock, property_list) +function match_blocks!(block1::BuildingBlock, block2::BuildingBlock, property_list) for fn in property_list - @constraint model fn(block1) == fn(block2) + @constraint global_model() fn(block1) == fn(block2) end end @@ -264,4 +272,50 @@ function match_blocks!(block1::BuildingBlock, block2::BuildingBlock) match_blocks!(block1, block2, property_list) end +""" + scanner_constraints!(building_block[, scanner]) + +Adds the gradient strength and slew rate constraints from a specific [`Scanner`](@ref) to a [`BuildingBlock`]{@ref}. + +This is applied iteratively to each part of a `Sequence`. +""" +scanner_constraints!(building_block::BuildingBlock) = scanner_constraints!(building_block, global_scanner()) + +function scanner_constraints!(building_block::BuildingBlock, scanner::Scanner) + for func in [gradient_strength, slew_rate] + if isfinite(func(scanner)) + scanner_constraints!(building_block, scanner, func) + end + end +end + +function scanner_constraints!(building_block::BuildingBlock, scanner::Scanner, func::Function) + model = global_model() + if func in variables(building_block) + # apply constraint at this level + res_bb = func(building_block) + if res_bb isa AbstractVector + if isnothing(building_block.rotate) + # no rotation; apply constraint to each dimension independently + for expr in res_bb + @constraint model expr <= func(scanner) + @constraint model expr >= -func(scanner) + end + else + # with rotation: apply constraint to total squared + total_squared = sum(map(n->n^2, res_bb)) + @constraint model total_squared <= func(scanner)^2 + end + else + @constraint model res_bb <= func(scanner) + @constraint model res_bb >= -func(scanner) + end + elseif building_block isa ContainerBlock + # apply constraints at lower level + for (_, child_block) in get_children_blocks(building_block) + scanner_constraints!(child_block, scanner, func) + end + end +end + end \ No newline at end of file diff --git a/src/scanners.jl b/src/scanners.jl index 6c026a8..b35eef4 100644 --- a/src/scanners.jl +++ b/src/scanners.jl @@ -2,10 +2,6 @@ Define general [`Scanner`](@ref) type and methods as well as some concrete scanners. """ module Scanners -import JuMP: Model, @constraint -import ..Variables: gradient_strength, slew_rate -import ..BuildingBlocks: BuildingBlock, get_children_blocks, ContainerBlock -import ..Variables: variables const gyromagnetic_ratio = 42576.38476 # (kHz/T) @@ -88,45 +84,4 @@ predefined_scanners = Dict( :Siemens_Connectom => Siemens_Connectom, ) -""" - scanner_constraints!(building_block[, scanner]) - -Adds any constraints from a specific scanner to a [`BuildingBlock`]{@ref}. -""" -function scanner_constraints!(building_block::BuildingBlock, scanner::Scanner) - for func in [gradient_strength, slew_rate] - if isfinite(func(scanner)) - scanner_constraints!(building_block, scanner, func) - end - end -end - -function scanner_constraints!(model::Model, building_block::BuildingBlock, scanner::Scanner, func::Function) - if func in variables(building_block) - # apply constraint at this level - res_bb = func(building_block) - if res_bb isa AbstractVector - if isnothing(building_block.rotate) - # no rotation; apply constraint to each dimension independently - for expr in res_bb - @constraint model expr <= func(scanner) - @constraint model expr >= -func(scanner) - end - else - # with rotation: apply constraint to total squared - total_squared = sum(map(n->n^2, res_bb)) - @constraint model total_squared <= func(scanner)^2 - end - else - @constraint model res_bb <= func(scanner) - @constraint model res_bb >= -func(scanner) - end - elseif building_block isa ContainerBlock - # apply constraints at lower level - for (_, child_block) in get_children_blocks(building_block) - scanner_constraints!(model, child_block, scanner, func) - end - end -end - end \ No newline at end of file diff --git a/src/variables.jl b/src/variables.jl index d1b7f0d..004b9a8 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -1,6 +1,7 @@ module Variables import JuMP: @variable, Model, @objective, objective_function, value, AbstractJuMPScalar -import .. +import ..Scanners: gradient_strength, slew_rate +import ..BuildSequences: global_model all_variables_symbols = [ :block => [ -- GitLab