diff --git a/README.md b/README.md index 03008d0..1edfc93 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ An individual element looks like: Ele: "qf" (b1>>2) Quadrupole branch Branch 1: "b1" ix_ele 2 - AlignmentGroup: + BodyShiftGroup: offset [0.0, 0.0, 0.0] m offset_tot [0.0, 0.0, 0.0] m x_rot 0 rad x_rot_tot 0 rad y_rot 0 rad y_rot_tot 0 rad diff --git a/manual/construction.tex b/manual/construction.tex index b6d25a5..f6f0fb7 100644 --- a/manual/construction.tex +++ b/manual/construction.tex @@ -21,4 +21,6 @@ \chapter{Constructing Lattices} * Show how to construct a Bmad replacement line using a function. -* Show how to get the effect of a Bmad List by replacing elements after expansion. \ No newline at end of file +* Show how to get the effect of a Bmad List by replacing elements after expansion. + +* Default name for end element is \vn{"endN"} where N is the index of the branch. \ No newline at end of file diff --git a/manual/ele-anatomy.tex b/manual/ele-anatomy.tex index 17c7bb9..a2ff38b 100644 --- a/manual/ele-anatomy.tex +++ b/manual/ele-anatomy.tex @@ -69,7 +69,7 @@ \section{Element Parameter Groups} \begin{example} julia> subtypes(EleParameterGroup) 28-element Vector{Any}: - AlignmentGroup + BodyShiftGroup ApertureGroup BMultipoleGroup ... @@ -83,7 +83,7 @@ \section{Element Parameter Groups} Associated parameter groups =========================== - • AlignmentGroup -> Element position/orientation shift. + • BodyShiftGroup -> Element position/orientation shift. • ApertureGroup -> Vacuum chamber aperture. • BMultipoleGroup -> Magnetic multipoles. • EMultipoleGroup -> Electric multipoles. diff --git a/manual/ele-param-groups.tex b/manual/ele-param-groups.tex index 4a4c3d1..b6f129e 100644 --- a/manual/ele-param-groups.tex +++ b/manual/ele-param-groups.tex @@ -13,7 +13,7 @@ \chapter{Element Parameter Groups} {\it Group} & {\it Section} & {\it Group} & {\it Section} \\ \midrule ACKickerGroup & \sref{s:ackicker.g} & LengthGroup & \sref{s:length.g} \\ - AlignmentGroup & \sref{s:alignment.g} & LordSlaveStatusGroup & \sref{s:lord.slave.g} \\ + BodyShiftGroup & \sref{s:alignment.g} & LordSlaveStatusGroup & \sref{s:lord.slave.g} \\ ApertureGroup & \sref{s:aperture.g} & MasterGroup & \sref{s:master.g} \\ BMultipoleGroup & \sref{s:bmultipole.g} & OrientationGroup & \sref{s:orientation.g} \\ BeamBeamGroup & \sref{s:beam.beam.g} & OriginEleGroup & \sref{s:origin.ele.g} \\ @@ -44,7 +44,7 @@ \chapter{Element Parameter Groups} To see which element types contain a given group, use the \vn{info(::EleParameterGroup)} method. Example: \begin{example} - julia> info(AlignmentGroup) + julia> info(BodyShiftGroup) ApertureGroup: Vacuum chamber aperture. ... Found in: @@ -104,13 +104,13 @@ \section{ACKickerGroup} \newpage %--------------------------------------------------------------------------------------------------- -\section{AlignmentGroup} +\section{BodyShiftGroup} \label{s:alignment.g} \begin{figure}[bt] \centering \includegraphics{alignment-ref.pdf} \caption[Element alignment.] -{AlignmentGroup parameters The reference point is the origin +{BodyShiftGroup parameters The reference point is the origin about which the element alignment is calculated. A) For straight elements, the reference point is in the center of the element. For \vn{Bend} elements, the reference point is at the midpoint of the chord connecting @@ -127,7 +127,7 @@ \section{AlignmentGroup} } \label{f:alignment} \end{figure} -The \vn{AlignmentGroup} gives the alignment (position and angular orientation) of the physical element +The \vn{BodyShiftGroup} gives the alignment (position and angular orientation) of the physical element relative to the nominal position defined by the branch coordinates (\sref{s:orient}). Alignment is specified with respect to the ``alignment reference point'' of an element as shown in Fig~\ref{s:alignment}. The \vn{Bend} reference point is chosen to be the center of the chord @@ -136,7 +136,7 @@ \section{AlignmentGroup} common simulation problem is to simulate a bend with a \vn{z_rot} keeping the entrance and exit endpoints fixed. -The parameters of the \vn{AlignmentGroup} are: +The parameters of the \vn{BodyShiftGroup} are: \begin{example} offset::Vector - $[x, y, z]$ offset. x_rot::Number - Rotation around the x-axis. @@ -148,12 +148,12 @@ \section{AlignmentGroup} parameters are with respect to the branch reference coordinates. There are output alignment parameters: \begin{example} - q_align::Quaternion - Quaternion representation of x_rot, y_rot, z_rot. + q_shift::Quaternion - Quaternion representation of x_rot, y_rot, z_rot. offset_tot::Vector - $[x, y, z]$ offset. x_rot_tot::Number - Rotation around the x-axis. y_rot_tot::Number - Rotation around the z-axis. z_rot_tot::Number - Rotation around the z-axis. - q_align_tot::Quaternion - Quaternion representation of tot rotations. + q_shift_tot::Quaternion - Quaternion representation of tot rotations. \end{example} The ``total alignment'' parameters which have a \vn{_tot} suffix are always the alignment of the element with with respect to the branch coordinates. @@ -165,8 +165,8 @@ \section{AlignmentGroup} of any \vn{Girder}. \vn{Girder} elements themselves also have both relative and total alignments since Girders can support other Girders. -The \vn{q_align} output parameter gives the quaternion representation of -\vn{x_rot}, \vn{y_rot} and \vn{z_rot}. Similarly, the\vn{q_align_tot} output parameter gives the +The \vn{q_shift} output parameter gives the quaternion representation of +\vn{x_rot}, \vn{y_rot} and \vn{z_rot}. Similarly, the\vn{q_shift_tot} output parameter gives the quaternion representation of \vn{x_rot_tot}, \vn{y_rot_tot} and \vn{z_rot_tot}. %--------------------------------------------------------------------------------------------------- @@ -191,7 +191,7 @@ \section{ApertureGroup} aperture_at::BodyLoc.T - Aperture location. Default: BodyLoc.ENTRANCE_END wall::Wall2D - Aperture defined by vertex array. custom_aperture::Dict - Custom aperture information. - aperture_shifts_with_alignment::Bool + aperture_shifts_with_body::Bool - Alignment affects aperture? Default: false. \end{example} @@ -895,7 +895,7 @@ \section{OriginEleGroup} The \vn{OriginEleGroup} is used with \vn{Fiducial}, \vn{FloorShift}, and \vn{Girder} elements. The \vn{OriginEleGroup} is used to set the coordinate reference frame from which -the orientation set by the \vn{AlignmentGroup} is measured. To specify that the floor coordinates are +the orientation set by the \vn{BodyShiftGroup} is measured. To specify that the floor coordinates are to be used, set the \vn{origin_ele} to \vn{NULL_ELE}. Typically this is the same as using the beginning element of the first branch of a lattice as long as the first element does not have any orientation shifts. diff --git a/manual/ele-types.tex b/manual/ele-types.tex index 42a82a9..c32bea0 100644 --- a/manual/ele-types.tex +++ b/manual/ele-types.tex @@ -49,7 +49,7 @@ \section{ACKicker} Element parameter groups associated with this element type are: \TOPrule \begin{example} - AlignmentGroup -> Element position/orientation shift. \sref{s:align.g} + BodyShiftGroup -> Element position/orientation shift. \sref{s:align.g} ApertureGroup -> Vacuum chamber aperture. \sref{s:aperture.g} BMultipoleGroup -> Magnetic multipoles. \sref{s:bmultipole.g} OrientationGroup -> Floor floor position and orientation. \sref{s:orientation.g} @@ -143,7 +143,7 @@ \section{Bend} Element parameter groups associated with this element type are: \TOPrule \begin{example} - AlignmentGroup -> Element position/orientation shift. \sref{s:align.g} + BodyShiftGroup -> Element position/orientation shift. \sref{s:align.g} ApertureGroup -> Vacuum chamber aperture. \sref{s:aperture.g} BMultipoleGroup -> Magnetic multipoles. \sref{s:bmultipole.g} BendGroup -> Bend element parameters. \sref{s:bend.g} @@ -562,7 +562,7 @@ \section{Girder} OrientationGroup -> Floor floor position and orientation. \sref{s:orientation.g} LengthGroup -> Length and s-position parameters. \sref{s:length.g} DescriptionGroup -> Element descriptive strings. \sref{s:descrip.g} - AlignmentGroup -> Alignment with respect to the reference. \sref{s:alignment.g} + BodyShiftGroup -> Alignment with respect to the reference. \sref{s:alignment.g} \end{example} \BOTTOMrule @@ -601,7 +601,7 @@ \section{Girder} The reference frame from which a \vn{Girder}'s orientation is measured is set by the \vn{origin_ele} and \vn{origin_ele_ref_point} parameters (\sref{s:origin.ele.g}). -Orientation shifts are controlled by the \vn{AlignmentGroup} (\sref{s:align.g}). +Orientation shifts are controlled by the \vn{BodyShiftGroup} (\sref{s:align.g}). When a girder is shifted in space, the elements it supports are also shifted. In this case, the orientation diff --git a/src/AcceleratorLattice.jl b/src/AcceleratorLattice.jl index 12ff19f..d148215 100644 --- a/src/AcceleratorLattice.jl +++ b/src/AcceleratorLattice.jl @@ -57,7 +57,6 @@ include("lat_construction.jl") include("output_lat.jl") include("query.jl") include("find.jl") -include("external_ele.jl") # Note! Element types, enums, and Holy traits are exported automatically when constructed. @@ -70,7 +69,7 @@ export split!, construct_ele_type, ele_at_s, toggle_integrated! export eles_search, eles_substitute_lords!, eles_sort! export next_ele, ele_at_offset, ele_param_value_str, strip_AL, ele_param_group_symbols export branch, matches_branch, create_ele_vars, eval_str, Vertex1, LatticeGlobal -export EleParameterGroup, AlignmentGroup, OrientationGroup, BMultipole, BMultipoleGroup, BeamBeamGroup +export EleParameterGroup, BodyShiftGroup, OrientationGroup, BMultipole, BMultipoleGroup, BeamBeamGroup export EMultipole, EMultipoleGroup, BendGroup, ApertureGroup, DescriptionGroup, RFGroup, SolenoidGroup export TrackingGroup, LengthGroup, ReferenceGroup, DownstreamReferenceGroup, ForkGroup export MasterGroup, LordSlaveStatusGroup, ACKickerGroup @@ -83,7 +82,7 @@ export machine_location, body_location, EleRegion, holy_traits, output_parameter export BranchType, LordBranch, TrackingBranch, MultipassBranch, SuperBranch, transform export str_split, str_match, str_unquote, str_quote, str_to_int, associated_names export ELE_PARAM_GROUP_INFO, ELE_TYPE_INFO, PARAM_GROUPS_LIST, OPEN, CLOSED -export rotX, rotY, rotZ, rot, rot! +export rotX, rotY, rotZ, rot, rot!, bend_quaternion, lat_ele_dict # From LinearAlgebra export norm diff --git a/src/accessor.jl b/src/accessor.jl index a91895b..b6a420e 100644 --- a/src/accessor.jl +++ b/src/accessor.jl @@ -207,10 +207,11 @@ end """ Base.getindex(lat::Lattice, name::AbstractString) Base.getindex(lat::Lattice, ix_branch::Int) + Base.getindex(lat::Lattice, typ::Type{T} where T <: BranchType) If `lat[name]` matches a branch name, return the branch. No wild cards permitted here. If `lat[name]` does not match a branch name, return list of matching lattice elements. -In this case the returned vector is equivalent to `eles_search(lat, name)`. +In this case, the returned vector is equivalent to `eles_search(lat, name)`. For `lat[ix_branch]`, return `lat.branch[ix_branch]`. """ Base.getindex(lat::Lattice, name::AbstractString) @@ -223,26 +224,44 @@ function Base.getindex(lat::Lattice, name::AbstractString) return eles_search(lat, name) end +#------------ + function Base.getindex(lat::Lattice, ix_branch::Int) if ix_branch > length(lat.branch); error("Index above length of lat.branch[] array."); end return lat.branch[ix_branch] end +#------------ + +Base.getindex(lat::Lattice, typ::Type{T} where T <: BranchType) = lat.branch[typ] + #--------------------------------------------------------------------------------------------------- # Base.getindex(branch::Vector{Branch}, name::AbstractString) """ Base.getindex(branch::Vector{Branch}, name::AbstractString) + Base.getindex(branch::Vector{Branch}, typ::Type{T} where T <: BranchType) -Match `branch[name]` to branch in `branch[]` array using the names of the branches. -""" Base.getindex(branch::Vector{Branch}, name::AbstractString) +Match `branch[name]` or `branch[type]` to branch in `branch[]` array using the names or types +of the branches. If there are multiple matches the match with the smallest index is returned. +""" Base.getindex(branch::Vector{Branch}) function Base.getindex(branch::Vector{Branch}, name::AbstractString) for br in branch if br.name == name; return br; end end - error(f"No branch with name: {name}") + error("Cannot find branch with name: $name") +end + +#----------- + +function Base.getindex(branch::Vector{Branch}, typ::Type{T} where T <: BranchType) + for br in branch + if br.type == typ; return br; end + end + + error("Cannot find branch of type: $typ") end #--------------------------------------------------------------------------------------------------- @@ -459,49 +478,49 @@ function output_parameter(sym::Symbol, ele::Ele, output_group::Type{T}) where T if :DownstreamReferenceGroup ∉ keys(ele.pdict); return NaN; end return ele.E_tot_ref_downstream / massof(ele.species_ref_downstream) - elseif sym == :q_align - if :AlignmentGroup ∉ keys(ele.pdict); return NaN; end - ag = ele.pdict[:AlignmentGroup] + elseif sym == :q_shift + if :BodyShiftGroup ∉ keys(ele.pdict); return NaN; end + ag = ele.pdict[:BodyShiftGroup] return Quaternion(ag.x_rot, ag.y_rot, ag.z_rot) elseif sym == :offset_tot - if :AlignmentGroup ∉ keys(ele.pdict); return NaN; end + if :BodyShiftGroup ∉ keys(ele.pdict); return NaN; end if isnothing(girder(ele)); return ele.offset; end - ag = ele.pdict[:AlignmentGroup] - orient_girder = OrientationGroup(girder(ele).offset_tot, girder(ele).q_align_tot) - orient_ele = OrientationGroup(ele.offset, ele.q_align) + ag = ele.pdict[:BodyShiftGroup] + orient_girder = OrientationGroup(girder(ele).offset_tot, girder(ele).q_shift_tot) + orient_ele = OrientationGroup(ele.offset, ele.q_shift) return coord_transform(orient_ele, orient_girder).r elseif sym == :x_rot_tot - if :AlignmentGroup ∉ keys(ele.pdict); return NaN; end + if :BodyShiftGroup ∉ keys(ele.pdict); return NaN; end if isnothing(girder(ele)); return ele.x_rot; end - ag = ele.pdict[:AlignmentGroup] - orient_girder = OrientationGroup(girder(ele).offset_tot, girder(ele).q_align_tot) - orient_ele = OrientationGroup(ele.offset, ele.q_align) + ag = ele.pdict[:BodyShiftGroup] + orient_girder = OrientationGroup(girder(ele).offset_tot, girder(ele).q_shift_tot) + orient_ele = OrientationGroup(ele.offset, ele.q_shift) return rot_angles(coord_transform(orient_ele, orient_girder).q)[1] elseif sym == :y_rot_tot - if :AlignmentGroup ∉ keys(ele.pdict); return NaN; end + if :BodyShiftGroup ∉ keys(ele.pdict); return NaN; end if isnothing(girder(ele)); return ele.y_rot; end - ag = ele.pdict[:AlignmentGroup] - orient_girder = OrientationGroup(girder(ele).offset_tot, girder(ele).q_align_tot) - orient_ele = OrientationGroup(ele.offset, ele.q_align) + ag = ele.pdict[:BodyShiftGroup] + orient_girder = OrientationGroup(girder(ele).offset_tot, girder(ele).q_shift_tot) + orient_ele = OrientationGroup(ele.offset, ele.q_shift) return rot_angles(coord_transform(orient_ele, orient_girder).q)[2] elseif sym == :z_rot_tot - if :AlignmentGroup ∉ keys(ele.pdict); return NaN; end + if :BodyShiftGroup ∉ keys(ele.pdict); return NaN; end if isnothing(girder(ele)); return ele.z_rot; end - ag = ele.pdict[:AlignmentGroup] - orient_girder = OrientationGroup(girder(ele).offset_tot, girder(ele).q_align_tot) - orient_ele = OrientationGroup(ele.offset, ele.q_align) + ag = ele.pdict[:BodyShiftGroup] + orient_girder = OrientationGroup(girder(ele).offset_tot, girder(ele).q_shift_tot) + orient_ele = OrientationGroup(ele.offset, ele.q_shift) return rot_angles(coord_transform(orient_ele, orient_girder).q)[3] - elseif sym == :q_align_tot - if :AlignmentGroup ∉ keys(ele.pdict); return NaN; end - if isnothing(girder(ele)); return ele.q_align; end - ag = ele.pdict[:AlignmentGroup] - orient_girder = OrientationGroup(girder(ele).offset_tot, girder(ele).q_align_tot) - orient_ele = OrientationGroup(ele.offset, ele.q_align) + elseif sym == :q_shift_tot + if :BodyShiftGroup ∉ keys(ele.pdict); return NaN; end + if isnothing(girder(ele)); return ele.q_shift; end + ag = ele.pdict[:BodyShiftGroup] + orient_girder = OrientationGroup(girder(ele).offset_tot, girder(ele).q_shift_tot) + orient_ele = OrientationGroup(ele.offset, ele.q_shift) return coord_transform(orient_ele, orient_girder).q end diff --git a/src/bookkeeper.jl b/src/bookkeeper.jl index e9fbebb..221abb7 100644 --- a/src/bookkeeper.jl +++ b/src/bookkeeper.jl @@ -1,24 +1,27 @@ #--------------------------------------------------------------------------------------------------- -# bookkeeper!(Lattice) +# bookkeeper!(Lattice; init::Bool = false) """ - bookkeeper!(lat::Lattice) + bookkeeper!(lat::Lattice; init::Bool = false) All Lattice bookkeeping. For example, if the reference energy is changed at the start of a branch the bookkeeping code will propagate that change through the reset of the lattice. + +If `init` is set `true`, maximal bookkeeping will be done. +Setting `true` meant to be used when the lattice is instantiated. """ bookkeeper!(lat::Lattice) -function bookkeeper!(lat::Lattice) - if !lat.parameters_have_changed; return; end +function bookkeeper!(lat::Lattice; init::Bool = false) + if !lat.parameters_have_changed && !init; return; end lat.parameters_have_changed = false push_bookkeeping_state!(lat, autobookkeeping = false, auditing_enabled = false) - try +# try # Tracking branch bookkeeping for (ix, branch) in enumerate(lat.branch) if branch.type != TrackingBranch; continue; end branch.pdict[:ix_branch] = ix - bookkeeper_tracking_branch!(branch) + bookkeeper_tracking_branch!(branch, init = init) end # Check for unbookkeeped parameters @@ -30,10 +33,10 @@ function bookkeeper!(lat::Lattice) end end - catch this_err - pop_bookkeeping_state!(lat) - rethrow(this_err) - end +# catch this_err +# pop_bookkeeping_state!(lat) +# rethrow(this_err) +# end pop_bookkeeping_state!(lat) return @@ -70,23 +73,33 @@ function check_if_settable(ele::Ele, sym::Symbol, pinfo::Union{ParamInfo, Nothin end #--------------------------------------------------------------------------------------------------- -# bookkeeper_tracking_branch!(Branch) +# bookkeeper_tracking_branch!(Branch; init = true) """ - Internal: bookkeeper_tracking_branch!(branch::Branch) + Internal: bookkeeper_tracking_branch!(branch::Branch; init::Bool = false) + +Branch bookkeeping. This function is called by `bookkeeper!`. +This processes tracking branches only. -Branch bookkeeping. This function is called by `bookkeeper_tracking_branch!(lat::Lattice)`. -This processes tracking branches only. Lord branches are ignored. +If `init` is set `true`, maximal bookkeeping will be done. +Setting `true` meant to be used when the lattice is instantiated. """ bookkeeper_tracking_branch!(branch::Branch) -function bookkeeper_tracking_branch!(branch::Branch) - if branch.pdict[:type] == LordBranch; return; end +function bookkeeper_tracking_branch!(branch::Branch; init::Bool = false) + if branch.pdict[:type] == LordBranch; error("Confused bookkeeping! Please report this!"); end + + if init + ix_min = 1 + ix_max = length(branch.ele) + changed = ChangedLedger(true, true, true, true) + else + ix_min = branch.pdict[:ix_ele_min_changed] + if ix_min > length(branch.ele); return; end + ix_max = branch.pdict[:ix_ele_max_changed] + changed = ChangedLedger() + end - ix_min = branch.pdict[:ix_ele_min_changed] - if ix_min > length(branch.ele); return; end ix_min == 1 ? previous_ele = NULL_ELE : previous_ele = branch.ele[ix_min-1] - ix_max = branch.pdict[:ix_ele_max_changed] - changed = ChangedLedger() for ele in branch.ele[ix_min:end] # If UnionEle or the first super slave then process super lord(s). @@ -122,7 +135,7 @@ These low level routines (there are several with this signature) are called via ## Arguments - `ele` -- Element to do bookkeeping on. -- `previous_ele` -- Element in the branch before `ele`. Will be `NULL_ELE` if this is the first branch element. +- `previous_ele` -- Element in the branch before `ele`. Will be `NULL_ELE` if `ele` is first element in branch. """ bookkeeper_ele!(ele::Ele) function bookkeeper_ele!(ele::Ele, changed::ChangedLedger, previous_ele::Ele) @@ -237,7 +250,7 @@ function bookkeeper_superslave!(slave::Ele, changed::ChangedLedger, previous_ele end end - if group == AlignmentGroup && (group_changed ||changed.this_ele_length) + if group == BodyShiftGroup && (group_changed ||changed.this_ele_length) if haskey(lord.pdict, :BendGroup) bgl = lord.pdict[:BendGroup] bgs = slave.pdict[:BendGroup] @@ -252,8 +265,11 @@ function bookkeeper_superslave!(slave::Ele, changed::ChangedLedger, previous_ele ct = rot(ct, bend_quaternion(0.5*bgs.angle, bg.ref_tilt)) # translate from beginning of slave to center of slave chord. ct.r = ct.r + [0.0, 0.0, 0.5*bgs.l_chord] - # Apply total transformation of AlignmentGroup. - slave.AlignmentGroup = coord_transform(lord.pdict[:AlignmentGroup], ct) + # Apply total transformation of BodyShiftGroup. + bs = lord.pdict[:BodyShiftGroup] + lord_shift = OrientationGroup(bs.offset, Quaternion(bs.x_rot, bs.y_rot, bs.z_rot)) + slave_shift = coord_transform(lord_shift, ct) + slave.BodyShiftGroup = BodyShiftGroup(slave_shift.r, rot_angles(slave_shift.q)...) end end end @@ -465,12 +481,19 @@ function elegroup_bookkeeper!(ele::Ele, group::Type{BendGroup}, changed::Changed # Not a slave else - sym1 = param_conflict_check(ele, :L, :L_chord) + conflict1 = param_conflict_check(ele, :L, :L_chord) + param_conflict_check(ele, :g, :bend_field_ref) param_conflict_check(ele, :e1, :e1_rect) param_conflict_check(ele, :e2, :e2_rect) - if haskey(cdict, :angle) && haskey(cdict, :g) && length(sym1) == 1 - error("Conflict: $(sym1[1]), g, and angle cannot simultaneously be specified for a Bend element $(ele.name)"); end + if haskey(cdict, :bend_field_ref) + # Use previous_ele for ref parameters since BendGroup is first group to be bookkeeped + bg.g = bg.bend_field_ref * C_LIGHT * charge(previous_ele.species_ref_downstream) / previous_ele.pc_ref_downstream + end + + if haskey(cdict, :angle) && haskey(cdict, :g) && length(conflict1) == 1 + error("Conflict: $(conflict1[1]), g, and angle cannot simultaneously be specified for a Bend element $(ele.name)") + end if haskey(cdict, :angle) && haskey(cdict, :g) L = bg.g * bg.angle @@ -488,7 +511,7 @@ function elegroup_bookkeeper!(ele::Ele, group::Type{BendGroup}, changed::Changed bg.angle = L * bg.g end - bg.bend_field_ref = bg.g * ele.pc_ref / (C_LIGHT * charge(ele.species_ref)) + bg.bend_field_ref = bg.g * previous_ele.pc_ref_downstream / (C_LIGHT * charge(previous_ele.species_ref_downstream)) if haskey(cdict, :L_chord) bg.angle = 2 * asin(bg.L_chord * bg.g / 2) @@ -704,6 +727,8 @@ function elegroup_bookkeeper!(ele::Ele, group::Type{OrientationGroup}, if has_changed(ele, OrientationGroup) || changed.this_ele_length; changed.floor_position = true; end if !changed.floor_position; return; end + if is_null(previous_ele); return; end # Happens with beginning element + ele.OrientationGroup = propagate_ele_geometry(previous_ele) clear_changed!(ele, OrientationGroup) @@ -969,3 +994,32 @@ function pop_bookkeeping_state!(lat::Lattice) pop!(lat.private[:bookkeeping_state]) end +#--------------------------------------------------------------------------------------------------- +# set_branch_min_max_changed! + +""" + function set_branch_min_max_changed!(branch::Branch, ix_ele::Number) + function set_branch_min_max_changed!(branch::Branch, ix_ele_min::Number, ix_ele_max::Number) + +Sets `branch.ix_ele_min_changed` and `branch.ix_ele_max_changed` to record the indexes at which +element parameters have changed. This is used by `bookkeeper!` to minimize computation time. + +The arguments `ix_ele`, `ix_ele_min`, and `ix_ele_max` are all element indexes where there +has been a change in parameters. + +Note: Elements whose index has shifted but whose parameters have not changed, do not need to be +marked as changed. + +""" set_branch_min_max_changed! + +function set_branch_min_max_changed!(branch::Branch, ix_ele::Number) + branch.ix_ele_min_changed = min(branch.ix_ele_min_changed, ix_ele) + branch.ix_ele_max_changed = max(branch.ix_ele_max_changed, ix_ele) + if !isnothing(branch.lat); branch.lat.parameters_have_changed = true; end +end + +function set_branch_min_max_changed!(branch::Branch, ix_ele_min::Number, ix_ele_max::Number) + branch.ix_ele_min_changed = min(branch.ix_ele_min_changed, ix_ele_min) + branch.ix_ele_max_changed = max(branch.ix_ele_max_changed, ix_ele_max) + if !isnothing(branch.lat); branch.lat.parameters_have_changed = true; end +end diff --git a/src/external_ele.jl b/src/external_ele.jl deleted file mode 100644 index 483aa24..0000000 --- a/src/external_ele.jl +++ /dev/null @@ -1,125 +0,0 @@ -#--------------------------------------------------------------------------------------------------- -# lat_ele_dict - -""" - lat_ele_dict(lat::Lattice) - -Return a dictionary of `ele_name => Vector{Ele}` mapping of lattice element names to arrays of -elements with that name. - -### Input - -- `lat` -- Lattice to use. - -### Output - -Dict{String, Vector{Ele}} dictionary where the keys are element names and the values are -vectors of elements of whose name matches the key - -### Example - -eled = lat_ele_dict(lat) # Create Dictionary -eled["q23w"] - - -""" - -function lat_ele_dict(lat::Lattice) - eled = Dict{String,Vector{Ele}}() - for branch in lat.branch - for ele in branch.ele - if haskey(eled, ele.name) - eled[ele.name] - push!(eled[ele.name], ele) - else - eled[ele.name] = Vector{Ele}([ele]) - end - end - end - - return eled -end - -#--------------------------------------------------------------------------------------------------- -# kill_external_ele - -""" -Set external variables corresponding to elements with the same name in a lattice to `nothing`. -(Currently there is no way to undefine the variables). -See also `create_external_ele`. - -The `prefix` argument is needed if a prefix was given in `create_ele_vars`. - -The `this_module` argument is needed if the variables are not in the `Main` module. -Note: `@__MODULE__` is the name of the module of the calling routine. -""" -function kill_external_ele(lat::Lattice; prefix::AbstractString = "", this_module = Main) - for branch in lat.branch - for ele in branch.ele - nam = prefix * ele.name - if !isdefined(this_module, Symbol(nam)); continue; end - eval( :($(Symbol(nam)) = nothing) ) - end - end - - return -end - -#--------------------------------------------------------------------------------------------------- -# create_external_ele - -""" -Creates `Ele` variables external to a lattice with the same name as the elements in the lattice. - -For example, if "q23w" is the name of an element in the lattice, this routine will create a -variable with the name `q23w`. - -In the case where multiple lattice elements have the same name, the corresponding variable -will be a vector of `Ele`s. - -The `prefix` arg can be used to distinguish between elements of the same name in different lattices. - -The `this_module` arg is needed if the variables are not to be in the Main module. -Use `@__MODULE__` for the name of the module of the code calling `create_ele_vars`. - -The routine kill_external_ele will remove these external elements. -""" -function create_external_ele(lat::Lattice; prefix::AbstractString = "", this_module = Main) - eled = lat_ele_dict(lat) - - for (name, evec) in eled - if length(evec) == 1 - eval( :($(Symbol(this_module)).$(Symbol(name)) = $(evec[1])) ) - else - eval( :($(Symbol(this_module)).$(Symbol(name))= $(evec)) ) - end - end - - return -end - -#--------------------------------------------------------------------------------------------------- -# create_unique_ele_names! - -""" -function create_unique_ele_names!(lat::Lattice; suffix::AbstractString = "!#") - -Modifies a lattice so that all elements have a unique name. - -For elements whose names are not unique, the `suffix` arg is appended to the element name -and an integer is substituted for the "#" character in the suffix arg. If no "#" -character exists, a "#" character is appended to the suffix arg. -""" -function create_unique_ele_names!(lat::Lattice; suffix::AbstractString = "!#") - if !occursin("#", suffix); suffix = suffix * "#"; end - eled = lat_ele_dict(lat) - - for (name, evec) in eled - if length(evec) == 1; continue; end - for (ix, ele) in enumerate(evec) - ele.name = ele.name * replace(suffix, "#" => string(ix)) - end - end - - return -end diff --git a/src/find.jl b/src/find.jl index 0989c73..6e17683 100644 --- a/src/find.jl +++ b/src/find.jl @@ -349,7 +349,7 @@ If there are more than two block expressions involved, evaluation is left to rig # This works since all slaves have a "!" in their name. eles_search(lat, "Marker::%5-1") # All elements just before Marker elements with two character names # ending in the digit "5". - eles_search(lat.branch["super"], "ID=`abc`") # All super lord elements with ID string equal to "abc". + eles_search(lat.branch[SuperBranch], "ID=`abc`") # All super lord elements with ID string equal to "abc". eles_search(lat, "1>>Patch::4:10") # All Patch elements in branch 1 between elements 4 through 10. eles_search(lat, "Patch::1>>4:10") # Same as above eles_search(lat, "Qa+1:Qb+2") # All elements between the element after "Qa" through the @@ -543,3 +543,60 @@ function ele_at_s(branch::Branch, s::Real; select::Select.T = Select.UPSTREAM, e end end end + +#--------------------------------------------------------------------------------------------------- +# lat_ele_dict + +""" + lat_ele_dict(lat::Lattice) -> Dict{String, Vector{Ele}} + +Return a dictionary of `ele_name => Vector{Ele}` mapping of lattice element names to arrays of +elements with that name. Using a dictionary for name lookup will be much faster than +searching using the `eles` function. + +## Output + +Dict{String, Vector{Ele}} dictionary where the keys are element names and the values are +vectors of elements of whose name matches the key. The element vectors are ordered by s-position. + +## Example +``` +eled = lat_ele_dict(lat) # Create Dictionary +eled["q23w"] +``` +""" lat_ele_dict + +function lat_ele_dict(lat::Lattice) + eled = Dict{String,Vector{Ele}}() + + for branch in lat.branch + if branch.type == SuperBranch || branch.type == MultipassBranch; continue; end + for ele in branch.ele + lat_ele_dict(ele, eled) + end + end + + return eled +end + +# Internal function used by lat_ele_dict above. + +function lat_ele_dict(ele::Ele, eled::Dict) + if haskey(eled, ele.name) + eled[ele.name] + push!(eled[ele.name], ele) + else + eled[ele.name] = Vector{Ele}([ele]) + end + + if ele.slave_status == Slave.SUPER + for lord in ele.super_lords + lat_ele_dict(lord, eled) + end + end + + if ele.slave_status == Slave.MULTIPASS + lat_ele_dict(ele.multipass_lord, eled) + end +end + diff --git a/src/geometry.jl b/src/geometry.jl index 0ae0d30..cb628b7 100644 --- a/src/geometry.jl +++ b/src/geometry.jl @@ -2,8 +2,9 @@ # propagate_ele_geometry """ - propagate_ele_geometry(floor_start::OrientationGroup, ele::Ele) - propagate_ele_geometry(ele::Ele) + propagate_ele_geometry(geometry::Type{<:EleGeometry}, floor_start::OrientationGroup, + ele::Ele) -> OrientationGroup + propagate_ele_geometry(ele::Ele) -> OrientationGroup Returns the floor position at the end of the element given the floor position at the beginning. Normally this routine is called with `floor_start` equal to ele.param[:floor_position]. @@ -33,7 +34,7 @@ function propagate_ele_geometry(::Type{STRAIGHT}, fstart::OrientationGroup, ele: end function propagate_ele_geometry(::Type{CIRCULAR}, fstart::OrientationGroup, ele::Ele) - df = coord_transform(ele.L, ele.BendGroup.angle, ele.BendGroup.tilt_ref) + df = coord_transform(ele.L, ele.BendGroup.g, ele.BendGroup.tilt_ref) return coord_transform(fstart, df) end @@ -57,9 +58,9 @@ point that is an arc distance `ds` from the first point. The transformation is r_end = r_start + rot(dr, q_start) q_end = q_start * dq -""" coord_transform(ds::Number, g::Number, tilt_ref::Number) +""" coord_transform(ds::Number, g::Number, tilt_ref::Number = 0.0) -function coord_transform(ds::Number, g::Number, tilt_ref::Number) +function coord_transform(ds::Number, g::Number, tilt_ref::Number = 0.0) if g == 0 return OrientationGroup([0.0, 0.0, ds], Quaternion()) @@ -102,7 +103,7 @@ Quaternion representing the coordinate rotation for a bend through an angle `ang a `tilt_ref` reference tilt. """ bend_quaternion -return bend_quaternion(angle::Number, tilt_ref::Number) +function bend_quaternion(angle::Number, tilt_ref::Number) if tilt_ref == 0 return rotY(-angle) else diff --git a/src/lat_construction.jl b/src/lat_construction.jl index 8bfc991..66e8565 100644 --- a/src/lat_construction.jl +++ b/src/lat_construction.jl @@ -262,6 +262,7 @@ function new_tracking_branch!(lat::Lattice, bline::BeamLine) add_beamline_ele_to_branch!(branch, BeamLineItem(bline.pdict[:end_ele])) else @ele end_ele = Marker() + end_ele.pdict[:name] = "end$(length(lat.branch))" add_beamline_ele_to_branch!(branch, BeamLineItem(end_ele)) end @@ -333,7 +334,7 @@ function Lattice(root_lines::Vector{BeamLine}; name::AbstractString = "lat") new_lord_branch!(lat, "girder", GirderBranch) init_multipass_bookkeeper!(lat) - bookkeeper!(lat) + bookkeeper!(lat, init = true) lat_sanity_check(lat) lat.auditing_enabled = true diff --git a/src/manipulation.jl b/src/manipulation.jl index 088ca79..e8023b8 100644 --- a/src/manipulation.jl +++ b/src/manipulation.jl @@ -23,10 +23,11 @@ end Base.copy(branch::Branch) Shallow copy constructer for a lattice branch. +Note: copy will point to the same lattice as the input branch. """ Base.copy(branch::Branch) function Base.copy(branch::Branch) - branch_copy = Branch(branch.name, copy(branch.ele), copy(branch.pdict)) + branch_copy = Branch(branch.name, branch.lat, copy(branch.ele), copy(branch.pdict)) for ix in 1:length(branch.ele) branch_copy.ele[ix] = copy(branch.ele[ix]) branch_copy.ele[ix].branch => branch_copy @@ -198,6 +199,40 @@ function Base.push!(branch::Branch, ele::Ele; adjust_orientation = true) return ele end +#--------------------------------------------------------------------------------------------------- +# create_unique_ele_names! + +""" + create_unique_ele_names!(lat::Lattice; suffix::AbstractString = "!#") + +Modifies a lattice so that all elements have a unique name. + +For elements whose names are not unique in lattice `lat`, +the `suffix` arg is appended to the element name +and an integer is substituted for the "#" character in the suffix arg starting from `1` +for the first instance, etc. +If no "#" character exists, a "#" character is appended to the suffix arg. + +## Example +``` + create_unique_ele_names!(lat, suffix = "_#x") +``` +In this example, elements that originally have names like `"abc"` would, after the function call, +have names `"abc_1x"`, `"abc_2x"`, etc. +""" +function create_unique_ele_names!(lat::Lattice; suffix::AbstractString = "!#") + if !occursin("#", suffix); suffix = suffix * "#"; end + eled = lat_ele_dict(lat) + + for (name, evec) in eled + if length(evec) == 1; continue; end + for (ix, ele) in enumerate(evec) + ele.name = ele.name * replace(suffix, "#" => string(ix)) + end + end + + return +end #--------------------------------------------------------------------------------------------------- # split!(branch, s_split) @@ -334,7 +369,7 @@ function split!(branch::Branch, s_split::Real; select::Select.T = Select.UPSTREA slave1.pdict[:changed][AllGroup] = true slave2.pdict[:changed][AllGroup] = true - sbranch = branch.lat.branch["super"] + sbranch = branch.lat[SuperBranch] push!(sbranch.ele, lord) lord.pdict[:slaves] = Vector{Ele}([slave1, slave2]) lord.lord_status = Lord.SUPER @@ -347,101 +382,3 @@ function split!(branch::Branch, s_split::Real; select::Select.T = Select.UPSTREA return slave2, true end -#--------------------------------------------------------------------------------------------------- -# set_drift_slice_names - -""" -""" - -function set_drift_slice_names(drift::Drift) - # Drift slice case - - if haskey(drift.pdict, :drift_master) - set_drift_slice_names(drift.pdict[:drift_master]) - return - end - - # Drift master case - - if !haskey(drift.pdict, :slices); return; end - - n = 0 - for slice in drift.pdict[:slices] - # A slice may have been replaced by an element via superposition so need to check that a - # slice still represents a valid element. - if !haskey(slice.pdict, :branch); continue; end - branch = slice.branch - if length(branch.ele) < slice.ix_ele; continue; end - if !(branch.ele[slice.ix_ele] === slice); continue; end - n += 1 - slice.name = drift.name * "!$n" - end -end - -#--------------------------------------------------------------------------------------------------- -# set_super_slave_names! - -""" - Internal: set_super_slave_names!(lord::Ele) -> nothing - -`lord` is a super lord and all of the slaves of this lord will have their name set. -""" - -function set_super_slave_names!(lord::Ele) - if lord.lord_status != Lord.SUPER; error("Argument is not a super lord: $(ele_name(lord))"); end - - name_dict = Dict{String,Int}() - for slave in lord.slaves - if length(slave.super_lords) == 1 - slave.name = lord.name - else - slave.name = "" - for this_lord in slave.super_lords - slave.name = slave.name * "!" * this_lord.name - end - slave.name = slave.name[2:end] - end - - name_dict[slave.name] = get(name_dict, slave.name, 0) + 1 - end - - index_dict = Dict{String,Int}() - for slave in lord.slaves - if name_dict[slave.name] == 1 - slave.name = slave.name * "!s" - else - index_dict[slave.name] = get(index_dict, slave.name, 0) + 1 - slave.name = slave.name * "!s" * string(index_dict[slave.name]) - end - end -end - -#--------------------------------------------------------------------------------------------------- -# set_branch_min_max_changed! - -""" - function set_branch_min_max_changed!(branch::Branch, ix_ele::Number) - function set_branch_min_max_changed!(branch::Branch, ix_ele_min::Number, ix_ele_max::Number) - -Sets `branch.ix_ele_min_changed` and `branch.ix_ele_max_changed` to record the indexes at which -element parameters have changed. This is used by `bookkeeper!` to minimize computation time. - -The arguments `ix_ele`, `ix_ele_min`, and `ix_ele_max` are all element indexes where there -has been a change in parameters. - -Note: Elements whose index has shifted but whose parameters have not changed, do not need to be -marked as changed. - -""" set_branch_min_max_changed! - -function set_branch_min_max_changed!(branch::Branch, ix_ele::Number) - branch.ix_ele_min_changed = min(branch.ix_ele_min_changed, ix_ele) - branch.ix_ele_max_changed = max(branch.ix_ele_max_changed, ix_ele) - if !isnothing(branch.lat); branch.lat.parameters_have_changed = true; end -end - -function set_branch_min_max_changed!(branch::Branch, ix_ele_min::Number, ix_ele_max::Number) - branch.ix_ele_min_changed = min(branch.ix_ele_min_changed, ix_ele_min) - branch.ix_ele_max_changed = max(branch.ix_ele_max_changed, ix_ele_max) - if !isnothing(branch.lat); branch.lat.parameters_have_changed = true; end -end \ No newline at end of file diff --git a/src/parameters.jl b/src/parameters.jl index 9a16fef..f69a179 100644 --- a/src/parameters.jl +++ b/src/parameters.jl @@ -88,21 +88,21 @@ ELE_PARAM_INFO_DICT = Dict( :amp_function => ParamInfo(ACKickerGroup, Function, "Amplitude function."), - :offset => ParamInfo(AlignmentGroup, Vector{Number}, "[x, y, z] offset of element or, for a patch, exit coordinates.", "m"), - :x_rot => ParamInfo(AlignmentGroup, Number, "X-axis rotation of element or, for a patch, exit coordinates.", "rad"), - :y_rot => ParamInfo(AlignmentGroup, Number, "Y-axis rotation of element or, for a patch, exit coordinates.", "rad"), - :z_rot => ParamInfo(AlignmentGroup, Number, "Z-axis rotation of element or, for a patch, exit coordinates.", "rad"), - - :q_align => ParamInfo(AlignmentGroup, Quaternion, "Quaternion orientation.", "", OutputGroup), - :q_align_tot => ParamInfo(AlignmentGroup, Quaternion, "Quaternion orientation including Girder orientation.", "", OutputGroup), - :offset_tot => ParamInfo(AlignmentGroup, Vector{Number}, "[x, y, z] element offset including Girder orientation.", "m", OutputGroup), - :x_rot_tot => ParamInfo(AlignmentGroup, Number, "X-axis element rotation including Girder orientation.", "rad", OutputGroup), - :y_rot_tot => ParamInfo(AlignmentGroup, Number, "Y-axis element rotation including Girder orientation.", "rad", OutputGroup), - :z_rot_tot => ParamInfo(AlignmentGroup, Number, "Z-axis element rotation including Girder orientation.", "rad", OutputGroup), + :offset => ParamInfo(BodyShiftGroup, Vector{Number}, "[x, y, z] offset of element or, for a patch, exit coordinates.", "m"), + :x_rot => ParamInfo(BodyShiftGroup, Number, "X-axis rotation of element or, for a patch, exit coordinates.", "rad"), + :y_rot => ParamInfo(BodyShiftGroup, Number, "Y-axis rotation of element or, for a patch, exit coordinates.", "rad"), + :z_rot => ParamInfo(BodyShiftGroup, Number, "Z-axis rotation of element or, for a patch, exit coordinates.", "rad"), + + :q_shift => ParamInfo(BodyShiftGroup, Quaternion, "Quaternion orientation.", "", OutputGroup), + :q_shift_tot => ParamInfo(BodyShiftGroup, Quaternion, "Quaternion orientation including Girder orientation.", "", OutputGroup), + :offset_tot => ParamInfo(BodyShiftGroup, Vector{Number}, "[x, y, z] element offset including Girder orientation.", "m", OutputGroup), + :x_rot_tot => ParamInfo(BodyShiftGroup, Number, "X-axis element rotation including Girder orientation.", "rad", OutputGroup), + :y_rot_tot => ParamInfo(BodyShiftGroup, Number, "Y-axis element rotation including Girder orientation.", "rad", OutputGroup), + :z_rot_tot => ParamInfo(BodyShiftGroup, Number, "Z-axis element rotation including Girder orientation.", "rad", OutputGroup), :aperture_shape => ParamInfo(ApertureGroup, ApertureShape, "Aperture shape. Default is ELLIPTICAL."), :aperture_at => ParamInfo(ApertureGroup, BodyLoc.T, "Aperture location. Default is BodyLoc.ENTRANCE_END."), - :aperture_shifts_with_alignment + :aperture_shifts_with_body => ParamInfo(ApertureGroup, Bool, "Do element alignment shifts move the aperture?"), :x_limit => ParamInfo(ApertureGroup, Vector{Number}, "Min/Max horizontal aperture limits.", "m"), :y_limit => ParamInfo(ApertureGroup, Vector{Number}, "Min/Max vertical aperture limits.", "m"), @@ -598,14 +598,14 @@ end Table of what element groups are associated with what element types. Order is important. Bookkeeping routines rely on: - - `LengthGroup` being first (`LengthGroup` bookkeeping may be done a second time if `BendGroup` modifies `L`). + - `LengthGroup` being first (except for a `Bend` where BendGroup is `first`). - `BendGroup` after `ReferenceGroup` and `MasterGroup` (in case the reference energy is changing). - `BMultipoleGroup` and `EMultipoleGroup` after `MasterGroup` (in case the reference energy is changing). - `RFCommonGroup` comes last (triggers autoscale/autophase and `ReferenceGroup` correction). """ PARAM_GROUPS_LIST base_group_list = [LengthGroup, LordSlaveStatusGroup, DescriptionGroup, ReferenceGroup, - DownstreamReferenceGroup, OrientationGroup, TrackingGroup, AlignmentGroup, ApertureGroup] + DownstreamReferenceGroup, OrientationGroup, TrackingGroup, BodyShiftGroup, ApertureGroup] multipole_group_list = [MasterGroup, BMultipoleGroup, EMultipoleGroup] general_group_list = [base_group_list..., multipole_group_list...] @@ -613,17 +613,17 @@ PARAM_GROUPS_LIST = Dict( ACKicker => [general_group_list..., ACKickerGroup], BeamBeam => [base_group_list..., BeamBeamGroup], BeginningEle => [base_group_list..., TwissGroup, InitParticleGroup], - Bend => [general_group_list..., BendGroup], + Bend => [BendGroup, general_group_list...], Collimator => [base_group_list...], Converter => [base_group_list...], CrabCavity => [base_group_list...], Drift => [base_group_list...], EGun => [general_group_list...], - Fiducial => [DescriptionGroup, OrientationGroup, AlignmentGroup, OriginEleGroup], - FloorShift => [DescriptionGroup, OrientationGroup, AlignmentGroup, OriginEleGroup], + Fiducial => [DescriptionGroup, OrientationGroup, BodyShiftGroup, OriginEleGroup], + FloorShift => [DescriptionGroup, OrientationGroup, BodyShiftGroup, OriginEleGroup], Foil => [base_group_list...], Fork => [base_group_list..., ForkGroup], - Girder => [LengthGroup, DescriptionGroup, OrientationGroup, AlignmentGroup, OriginEleGroup, GirderGroup], + Girder => [LengthGroup, DescriptionGroup, OrientationGroup, BodyShiftGroup, OriginEleGroup, GirderGroup], Instrument => [base_group_list...], Kicker => [general_group_list...], LCavity => [base_group_list..., MasterGroup, RFAutoGroup, RFGroup], @@ -651,7 +651,7 @@ end ELE_PARAM_GROUP_INFO = Dict( ACKickerGroup => EleParameterGroupInfo("ACKicker element parameters.", false), - AlignmentGroup => EleParameterGroupInfo("Element position/orientation shift.", false), + BodyShiftGroup => EleParameterGroupInfo("Element position/orientation shift.", false), ApertureGroup => EleParameterGroupInfo("Vacuum chamber aperture.", false), BeamBeamGroup => EleParameterGroupInfo("BeamBeam element parameters.", false), BendGroup => EleParameterGroupInfo("Bend element parameters.", true), diff --git a/src/quaternion.jl b/src/quaternion.jl index 9f152ff..a76d24f 100644 --- a/src/quaternion.jl +++ b/src/quaternion.jl @@ -17,7 +17,6 @@ rotZ(angle::Number) = Quaternion(cos(angle/2), [0, 0, sin(angle/2)]) # Quaternion """ - Quaternion(aa::AxisAngle) Quaternion(m::Matrix{T}) Quaternion(x_rot::Number, y_rot::Number, z_rot::Number) Quaternion(axis::Vector, angle::Number) @@ -33,13 +32,6 @@ Quaternion() = Quaternion(1.0, 0.0, 0.0, 0.0) Quaternion(x_rot::Number, y_rot::Number, z_rot::Number) = rotY(y_rot) * rotX(x_rot) * rotZ(z_rot) -function Quaternion(aa::AxisAngle) - if aa.angle == 0; return UNIT_QUAT; end - m = mag(aa.axis) - if m == 0; error("RangeError: Length of axis is zero."); end - return Quaternion(cos(0.5*aa.angle), sin(0.5*aa.angle)*axis / m) -end - function Quaternion(m::Matrix{T}) where T <: Number trace = tr(m) @@ -66,10 +58,6 @@ function Quaternion(axis::Vector, angle::Number) return Quaternion(cos(0.5*angle), axis*sin(0.5*angle)) end -#--------------------------------------------------------------------------------------------------- - -const UNIT_QUAT = Quaternion(1.0, [0.0, 0.0, 0.0]) - #--------------------------------------------------------------------------------------------------- # rot! diff --git a/src/query.jl b/src/query.jl index aad6421..575e457 100644 --- a/src/query.jl +++ b/src/query.jl @@ -102,7 +102,7 @@ end is_null(ele::Ele) is_null(branch::Branch -Test if argument is either of the NULL_ELE or NULL_BRANCH constants. +Test if argument is either of the `NULL_ELE` or `NULL_BRANCH` constants. """ is_null is_null(ele::Ele) = return (ele.name == "NULL_ELE") diff --git a/src/show.jl b/src/show.jl index c28b751..5820a8d 100644 --- a/src/show.jl +++ b/src/show.jl @@ -25,7 +25,7 @@ but not a key. This restriction is not fundamental and could be remove with a li """ show_column2 show_column2 = Dict{Type{T} where T <: BaseEleParameterGroup, Dict{Symbol,Symbol}}( - AlignmentGroup => Dict{Symbol,Symbol}( + BodyShiftGroup => Dict{Symbol,Symbol}( :offset => :offset_tot, :x_rot => :x_rot_tot, :y_rot => :y_rot_tot, @@ -34,7 +34,7 @@ show_column2 = Dict{Type{T} where T <: BaseEleParameterGroup, Dict{Symbol,Symbol ApertureGroup => Dict{Symbol,Symbol}( :x_limit => :y_limit, - :aperture_shape => :aperture_shifts_with_alignment, + :aperture_shape => :aperture_shifts_with_body, ), diff --git a/src/struct.jl b/src/struct.jl index f2983f3..6826a13 100644 --- a/src/struct.jl +++ b/src/struct.jl @@ -282,7 +282,7 @@ parameter group. To see in which element types contain a given parameter group, use the `info(::EleParameterGroup)` method. To see what parameter groups are contained in a Example: ``` - info(AlignmentGroup) # List element types that contain AlignmentGroup + info(BodyShiftGroup) # List element types that contain BodyShiftGroup ``` """ BaseEleParameterGroup, EleParameterGroup, EleParameterSubGroup @@ -466,46 +466,6 @@ ACKicker parameters. amp_function::Union{Function, Nothing} = nothing end -#--------------------------------------------------------------------------------------------------- -# AlignmentGroup - -""" - mutable struct AlignmentGroup <: EleParameterGroup - -Orientation of an element. - -- For `Patch` elements this is the orientation of the exit face with respect to the entrance face. -- For `FloorShift` and `Fiducial` elements this is the orientation of the element with respect - to the reference element. -- For other elements this is the orientation of element body alignmnet point with respect to -the supporting girder if it exists or with respect to the machine coordinates. - -## Fields -• `offset::Vector` - [x, y, z] offsets not including any Girder. \\ -• `x_rot::Number` - Rotation around the x-axis not including any Girder alignment shifts. \\ -• `y_rot::Number` - Rotation around the y-axis not including any Girder alignment shifts. \\ -• `z_rot::Number` - Rotation around the z-axis not including any Girder alignment shifts. \\ - -# Associated Output Parameters -The `tot` parameters are defined only for elements that can be supported by a `Girder`. -These parameters are the body coordinates with respect to machine coordinates. -These parameters are calculated by `AcceleratorLattice` and will be equal to the corresponding -non-tot fields if there is no `Girder`. -• `q_align::Quaternion` - `Quaternion` representation of `x_rot`, `y_rot`, `tilt` orientation. \\ -• `q_align_tot:: Quaternion` - `Quaternion` representation of orienttion with Girder shifts. -• `offset_tot::Vector` - `[x, y, z]` offsets including Girder alignment shifts. \\ -• `x_rot_tot::Number` - Rotation around the x-axis including Girder alignment shifts. \\ -• `y_rot_tot::Number` - Rotation around the y-axis including Girder alignment shifts. \\ -• `z_rot_tot::Number` - Rotation around the z-axis including Girder alignment shifts. \\ -""" AlignmentGroup - -@kwdef mutable struct AlignmentGroup <: EleParameterGroup - offset::Vector = [0.0, 0.0, 0.0] - x_rot::Number = 0 - y_rot::Number = 0 - z_rot::Number = 0 -end - #--------------------------------------------------------------------------------------------------- # ApertureGroup @@ -520,7 +480,7 @@ Vacuum chamber aperture struct. • `wall::Wall2D` - Aperture defined by an array of vertices. \\ • `aperture_shape::ApertureShape.T` - Aperture shape. Default is `ApertureShape.ELLIPTICAL`. \\ • `aperture_at::BodyLoc.T` - Where aperture is. Default is `BodyLoc.ENTRANCE_END`. \\ -• `aperture_shifts_with_alignment:Bool` - Do element alignments shifts move the aperture? Default is `false`. \\ +• `aperture_shifts_with_body:Bool` - Do element alignments shifts move the aperture? Default is `false`. \\ • `custom_aperture::Dict` - Custom aperture information. \\ """ ApertureGroup @@ -530,7 +490,7 @@ Vacuum chamber aperture struct. aperture_shape::typeof(ApertureShape) = ELLIPTICAL aperture_at::BodyLoc.T = BodyLoc.ENTRANCE_END wall::Wall2D = Wall2D() - aperture_shifts_with_alignment::Bool = true + aperture_shifts_with_body::Bool = true custom_aperture::Dict = Dict() end @@ -639,6 +599,46 @@ Vector of magnetic multipoles. pole::Vector{BMultipole} = Vector{BMultipole}(undef,0) # Vector of multipoles. end +#--------------------------------------------------------------------------------------------------- +# BodyShiftGroup + +""" + mutable struct BodyShiftGroup <: EleParameterGroup + +Defines the position and orientation of an element. + +- For `Patch` elements this is the orientation of the exit face with respect to the entrance face. +- For `FloorShift` and `Fiducial` elements this is the orientation of the element with respect + to the reference element. +- For other elements this is the orientation of element body alignment point with respect to +the supporting girder if it exists or with respect to the machine coordinates. + +## Fields +• `offset::Vector` - [x, y, z] offsets not including any Girder. \\ +• `x_rot::Number` - Rotation around the x-axis not including any Girder alignment shifts. \\ +• `y_rot::Number` - Rotation around the y-axis not including any Girder alignment shifts. \\ +• `z_rot::Number` - Rotation around the z-axis not including any Girder alignment shifts. \\ + +# Associated Output Parameters +The `tot` parameters are defined only for elements that can be supported by a `Girder`. +These parameters are the body coordinates with respect to machine coordinates. +These parameters are calculated by `AcceleratorLattice` and will be equal to the corresponding +non-tot fields if there is no `Girder`. +• `q_shift::Quaternion` - `Quaternion` representation of `x_rot`, `y_rot`, `tilt` orientation. \\ +• `q_shift_tot:: Quaternion` - `Quaternion` representation of orienttion with Girder shifts. +• `offset_tot::Vector` - `[x, y, z]` offsets including Girder alignment shifts. \\ +• `x_rot_tot::Number` - Rotation around the x-axis including Girder alignment shifts. \\ +• `y_rot_tot::Number` - Rotation around the y-axis including Girder alignment shifts. \\ +• `z_rot_tot::Number` - Rotation around the z-axis including Girder alignment shifts. \\ +""" BodyShiftGroup + +@kwdef mutable struct BodyShiftGroup <: EleParameterGroup + offset::Vector = [0.0, 0.0, 0.0] + x_rot::Number = 0 + y_rot::Number = 0 + z_rot::Number = 0 +end + #--------------------------------------------------------------------------------------------------- # DescriptionGroup @@ -844,7 +844,7 @@ end Used with `Fiducial`, `FloorShift`, and `Girder` elements. The `OriginEleGroup` is used to set the coordinate reference frame from which -the orientation set by the `AlignmentGroup` is measured. +the orientation set by the `BodyShiftGroup` is measured. ## Fields • `origin_ele::Ele` - Origin reference element. Default is NULL_ELE. \\ @@ -1195,6 +1195,13 @@ following elements. Ledger parameters, when toggled to true, will never be reset for the remainder of the branch bookkeeping. The exception is the `this_ele_length` parameter which is reset for each element. + +# Fields +• `this_ele_length::Bool` \\ +• `s_position::Bool` \\ +• `ref_group::Bool` \\ +• `floor_position::Bool` \\ + """ ChangedLedger @kwdef mutable struct ChangedLedger diff --git a/src/superimpose.jl b/src/superimpose.jl index b2fa7f0..fd443b8 100644 --- a/src/superimpose.jl +++ b/src/superimpose.jl @@ -224,7 +224,7 @@ function superimpose!(super_ele::Ele, ref::T; ele_origin::BodyLoc.T = BodyLoc.CE # Here if a super lord element needs to be constructed. lord_list = [] - sbranch = branch.lat.branch["super"] + sbranch = lat[SuperBranch] lord1 = push!(sbranch, super_ele) lord1.lord_status = Lord.SUPER @@ -293,3 +293,73 @@ function superimpose!(super_ele::Ele, ref::T; ele_origin::BodyLoc.T = BodyLoc.CE return super_list end + +#--------------------------------------------------------------------------------------------------- +# set_super_slave_names! + +""" + Internal: set_super_slave_names!(lord::Ele) -> nothing + +`lord` is a super lord and all of the slaves of this lord will have their name set. +""" + +function set_super_slave_names!(lord::Ele) + if lord.lord_status != Lord.SUPER; error("Argument is not a super lord: $(ele_name(lord))"); end + + name_dict = Dict{String,Int}() + for slave in lord.slaves + if length(slave.super_lords) == 1 + slave.name = lord.name + else + slave.name = "" + for this_lord in slave.super_lords + slave.name = slave.name * "!" * this_lord.name + end + slave.name = slave.name[2:end] + end + + name_dict[slave.name] = get(name_dict, slave.name, 0) + 1 + end + + index_dict = Dict{String,Int}() + for slave in lord.slaves + if name_dict[slave.name] == 1 + slave.name = slave.name * "!s" + else + index_dict[slave.name] = get(index_dict, slave.name, 0) + 1 + slave.name = slave.name * "!s" * string(index_dict[slave.name]) + end + end +end + +#--------------------------------------------------------------------------------------------------- +# set_drift_slice_names + +""" +""" + +function set_drift_slice_names(drift::Drift) + # Drift slice case + + if haskey(drift.pdict, :drift_master) + set_drift_slice_names(drift.pdict[:drift_master]) + return + end + + # Drift master case + + if !haskey(drift.pdict, :slices); return; end + + n = 0 + for slice in drift.pdict[:slices] + # A slice may have been replaced by an element via superposition so need to check that a + # slice still represents a valid element. + if !haskey(slice.pdict, :branch); continue; end + branch = slice.branch + if length(branch.ele) < slice.ix_ele; continue; end + if !(branch.ele[slice.ix_ele] === slice); continue; end + n += 1 + slice.name = drift.name * "!$n" + end +end + diff --git a/test/bookkeeper_test.jl b/test/bookkeeper_test.jl index f06ddd9..584e0dd 100644 --- a/test/bookkeeper_test.jl +++ b/test/bookkeeper_test.jl @@ -3,13 +3,23 @@ using AcceleratorLattice, Test @ele begin1 = BeginningEle(species_ref = Species("electron"), pc_ref = 1e6) @ele begin2 = BeginningEle(species_ref = Species("proton"), pc_ref = 1e6) -@ele b1 = Bend(g = 1, angle = pi/2, Kn2L = 0.1, Bs3 = 0.2, En4 = 0.3, Es5L = 0.4) -b2 = copy(b1) -b2.tilt_ref = pi/2 +@ele bend1 = Bend(g = 1, angle = pi/2, Kn2L = 0.1, Bs3 = 0.2, En4 = 0.3, Es5L = 0.4) +bend2 = copy(bend1) +bend2.name = "bend2" +bend2.tilt_ref = pi/2 -line1 = BeamLine([begin1, b1]) -line2 = BeamLine([begin2, b2]) +@ele bend3 = Bend(g = 0, L = 2) + +line1 = BeamLine([begin1, bend1, bend3]) +line2 = BeamLine([begin2, bend2]) lat = Lattice([line1, line2]) b1 = lat.branch[1] -b2 = lat.branch[2] \ No newline at end of file +b2 = lat.branch[2] + +@testset "bend_bookkeeping" begin + + +end + +# test patches \ No newline at end of file diff --git a/test/superimpose_test.jl b/test/superimpose_test.jl index 7b710cc..4783157 100644 --- a/test/superimpose_test.jl +++ b/test/superimpose_test.jl @@ -37,7 +37,7 @@ b2 = lat.branch[2] @testset "Superimpose1" begin @test [e.name for e in b1.ele] == ["beginning", "zm1", "zm2", "m1", "zm3", "d1!1", "zs2", "d1!2", "lc1!s1", - "zm4", "lc1!s2", "d1!1", "zs2!s1", "m2", "zs2!s2", "d1!2", "d3", "end_ele"] + "zm4", "lc1!s2", "d1!1", "zs2!s1", "m2", "zs2!s2", "d1!2", "d3", "end1"] @test [e.name for e in b2.ele] == ["lc1", "zs2"] @test b1.ele[9].super_lords == [b2.ele[1]] @test b1.ele[11].super_lords == [b2.ele[1]]