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

# direct parallelization of serial simplex algorithm
#$Log: not supported by cvs2svn $

if ![info exists env(OAG_TOP_DIR)] {
    set env(OAG_TOP_DIR) /usr/local
}
set auto_path [linsert $auto_path 0  $env(OAG_TOP_DIR)/oag/apps/lib/$env(HOST_ARCH)]

APSStandardSetup

set usage {usage: parallelSingleSimplex -input <filename> [-help 1] [-tolerance <value>] [-target <value>] [-restartFile <filename>] [-compareWithPrevBest <1>] [-logFile <filename>] [-restart <number>] [-iterations <number>] }

proc PrintHelp {} {
    global usage
    puts stdout "$usage\n"
    puts stdout "Description of commandline parameters:"
    puts stdout "input:  name of the input file, as described above."
    puts stdout "logFile: name of file for storing the result and parameter values of each simulation."
    puts stdout "tolerance:  convergence critia:"
    puts stdout "        if tolerance is <=0, fractional tolerance is used for evaluation "
    puts stdout "        if tolerance is >0, absolute tolerance (i.e., abs(HighResult-lowResult) ) is used for evaluation."
    puts stdout "target: if target is given and the best result obtained so far is less or equal to target, optimization is done."
    puts stdout "restartFile: the best parameters will be saved into this file before program "
    puts stdout "             terminates, which can be used as input in next run"
    puts stdout "restart:  if >0,optimizer restarts with best result obtained so far if no progress made."
    puts stdout "iterations: >0, optimizer exits when the number of iterations in one cycle exceeds it."
    puts stdout "            =0, optimizer exist until it converges or without progress."
    puts stdout ""
    puts stdout "Description of input file:"
    puts stdout "Parameters: "
    puts stdout "preProcessingScript: script to run prior to running the job. (May be blank.)"
    puts stdout "  The script must accept the following arguments and syntax: "
    puts stdout "    -rootname <string>       rootname for the job."
    puts stdout "    -tagList <string>        list of tags for varied quantities."
    puts stdout "    -valueList <string>      list of values associated with tags."
    puts stdout "    The tag names are the same as the parameter names given in the"
    puts stdout "    parameterName column (see below)."
    puts stdout "  E.g., the script will be called with arguments like these:"
    puts stdout "    -rootname run-000000 -tagList \"Var1 Var2\" -valueList \"1.7 -2.8\""
    puts stdout "postProcessingScript: script to run to postprocess a job.  Called with"
    puts stdout "  two arguments giving the rootname and runID of the job.  This script must produce"
    puts stdout "  a one-row SDDS file named <rootname>.proc that contains at least the "
    puts stdout "  following columns:"
    puts stdout "  runName --- the rootname of the run for this file"
    puts stdout "  runID --- the ID of this job."
    puts stdout "  penaltyValue --- the value of the penalty function"
    puts stdout "  This file must also have all the columns from <rootname>.inp sddsxref\'d in."
    puts stdout "runScript: script to run the simulation job.  The script must accept the"
    puts stdout "  all the arguments as the preProcessingScript, plus runID."
    puts stdout "  it also accepts preProcessingScript and postProcessingScript arguments."
    puts stdout "  pre/post ProcessingScript arguments are optional."
    puts stdout "  The purpose of adding pre/post ProcessingScript to runScript"
    puts stdout "  is to parallel the pre/post processing instead of running in sequential"
    puts stdout "  If runScript is blank, then you"
    puts stdout "  must give inputFileTemplate, inputFileExtension, and programName."
    puts stdout "  The script must create a file <rootname>.run as soon as it starts, "
    puts stdout "   then run preProcessingScript (if given)."
    puts stdout "   If postProcessingscript is given, run it after all other tasks are done"
    puts stdout "      and create <rootname>.proc"
    puts stdout "   otherwise, create <rootname>.proc directly."
    puts stdout "   at last, create <rootname>.done right before finishing."
    puts stdout "inputFileTemplate: name of the template file for creating input files."
    puts stdout "  Sequences of the form <name> are substituted with values."
    puts stdout "inputFileExtension: extension to use for input files."
    puts stdout "programName: name of the program to run with the input files.  The"
    puts stdout "  input filename is given as the sole argument of the program."
    puts stdout ""
    puts stdout "Columns:"
    puts stdout "parameterName: names of the parameters to vary in the optimization."
    puts stdout "  These should appear in the input file template in the form <name>."
    puts stdout "initialValue: initial values of the parameters"
    puts stdout "errorLevel: rms width of the gaussian random errors to add to the"
    puts stdout "  parameter values as mutations."
    puts stdout "lowerLimit: smallest allowable values for the parameters"
    puts stdout "upperLimit: largest allowable values for the parameters."
}

proc CheckFileExistent {args} {
    set filename ""
    set timeout 500
    APSParseArguments {filename timeout}
    
  #  set count 0
    set timeout [expr [clock seconds] + $timeout]
    set rows 0
    while {[clock seconds]<$timeout} {
        if {[exec sddscheck $filename]=="ok"} {
            if [catch {exec sdds2stream $filename -rows=bar} rows] {
                set rows 0
               # return -code error "Unable to read $filename: $rows"
            }
            if {$rows} {
                break
            }
        }
        after 1000
    }
    return $rows
}

proc SaveToLogFile {args} {
    set option p
    set step ""
    set jobs 0
    set operation ""
    APSParseArguments {jobs option step jobs operation}
    global logFile parameterName logFileID pVector reflectionVector expansionVector contractionVector shrinkVector
    global rootname logDataArray procColumns logColumns

    if ![string length $logFile] {
        return
    }
    set runName ${rootname}_${option}-
    set procFiles [lsort [glob ${runName}*.proc]]
    set tmpRoot /tmp/[APSTmpString]
    if [llength $procFiles]==1 {
        exec cp $procFiles $tmpRoot.1
    } else {
        if [catch {eval exec sddscombine $procFiles $tmpRoot.1 -merge -overwrite} result] {
            return -code error $result
        }
    }
    if {$option=="p"} {
        set operation initialSimplex
    } else {
        set operation $option
    }
    set options ""
    set index 0
    for {set i 0} {$i<$jobs} {incr i} {
        lappend idList $i
        lappend stepList $step
        lappend operList $operation
    }
    foreach name $parameterName {
        for {set i 0} {$i<$jobs} {incr i} {
            lappend $name [lindex [set ${option}Vector($i.Value)] $index]
        }
        incr index
        lappend options "-col=$name,type=double"
        lappend options "-data=[join [set $name] ,]"
    }
    if [catch {eval exec sddsmakedataset $tmpRoot.2 \
                 -col=step,type=long -data=[join $stepList ,] \
                 -col=operation,type=string -data=[join $operList ,]\
                 -col=runID,type=long -data=[join $idList ,] $options} result] {
        return -code error $result
    }
    if [catch {exec sddsxref $tmpRoot.2 $tmpRoot.1 $tmpRoot.4 -fillIn -nowarnings \
                 -leave=[join $parameterName ,],step,operation,runID } result] {
        return -code error $result
    }
    if {$step==1} {
        file copy -force $tmpRoot.4 $logFile
    } else {
        if [catch {exec sddscombine $logFile $tmpRoot.4 -merge $tmpRoot.5 } result] {
            return -code error $result
        }
        file copy -force $tmpRoot.5 $logFile
    }
    set files [glob ${tmpRoot}.*]
    eval file delete -force $files
}

proc GetRandomValue {args} {
    global idum iv
    set IA 16807
    set IM 2147483647
    set AM [expr 1.0/$IM]
    set IQ 127773
    set IR 2836
    set NTAB 32
    set NDTV [expr 1 + ($IM-1)/$NTAB]
    set EPS 1.2e-7
    set RNMX [expr 1.0-$EPS]
    set iy 0
    
    if {$idum<0 && !$iy} {
        set a [expr $idum * -1.0]
        if {$a<1} {
            set idum 1
        } else {
            set idum $a
        }
        for {set j [expr $NTAB+7]} {$j>=0} {incr j -1} {
            set k [expr int($idum/$IQ)]
            set idum [expr int($IA * ($idum - $k * $IQ)- $IR*$k)]
            if {$idum<0} {
                set idum [expr $idum + $IM]
            }
            if {$j<$NTAB} {set iv($j) $idum}
        }
        set iy $iv(0)
    }
    set k [expr int($idum/ $IQ)]
    set idum [expr int($IA * ($idum - $k*$IQ)-$IR*$k)]
    if {$idum<0} {set idum [expr $idum + $IM] }
    set j [expr int($iy/$NDTV)]
    set iy $iv($j)
    set iv($j) $idum
    set temp [expr $AM*$iy]

    if {$temp>$RNMX} {
        return $RNMX
    } else {
        return $temp
    }
}

proc CreateInitialMatrix {args} {
    #for N dimension independent variables, create P0[N+1][N] array, while for i=1 to N
    # P[i]=P[0]+delta[i] delta[i] is the change of ith variable
    # P[0] is the initial value of N variables i.e. P[0][1],P[0][2], ..., P[0][N]
    # this array is named as pVector
    global inputParameterList inputColumnList simplexPoints
    eval global $inputParameterList 
    eval global $inputColumnList
    global rootname template errorFactor pVector steps
    
    puts stderr "create initial simplex..."
    if ![info exist pVector(parameterName)] {
        set pVector(parameterName) ""
        set pVector(dimension) [llength $parameterName]
        foreach pN $parameterName { lappend pVector(parameterName) <$pN> }
    }
    for {set i 0} {$i <= $pVector(dimension)} {incr i} {
        set pVector($i.Value) ""
        set pVector($i.procOpt) ""
    }
    for {set point 0} {$point <= $pVector(dimension)} {incr point} {
        set pExist($point) 0
        set index 1
        foreach pN $parameterName iV $initialValue eL $errorLevel lL $lowerLimit uL $upperLimit {
            if {$iV<$lL || $iV>$uL || $uL<$lL} {
                puts stderr "Invalid initial value - $iV given for $pN, it exceeds the $lL - $uL."
                exit
            }
            if {$point==0 || $index != $point } {
                set value $iV
            } else {
                set value [expr $eL*$errorFactor+$iV]
                if {$value <= $lL} {
                    set el [expr ($uL-$lL)/12.0]
                    set value [expr $iL + $el]
                } elseif {$value >= $uL} {
                    set el [expr ($uL-$lL)/12]
                    set value [expr $uL-$el]
                }
            }
            lappend pVector($point.Value) $value
            lappend pVector($point.procOpt) -define=column,$pN,$value,type=double
            incr index
        }
        set runName ${rootname}_p-[format %06ld $point]
        set fileList [glob -nocomplain ${runName}.*]
        eval file delete -force file $fileList
        set replOpt [concat $runName $pVector($point.Value)]
        puts stderr "[exec date +%H:%M:%S] submit job $runName"
        SubmitOneJob -runName $runName -procOpt "$pVector($point.procOpt)" \
          -replOpt "$replOpt" -valueList "$pVector($point.Value)" -id $point
    }
    puts stderr "Jobs for creating initial Simplex submitted."
    set jobs [expr $pVector(dimension) +1]
    AnalyzeResults -jobs $jobs -option p -step $steps
}

proc AnalyzeResults {args} {
    set jobs 0
    set option p
    set step ""
    APSParseArguments {jobs option step}
    
    global pLowID pHighID reflLowID reflHighID expanLowID expanHighID shrinkLowID shrinkHighID
    global pLowResult pHighResult reflLowResult reflHighResult 
    global bestResult pVector reflVector rootname expanVector shrinkVector
    global postProcessingScript programName parameterName logFile

    set jobs [expr $pVector(dimension)+1]
    for {set i 0} {$i<$jobs} {incr i} {
        set root [format ${rootname}_${option}-%06ld $i]
        lappend rootList $root
        lappend idList $i
        set $root 0
    }
    set count 0
    while {1} {
        set notdone 0
        foreach root $rootList {
            if ![file exist $root.done] {
                set notdone 1
                if {$count>100} {
                    puts stderr "[clock format [clock seconds] -format %H:%M:%S]: job $root is not done yet."
                    set count 0
                }
            }
        }
        if {!$notdone} {
            break
        }
        after 1000
        incr count
    }
    after 1000
    set count 0
    set len [llength $rootList] 
    for {set i 0} {$i<$len} {incr i} {
        set root [lindex $rootList $i]
        set ID [lindex $idList $i]
        set rows [CheckFileExistent -filename $root.proc]
        if {!$rows || $rows!=1} {
            if {$count>1000} {
                puts stderr "Problem with $root.proc file"
                exit
            }
            if ![string length $postProcessingScript] {
                puts stderr "postProcessingScript is not provided for creating *.proc file."
                exit
            }
            set valueList [set ${option}Vector($ID.Value)]
            if [catch {exec $postProcessingScript -tagList "$parameterName" \
                         -valueList "$valueList" \
                         -runID $ID -rootname $root } result] {
                puts stderr "$result"
                exit
            }
            if {[catch {exec sdds2stream -rows=bare $root.proc} rows] || $rows!=1} {
                puts stderr "Problem with $root.proc: $rows"
                set index [expr $index-1]
                after 1000
                incr count
                continue
            }
        }
	if [catch {exec sdds2stream $root.proc -col=penaltyValue} results] {
            puts stderr "unable to get the penalty value of $root.proc, [exec sddscheck $root.proc]"
            exit
        }
        set ${option}Vector($ID.fValue) [lindex $results 0]
        if [catch {expr [set ${option}Vector($ID.fValue)] / 2} result] {
	    set ${option}Vector($ID.fValue) 1.0e200
            #puts stderr "unable to get the penalty value of $root: $result"
            #exit
        }
        puts stderr "$option ID=$ID done, penaltyValue=[set ${option}Vector($ID.fValue)], parameter values are:"
        set index 0
        foreach pN $parameterName {
            puts stderr "        $pN:  [lindex [set ${option}Vector($ID.Value)] $index]"
            incr index
        }
    }
    
    set lowID 0
    set highID 0
    set lowResult [set ${option}Vector(0.fValue)]
    set highResult $lowResult
    for {set i 1} {$i < $jobs} {incr i} {
        set val [set ${option}Vector($i.fValue)]
        if {$val<$lowResult} {
            set lowID $i
            set lowResult $val
        }
        if {$val>$highResult} {
            set highID $i
            set highResult $val
        }
    }
    set ${option}LowID $lowID
    set ${option}HighID $highID
    set ${option}LowResult  $lowResult
    set ${option}HighResult $highResult
    puts stderr "[exec date +%H:%M:%S] option=$option: HighID=$highID, LowID=$lowID"
    puts stderr "HighResult=[set ${option}HighResult], LowResult=[set ${option}LowResult]"
    SaveToLogFile -step $step -option $option -jobs $jobs
    set fileList [glob -nocomplain ${rootname}_${option}*]
    eval file delete -force file $fileList
    return [set ${option}LowResult]
}

proc SubmitRunScript {args} {
    set script ""
    set tagList ""
    set valueList ""
    set rootname ""
    APSStrictParseArguments {script rootname tagList valueList}
	
    eval file delete -force $rootname.log $rootname.done $rootname.run
    set tmpFile [file rootname $rootname].csh
    APSAddToTempFileList $tmpFile
    set fd [open $tmpFile w]
    puts $fd "#!/bin/csh "
    puts $fd "source ~/.cshrc"
    puts $fd "unset savehist"
    puts $fd "echo running $script $rootname $tagList $valueList on [exec uname -a]"
    puts $fd "cd [pwd]"
    puts $fd "$script -rootname $rootname -tagList '$tagList' -valueList '$valueList' >& $rootname.log"
    close $fd
	
    catch {exec cat $tmpFile | qsub -V -o [pwd] -j y -N [file root [file tail $rootname]] -hard -q all.q } result
	
    return "$result"
}

proc SubmitJob {args} {
    set code ""
    set input "" 
    APSStrictParseArguments {code input}

    eval file delete -force $input.log [file rootname $input].done 
    set tmpFile [file rootname $input].csh
    set fd [open $tmpFile w]
    puts $fd "#!/bin/csh "
    puts $fd "source ~/.cshrc"
    puts $fd "unset savehist"
    puts $fd "echo running $code $input on [exec uname -a]"
    puts $fd "cd [pwd]"
    puts $fd "$code $input >& $input.log"
    close $fd

    catch {exec cat $tmpFile | qsub -V -o [pwd] -j y -N [file root [file tail $input]] -hard -q all.q } result

    return "$result"
}

proc SubmitOneJob {args} {
    set runName ""
    set procOpt ""
    set replOpt ""
    set valueList ""
    set id ""
    APSParseArguments {runName procOpt replOpt valueList id}
    global inputParameterList inputColumnList executionMode
    eval global $inputParameterList 
    eval global $inputColumnList
    
    set origOpt [concat <rootname> $parameterName]
    eval exec sddssequence -pipe -define=dummy,type=short -sequence=begin=0,end=0,number=1 \
      | sddsprocess -pipe=in $runName.inp \
      $procOpt \
      -print=column,runName,$runName \
      -define=column,runID,[os editstring Z- $runName],type=long 

    switch $executionMode {
        runScript {
            puts stderr "Submitting script: $runScript for $runName"
            catch {SubmitRunScript -script $runScript -rootname $runName \
                -tagList "$parameterName" -valueList "$valueList"} result
        }
        programName {
            puts stderr "Submitting job"
            exec replaceText -original=[join $origOpt ,] -replacement=[join $replOpt ,] \
                $inputFileTemplate $runName.$inputFileExtension
            catch {SubmitJob -code $programName -input $runName.$inputFileExtension \
                } result
        }
	generateRunScript {
	    puts stderr "Generating and submitting script"
	    catch {eval exec $generateRunScript -rootname $runName -tagList \"$parameterName\" \
		-valueList \"$valueList\" -runID $id} result
	    puts stderr "$generateRunScript returns: $result"
	}
	default {
	}
    }
}

proc CheckResult {args} {
    global tolerance target targetSupplied pVector bestResult bestPoint epson1
    global lowID highID steps
    
    set min $pVector(0.fValue)
    set max $min
    set highID 0
    set lowID 0
    for {set i 1} {$i<=$pVector(dimension)} {incr i} {
        if {$pVector($i.fValue)<$min} {
            set min $pVector($i.fValue)
            set lowID $i
        }
        if {$pVector($i.fValue)>$max} {
            set max $pVector($i.fValue)
            set highID $i
        }
    }
    if {$bestResult<0} {
        set bestResult $pVector($lowID.fValue)
        set bestPoint $pVector($highID.Value)
    } elseif {$pVector($lowID.fValue)<$bestResult} {
        set bestResult $pVector($lowID.fValue)
        set bestPoint $pVector($lowID.Value)
    }
    set delta 0
    for {set index 0} {$index<$pVector(dimension)} {incr index} {
        set value1 [lindex $pVector($highID.Value) $index]
        set value2 [lindex $pVector($lowID.Value) $index]
        set delta [expr $delta + ($value1-$value2)*($value1-$value2)]
    }
    set delta [expr sqrt($delta)]
    set result 0
    if {$targetSupplied} {
        if {$bestResult<=$target} {
            set result 1
        }
    } else {
        set tol [expr abs($max-$bestResult)]
        if {$tol<=$tolerance} {
            set result 1
        }
    }
    if {!$result && $delta<=$epson1} {
        set result 2
    }
    if $result {
        PrintBestResult -result $result
        exit
    } else {
        puts stderr "step $steps: highResult=$pVector($highID.fValue), lowResult=$pVector($lowID.fValue) bestResult=$bestResult"
    }
    return $result
}

proc PrintBestResult {args} {
    set result 1
    APSParseArguments {result} 
    global parameterName pVector lowID highID bestResult bestPoint rootname steps epson1 
    global restartFile input logFileID iterations restart totalIterations
    
    
    if [string length $logFileID] {close $logFileID}
    if {$result==1} {
        puts stderr "[exec date +%H:%M:%S] Optimization done after $steps steps."
        puts stderr "The best result is: $bestResult"
    } elseif {$result==2} {
        puts stderr "[exec date +%H:%M:%S] Optimization exits after $steps iterations, since the parameter changes are smaller than $epson1."
        puts stderr "The best result obtained so far is: $bestResult"
    } elseif {$result==3} {
        puts stderr "[exec date +%H:%M:%S] Optimization exits since the number of iterations exceeds $totalIterations."
        puts stderr "The best result obtained so far is: $bestResult"
    } elseif {$result==-1} {
        puts stderr "[exec date +%H:%M:%S] No progress after $steps iterations."
        puts stderr "The best result obtained so far is: $bestResult"
    }
    puts stderr "The parameter values are:"
    foreach pN $parameterName val $bestPoint {
        puts stderr "$pN: $val"
    }
    set files [glob -nocomplain ${rootname}_*]
    catch {eval file delete -force file $files }
    set files [glob -nocomplain ${rootname}-*]
    catch {eval file delete -force file $files}
    set tmpfile /tmp/[APSTmpString]
    if [string length $restartFile] {
        if [catch {exec sddsmakedataset $tmpfile -col=parameterName,type=string \
                     -data=[join $parameterName ,] \
                     -col=initialValue1,type=double -data=[join $bestPoint ,] \
                     -par=penaltyValue,type=double -data=$bestResult } result] {
            return -code error $result
        }
        if [catch {exec sddsxref $input $tmpfile -pipe=out -match=parameterName \
                     -take=initialValue1 \
                     | sddsconvert -pipe=in -delete=col,initialValue \
                     -rename=col,initialValue1=initialValue $restartFile } result] {
            return -code error $result
        }
        file delete -force $tmpfile
    }
}


proc RunParallelOperations {args} {
    global steps
    global lowID highID lowID highID pVector sum reflThroughLowPoint lowerLimit upperLimit parameterName
    global rootname postProcessingScript reflectionVector expansionVector contractionVector shrinkVector bestResult

    #calculate PSum and centroid
    for {set index 0} {$index<$pVector(dimension)} {incr index} {
        set pVector($index.sum) 0
        for {set vertex 0} {$vertex<=$pVector(dimension)} {incr vertex} {
            set pVector($index.sum) [expr $pVector($index.sum) + [lindex $pVector($vertex.Value) $index]]
        }
        set pVector($index.center) [expr $pVector($index.sum)/($pVector(dimension)+1)]
    }
    set valueList $pVector($highID.Value)
    if $reflThroughLowPoint {
        set reflPoint $pVector($highID.Value)
    } else {
        for {set i 0} {$i<$pVector(dimension)} {incr i} {
            lappend reflPoint $pVector($i.center)
        }
    }
    if ${sum} {
        set factor [expr $pVector(dimension)+1]
    } else {
        set factor 1
    }
    foreach operation {reflection expansion contraction} lamda {-1 -2 -0.5} {
        set ${operation}Vector(0.beyondLimit) 0
        set ${operation}Vector(0.Value) ""
        set ${operation}Vector(0.ProcOpt) ""
        set fac1 [expr (1.0-$lamda)/$pVector(dimension)]
        set fac2 [expr $fac1-$lamda]
        foreach val $valueList refl $reflPoint lL $lowerLimit uL $upperLimit pN $parameterName {
            set value [expr $refl*$factor*$fac1-$val*$fac2]
            if {$value<=$lL} {
                set value $lL
                set ${operation}Vector(0.beyondLimit) 1
            } elseif {$value>=$uL} {
                set value $uL
                set ${operation}Vector(0.beyondLimit) 1
            }
            lappend ${operation}Vector(0.Value) $value
            lappend ${operation}Vector(0.ProcOpt) -define=column,$pN,$value,type=double
        }
        set runName ${rootname}_${operation}-0
        if ![set ${operation}Vector(0.beyondLimit)] {
            set fileList [glob -nocomplain ${runName}*]
            eval file delete -force file $fileList
            puts stderr "[exec date +%H:%M:%S] submit job $runName"
            SubmitOneJob -runName $runName -procOpt "[set ${operation}Vector(0.ProcOpt)]" \
              -replOpt "[concat $runName [set ${operation}Vector(0.Value)]]" -valueList "[set ${operation}Vector(0.Value)]" -id 0
        } else {
            set options -defaultType=double
            foreach pN $parameterName val [set ${operation}Vector(0.Value)] {
                lappend options "-col=$pN"
                lappend options "-data=$val"
            }
            lappend options "-col=penaltyValue"
            lappend options "-data=1.0e200"
            if [catch {eval exec sddsmakedataset $runName.proc $options} result] {
                puts stderr "unable to create $runName.proc file: $result"
                exit
            }
        }
    }
    #submit shrinkage job
    set shrinkVector(0.Value) ""
    set shrinkVector(0.ProcOpt) ""
    set shrinkVector(0.beyondLimit) 0
    foreach val $valueList refl $pVector($lowID.Value) lL $lowerLimit uL $upperLimit pN $parameterName {
        set value [expr 0.5*($val+$refl)]
        if {$value<=$lL} {
            set value $lL
            set shrinkVector(0.beyondLimit) 1
            set shrinkVector(0.fValue) 1.0e200         
        } elseif {$value>=$uL} {
            set value $uL
            set shrinkVector(0.beyondLimit) 1
            set shrinkVector(0.fValue) 1.0e200    
        }
        lappend shrinkVector(0.Value) $value
        lappend shrinkVector(0.ProcOpt) -define=column,$pN,$value,type=double
    }
    set runName ${rootname}_shrink-0
    if !$shrinkVector(0.beyondLimit) {
        set fileList [glob -nocomplain ${runName}*]
        eval file delete -force file $fileList
        puts stderr "[exec date +%H:%M:%S] submit job $runName"
        SubmitOneJob -runName $runName -procOpt "$shrinkVector(0.ProcOpt)" \
          -replOpt "[concat $runName $shrinkVector(0.Value)]" -valueList "$shrinkVector(0.Value)" -id 0
    } else {
        set options -defaultType=double
        foreach pN $parameterName val [set ${operation}Vector(0.Value)] {
            lappend options "-col=$pN"
            lappend options "-data=$val"
        }
        lappend options "-col=penaltyValue"
        lappend options "-data=1.0e200"
        if [catch {eval exec sddsmakedataset $runName.proc $options} result] {
            puts stderr "unable to create $runName.proc file: $result"
            exit
        }
    }
    set count 0
    while {1} {
        set notDone 0
        foreach operation {reflection expansion contraction shrink} {
            if [set ${operation}Vector(0.beyondLimit)] {
                continue
            }
            set runName ${rootname}_${operation}-0
            if [file exist ${runName}.done] {
                set rows [CheckFileExistent -filename $runName.proc]
                if {!$rows || $rows!=1} {
                    if ![string length $postProcessingScript] {
                        puts stderr "postProcessingScript is not provided for creating *.proc file."
                        exit
                    }
                    if [catch {exec $postProcessingScript -tagList "$parameterName" \
                                 -valueList "[set ${operation}Vector(0.Value)]" \
                                 -runID 0 -rootname $runName } result] {
                        puts stderr "$result"
                        exit
                    }
                    if {[catch {exec sdds2stream -rows=bare $runName.proc} rows] || $rows!=1} {
			puts stderr "Problem with $runName.proc: $rows"
			after 5000
			file delete $runName.proc
			set notdone 1
		    }
                }
            } else {
                set notDone 1
                if {$count>100} {
                    puts stderr "[exec date +%H:%M:%S]: job $runName is not done yet."
                    set count 0
                }
            }
        }
        if !$notDone {
            break
        }
        after 1000
        incr count
    }
    foreach operation {reflection expansion contraction shrink} {
        set runName ${rootname}_${operation}-0
        if ![CheckFileExistent -filename ${runName}.proc] {
            puts stderr "Problem with ${runName}.proc"
            exit
        }
        if [catch {exec sdds2stream ${runName}.proc -col=penaltyValue} results] {
            puts stderr "unable to get the penalty value of $runName.proc, [exec sddscheck $runName.proc]"
            exit
        }
        set ${operation}Vector(0.fValue) [lindex $results 0]
        if [catch {expr [set ${operation}Vector(0.fValue)] / 2} result] {
            set ${operation}Vector(0.fValue) 1.0e200
        }
        puts stderr "[exec date +%H:%M:%S] $operation done, penaltyValue=[set ${operation}Vector(0.fValue)], parameter values are:"
        set index 0
        foreach pN $parameterName {
            puts stderr "        $pN:  [lindex [set ${operation}Vector(0.Value)] $index]"
            incr index
        }
        SaveToLogFile -step $steps -option $operation -jobs 1
    }
    set max $pVector($highID.fValue)
    set replOpt ""
    foreach operation {reflection expansion contraction shrink} {
        set value [set ${operation}Vector(0.fValue)]
        if {$value<$max} {
            set max $value
            set replOpt $operation
        }
    }
    if [string length $replOpt] {
        puts stderr "step $steps: replace the high point by result from $replOpt."
        set pVector($highID.Value) [set ${replOpt}Vector(0.Value)]
        set pVector($highID.fValue) [set ${replOpt}Vector(0.fValue)]
        set returnValue 1
    } else {
        set returnValue 0
    }
    if $returnValue {
        CheckResult
    }
    return $returnValue
}

proc LoadAndCheckInputFile {} {
    global input inputParameterList inputColumnList inputFileTemplate
    if [catch {sdds load $input inputData} result] {
        return -code error "$result"
    }
    set inputParameterList $inputData(ParameterNames)
    foreach param $inputParameterList {
        global $param
        set $param [lindex $inputData(Parameter.$param) 0]
    }
    set inputColumnList $inputData(ColumnNames)
    foreach col $inputColumnList {
        global $col
        set $col [lindex $inputData(Column.$col) 0]
    }
    foreach newParam {runScript preProcessingScript} {
        if ![info exists $newParam] {
            global $newParam
            lappend inputParameterList $newParam
            set $newParam ""
        }
    }
    set count 0
    global executionMode
    foreach item {runScript programName generateRunScript} {
	if [string length [set $item]] {
	    set executionMode $item
	    set count 1
	}
    }
    if $count!=1 {
	puts stderr "Error: give one and only one of runScript programName generateRunScript"
	exit 1
    }
    if [string length $runScript] {
        set inputFileExtension run
    }

    if [string length $programName] {
        if ![string length $inputFileTemplate] {
            return -code error "inputFileTemplate is required for runProgram!"
        }
        if ![string length $inputFileExtension] {
            return -code error "inputFileExtension is required for runProgram!"
        }
    }
}

proc RunOptimize {args} {
    global steps iterations restart initialValue pVector lowID
    puts stderr ""
    incr steps
    CreateInitialMatrix
    CheckResult
    set iter 0
    while {1} {
        incr iter
        if {$iterations && !$restart && $iter>$iterations} {
            PrintBestResult -result 3
            exit
        }
        if {$restart && $iterations && $iter>$iterations} {
            puts stderr "[exec date +%H:%M:%S]: iterations in one cycle exceeds $iterations, restart."
            incr steps
            set initialValue $pVector($lowID.Value)
            CreateInitialMatrix
            CheckResult
            set iter 0
            set restart [expr $restart-1]
        }
        incr steps
        if ![RunParallelOperations] {
            if {$restart} {
                puts stderr "[exec date +%H:%M:%S]: no progress, restart."
                incr steps
                CreateInitialMatrix
                CheckResult
                set iter 0
            } else {
                PrintBestResult -result -1
                exit
            }
        }
    }
}

set args $argv
set steps 0
set idum -1
set bestResult -1
set epson1 1.0e-3
set reflThroughLowPoint 0
set inputFileTemplate ""
set inputFileExtension ""
set sum 1
set input ""
set logFile ""
set logFileID ""
set tolerance 0.01
set target 0
set targetSupplied 0
set errorFactor 1
set help 0
set restartFile ""
set iterations 0
set restart 0
if {[APSStrictParseArguments {logFile input errorFactor help tolerance target \
                                restartFile restart iterations sum}] || \
        ![string length  $input] || $errorFactor<=0 || $tolerance<=0} {
    if $help {
        PrintHelp
        exit 
    }    
    return -code error "$usage"
}
if {$restart<0} {
    puts stderr "Invalid restart value given: restart value is less than 0"
    exit
}
if {$target} {
    set targetSupplied 1
}

if ![file exists $input] {
    return -code error "not found: $input"
}
if {[string length $logFile] && [file exists $logFile] } {
    puts stderr "log file $logFile already exists, exit."
    exit
}
set rootname [file rootname $input]

if [file exist $rootname.run] {
    return -code error "rootname in use: $rootname"
}

LoadAndCheckInputFile 
set files [glob -nocomplain ${rootname}*.run]
if [llength $files] {
    puts stderr "There are jobs for $rootname running, can not start new run!"
    exit
}
set totalIterations [expr ($restart+1)*$iterations]

RunOptimize


