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

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

proc APSSetStatus {text} {
    global apsStatus
    set apsStatus "[clock format [clock seconds] -format %T]: $text"
    update
}

set n1 -1
set n2 -1
set Q1 2.8
set I1 80.0
set Q2 5.6
set I2 100.0
set srEff 90
set lifetime 1.0
set swapInterval 60.0
set droopLimit 100
set droop 0
set nTotal -1
set IAll -1
set IUltimate -1
set injectorEff 90
set qLinac 1.0
set qLinacMax 1.0
set qBtsMax 10.0
set qBts -1
set nbFixed 162
set ITarget 100
set npLinacFill -1
set npLinacSwap -1
set regulation 1

APSApplication . -name SRFillPlanner -version 1.0 \
  -overview {This application provides guidance in planning a storage ring fill for user beam or vacuum conditioning.}

set apsStatus "Ready."
APSScrolledStatus .record -parent .userFrame -width 90 -height 5 -textVariable apsStatus -lineLimit 1000

set widgetList [APSTabFrame .calcs -parent .userFrame -label "" \
    -labelList {"Fixed # Bunches" "Variable # Bunches" } -width 800 -height 400 -packOption "-expand yes -fill both"]

set w [lindex $widgetList 0]

APSLabeledEntry .eff -parent $w -textVariable srEff -label "Injection efficiency (%): " -width 22 \
                -contextHelp "Enter the injection efficiency."
APSLabeledEntry .lifetime -parent $w -textVariable lifetime -label "Beam lifetime (h): " -width 22 \
                -contextHelp "Enter the beam lifetime you expect at your target current."
APSLabeledEntry .n1 -parent $w -textVariable nbFixed -label "Number of bunches: " -width 22 \
                -contextHelp "The number of bunches to run for beam delivery."

APSLabeledEntry .itarget -parent $w -textVariable ITarget -label "Target current (mA): " -width 22 \
                -contextHelp "Enter the current you want to run."
APSLabeledEntry .regulation -parent $w -textVariable regulation -label "Target current regulation (%): " -width 22 \
                -contextHelp "Enter the desired level of current regulation"

APSLabeledEntry .etainj -parent $w -textVariable injectorEff -label "Linac-to-BTS efficiency (%): " -width 22 \
  -contextHelp "Enter the charge-transport efficiency from the linac to the BTS"
APSLabeledEntry .qlinacmax -parent $w -textVariable qLinacMax -label "Maximum linac charge/pulse (nC): " -width 22 \
  -contextHelp "The maximum available charge of the linac bunches."
APSLabeledEntry .qbtsmax -parent $w -textVariable qBtsMax -label "Maximum BTS charge/shot (nC): " -width 22 \
  -contextHelp "The maximum available charge of the linac bunches."
APSLabeledEntry .droopLimit -parent $w -textVariable droopLimit -label "Bunch charge drop before replacement (%)" -width 22 \
  -contextHelp "The maximum desired variation in bunch charge in the ring."
APSLabeledOutput .qlinac -parent $w -textVariable qLinac -label "Linac charge/pulse (nC): " -width 22 \
  -contextHelp "The charge of the linac bunches."
APSLabeledOutput .npFill -parent $w -textVariable npLinacFill -label "Linac pulses for filling: " -width 22 \
  -contextHelp "The number of linac pulses for filling from zero."
APSLabeledOutput .npSwap -parent $w -textVariable npLinacSwap -label "Linac pulses for swap-out operation: " -width 22 \
  -contextHelp "The number of linac pulses for swap-out operation."
APSLabeledOutput .qbts -parent $w -textVariable qBts -label "BTS charge/cycle (nC): " -width 22 \
  -contextHelp "The charge of the BTS pulses."
APSLabeledOutput .swap -parent $w -textVariable swapInterval -label "Swap-out interval (s): " -width 22 \
  -contextHelp "The swap-out interval after 1 Hz filling."
APSLabeledOutput .droop -parent $w -textVariable droop -label "Bunch charge droop (%): " -width 22 \
  -contextHelp "The drop in bunch charge between swapping of a particular bunch."

APSButton .run -parent $w -command ComputeFixedNumberOfBunches -text "Compute"

set w [lindex $widgetList 1]
APSLabeledEntry .eff -parent $w -textVariable srEff -label "Injection efficiency (%): " -width 22 \
                -contextHelp "Enter the injection efficiency."
APSLabeledEntry .lifetime -parent $w -textVariable lifetime -label "Beam lifetime (h): " -width 22 \
                -contextHelp "Enter the beam lifetime you expect at your target current."

APSLabeledEntry .q1 -parent $w -textVariable Q1 -label "Bunch charge for 1 Hz filling (nC): " -width 22 \
                -contextHelp "Enter the charge of the bunches you'll fill at 1 Hz."
APSLabeledEntry .i1 -parent $w -textVariable I1 -label "Current after 1 Hz filling (mA): " -width 22 \
                -contextHelp "Enter the current you want after the initial 1 Hz filling."
APSLabeledOutput .n1 -parent $w -textVariable n1 -label "Number of bunches for 1 Hz filling: " -width 22 \
                -contextHelp "The number of bunches you'll need to fill at 1 Hz."

APSLabeledEntry .swap -parent $w -textVariable swapInterval -label "Swap-out interval (s): " -width 22 \
                -contextHelp "Enter the swap-out interval you'll use after 1 Hz filling."
APSLabeledEntry .q2 -parent $w -textVariable Q2 -label "Bunch charge for swap-out shots (nC): " -width 22 \
                -contextHelp "Enter the charge of the bunches you'll use for swap-out."
APSLabeledEntry .i2 -parent $w -textVariable I2 -label "Desired current after filling all bunches (mA): " -width 22 \
                -contextHelp "Enter the current you want after all bunches are filled."
APSLabeledOutput .n2 -parent $w -textVariable n2 -label "Number of bunches empty after 1 Hz filling: " -width 22 \
                -contextHelp "The number of bunches you'll need to fill using swap-out."

APSLabeledOutput .ntotal -parent $w -textVariable nTotal -label "Total number of bunches: " -width 22 \
                -contextHelp "The number of bunches you'll need to fill using swap-out."
APSLabeledOutput .iAll -parent $w -textVariable IAll -label "Expected current after filling all bunches (mA): " -width 22 \
                -contextHelp "Shows the expected current after all bunches are filled."
APSLabeledOutput .iUltimate -parent $w -textVariable IUltimate -label "Expected current in long term (mA): " -width 22 \
                -contextHelp "Shows the expected current after a long time as passed."

APSButton .run -parent $w -text Compute -command "Compute"

proc ComputeFixedNumberOfBunches {} {
    global srEff lifetime swapInterval injectorEff qLinac nbFixed ITarget npLinacFill npLinacSwap regulation qLinacMax qBtsMax
    global droop droopLimit qBts
    set npLinacFill -1
    set npLinacSwap -1
    set qLinac -1
    set tau [expr 3600.0*$lifetime]
    if [expr $droopLimit<100] {
        set swapInterval [expr int(-$tau*log(1.0-$droopLimit/100.0)/$nbFixed)]
    } else {
        set swapInterval [expr int($regulation/100.0*$tau)]
    }
    while {[expr $swapInterval>0]} {
        set qTarget [expr $ITarget*3.68/$nbFixed]
        if $swapInterval<1 {
            break
        }
        set F [expr $nbFixed*(1-exp(-$swapInterval/$tau)) + exp(-($nbFixed/2.0+$swapInterval)/$tau)]
        set dFBest 1e300
        set nsBest -1
        set nfBest -1
        set totalEff [expr $srEff/100.0*$injectorEff/100.0]
        for {set ns 1} {$ns<21} {incr ns} { # Loop over number of possible linac pulses/cycle for swapout
            for {set nf 1} {$nf<=$ns} {incr nf} { # Loop over number of possible linac pulses/cycle for filling
                set qLinacTrial [expr $qTarget/$totalEff/$nf]
                if {[expr $qLinacTrial>$qLinacMax] || [expr $qLinacTrial<0.5]} continue
                set qBtsTrial [expr $ns*$qLinacTrial*($injectorEff/100.0)]
                if [expr $qBtsTrial>$qBtsMax] continue
                set dF [expr (1.0*$ns)/$nf-$F]
                if {[expr $dF>0] && [expr $dF<$dFBest]} {
                    set dFBest $dF
                    set nsBest $ns
                    set nfBest $nf
                }
            }
        }
        set npLinacFill $nfBest
        set npLinacSwap $nsBest
        #APSSetStatus "npLinacFill=$npLinacFill npLinacSwap=$npLinacSwap"
        set qLinac [expr $qTarget/$totalEff/$npLinacFill]
        set qBts [expr $qLinac*$npLinacSwap*($injectorEff/100.0)]
        if {[expr $npLinacFill==-1]} {
            APSSetStatus "No solution for swap-out interval of $swapInterval. Reducing the swap-out interval."
            incr swapInterval -1
        } else {
            break
        }
    }
    if {[expr $npLinacFill==-1]} {
        APSSetStatus "No solution found. Possible solutions:"
        APSSetStatus "Increase injection efficiency, increase injector efficiency,"
        APSSetStatus "Increase BTS charge limit, increase linac charge,"
        APSSetStatus "increase number of bunches, decrease the desired current."
    } else {
        set droop [expr 100*(1-exp(-$nbFixed*$swapInterval/$tau))]
        APSSetStatus "F=[expr $nbFixed*(1-exp(-$swapInterval/$tau)) + exp(-($nbFixed/2.0+$swapInterval)/$tau)]"
    }
}


proc Compute  {} {
    global n1 n2 Q1 Q2 I1 I2 srEff lifetime swapInterval nTotal IAll IUltimate

    set T0 3.68

    APSSetStatus "Running."
    
    set n1 [expr int($I1*$T0/$Q1/($srEff/100.0))]
    set iterations 10
    set r [expr exp(-1.0/(3600*$lifetime))]
    while {$iterations>0} {
        set R [expr (1.0-pow($r,$n1))/(1.0-$r)/$n1]
        set n1New [expr int($I1*$T0/$Q1/($srEff/100.0)/$R)]
        if {$n1New==$n1} {
            break
        }
        set n1 $n1New
        incr iterations -1
    }
    APSSetStatus "Need $n1 initial bunches at 1Hz"
    
    set r [expr exp(-$swapInterval/(3600*$lifetime))]
    set bestN2 -1
    set bestdI2 1e10
    for {set n2t [expr 324-$n1]} {$n2t>0} {incr n2t -1} {
        set If [expr ($Q1*$R*$n1*pow($r,$n2t) + $Q2*(1-pow($r,$n2t))/(1.0-$r))/$T0*($srEff/100.0)]
        set dI2 [expr abs($If-$I2)]
        if [expr $dI2<$bestdI2] {
            set bestIf $If
            set bestdI2 $dI2
            set bestN2 $n2t
        }
    }
    set n2 $bestN2
    set IAll $bestIf
    set nTotal [expr $n1+$n2]
    APSSetStatus "Need $n2 bunches empty after 1 Hz filling"
    APSSetStatus "Total of $nTotal bunches"
    if [expr $nTotal==324] {
        APSSetStatus "Consider increasing the second-stage charge, decreasing the target current, or decreasing the swap-out interval."
    }
    
    set IUltimate [expr $Q2/$T0*($srEff/100.0)*(1-pow($r,$nTotal))/(1.0-$r)]
    APSSetStatus "Ultimate current predicted to be $IUltimate mA"
}
