
#
# $Log: not supported by cvs2svn $
# Revision 1.12  2003/12/18 17:39:30  borland
# APSGetAllLinacQuadStrengths now computes the K1's that are needed by elegant (for
# the split quads over the structures for example).  Matching procedures changed to
# expect these values, rather than computing them again.
# Added simplex restarts control to BC matching.
#
# Revision 1.11  2003/12/18 15:24:56  borland
# Added one more starting point for matching to BC.  Added some status messages.
#
# Revision 1.10  2003/11/17 04:11:05  borland
# Added correct computation of the initial momentum for all the starting points
# for matching from BC measurements.
#
# Revision 1.9  2003/11/13 18:12:28  borland
# Added procedures for configuring power supplies from a lattice file and
# making lattice information from power supply settings.
#
# Revision 1.8  2002/09/01 22:38:44  emery
# Changed latticedirectory  name  to /home/helios/oagData/linac/lattices.
#
# Revision 1.7  2002/08/07 18:56:07  borland
# APSGetAllLinacQuadStrengths now takes -snapshot argument to allow finding the
# quad strengths for a saved condition.
#
# Revision 1.6  2002/03/09 18:59:34  borland
# Added APSMatchFromMeasuredTwissBC.
#
# Revision 1.5  2002/01/27 14:46:14  borland
# Added APSMatchFromMeasuredTwissLIFS1.
#
# Revision 1.4  2002/01/27 01:24:20  borland
# Removed some debugging statements.
#
# Revision 1.3  2002/01/26 23:09:19  borland
# First working version.
#
# Revision 1.2  2002/01/26 16:03:12  borland
# Latest version, still untested.
#
# Revision 1.1  2002/01/23 23:13:38  borland
# First version, untested.
#
#

proc APSMeasureL1Momentum {args} {
    set output ""
    set initialValue -1.5
    set finalValue 1.5
    set pointsToAverage 20
    set settings 7
    set postChangePause 10
    set intermeasurementPause 0.5
    set statusCallback APSNoOp
    set plot 0
    set interactive 0
    set pixelsPerMM 9.08
    set expFile /home/helios/oagData/linac/experimentFiles/L1V1Momentum.exp
    if {[APSStrictParseArguments {output initialValue finalValue pointsToAverage settings \
                                    postChangePause intermeasurementPause pixelsPerMM \
                                    statusCallback plot interactive}] || \
          ![string length $output] || $initialValue==$finalValue || \
          $pointsToAverage<3 || $settings<3 || $postChangePause<1 || \
          $intermeasurementPause<=0} {
        return -code error "APSMeasureL1Momentum: invalid arguments"
    }
    if ![string length $statusCallback] {
        set statusCallback APSNoOp
    }
    if ![file exists $output] {
        eval $statusCallback {"Setting L1:QM1-5 to zero."}
        if [catch {exec cavget \
                     -list=L1:Q1:SetDacCurrentC,L1:Q2:SetDacCurrentC,L1:Q3:SetDacCurrentC,L1:Q4:SetDacCurrentC,L1:Q5:SetDacCurrentC \
                     -cavputform} quadSettings] {
            return -code error "APSMeasureL1Momentum: $result"
        }exit
        eval $statusCallback {"Waiting for quads to ramp to zero."}
        if [catch {exec cavput -list=L1:Q1:SetDacCurrentC=0,L1:Q2:SetDacCurrentC=0,L1:Q3:SetDacCurrentC=0,L1:Q4:SetDacCurrentC=0,L1:Q5:SetDacCurrentC=0
            after 2000 
            exec cawait -timeLimit=300 \
                     -waitfor=L1:Q1:MeasCurrentM,lower=-0.1,upper=0.1 \
                     -waitfor=L1:Q2:MeasCurrentM,lower=-0.1,upper=0.1 -and \
                     -waitfor=L1:Q3:MeasCurrentM,lower=-0.1,upper=0.1 -and \
                     -waitfor=L1:Q4:MeasCurrentM,lower=-0.1,upper=0.1 -and \
                     -waitfor=L1:Q5:MeasCurrentM,lower=-0.1,upper=0.1 -and} result] {
            catch {exec cavput -list=$quadSettings}
            return -code error "APSMeasureL1Momentum: Quads did not go to the desired values in the time allowed: $result."
        }
        # need to set up the camera here
        # add BPMs to data collection.
        # option for using BPM data
        eval $statusCallback {"Running experiment."}
        global apsMeasureL1MomentumDone
        set apsMeasureL1MomentumDone 0
        set unixCommand "sddsexperiment $expFile $output -verbose \
              -macro=pointsToAverage=$pointsToAverage,initialValue=$initialValue,finalValue=$finalValue,settings=$settings,postChangePause=$postChangePause,intermeasurementPause=$intermeasurementPause"
        if $interactive {
            APSExecLog [APSUniqueName .] -unixCommand $unixCommand \
              -callback "set apsMeasureL1MomentumDone 1" \
              -cancelCallback "set apsMeasureL1MomentumDone 2" \
              -abortCallback "set apsMeasureL1MomentumDone 2"
        } else {
            APSExec -unixCommand $unixCommand \
              -callback "set apsMeasureL1MomentumDone 1" 
        }
        while {!$apsMeasureL1MomentumDone} {
            if $interactive update
            after 1000
        }
        eval $statusCallback {"Returning quads to original setpoints."}
        if [catch {exec cavput -list=$quadSettings} result] {
            return -code error "APSMeasureL1Momentum: $result"
        }
        if $apsMeasureL1MomentumDone!=1 {
            file delete -force $output
            return -code error "APSMeasureL1Momentum: measurement aborted."
        }

        if [catch {exec sddspfit $output -pipe=out -terms=2 \
                     -column=L1:V1:SetDacCurrentC,LI:VD1:y:fit:centroidM,ysigma=SigmaLI:VD1:y:fit:centroidM \
                     | sddsprocess -pipe=in $output.fit \
                     "-define=parameter,Momentum,$pixelsPerMM Slope / -0.4106 * 299.79 *,units=MeV/c" \
                     "-define=parameter,MomentumSigma,SlopeSigma Slope / Momentum * abs,units=MeV/c" \
                     "-print=parameter,MomentumLabel,p = %.2f +/- %.2f MeV/c,Momentum,MomentumSigma"} result] {
            return -code error "APSMeasureL1Momentum: $result"
        }
    }

    if $interactive {
        exec sddsplot -column=L1:V1:SetDacCurrentC,LI:VD1:y:fit:centroidM,SigmaLI:VD1:y:fit:centroidM \
          -graph=error $output.fit -title=@MomentumLabel -topline=@sddspfitLabel \
          -column=L1:V1:SetDacCurrentC,LI:VD1:y:fit:centroidMFit \
          -graph=line,type=1 $output.fit &
    }
}


# Computes the K1 values for a list of linac quadrupoles at
# specified momenta

proc APSGetLinacQuadStrengths {args} {
    set output ""
    set quadNameList ""
    set momentumList ""
    set momentum 0
    if [APSStrictParseArguments {output quadNameList momentum momentumList}] {
        return -code error "APSGetLinacQuadStrengths: bad arguments"
    }
    
    if ![string length $output] {
        return -code error "APSGetLinacQuadStrengths: no output file"
    }
    if ![llength $quadNameList] {
        return -code error "APSGetLinacQuadStrengths: no quadrupoles listed"
    }
    if [llength $momentumList] {
        if [llength $momentumList]!=[llength $quadNameList] {
            return -code error "APSGetLinacQuadStrengths: lists of momentum and quad name don't match in length"
        }
    } else {
        if $momentum<=0 {
            return -code error "APSGetLinacQuadStrengths: neither momentumList nor momentum was given."
        }
        set momentumList [APSReplicateItem -item $momentum -number [llength $quadNameList]]
    }

    set tmpRoot /tmp/[APSTmpString]
    APSAddToTmpFileList -ID APSGetLinacQuadStrengths -fileList $tmpRoot.1

    set quadData(ColumnNames) {ElementName Momentum}
    set quadData(ColumnInfo.ElementName) "type SDDS_STRING"
    set quadData(ColumnInfo.Momentum) "type SDDS_DOUBLE"
    set quadData(Column.ElementName) [list $quadNameList]
    set quadData(Column.Momentum) [list $momentumList]
    if [catch {sdds save $tmpRoot.1 quadData } result] {
        return -code error "APSGetLinacQuadStrengths: $result"
    }

    set excitDir /home/helios/OAG/oagData/linac/magnets
    if [catch {exec sddsprocess $tmpRoot.1 -pipe=out \
                 -print=column,ControlName,%s:CurrentAO,ElementName \
                 | sddssnapshot -pipe -nameOfData=Current -unitsOfData=A \
                 | sddsxref -pipe $excitDir/crossReference.sdds -match=ElementName -take=* \
                 | sddsprocess -pipe=in $output \
                 "-print=column,InterpCommand,sddsinterp -print=bare\\\,stdout $excitDir/%s.excitation /dev/null -atValues=%21.15e -column=Current\\\,IntegratedStrength | token -n=2,ElementName,Current" \
                 -system=column,InterpResult,InterpCommand \
                 -scan=column,IntegratedStrength,InterpResult,%le,type=double,units=Tm \
                 "-define=column,K1Actual,IntegratedStrength EffectiveLength / Momentum 299.792458 / / Sign *,units=1/m^2"} result] {
        return -code error "APSGetLinacQuadStrengths: $result"
    }
    APSDeleteTmpFileList -ID APSGetLinacQuadStrengths
}

# Computes the K1 values for all linac quadrupoles given a momentum profile.
# The momemtum profile gives the momenta at the end of L1, end of L2, end of
# L4, and end of L5.

proc APSGetAllLinacQuadStrengths {args} {
    set output ""
    set momentumProfile {30 150 238 325}
    set snapshot ""
    if [APSStrictParseArguments {output momentumProfile snapshot}] {
        return -code error "APSGetAllLinacQuadStrengths: bad arguments"
    }
    if ![string length $output] {
        return -code error "APSGetAllLinacQuadStrengths: no output file"
    }
    if [llength $momentumProfile]!=4 {
        return -code error "APSGetAllLinacQuadStrengths: provide four values for momentum profile"
    }
    set latticeDir /home/helios/oagData/linac/lattices
    set tmpRoot /tmp/[APSTmpString]
    catch {exec elegant $latticeDir/momentumProfile.ele \
             -macro=rootname=$tmpRoot \
             -macro=latticeDir=$latticeDir \
             -macro=P1=[expr [lindex $momentumProfile 0]/0.51099906] \
             -macro=L2CellVoltage=[expr ([lindex $momentumProfile 1]-[lindex $momentumProfile 0])/(4.*86e-6)] \
             -macro=L4CellVoltage=[expr ([lindex $momentumProfile 2]-[lindex $momentumProfile 1])/(4.*86e-6)] \
             -macro=L5CellVoltage=[expr ([lindex $momentumProfile 3]-[lindex $momentumProfile 2])/(4.*86e-6)]} result
    APSAddToTmpFileList -ID APSGetAllLinacQuadStrengths -fileList [glob -nocomplain $tmpRoot.*]
    if {![file exists $tmpRoot.done] || ![file exists $tmpRoot.cen]} {
        return -code error "APSGetAllLinacQuadStrengths: problem running elegant: $result"
    }
    if ![string length $snapshot] {
        set snapCmd "sddssnapshot -pipe -nameOfData=Current -unitsOfData=A"
    } else {
        if [catch {exec sddsprocess $snapshot $tmpRoot.snap \
                     -match=column,ControlName=*:Q*CurrentAO \
                     -scan=column,Current,ValueString,%le,units=A} result] {
            return -code error "APSGetAllLinacQuadStrengths: $result"
        }
        set snapCmd "sddsxref -pipe $tmpRoot.snap -match=ControlName -take=Current -nowarning"
        APSAddToTmpFileList -ID APSGetAllLinacQuadStrengths -fileList "$tmpRoot.snap"
    }
    set excitDir /home/helios/OAG/oagData/linac/magnets
    if [catch {eval exec sddsprocess $tmpRoot.param -pipe=out \
                 -match=column,ElementType=*QUAD -match=column,ElementParameter=L \
                 | sddsxref $tmpRoot.cen -match=ElementName -take=pCentral -pipe \
                 | sddsbreak -pipe -change=ElementName \
                 | sddsprocess -pipe -process=ElementName,first,ElementName \
                 -process=pCentral,ave,pCentral \
                 -process=ParameterValue,sum,LSum \
                 -print=parameter,ControlName,%s:CurrentAO,ElementName \
                 | sddscollapse -pipe \
                 | $snapCmd \
                 | sddsxref -pipe $excitDir/crossReference.sdds -match=ElementName -take=* \
                 | sddsprocess -pipe \
                 -print=column,ExcitationFile,$excitDir/%s.excitation,ElementName \
                 | sddsinterpset -pipe \
                 -data=fileColumn=ExcitationFile,interpolate=IntegratedStrength,functionOf=Current,column=Current \
                 | sddsprocess -pipe=in $output \
                 {"-define=column,K1L,IntegratedStrength pCentral mev * 299.792458 / / Sign *,units=1/m"} \
                 {"-define=column,K1Actual,K1L EffectiveLength /,units=1/m^2"} \
                 {"-define=column,K1Elegant,K1L LSum /,units=1/m^2"} \
                 -print=column,ElementParameter,K1 \
                 -define=column,ParameterValue,K1Elegant} result] {
        return -code error "APSGetAllLinacQuadStrengths: $result"
    }
}

proc APSPropagateMeasuredTwissLIFS1 {args} {
    set xMeasurement ""
    set yMeasurement ""
    set momentumProfile {30 150 238 325}
    set rootname ""
    set config 200pC-30MeV-150MeV-dZ60-B4Nom
    set plot 0
    if {[APSStrictParseArguments {rootname momentumProfile config \
                                    xMeasurement yMeasurement plot}] || \
          ![string length $xMeasurement] || \
          ![string length $yMeasurement] || \
          ![string length $rootname]} {
        return -code error "APSPropagateMeasuredTwissLIFS1: bad arguments"
    }
    foreach plane {x y} {
        set filename [set ${plane}Measurement]
        if ![string length $filename] {
            return -code error "APSPropagateMeasuredTwissLIFS1: supply $filename"
        }
        if [catch {sdds load $filename MeasData} result] {
            return -code error "APSPropagateMeasuredTwissLIFS1: $filename: $result"
        }
        set beta($plane) [lindex $MeasData(Parameter.beta$plane) 0]
        set alpha($plane) [lindex $MeasData(Parameter.alpha$plane) 0]
    }
    if ![file exists $rootname.measParam] {
        if [catch {APSGetAllLinacQuadStrengths -output $rootname.measParam \
                     -momentumProfile $momentumProfile} result] {
            return -code error "APSPropagateMeasuredTwissLIFS1: $result"
        }
    }
    set latticeDir /home/helios/oagData/linac/lattices
    if ![file exists [set chicaneConfig $latticeDir/L1O_L3FS5/2001-1219/$config/transverse.param]] {
        return -code error "not found: $latticeDir/L1O_L3FS5/2001-1219/$config/transverse.param"
    }
                 
    catch {exec elegant $latticeDir/propagateLIFS1Meas.ele \
             -macro=rootname=$rootname,latticeDir=$latticeDir \
             -macro=inputParameters=$rootname.measParam \
             -macro=chicaneConfig=$chicaneConfig \
             -macro=betax=$beta(x),betay=$beta(y) \
             -macro=alphax=$alpha(x),alphay=$alpha(y) \
             -macro=P1=[expr [lindex $momentumProfile 0]/0.51099906] \
             -macro=L2CellVoltage=[expr ([lindex $momentumProfile 1]-[lindex $momentumProfile 0])/(4.*86e-6)] \
             -macro=L4CellVoltage=[expr ([lindex $momentumProfile 2]-[lindex $momentumProfile 1])/(4.*86e-6)] \
             -macro=L5CellVoltage=[expr ([lindex $momentumProfile 3]-[lindex $momentumProfile 2])/(4.*86e-6)]} result
    if {![file exists $rootname.done] || ![file exists $rootname.twi]} {
        return -code error "APSPropagateMeasuredTwissLIFS1: problem with elegant run: $result"
    }
    if $plot {
        exec sddsplot -col=s,beta? $rootname.twi -graph=line,vary -uns=y \
          -col=s,Profile $rootname.mag -overlay=xmode=norm,yfact=0.04 &
    }
}

proc APSMatchFromMeasuredTwissLIFS1 {args} {
    set xMeasurement ""
    set yMeasurement ""
    set momentumProfile {30 150 238 325}
    set rootname ""
    set config 200pC-30MeV-150MeV-dZ60-B4Nom
    set plot 0
    if {[APSStrictParseArguments {rootname momentumProfile config \
                                    xMeasurement yMeasurement plot}] || \
          ![string length $xMeasurement] || \
          ![string length $yMeasurement] || \
          ![string length $rootname]} {
        return -code error "APSMatchFromMeasuredTwissLIFS1: bad arguments"
    }
    foreach plane {x y} {
        set filename [set ${plane}Measurement]
        if ![string length $filename] {
            return -code error "APSMatchFromMeasuredTwissLIFS1: supply $filename"
        }
        if [catch {sdds load $filename MeasData} result] {
            return -code error "APSMatchFromMeasuredTwissLIFS1: $filename: $result"
        }
        set beta($plane) [lindex $MeasData(Parameter.beta$plane) 0]
        set alpha($plane) [lindex $MeasData(Parameter.alpha$plane) 0]
    }
    if ![file exists $rootname.measParam] {
        if [catch {APSGetAllLinacQuadStrengths -output $rootname.measParam \
                     -momentumProfile $momentumProfile} result] {
            return -code error "APSMatchFromMeasuredTwissLIFS1: $result"
        }
    }
    set latticeDir /home/helios/oagData/linac/lattices
    if ![file exists [set chicaneConfig $latticeDir/L1O_L3FS5/2001-1219/$config/transverse.param]] {
        return -code error "not found: $latticeDir/L1O_L3FS5/2001-1219/$config/transverse.param"
    }
                 
    catch {exec elegant $latticeDir/matchFromLIFS1.ele \
             -macro=rootname=$rootname,latticeDir=$latticeDir \
             -macro=inputParameters=$rootname.measParam \
             -macro=chicaneConfig=$chicaneConfig \
             -macro=betax=$beta(x),betay=$beta(y) \
             -macro=alphax=$alpha(x),alphay=$alpha(y) \
             -macro=P1=[expr [lindex $momentumProfile 0]/0.51099906] \
             -macro=L2CellVoltage=[expr ([lindex $momentumProfile 1]-[lindex $momentumProfile 0])/(4.*86e-6)] \
             -macro=L4CellVoltage=[expr ([lindex $momentumProfile 2]-[lindex $momentumProfile 1])/(4.*86e-6)] \
             -macro=L5CellVoltage=[expr ([lindex $momentumProfile 3]-[lindex $momentumProfile 2])/(4.*86e-6)]} result
    if {![file exists $rootname.done] || ![file exists $rootname.twi]} {
        return -code error "APSMatchFromMeasuredTwissLIFS1: problem with elegant run: $result"
    }
    if $plot {
        exec sddsplot \
          -col=s,beta? $rootname.twi -graph=line,vary -uns=y \
          -col=s,Profile $rootname.mag -overlay=xmode=norm,yfact=0.04 &
    }
}

proc APSMatchFromMeasuredTwissBC {args} {
    set measurement ""
    set momentumProfile {30 150 238 325}
    set rootname ""
    set config 200pC-30MeV-150MeV-dZ60-B4Nom
    set plot 0
    set startingPoint L2AS2I
    set statusCallback APSNoOp
    set maxBetax 30
    set maxBetay 30
    set simplexRestarts 1
    if {[APSStrictParseArguments {rootname momentumProfile config simplexRestarts \
                                    measurement plot statusCallback startingPoint \
                                    maxBetax maxBetay}] || \
          ![string length $measurement] || ![string length $rootname]} {
        return -code error "APSMatchFromMeasuredTwissBC: bad arguments"
    }
    set startingPointList {RG2AM L2AS1I L2AS2I L2AS3I L2AS4I L2AS4O L1AS1O}
    if [lsearch -exact $startingPointList $startingPoint]==-1 {
        return -code error "APSMatchFromMeasuredTwissBC: bad starting point.  Choose one of [join $startingPointList ,]"
    }
    set latticeDir /home/helios/oagData/linac/lattices
    if ![file exists $latticeDir/matchFrom${startingPoint}UsingBCMeas.ele] {
        return -code error "File not found:  $latticeDir/matchFrom${startingPoint}UsingBCMeas.ele"
    }
    eval $statusCallback {"Retrieving twiss parameters from $measurement"}
    foreach plane {x y} {
        if ![string length $measurement] {
            return -code error "APSMatchFromMeasuredTwissBC: supply $measurement"
        }
        if [catch {sdds load $measurement MeasData} result] {
            return -code error "APSMatchFromMeasuredTwissBC: $measurement: $result"
        }
        set beta($plane) [lindex $MeasData(Column.beta${plane}Mean) 0]
        set alpha($plane) [lindex $MeasData(Column.alpha${plane}Mean) 0]
    }
    eval $statusCallback {"Determining quadrupole strengths used for emittance measurement."}
    eval $statusCallback {"Momentum profile is $momentumProfile"}
    if ![file exists $rootname.measParam] {
        if [file exists [file rootname $measurement].snap] {
            eval $statusCallback {"Using measurement snapshot [file rootname $measurement].snap"}
            if [catch {APSGetAllLinacQuadStrengths -output $rootname.measParam \
                         -snapshot [file rootname $measurement].snap \
                         -momentumProfile $momentumProfile} result] {
                return -code error "APSMatchFromMeasuredTwissBC: $result"
            }
        } else {
            eval $statusCallback {"Warning: Using present quadrupole settings!"}
            if [catch {APSGetAllLinacQuadStrengths -output $rootname.measParam \
                         -momentumProfile $momentumProfile} result] {
                return -code error "APSMatchFromMeasuredTwissBC: $result"
            }
        }
    }
    set latticeDir /home/helios/oagData/linac/lattices
    if ![file exists [set chicaneConfig $latticeDir/L1O_L3FS5/2001-1219/$config/transverse.param]] {
        return -code error "not found: $latticeDir/L1O_L3FS5/2001-1219/$config/transverse.param"
    }
    set M1 [lindex $momentumProfile 0]
    set M2 [lindex $momentumProfile 1]
    switch $startingPoint {
        L1AS1O -
        RG2AM -
        L2AS1I {
            set P1 [expr $M1/0.51099906]
        }
        L2AS2I {
            set P1 [expr (($M2-$M1)/4.0+$M1)/0.51099906]
        }
        L2AS3I {
            set P1 [expr (2.0*($M2-$M1)/4.0+$M1)/0.51099906]
        }
        L2AS4I -
        L2AS4O {
            set P1 [expr (3.0*($M2-$M1)/4.0+$M1)/0.51099906]
        }
        default {
            return -code error "APSMatchFromMeasuredTwissBC: problem with starting point: $startingPoint"
        }
    }

    global APSMatchFromMeasuredTwissBCDone
    set APSMatchFromMeasuredTwissBCDone 0
    eval $statusCallback {"Running elegant..."}
    APSExecLog [APSUniqueName .] -unixCommand \
      "elegant $latticeDir/matchFrom${startingPoint}UsingBCMeas.ele \
             -macro=rootname=$rootname,latticeDir=$latticeDir \
             -macro=inputParameters=$rootname.measParam \
             -macro=chicaneConfig=$chicaneConfig,restarts=$simplexRestarts \
             -macro=betaxBC=$beta(x),betayBC=$beta(y) \
             -macro=alphaxBC=$alpha(x),alphayBC=$alpha(y) \
             -macro=P1=$P1,maxBetax=$maxBetax,maxBetay=$maxBetay \
             -macro=L2CellVoltage=[expr ([lindex $momentumProfile 1]-[lindex $momentumProfile 0])/(4.*86e-6)] \
             -macro=L4CellVoltage=[expr ([lindex $momentumProfile 2]-[lindex $momentumProfile 1])/(4.*86e-6)] \
             -macro=L5CellVoltage=[expr ([lindex $momentumProfile 3]-[lindex $momentumProfile 2])/(4.*86e-6)] | tee $rootname.log" \
      -callback "set APSMatchFromMeasuredTwissBCDone 1" \
      -cancelCallback "set APSMatchFromMeasuredTwissBCDone 2" \
      -abortCallback "set APSMatchFromMeasuredTwissBCDone 2" 
    while {!$APSMatchFromMeasuredTwissBCDone} {

        update
        eval $statusCallback {"Waiting for elegant..."}
        after 5000
    }
    eval $statusCallback {"Process finished."}
    if {![file exists $rootname.done] || ![file exists $rootname.twi]} {
        bell
        return -code error "APSMatchFromMeasuredTwissBC: problem with elegant run."
    }
    eval $statusCallback {"Plotting results."}
    if $plot {
        exec sddsplot -same=x,y,global \
          -col=s,beta? $rootname.inittwi -graph=line,vary -uns=y -clip=2,0 \
          "-title=Inferred from measurement" -end \
          -col=s,beta? $rootname.twi -graph=line,vary -uns=y \
          "-title=Simulated after matching" -end \
          -col=s,Profile $rootname.mag -overlay=xmode=norm,yfact=0.04 -omnipresent &
    }
}

# Generates the magnet setpoints (currents) from the chosen lattice file

set apsLinacPSDeviceFile /home/helios/oagData/deviceConfig/linac/PSOps.pvTable
set apsLEUTLPSDeviceFile /home/helios/oagData/deviceConfig/linac/LTPToEndPSOps.pvTable
set apsLinacMagnetsDir /home/helios/oagData/linac/magnets/

proc APSGenerateLinacSetpointFile {args} {
    global apsLinacPSDeviceFile apsLEUTLPSDeviceFile apsLinacMagnetsDir
    set rootname ""
    set output ""
    set beginMomentum 0
    set postL2Momentum 0
    set postL4Momentum 0
    set postL5Momentum 0
    set convertAll 0
    set rescale 0
    set includeChicane 0
    APSStrictParseArguments {rootname output convertAll beginMomentum \
                               postL2Momentum postL4Momentum postL5Momentum rescale \
                               includeChicane}


    if ![string length $output] {
        return -code error "No output filename given"
    }
    if ![file exist $rootname.param] {
        return -code "not found: $rootname.param"
    }
    if {$includeChicane && ![file exists $rootname.flr]} {
        return -code error "not found: $rootname.flr"
    }

    if [string compare [set outputDir [file dirname $output]] .]==0 {
        set outputDir [pwd]
    }

    set tmpRoot /tmp/[APSTmpString]
    switch $rescale {
        0 {
            # Find file giving the momentum at each element
            if ![file exists [set paramFile $rootname.param]] {
                return -code error "not found: $rootname.param"
            }
            if ![file exists [set profileFile $rootname.cen]] {
                if ![file exists [set profileFile $rootname.twi]] {
                    return -code error "not found: $rootname.cen or $rootname.twi"
                }
                set pCenColumn pCentral0
            } else {
                set pCenColumn pCentral
            }
        }
        1 {
            # run elegant to get scaled file
            set adjustFile [file join [file dirname [file dirname $rootname]] adjustp.ele]
            if ![file exists $adjustFile] {
                return -code error "not found: $adjustFile"
            }
            global elegantRunDone
            set elegantRunDone 0
            set elegantCmd "elegant $adjustFile \
                         -macro=latticeDir=[file dirname [file dirname $rootname]] \
                         -macro=rootname=$tmpRoot,startingFile=$rootname.param \
                         -macro=p0=[expr $beginMomentum/0.51099906] \
                         -macro=postL2Momentum=$postL2Momentum \
                         -macro=postL4Momentum=$postL4Momentum \
                         -macro=postL5Momentum=$postL5Momentum"
            catch {eval exec $elegantCmd >& $tmpRoot.log} result
            if ![file exists $tmpRoot.done] {
                APSFileDisplayWindow [APSUniqueName .] -fileName $tmpRoot.log \
                  -deleteOnClose 1 -width 130 
                update
                APSDeleteTmpFileList -ID GenerateLinacSetpointFile
                return -code error "elegant run failed.  Look on /tmp for files."
            }
            APSAddToTmpFileList -ID GenerateLinacSetpointFile -fileList \
              [glob -nocomplain $tmpRoot.*]
            # Find file giving the momentum at each element
            if ![file exists [set paramFile $tmpRoot.param]] {
                APSDeleteTmpFileList -ID GenerateLinacSetpointFile
                return -code error "not found: $paramFile"
            }
            if ![file exists [set profileFile $tmpRoot.cen]] {
                APSDeleteTmpFileList -ID GenerateLinacSetpointFile
                return -code error "not found: $profileFile"
            }
            set pCenColumn pCentral
        }
    }
    
    # generate setpoint for each element by interpolating excitation curves
    # 1a. Extract quad elements from parameters file (data: K1, L)
    # 1b. Extract bend elements from parameters file (data: ANGLE)
    # 2. Xref in the momentum from the appropriate file
    # 3. Compute the integrated strength (IS), summed over pieces.
    # 4. Multiply IS by BnLMultiplier, which accounts for strength differences between
    #    magnets using the same excitation curve.  If BnLMultiplier<1, then the
    #    magnet is weaker than the excitation curve says.
    # 5. Interpolate the excitation curve to get the current.
    APSAddToTmpFileList -ID GenerateLinacSetpointFile \
       -fileList "$tmpRoot.K1 $tmpRoot.ANGLE $tmpRoot.B1L $tmpRoot.beamlines $tmpRoot.CN $tmpRoot.dev"
    APSAddToTmpFileList -ID GenerateLinacSetpointFile -fileList "$tmpRoot.K1L $tmpRoot.GS $tmpRoot.alpha"
    APSAddToTmpFileList -ID GenerateLinacSetpointFile -fileList "$tmpRoot.res0 $tmpRoot.res1 $tmpRoot.res2"
    APSAddToTmpFileList -ID GenerateLinacSetpointFile -fileList "$tmpRoot.flr $tmpRoot.flr1 $tmpRoot.flr2 $tmpRoot.flr3 $tmpRoot.VP "
    if $convertAll {
        set xrefExtraOpt -fillIn
    } else {
        set xrefExtraOpt -nowarning
    }        
    if [catch {exec sddscombine $apsLinacPSDeviceFile $apsLEUTLPSDeviceFile -merge \
                 $tmpRoot.dev -overwrite} result] {
        APSDeleteTmpFileList -ID GenerateLinacSetpointFile
        return -code error "GenerateLinacSetpointFile: $result"
    }
    if [catch {exec sddsprocess $paramFile -pipe=out -match=col,ElementType=*QUAD* \
                 -match=col,ElementParameter=K1 \
                 | sddsconvert -pipe=in $tmpRoot.K1 \
                 -rename=column,ParameterValue=K1
        exec sddsprocess $paramFile -pipe=out -match=col,ElementType=*BEN* -nowarning \
                 -match=column,ElementParameter=ANGLE \
                 | sddsconvert -pipe=in $tmpRoot.ANGLE \
                 -rename=column,ParameterValue=GeometricIntegratedStrength
        exec sddsprocess $paramFile $tmpRoot.alpha -match=col,ElementType=ALPH -nowarning \
                 -match=column,ElementParameter=XMAX \
                 "-define=column,GeometricIntegratedStrength,ParameterValue sqr rec 3.304453768404287 *"
        exec sddsprocess $tmpRoot.dev $tmpRoot.CN -match=col,Operation=SetCurrent 
        exec sddsprocess $paramFile -pipe=out -match=col,ElementType=*QUAD* \
                 -match=col,ElementParameter=L \
                 | sddsconvert -pipe -rename=col,ParameterValue=L \
                 | sddsxref -pipe $tmpRoot.K1 -take=K1 -match=ElementName \
                 | sddsprocess -pipe=in $tmpRoot.K1L \
                 "-define=column,GeometricIntegratedStrength,K1 L *"
        exec sddscombine -merge $tmpRoot.ANGLE $tmpRoot.K1L $tmpRoot.alpha -pipe=out \
                 | tee $tmpRoot.GS \
                 | sddsxref -pipe $profileFile -take=$pCenColumn -match=ElementName \
                 | sddsprocess -pipe \
                 "-define=column,IntegratedStrength,GeometricIntegratedStrength $pCenColumn * 0.001704509390960 *,units=T*m^n" \
                 | sddsbreak -pipe -change=ElementName \
                 | sddsprocess -pipe -process=ElementName,first,%s -process=GeometricIntegratedStrength,sum,%s \
                 -process=IntegratedStrength,sum,%s -process=$pCenColumn,ave,pCentral \
                 | sddscollapse -pipe \
                 | sddsxref -pipe $apsLinacMagnetsDir/crossReference.sdds -nowarning \
                 -match=ElementName -take=*  \
                 | sddsxref -pipe $tmpRoot.CN -match=ElementName=DeviceName -take=ControlName \
                    -nowarning $xrefExtraOpt \
                 | sddsprocess -pipe \
                 "-redefine=column,IntegratedStrength,IntegratedStrength BnLMultiplier * Sign *,units=T" \
                 -print=column,ExcitationFile,$apsLinacMagnetsDir/%s.excitation,ElementName \
                 | sddsinterpset -pipe \
                 -order=1 -data=fileColumn=ExcitationFile,interpolate=Current,functionOf=IntegratedStrength,column=IntegratedStrength \
                 -belowRange=value=0 -aboveRange=extrapolate \
                 | sddsprocess -pipe \
                 "-print=column,ValueString,%21.15e,Current" \
                 "-define=column,OverLimit,Current abs SupplyLimit > ? 1 : 0 \$ ,type=long" \
                 -edit=column,Beamline,ControlName,S/:/100D \
                 "-define=column,Momentum,pCentral 0.51099906 *,units=MeV/c" \
                 | sddsconvert -pipe=in $tmpRoot.res0 \
                 -retain=column,ElementName,ControlName,Current,ValueString,Beamline,IntegratedStrength,pCentral,Momentum,GeometricIntegratedStrength,SupplyLimit,OverLimit} result] {
        APSDeleteTmpFileList -ID GenerateLinacSetpointFile 
        return -code error "GenerateLinacSetpointFile: $result"
    }
    
    set output0 $tmpRoot.res0
    # See if L3:BM* magnets are there.  If so, need to refine the calculation for these magnets.
    # Also, compute trim strengths.
    if [exec sddsprocess -nowarning $tmpRoot.res0 -pipe=out -match=col,ElementName=L3:BM* \
          | tee $tmpRoot.res1 | sdds2stream -rows=bare -pipe] {
        if [catch {exec sddsprocess $tmpRoot.res0 $tmpRoot.res2 -match=col,ElementName=L3:BM*,!
                sdds load $tmpRoot.res1 L3BMData} result] {
            return -code error "GenerateLinacSetpointFile: $result"
        }
        foreach element {L3:BM1 L3:BM2 L3:BM3 L3:BM4} {
            set index [lsearch [lindex $L3BMData(Column.ElementName) 0] $element]
            if $index==-1 {
                return -code error "GenerateLinacSetpointFile: Missing one or more L3:BM* elements."
            }
            set strength [lindex [lindex $L3BMData(Column.GeometricIntegratedStrength) 0] $index]
            set pCentral [lindex [lindex $L3BMData(Column.pCentral) 0] $index]
            switch $element {
                L3:BM1 {
                    set dataList [APSFindL3BMCurrent -element $element -angle $strength \
                                             -momentum $pCentral]
                    set current(L3:BM:US) [lindex $dataList 0]
                    set current(L3:BM:USForTrim) [lindex $dataList 1]
                }
                L3:BM3 {
                    set dataList [APSFindL3BMCurrent -element $element -angle $strength \
                                    -momentum $pCentral]
                    set current(L3:BM:DS) [lindex $dataList 0]
                    set current(L3:BM:DSForTrim) [lindex $dataList 1]
                }
                L3:BM2 {
                    set current(L3:BM2T) [APSFindL3BMCurrent -element $element -angle $strength \
                                            -momentum $pCentral -trimForCurrent $current(L3:BM:USForTrim)]
                }
                L3:BM4 {
                    set current(L3:BM4T) [APSFindL3BMCurrent -element $element -angle $strength \
                                            -momentum $pCentral -trimForCurrent $current(L3:BM:DSForTrim)]
                }
            }
        }
        set L3BMOut(ColumnNames) $L3BMData(ColumnNames)
        foreach item $L3BMOut(ColumnNames) {
            set L3BMOut(ColumnInfo.$item) $L3BMData(ColumnInfo.$item)
            switch $item {
                Current -
                ValueString {
                    set L3BMOut(Column.$item) [list [list $current(L3:BM:US) $current(L3:BM2T) $current(L3:BM:DS) \
                                                         $current(L3:BM4T)] ]
                }
                ControlName {
                    set L3BMOut(Column.ControlName) [list {L3:BM:US:CurrentAO L3:BM2T:CurrentAO \
                                                              L3:BM:DS:CurrentAO L3:BM4T:CurrentAO}]
                }
                default {
                    set L3BMOut(Column.$item) $L3BMData(Column.$item)
                }
            }
        }
        if [catch {sdds save $tmpRoot.res1 L3BMOut
            exec sddscombine $tmpRoot.res2 $tmpRoot.res1 -merge -pipe=out \
                     | sddsprocess -pipe=in $output \
                     "-redefine=column,OverLimit,Current abs SupplyLimit > ? 1 : 0 \$ ,type=long" } result] {
            return -code error "GenerateLinacSetpointFile: $result"
        }
    } else {
        file copy -force $tmpRoot.res0 $output
    }
    set count1 [lindex [split [exec sddssort -col=ElementName -unique $tmpRoot.GS -pipe=out | sdds2stream -rows -pipe]] 0]
    set count2 [lindex [split [exec sdds2stream -rows $output]] 0]

    if $includeChicane {
        if [catch {exec sddsprocess $rootname.flr -pipe=out -match=col,ElementName=L3:BM\[34\]-VP \
                     | sddssort -pipe -col=ElementName \
                     | sddsconvert -pipe=in $tmpRoot.VP -rename=col,X=XVP,Z=ZVP
            exec sddsprocess $rootname.flr -pipe=out -match=col,ElementName=L3:BM\[34\]-C \
                     | sddssort -pipe -col=ElementName \
                     | sddsconvert -pipe -rename=col,X=XC,Z=ZC \
                     | sddsxref -pipe $tmpRoot.VP -take=*VP \
                     | sddsprocess -pipe=in $tmpRoot.flr1 \
                     -process=ZC,first,ZReference -process=XVP,last,XReference \
                     "-define=column,XOffset,XVP XReference - 1e3 *,units=mm" \
                     "-define=column,ZFinal,ZC ZReference - 0.770294 - 1e3 *,units=m" 
            exec sddsprocess $tmpRoot.flr1 -pipe=out -match=col,ElementName=L3:BM3* \
                     "-print=col,ControlName,L3:BC:XOffset:motorSM" \
                     | sddsconvert -pipe=in $tmpRoot.flr2 -rename=col,XOffset=ControlValue 
            exec sddsprocess $tmpRoot.flr1 -pipe=out -match=col,ElementName=L3:BM4* \
                     "-print=col,ControlName,L3:BC:ZFinal:motorSM" \
                     | sddsconvert -pipe=in $tmpRoot.flr3 -rename=col,ZFinal=ControlValue 
            exec sddscombine $tmpRoot.flr2 $tmpRoot.flr3 -pipe=out \
                     | sddsprocess -pipe=in $tmpRoot.flr \
                     "-reedit=column,ElementName,%/-C//%/-VP//ei/Pos/" \
                     "-print=column,Beamline,L3:Movers" \
                     "-print=column,ValueString,%21.15le,ControlValue"
            exec sddscombine $output $tmpRoot.flr $tmpRoot.res0 -merge -overwrite
            file rename -force $tmpRoot.res0 $output} result] {
            APSDeleteTmpFileList -ID GenerateLinacSetpointFile 
            return -code error "GenerateLinacSetpointFile: $result"
        }
    }

    if $count1!=$count2 {
        set elemNames \
          [exec sddsselect $tmpRoot.GS $output -match=ElementName -pipe=out -invert -reuse=rows \
             | sdds2stream -pipe -column=ElementName]
        set delete [APSMultipleChoice [APSUniqueName .] -modal 1 \
                      -question "$count1 elements are in the lattice, but $count2 setpoints were generated.  Probably one or more magnets are missing from the magnet cross reference or the device configuration file.  The elements in question are: [join $elemNames ,]\nWhat do you want to do?" \
                      -returnList {0 1} -labelList {continue abort}]
        if $delete {
            file delete -force $output
        }
    }
    APSDeleteTmpFileList -ID GenerateLinacSetpointFile 
}

proc APSMakeInjectorConditioningFile {args} {
    set output ""
    set sectorList ""
    set cycles -1
    set degauss 0
    APSStrictParseArguments {output sectorList cycles degauss}
    if ![string length $output] {
        return -code error "APSMakeInjectorConditioningFile: no output file"
    }

    set tmpRoot /tmp/[APSTmpString]

    set knownSectorList {L1 L2 L3 L4 L5 LTP PB PTB BB LA LEA}
    if ![llength $sectorList] {
        set sectorList $knownSectorList
    }

    if !$degauss {
        set fileToUse(L1) ""
        set fileToUse(L2) /home/helios/oagData/controlFiles/Linac/conditioning/Linac.stdTo0
        set fileToUse(L3) /home/helios/oagData/controlFiles/Linac/conditioning/Linac.stdTo0
        set fileToUse(L4) /home/helios/oagData/controlFiles/Linac/conditioning/Linac.stdTo0
        set fileToUse(L5) /home/helios/oagData/controlFiles/Linac/conditioning/Linac.stdTo0
        set fileToUse(LTP) /home/helios/oagData/controlFiles/LTP/LTP.stdTo0
        set fileToUse(PTB) /home/helios/oagData/controlFiles/PTB/PTB.stdTo0
        set fileToUse(PB)  /home/helios/oagData/controlFiles/LEUTL/LEUTL.std
        set fileToUse(BB)  /home/helios/oagData/controlFiles/LEUTL/LEUTL.std
        set fileToUse(LA)  /home/helios/oagData/controlFiles/LEUTL/LEUTL.std
        set fileToUse(LEA)  /home/helios/oagData/controlFiles/LEUTL/LEUTL.std
    } else {
        set fileToUse(L1) ""
        set fileToUse(L2) ""
        set fileToUse(L3) ""
        set fileToUse(L4) ""
        set fileToUse(L5) ""
        set fileToUse(LTP) /home/helios/oagData/controlFiles/LTP/LTP_degauss.sdds
        set fileToUse(PTB) /home/helios/oagData/controlFiles/PTB/PTB_degauss.sdds
        set fileToUse(PB)  /home/helios/oagData/controlFiles/LEUTL/LEUTL_degauss.sdds
        set fileToUse(BB)  /home/helios/oagData/controlFiles/LEUTL/LEUTL_degauss.sdds
        set fileToUse(LA)  /home/helios/oagData/controlFiles/LEUTL/LEUTL_degauss.sdds
        set fileToUse(LEA)  /home/helios/oagData/controlFiles/LEUTL/LEUTL_degauss.sdds
    }
    set fileList ""
    foreach sector $sectorList {
        set sector [string toupper $sector]
        if [lsearch -exact $knownSectorList $sector]==-1 {
            APSDeleteTmpFileList -ID APSMakeInjectorConditioningFile
            return -code error "APSMakeInjectorConditioningFile: unknown sector $sector given"
        }
        if [string length $fileToUse($sector)]==0 continue
        APSAddToTmpFileList -ID APSMakeInjectorConditioningFile -fileList $tmpRoot.$sector
        if {[catch {exec sddsprocess $fileToUse($sector) $tmpRoot.$sector -nowarning \
                      -match=column,ControlName=$sector*} result]} {
            APSDeleteTmpFileList -ID APSMakeInjectorConditioningFile
            return -code error "APSMakeInjectorConditioningFile: $result ($fileToUse($sector) with $sector)"
        }
        lappend fileList $tmpRoot.$sector
    }
    file delete -force $output
    if [llength $fileList]==0 {
        return 
    }
    if [catch {eval exec sddscombine $fileList $output -merge} result] {
        return -code error "APSMakeInjectorConditioningFile: $result"
    }
}

# Finds main or trim current for L3:BMs

proc APSFindL3BMCurrent {args} {
    set element ""
    set angle 0
    set momentum 0
    set trimForCurrent 0
    APSStrictParseArguments {element angle momentum trimForCurrent}
    global apsLinacMagnetsDir 

    # momentum is beta*gamma 
    set BRho [expr $momentum*0.001704509390960]
    set BLNeeded [expr $angle*$BRho]

    #APSSetVarAndUpdate status "FindL3BMCurrent: Element=$element, angle=$angle, momentum=$momentum"
    #APSSetVarAndUpdate status "  BRho = $BRho   BLNeeded=$BLNeeded"

    set data $apsLinacMagnetsDir/${element}.prop
    set excitation $apsLinacMagnetsDir/${element}.excitation

    if $trimForCurrent<=0 {
        # This is a magnet without a trim.
        # Find current for which B(I)*LEffective(I,angle) = BRho*angle

        if ![file exists $data] {
            return -code error "Not found: $data"
        }
        if ![file exists $excitation] {
            return -code error "Not found: $excitation"
        }

        set tmpRoot /tmp/[APSTmpString]
        APSAddToTmpFileList -ID FindL3BMCurrent -fileList "$tmpRoot.1 $tmpRoot.2 $tmpRoot.3"
        
        switch $element {
            L3:BM1 -
            L3:BM2 {
                set PSName L3:BM:US
            }
            L3:BM3 -
            L3:BM4 {
                set PSName L3:BM:DS
            }
            default {
                return -code error "Not found: $element"
            }
        }

        if [catch {exec sddscombine -merge $data -pipe=out \
                     | sddssort -pipe -column=B0 -column=angle \
                     | sddsbreak -pipe -change=B0 \
                     | sddsinterp -pipe -column=angle,B0,LEffective -atValue=$angle \
                     | sddscombine -merge -pipe \
                     | sddssort -column=B0 -pipe=in $tmpRoot.1} result] {
            return -code error "$result"
        }
        if [catch {exec sddsprocess $tmpRoot.1 -process=LEffective,ave,%sAve -pipe=out \
                     | sdds2stream -pipe -parameter=LEffectiveAve} L] {
            return -code error "$L"
        }
        set tries 5
        while {$tries} {
            set B [expr $BLNeeded/$L]
            if [catch {exec sddsinterp $tmpRoot.1 -atValue=$B -column=B0,LEffective -pipe=out \
                         | sdds2stream -column=LEffective -pipe} L] {
                return -code error "$L"
            }
            set dBL [expr $BLNeeded-$B*$L]
            if [expr abs($dBL)<1e-10] {
                break
            }
            incr tries -1
        }
        if $tries==0 {
            set percentError [expr 100.0*$dBL/$BLNeeded]
            APSSetVarAndUpdate status "Warning: convergence error of [format %.3g $percentError] % for $element"
        }
        if [catch {exec sddsinterp $excitation /dev/null -atValue=$B \
                     -column=B,Current -print=bare,stdout} result] {
            return -code error "$result"
        }
        set IMMF [lindex $result 1]
        # Now that I have the MMF current, compute the CurrentAO.
        if [catch {exec sddsprocess $apsLinacMagnetsDir/PSCal.sdds -pipe=out -match=col,PSName=$PSName \
                     | sdds2stream -column=CalSlope,CalIntercept -pipe} result] {
            return -code error "$result"
        }
        APSSetVarsFromList -variableList {Slope Intercept} -valueList $result
        set CurrentAO [expr $IMMF*$Slope+$Intercept]
        # APSSetVarAndUpdate status "CurrentAO = $CurrentAO   IMMF = $IMMF"
        return [list $CurrentAO $IMMF]
    } else {
        # This magnet has a trim, for which I calculate the setpoint here.
        # 1. Find BL for the main current and angle (both passed as argument).
        # 2. Find trim current to get the desired B*L
        if [catch {exec sddsinterp $excitation /dev/null -atValue=$trimForCurrent \
                     -column=Current,B -print=bare,stdout} result] {
            return -code error "$result"
        }
        set BMain [lindex $result 1]
        if [catch {exec sddscombine -merge $data -pipe=out \
                     | sddssort -pipe -column=B0 -column=angle \
                     | sddsbreak -pipe -change=B0 \
                     | sddsinterp -pipe -column=angle,B0,LEffective -atValue=$angle \
                     | sddscombine -merge -pipe \
                     | sddssort -column=B0 -pipe \
                     | sddsinterp -pipe -column=B0,LEffective -atValue=$BMain \
                     | sdds2stream -pipe -column=LEffective} result] {
            return -code error "$result"
        }
        set LEffective $result
        # APSSetVarAndUpdate status "BMain = $BMain   L = $LEffective   BLMain = [expr $BMain*$LEffective]   MainCurrent=$trimForCurrent"
        set dB [expr (abs($BLNeeded)-abs($BMain*$LEffective))/$LEffective]
        # APSSetVarAndUpdate status "dB = $dB"
        if [catch {exec sddsinterp $apsLinacMagnetsDir/L3:BMT.calibration -pipe=out \
                     -column=MainCurrent,Slope -atValue=$trimForCurrent \
                     -above=extrap -below=extrap \
                     | sdds2stream -pipe -column=Slope} slope] {
            return -code error "$slope"
        }
        # APSSetVarAndUpdate status "slope = $slope"
        return [expr $dB/$slope]
    }
}

set apsRestoreLinacSetpointFileCycles -1
set apsRestoreLinacSetpointFraction 1.0
proc APSRestoreLinacSetpointFile {args} {
    global apsLinacMagnetsDir apsLinacPSDeviceFile apsLEUTLPSDeviceFile
    global apsRestoreLinacSetpointFraction
    set fileName ""
    set statusCallback APSNoOp
    set condition 1
    set baseline ""
    APSStrictParseArguments {fileName statusCallback condition baseline}

    if {[string length $baseline] && ![file exists $baseline]} {
        return -code error "APSRestoreLinacSetpointFile: $baseline not found."
    }

    set action Restore
    if $condition {
        set action Condition
    }
    if [string compare [set outDir [file dirname $fileName]] .]==0 {
        set outDir [pwd]
    }

    eval $statusCallback "Working..."
    if {![string length $fileName] || ![file exists $fileName]} {
        return -code error "APSRestoreLinacSetpointFile: Give a valid filename"
    }
    set tmpRoot /tmp/[APSTmpString]
#    if $condition {
#        APSAddToTmpFileList -ID APSRestoreLinacSetpointFile -fileList $tmpRoot.data
#        if [catch {exec sddsprocess $fileName $tmpRoot.data \
#                     -match=col,ElementName=*Pos,!} result] {
#            return -code error "APSRestoreLinacSetpointFile: $result"
#        }
#        set fileName $tmpRoot.data
#    }

    # Present a dialog to allow choosing which beamlines to restore.
    APSAddToTmpFileList -ID APSRestoreLinacSetpointFile -fileList "$tmpRoot.snap $tmpRoot.snap0 $tmpRoot.snap1 $tmpRoot.snap2 $tmpRoot.dev $tmpRoot.std $tmpRoot.deg $tmpRoot.L3BMT"
    if [catch {exec sddscombine $apsLinacPSDeviceFile $apsLEUTLPSDeviceFile -merge \
                 $tmpRoot.dev -overwrite} result] {
        APSDeleteTmpFileList -ID APSRestoreLinacSetpointFile
       return -code error "APSRestoreLinacSetpointFile: $result"
    }
    if [catch {exec sddssort $fileName -pipe=out -column=Beamline -unique \
                 | sdds2stream -pipe -column=Beamline} beamlineList] {
        return -code error "APSRestoreLinacSetpointFile: $beamlineList"
    }
    set w [APSUniqueName .]
    APSDialogBox $w -name "$action Dialog" \
      -okCommand "set apsRestoreLinacSetpointFile 1" \
      -cancelCommand "set apsRestoreLinacSetpointFile 0" 

    global apsRestoreLinacSetpointFile 
    set apsRestoreLinacSetpointFile -1
    set totalElemList ""
    foreach beamline $beamlineList {
        if [catch {exec sddsprocess $fileName -pipe=out -match=column,Beamline=$beamline \
                     | sddssort -pipe -column=ElementName \
                     | sdds2stream -pipe -column=ElementName} elementList] {
            return -code error "APSRestoreLinacSetpointFile: $elementList"
        }
        set varList ""
        foreach element $elementList {
            global selectedForRestore$element
            set selectedForRestore$element 1
            lappend varList selectedForRestore$element
            lappend totalElemList $element
        }
        APSCheckButtonFrame .cb$beamline -parent $w.userFrame -label "Beamline $beamline: " \
          -orientation horizontal -allNone 1 \
          -variableList $varList -buttonList $elementList
    }
    if [string length $baseline] {
        global apsRestoreLinacSetpointFraction
        set apsRestoreLinacSetpointFraction 1.0
        APSLabeledEntry .fraction -parent $w.userFrame -label "Fraction to install: " \
          -textVariable apsRestoreLinacSetpointFraction -width 10 \
          -contextHelp "Fraction of new configuration to install, relative to baseline file $baseline"
    }
    if $condition {
        # Also, allow choosing the number of conditioning cycles
        global apsRestoreLinacSetpointFileCycles
        APSLabeledEntry .cycles -parent $w.userFrame -label "Conditioning Cycles (-1=use default)" \
          -textVariable apsRestoreLinacSetpointFileCycles -width 10 \
          -contextHelp "Enter the number of conditioning cycles.  If 0, then the number of cycles is left at the present value for each supply."
    }
    update
    tkwait variable apsRestoreLinacSetpointFile

    if $apsRestoreLinacSetpointFile {
        if {[string length $baseline] && [expr $apsRestoreLinacSetpointFraction<1.0] && \
              [expr $apsRestoreLinacSetpointFraction>0.0]} {
            set tmpFile /tmp/[APSTmpString]
            APSAddToTempFileList $tmpFile
            if [catch {exec sddsxref $fileName -pipe=out $baseline -match=ControlName \
                         -take=Current -rename=column,Current=Current0 \
                         | sddsprocess -pipe=in $tmpFile \
                         -scan=column,Value,ValueString,%f,type=float \
                         "-define=column,NewValue,Value Current0 - $apsRestoreLinacSetpointFraction * Current0 +" \
                         "-reprint=column,ValueString,%f,NewValue"} result] {
                return -code error "APSRestoreLinacSetpointFile: $result"
            }
            set fileName $tmpFile
        }

        # filter the file to get just the elements of interest
        set restoreList ""
        set matchOptCN ""
        set matchOptEN ""
        set matchOptDN ""
        foreach element $totalElemList {
            if ![set selectedForRestore${element}] continue
            lappend restoreList $element
            if ![string length $matchOptCN] {
                set matchOptCN -match=column,ControlName=${element}*
                set matchOptEN -match=column,ElementName=$element
                set matchOptDN -match=column,DeviceName=$element
            } else {
                set matchOptCN $matchOptCN,ControlName=${element}*,|
                set matchOptEN $matchOptEN,ElementName=$element,|
                set matchOptDN $matchOptDN,DeviceName=$element,|
            }
        }
        if ![string length $matchOptCN] {
            eval $statusCallback {"Choose one or more elements."}
            APSDeleteTmpFileList -ID APSRestoreLinacSetpointFile
            return
        }
        set logParams "$action: [join $outDir $fileName] [join $elementList ,]"
        APSLogScriptAction -procedure APSRestoreLinacSetpointFile -parameters $logParams \
          -action SettingUp -state Ok

        if $condition {
            if [catch {exec sddsprocess $fileName $tmpRoot.snap -delete=column,ControlName \
                         $matchOptEN -nowarning \
                         -print=param,SnapType,Absolute \
                         -print=col,ControlType,pv \
                         -print=col,Lineage,- -define=col,Count,1,type=long} result] {
                APSDeleteTmpFileList -ID APSRestoreLinacSetpointFile
                APSLogScriptAction -procedure APSRestoreLinacSetpointFile -parameters $logParams \
                  -action "Making device list" -state $result
                return -code error "1: $result"
            }
            if [catch {exec sddsprocess $tmpRoot.dev $matchOptDN -nowarning} result] {
                APSDeleteTmpFileList -ID APSRestoreLinacSetpointFile
                APSLogScriptAction -procedure APSRestoreLinacSetpointFile -parameters $logParams \
                  -action "Making device list" -state $result
                return -code error "1.1: $result"
            }
            ## Stop conditioning in case it is running 
            if [catch {exec sddsprocess $tmpRoot.dev -pipe=out \
                         -match=column,Operation=StopConditioning \
                         | sddsxref -pipe=in $tmpRoot.snap $tmpRoot.snap0 -nowarning \
                         -match=DeviceName=ElementName -take=* -transfer=param,*
                exec sddscasr -restore $tmpRoot.snap0} result] {
                APSLogScriptAction -procedure APSRestoreLinacSetpointFile -parameters $logParams \
                  -action "Halting conditioning" -state $result
                APSDeleteTmpFileList -ID APSRestoreLinacSetpointFile
                return -code error "2: $result"
            }
            if {[lsearch -exact $totalElemList L3:BM2]!=-1 || \
                  [lsearch -exact $totalElemList L3:BM4]!=-1} {
                ## set the dipole trims
                if [catch {exec sddsprocess $fileName $tmpRoot.L3BMT \
                             $matchOptEN \
                             -print=param,SnapType,Absolute \
                             -print=col,ControlType,pv \
                             -print=col,Lineage,- -define=col,Count,1,type=long \
                             -match=col,ControlName=L3:BM?T* -nowarning} result] {
                    APSLogScriptAction -procedure APSRestoreLinacSetpointFile -parameters $logParams \
                      -action "Setting dipole trims" -state $result
                    APSDeleteTmpFileList -ID APSRestoreLinacSetpointFile
                    return -code error "3: $result"
                }
                if {[exec sdds2stream -rows=bare $tmpRoot.L3BMT]!=0 && \
                      [catch {exec sddscasr -restore $tmpRoot.L3BMT} result]} {
                    APSDeleteTmpFileList -ID APSRestoreLinacSetpointFile
                    APSLogScriptAction -procedure APSRestoreLinacSetpointFile -parameters $logParams \
                      -action "Setting dipole trims" -state $result
                    return -code error "4: $result"
                }
            }
            if {[lsearch -exact $totalElemList L3:BM3Pos]!=-1 || \
                [lsearch -exact $totalElemList L3:BM4Pos]!=-1} {
                ## move the chicane
                if [catch {exec sddsprocess $fileName $tmpRoot.chicane \
                             -match=col,ElementName=L3:BM?Pos \
                             -print=param,SnapType,Absolute \
                             -print=col,ControlType,pv \
                             -print=col,Lineage,- -define=col,Count,1,type=long \
                             -nowarning} result] {
                    APSDeleteTmpFileList -ID APSRestoreLinacSetpointFile
                    return -code error "4.1: $result"
                }
                if {[exec sdds2stream -rows=bare $tmpRoot.chicane]!=0 && \
                      [catch {exec sddscasr -restore $tmpRoot.chicane} result]} {
                    APSDeleteTmpFileList -ID APSRestoreLinacSetpointFile
                    return -code error "4.2 $result"
                }
            }

            ## Configure
            # Make standardize and degauss files for Linac->LEUTL.  This is for
            # the supplies with standard record names.
            APSMakeInjectorConditioningFile -output $tmpRoot.std -cycles $apsRestoreLinacSetpointFileCycles \
              -sectorList {L1 L2 L3 L4 L5 LTP PB PTB BB LU LA LS}
            if [catch {exec sddsprocess $tmpRoot.std $matchOptCN -nowarning} result] {
                return -code error "4.1: $result"
            }
            APSMakeInjectorConditioningFile -degauss 1 -output $tmpRoot.deg -cycles $apsRestoreLinacSetpointFileCycles \
              -sectorList {L1 L2 L3 L4 L5 LTP PB PTB BB LU LA LS}
            if [catch {exec sddsprocess $tmpRoot.deg $matchOptCN -nowarning} result] {
                return -code error "4.2: $result"
            }

            if [catch {exec sdds2stream -rows=bare $tmpRoot.std} result] {
                APSDeleteTmpFileList -ID APSRestoreLinacSetpointFile
                return -code error "4.3: $result"
            }
            if {$result!=0 && [catch {exec standardize -configure $tmpRoot.std} result]} {
                APSDeleteTmpFileList -ID APSRestoreLinacSetpointFile
                return -code error "5: $result"
            }
            # Send back conditioning defaults all linac devices with aberrant conditioning
            # record names
            set conditioningDir /home/helios/OAG/oagData/controlFiles/Linac/conditioning
            if {[catch {exec sddscasr -restore $conditioningDir/linacConditioning.snap} result]} {
                APSDeleteTmpFileList -ID APSRestoreLinacSetpointFile
                return -code error "6: $result"
            }
            # Set the number of conditioning cycles for all devices
            if {$apsRestoreLinacSetpointFileCycles>=0 && \
                  [catch {exec sddsprocess $tmpRoot.dev -pipe=out \
                            -match=column,Operation=SetConditioningCycles \
                            | sddsxref -pipe $tmpRoot.snap -nowarning \
                            -match=DeviceName=ElementName -take=* -transfer=param,* \
                            | sddsconvert -pipe -dele=col,ValueString \
                            | sddsprocess -pipe=in $tmpRoot.snap0 \
                            -print=col,ValueString,$apsRestoreLinacSetpointFileCycles
                      exec sddscasr -restore $tmpRoot.snap0} result]} {
                APSDeleteTmpFileList -ID APSRestoreLinacSetpointFile
                return -code error "7: $result"
            }

            ## send new endpoints 
            if [catch {exec sddsprocess $tmpRoot.dev -pipe=out -nowarning \
                         -dele=col,ValueString \
                         -match=column,Operation=SetConditioningEndpoint \
                         | sddsxref -pipe=in $tmpRoot.snap $tmpRoot.snap1 -nowarning \
                         -match=DeviceName=ElementName -take=* -transfer=param,*} result] {
                APSDeleteTmpFileList -ID APSRestoreLinacSetpointFile
                return -code error "8: $result"
            }
            if [exec sdds2stream -rows=bare $tmpRoot.snap1]!=0 {
                if [catch {exec sddscasr -restore $tmpRoot.snap1} result] {
                    APSDeleteTmpFileList -ID APSRestoreLinacSetpointFile
                    return -code error "8a: $result"
                }
            }

            ## start conditioning
            # exclude L3:BM trims (L3:BM2 and L3:BM4)
            if [catch {exec sddsprocess $tmpRoot.dev -pipe=out \
                         -match=column,Operation=StartConditioning \
                         -match=column,DeviceName=L3:BM2,DeviceName=L3:BM4,|,! \
                         | sddsxref -pipe=in $tmpRoot.snap $tmpRoot.snap2 -nowarning \
                         -match=DeviceName=ElementName -take=* -transfer=param,*
                exec sddscasr -restore $tmpRoot.snap2} result] {
                APSDeleteTmpFileList -ID APSRestoreLinacSetpointFile
                return -code error "9: $result"
            }

            eval $statusCallback {"Conditioning initiated."}
            APSLogScriptAction -action APSRestoreLinacSetpointFile -parameters $logParams \
              -state conditioning

            set deviceList [exec sddsprocess $tmpRoot.snap -pipe=out \
                              -match=col,ElementName=L3*Pos,! \
                              | sdds2stream -pipe=in -column=ElementName ]
            set conditioning 1
            # wait for conditioning to complete
            set timeStarted [clock format [clock seconds] -format %H:%M:%S]
            while {$conditioning} {
                APSWaitWithUpdate -waitSeconds 15 -updateInterval 1
                eval $statusCallback {"Conditioning (since $timeStarted)..."}
                if [catch {APSDevSend -deviceList $deviceList -operation QueryConditioning \
                             -group LinacPS} result] {
                    eval $statusCallback {"Problem checking conditioning status."}
                    return -code error "10: $result"
                }
                set conditioning 0
                foreach item $result {
                    if $item {
                        set conditioning 1
                    }
                }
            }
        }
        APSLogScriptAction -action APSRestoreLinacSetpointFile -parameters $logParams \
          -state restoring
        # restore setpoints (necessary for degaussed supplies, prudent for
        # others).
        eval $statusCallback {"Restoring values."}
        if [catch {exec sddsprocess $fileName $tmpRoot.snap \
                     $matchOptEN \
                     -print=param,SnapType,Absolute \
                     -print=col,ControlType,pv \
                     -print=col,Lineage,- -define=col,Count,1,type=long
            exec sddscasr -restore $tmpRoot.snap} result] {
            APSDeleteTmpFileList -ID APSRestoreLinacSetpointFile
            return -code error "11: $result"
        }
        eval $statusCallback {"Restore completed."}
        APSLogScriptAction -action APSRestoreLinacSetpointFile -parameters $logParams \
          -state done
    } else {
        eval $statusCallback {"$action cancelled."}
    }
    APSDeleteTmpFileList -ID APSRestoreLinacSetpointFile
}

