#!/bin/sh
# \
exec oagwish "$0" "$@"

#
# $Log: not supported by cvs2svn $
# Revision 1.4  2003/12/18 17:40:08  borland
# Added control of the number of simplex restarts.
#
# Revision 1.3  2003/12/18 15:16:43  borland
# Improved some context help information.
#
# Revision 1.2  2003/11/18 00:52:02  borland
# Added more starting point options.
#
# Revision 1.1  2003/11/13 18:13:27  borland
# First version, not quite ready for release.
#
#

set auto_path [linsert $auto_path 0  /usr/local/oag/apps/lib/$env(HOST_ARCH)]
set auto_path [linsert $auto_path 0  /usr/local/oag/lib_patch/$env(HOST_ARCH)]
APSDebugPath
set CVSRevisionAuthor "\$Author: borland $"

APSApplication . -name LinacToBCRematch \
  -overview "Rematches L1, L2, and L3 quadrupoles based on a measurement of the emittance with the BC three-screen system."

proc setStatus {text} {
    APSSetVarAndUpdate status "[clock format [clock seconds] -format %H:%M:%S]: $text"
}

setStatus "Working..."
APSScrolledStatus .status -parent .userFrame -textVariable status -height 10 -width 85
update

set mainDir [APSGoToDailyDirectory]
set mainRootname ""
set initialMomentum 30.0
set finalMomentum  150.0
set emittanceMeasurementDir $mainDir
set emittanceMeasurementName ""
set useLatestMeasurement 1
set referenceConfig 200pC-30MeV-150MeV-dZ60-B4Nom
set simplexRestarts 0

set startingPoint L2AS2I
set startingPointList {L1AS1O RG2AM L2AS1I L2AS2I L2AS3I L2AS4I L2AS4O }
set startingPointDescriptionList {"L1AS1Out" "RG2AlphaMag" "L2AS1In" "L2AS2In" 
  "L2AS3In" "L2AS4In" "L2AS4Out" }

set maxBetax 30.0
set maxBetay 30.0

set mimInitialValue -1.5
set mimFinalValue 1.5
set mimPointsToAverage 20
set mimSettings 7
set mimPostChangePause 10
set mimIntermeasurementPause 0.5
set mimPixelsPerMM 9.08

proc FillMomentumProfileFrame {widget} {
    APSLabeledEntry .md -parent $widget -label "Main directory: " \
      -textVariable mainDir -width 60 \
      -contextHelp "Allows entry of the main directory for output files."
    APSButton .pick -parent $widget.md -text P -command \
      {set mainDir0 [APSFileSelectDialog [APSUniqueName .] -title MainDir -selectDir 1 -pattern * -path $mainDir]; if [string length $mainDir0] {set mainDir $mainDir0} }

    APSLabeledEntry .mr -parent $widget -label "Main rootname: " \
      -textVariable mainRootname -width 60 \
      -contextHelp "Allows entry of the main rootname for output files." 

    APSLabeledEntry .im -parent $widget -label "Momentum at L2 entrance: " \
      -textVariable initialMomentum -width 60 \
      -contextHelp "Holds the result of momentum measurement at L2 entrance. Also allows manual entry of the momentum."
    APSLabeledEntry .fm -parent $widget -label "Momentum at L2 exit: " \
      -textVariable finalMomentum -width 60 -contextHelp \
      "Allows entry of the momentum at the exit of L2."

    APSButton .mim -parent $widget -text "Measure momentum at L2 entrance" \
      -command MeasureInitialMomentum -contextHelp \
      "Invokes script to measure the momentum at the entrance to L2"
}

proc MeasureInitialMomentum {} {
    set w [APSUniqueName .]
    global mimDialogDone
    global mimInitialValue mimFinalValue mimPointsToAverage 
    global mimSettings 
    global mimPixelsPerMM 
    global mainDir mainRootname

    if {![string length $mainDir] || ![string length $mainRootname]} {
        return -code error "Set main directory and rootname first!"
    }

    set mimDialogDone 0
    APSDialogBox $w \
      -name "L1 Momentum Measurement Setup" \
      -okCommand "set mimDialogDone 1" \
      -cancelCommand "set mimDialogDone 2" 
    set w $w.userFrame
    APSLabeledEntry .initial -parent $w -label "Initial corrector value: " \
      -textVariable mimInitialValue -width 60
    APSLabeledEntry .final -parent $w -label "Final corrector value: " \
      -textVariable mimFinalValue -width 60
    APSLabeledEntry .settings -parent $w -label "Number of settings: " \
      -textVariable mimSettings -width 60
    APSLabeledEntry .points -parent $w -label "Points to average: " \
      -textVariable mimPointsToAverage -width 60
    APSLabeledEntry .pixelsPerMM -parent $w -label "Pixels per mm: " \
      -textVariable mimPixelsPerMM -width 60
    tkwait variable mimDialogDone
    if $mimDialogDone==2 return

    set mimOutputFile $mainRootname.PL2I
    if {[file exists $mainDir/$mainRootname.PL2I] && 
        [APSMultipleChoice [APSUniqueName .] \
           -question "$mainDir/$mimOutputFile exists" \
           -labelList "Overwrite Abort" -returnList "0 1"]} {
        return
    }
    if [catch {APSMeasureL1Momentum -output $mainDir/$mimOutputFile \
                 -initialValue $mimInitialValue \
                 -finalValue $mimFinalValue \
                 -pointsToAverage $mimPointsToAverage \
                 -settings $mimSettings \
                 -statusCallback setStatus -plot 1 -interactive 1} result] {
        return -code error "$result"
    }
    if ![file exists $mainDir/$mimOutputFile.fit] {
        return -code error "File $mainDir/$mimOutputFile.fit not found"
    }
    if [catch {exec sdds2stream -parameter=Momentum $mainDir/$mimOutputFile.fit} result] {
        return -code error "$result"
    }
    set initialMomentum $result
}

proc FillEmittanceMeasurementFrame {widget} {
    APSLabeledEntry .emdir -parent $widget -label "Directory: " \
      -textVariable emittanceMeasurementDir -width 60 -contextHelp \
      "Allows entry of the directory in which emittance measurement results may be found."
    APSButton .pick -parent $widget.emdir -text P -command \
      {set emittanceMeasurementDir0 [APSFileSelectDialog [APSUniqueName .] -pattern * -title MeasurementDir -selectDir 1 -path [file dirname $emittanceMeasurementDir]]; if [string length $emittanceMeasurementDir0] {set emittanceMeasurementDir $emittanceMeasurementDir0}}
    APSLabeledEntry .emname -parent $widget -label "Filename: " \
      -textVariable emittanceMeasurementName -width 60 -contextHelp \
      "Allows entry of the filename for the emittance measurement to use in matching.  Disabled if you elect to use the latest measurement."
    APSButton .pick -parent $widget.emname -text P -command \
      {set emittanceMeasurementName0 [file tail [APSFileSelectDialog [APSUniqueName .] -title PickEmittanceMeasurement -pattern *.results -path $emittanceMeasurementDir]]; if [string length $emittanceMeasurementName0] {set emittanceMeasurementName $emittanceMeasurementName0}}
    APSDisableWidget $widget.emname
    APSRadioButtonFrame .useLatest -parent $widget -packOption "-side left" \
      -label "Use latest measurement: " -variable useLatestMeasurement -limitPerRow 4 \
      -buttonList "Yes No" -valueList "1 0" -orientation horizontal \
      -commandList [list "APSDisableWidget $widget.emname" "APSEnableWidget $widget.emname"] \
      -contextHelp "Allows choosing to use the latest emittance measurement in the named directory, or manual entry of the measurement to use."
    APSButton .view -parent $widget -text View -command ViewEmittanceMeasurement
}

proc ViewEmittanceMeasurement {} {
    global emittanceMeasurementDir emittanceMeasurementName useLatestMeasurement
    if {[string length $emittanceMeasurementDir] && $useLatestMeasurement} {
        FindLatestEmittanceMeasurement
    }
    set tmpFile /tmp/[APSTmpString]
    exec sddsexpand $emittanceMeasurementDir/$emittanceMeasurementName \
      -pipe=out \
      | sddsprintout -pipe=in $tmpFile \
      -format=double=%10.3f,float=%10.3f \
      -parameter=betaxMean -parameter=betaxSigma,end \
      -parameter=alphaxMean -parameter=alphaxSigma,end \
      -parameter=xMismatchMean -parameter=xMismatchSigma,end \
      -parameter=betayMean -parameter=betaySigma,end \
      -parameter=alphayMean -parameter=alphaySigma,end \
      -parameter=yMismatchMean -parameter=yMismatchSigma,end \
      -parameter=exNormMean -parameter=exNormSigma,end \
      -parameter=eyNormMean -parameter=eyNormSigma,end 
    APSFileDisplayWindow [APSUniqueName .] \
      -comment "Emittance measurement $emittanceMeasurementDir/$emittanceMeasurementName " \
      -fileName $tmpFile -deleteOnClose 1 -width 80 
}

proc FindLatestEmittanceMeasurement {} {
    global emittanceMeasurementDir emittanceMeasurementName useLatestMeasurement
    if [llength [set measurementList [lsort [glob -nocomplain $emittanceMeasurementDir/*.results]]]]==0 {
        return -code error "No measurements found in $emittanceMeasurementDir"
    }
    set mtime0 0
    foreach file $measurementList {
        if [file mtime $file]>$mtime0 {
            set emittanceMeasurementName [file tail $file]
            set mtime0 [file mtime $file]
            }
    }
    setStatus "Using emittance measurement $emittanceMeasurementName"
}

proc FillMatchingFrame {widget} {
    APSLabeledEntry .md -parent $widget -label "Main directory: " \
      -textVariable mainDir -width 60 \
      -contextHelp "Allows entry of the main directory for output files."
    APSButton .pick -parent $widget.md -text P -command \
      {set mainDir0 [APSFileSelectDialog [APSUniqueName .] -title MainDir -selectDir 1 -pattern * -path $mainDir]; if [string length $mainDir0] {set mainDir $mainDir0} }

    APSLabeledEntry .mr -parent $widget -label "Main rootname: " \
      -textVariable mainRootname -width 60 \
      -contextHelp "Allows entry of the main rootname for output files." 

    APSLabeledEntry .refconfig -parent $widget -label "Reference configuration: " \
      -textVariable referenceConfig -width 60 -contextHelp \
      "Name of the reference configuration for the optics.  This file is only used to determine basic information such as the length of quadrupoles."

    APSLabeledEntry .maxbetax -parent $widget -label "Desired maximum betax (m): " \
      -textVariable maxBetax -width 60 -contextHelp \
      "Value of maximum desired horizontal beta function.  If too small, matching may fail."
    APSLabeledEntry .maxbetay -parent $widget -label "Desired maximum betay (m): " \
      -textVariable maxBetay -width 60 -contextHelp \
      "Value of maximum desired vertical beta function.  If too small, matching may fail."
    APSLabeledEntry .simplexRestarts -parent $widget -label "Simplex restarts: " \
      -textVariable simplexRestarts -width 60 -contextHelp \
      "The number of restarts allowed for the simplex matching algorithm.  If matching is poor, try increasing this value.  The running time is proportional to the value."

    global startingPointList startingPointDescriptionList
    APSRadioButtonFrame .rbf -parent $widget -label "Starting point: " \
      -variable startingPoint -limitPerRow 4 \
      -orientation horizontal \
      -buttonList $startingPointDescriptionList \
      -valueList $startingPointList \
      -contextHelp \
      "Select the starting point for matching.  Quadrupoles downstream of this point will be used for matching.  Note that for PC gun matching, starting at RG2 alpha magnet is not advised because the matching may not allow the L1:QM magnets to have the desired strength."

    APSButton .match -parent $widget -text "Compute match" \
      -command ComputeMatch  -contextHelp \
      "Runs matching simulation to compute new setpoints."

    APSButton .install -parent $widget -text "Install match" \
      -command InstallMatch -contextHelp \
      "Standardizes to the new setpoints."

    APSButton .view -parent $widget -text "View match" \
      -command ViewMatch -contextHelp \
      "Standardizes to the new setpoints."
}

proc ComputeMatch {} {
    global mainDir mainRootname
    global referenceConfig initialMomentum finalMomentum emittanceMeasurementDir emittanceMeasurementName
    global useLatestMeasurement startingPoint maxBetax maxBetay
    global simplexRestarts
    if {![string length $mainDir] || ![string length $mainRootname]} {
        return -code error "Set main directory and rootname first!"
    }
    if {![string length $emittanceMeasurementDir] || \
          ![file isdir $emittanceMeasurementDir]} {
        return -code error "Emittance measurement directory is invalid"
    }
    if $useLatestMeasurement {
        FindLatestEmittanceMeasurement
    }
    if ![string length $emittanceMeasurementName] {
        return -code error "Choose an emittance measurement!"
    }
    # last two values are actually irrelevant
    set momentumProfile [list $initialMomentum $finalMomentum 238 325]
    if [catch {APSMatchFromMeasuredTwissBC -startingPoint $startingPoint \
                 -measurement $emittanceMeasurementDir/$emittanceMeasurementName \
                 -momentumProfile $momentumProfile -maxBetax $maxBetax -maxBetay $maxBetay \
                 -config $referenceConfig -plot 1 -statusCallback setStatus \
                 -rootname $mainDir/$mainRootname -simplexRestarts $simplexRestarts} result] {
        return -code error "$result"
    }
    setStatus "Matching completed."
}

proc ViewMatch {} {
    global mainDir mainRootname
    if {![string length $mainDir] || ![string length $mainRootname]} {
        return -code error "Set main directory and rootname first!"
    }
    if ![file exists $mainDir/$mainRootname.param] {
        return -code error "Didn't find $mainDir/$mainRootname.param.  Did you run the matching?"
    }
    if [catch {ConvertMatch} result] {
        return -code error "$result"
    }
    set tmpFile /tmp/[APSTmpString]
    APSAddToTempFileList $tmpFile
    if [catch {exec sddsprintout $mainDir/$mainRootname.setpoints $tmpFile \
                 -col=ElementName -col=ValueString,format=%22s -col=SupplyLimit,format=%10.4f \
                 "-title=Magnet settings from $mainDir/$mainRootname matching" \
                 -col=OverLimit} result] {
        return -code error "$result"
    }
    APSFileDisplayWindow [APSUniqueName .] \
      -fileName $tmpFile -width 100 -comment "Magnet settings" \
      -deleteOnClose 1
    exec sddsplot -same=x,y,global \
      -col=s,beta? $mainDir/$mainRootname.inittwi -graph=line,vary -uns=y -clip=2,0 \
      "-title=Inferred from measurement" "-topline=$mainDir/$mainRootname" -end \
      -col=s,beta? $mainDir/$mainRootname.twi -graph=line,vary -uns=y \
      "-title=Simulated after matching" "-topline=$mainDir/$mainRootname" -end \
      -col=s,Profile $mainDir/$mainRootname.mag -overlay=xmode=norm,yfact=0.04 -omnipresent &
}

proc ConvertMatch {} {
    global mainDir mainRootname
    if {[file exists $mainDir/$mainRootname.setpoints] && \
        [file mtime $mainDir/$mainRootname.setpoints]>[file mtime $mainDir/$mainRootname.param]} {
        return 
    }
    if [catch {APSGenerateLinacSetpointFile -rootname $mainDir/$mainRootname \
                 -output $mainDir/$mainRootname.setpoints \
                 -convertAll 0 -rescale 0 -includeChicane 0} result] {
        return -code error "Problem generating setpoints: $result"
    }
    # remove L4 and L5 stuff since it may not be right.
    # remove L3:BMs as they aren't involved in matching.
    # remove L3:QM[12] as they aren't involved in matching
    exec sddsprocess $mainDir/$mainRootname.setpoints -nowarning \
      -match=column,ControlName=L4*,ControlName=L5*,|,! \
      -match=column,ControlName=L3:BM*,! \
      -match=column,ControlName=L3:QM\[12\]*,!
    file delete $mainDir/$mainRootname.setpoints~
    return 
}

proc InstallMatch {} {
    global mainDir mainRootname
    if {![string length $mainDir] || ![string length $mainRootname]} {
        return -code error "Set main directory and rootname first!"
    }
    if ![file exists $mainDir/$mainRootname.param] {
        return -code error "Didn't find $mainDir/$mainRootname.param.  Did you run the matching?"
    }
    if ![file exists $mainDir/$mainRootname.measParam] {
        return -code error "Didn't find $mainDir/$mainRootname.measParam.  Did you run the matching?"
    }
    if [catch {ConvertMatch} result] {
        return -code error "$result"
    }
    if [catch {APSRestoreLinacSetpointFile -fileName $mainDir/$mainRootname.setpoints \
                 -baseline $mainDir/$mainRootname.measParam \
                 -statusCallback setStatus} result] {
        return -code error "Problem restoring setpoint file: $result"
    }
}

set tabFrameWidgetList [APSTabFrame .main -parent .userFrame -label "" \
                          -labelList {"Momentum Profile" "Emittance Measurement" "Matching"} \
                          -width 800 -height 315]

FillMomentumProfileFrame [lindex $tabFrameWidgetList 0]

FillEmittanceMeasurementFrame [lindex $tabFrameWidgetList 1]

FillMatchingFrame [lindex $tabFrameWidgetList 2]

set devFile /tmp/[APSTmpString]
set apsLinacPSDeviceFile /home/helios/oagData/deviceConfig/linac/PSOps.pvTable
set apsLEUTLPSDeviceFile /home/helios/oagData/deviceConfig/linac/LTPToEndPSOps.pvTable
exec sddscombine $apsLinacPSDeviceFile $apsLEUTLPSDeviceFile -merge -overwrite $devFile
APSLinkToDevicePVs -fileList $devFile

setStatus "Ready."
update


#
# Consider removing the alternating beta function contraints (optionally).
#
# Provide controls over the number of passes/evals/restarts.  A limited number
#  of optimization steps might allow smoothly matching over several iterations.
#
# Need a way to terminate optimization without terminating the whole run.
# May need a new button on APSExecLog to send kill -15.  Probably also need
# to have four separate runs, instead of one big run.
#
# Add gain feature to installation.
# Default to 0 conditioning cycles?
# 
