https://hal.archives-ouvertes.fr/hal-01897934
Raw File
Tip revision: f370e484d8579b4ccc863d1c2a2f10decad21914 authored by Software Heritage on 27 July 2018, 00:00:00 UTC
hal: Deposit 299 in collection hal
Tip revision: f370e48
slalomCore.py
# -*- coding: utf-8 -*-

# ======================================================================================================
# SLALOM - Open-Source Solar Cell Multivariate Optimizer
# Copyright(C) 2012-2019 Sidi OULD SAAD HAMADY (1,2,*), Nicolas FRESSENGEAS (1,2). All rights reserved.
# (1) Université de Lorraine, Laboratoire Matériaux Optiques, Photonique et Systèmes, Metz, F-57070, France
# (2) Laboratoire Matériaux Optiques, Photonique et Systèmes, CentraleSupélec, Université Paris-Saclay, Metz, F-57070, France
# (*) sidi.hamady@univ-lorraine.fr
# SLALOM source code is available to download from:
# https://github.com/sidihamady/SLALOM
# https://hal.archives-ouvertes.fr/hal-01897934
# http://www.hamady.org/photovoltaics/slalom_source.zip
# Cite as: S Ould Saad Hamady and N Fressengeas, EPJ Photovoltaics, 9:13, 2018.
# See Copyright Notice in COPYRIGHT
# ======================================================================================================

# ------------------------------------------------------------------------------------------------------
# File:           slalomCore.py
# Type:           Class
# Use:            slalomCore is used throw the included slalom.py startup module
#                 To extend the slalomCore class, do not modify it directly but extend it by...
#                 ...creating a new inherited class. Here, perform only performance tuning and bug fixing.
# ------------------------------------------------------------------------------------------------------

slalomVersion = 'Version: 1.2 Build: 1904'

# Calculation
import math
import numpy as np
from scipy import optimize, interpolate, signal
from Bayes import BayesianOptimization
from Bayes import UtilityFunction
import random

# Control
import subprocess
import datetime, shutil, os, stat, sys, time
import zipfile
import traceback

import itertools

from slalomSimulator import *

def dispError(message, doExit = True, atExit = None, errFilename = None, **atExitArgs):
    """ print out an error message and exit if doExit set to True """
    try:
        if doExit:
            strT = "\n--------------------------- ERROR: ----------------------------\n"
        else:
            strT = "\n-------------------------- WARNING: ---------------------------\n"
        # end if
        strT += message
        if not message.endswith("\n"):
            strT += "\n"
        # end if
        strT +=      "---------------------------------------------------------------\n"
        print strT
        if doExit == False:
            return
        # end if

        if atExit is not None:
            atExit(atExitArgs)
        # end if
        if errFilename is not None:
            fileErr = open(errFilename, 'w')
            fileErr.write(message)
            fileErr.close()
        # end if
    finally:
        if doExit:
            # sys.exit raise SystemExit (inherits from BaseException)
            sys.exit(1)
        # end if
    # end try
# end dispError

class slalomCore(object):
    """ the SLALOM core class """

    def __init__(self, Device = None, pythonInterpreter = None, deviceSimulator = "atlas"):
        """ slalomCore constructor """

        self.__version__ = slalomVersion

        self.pythonInterpreter = "python"

        self.simulator = slalomSimulator(name = deviceSimulator)

        self.optimType = ""

        self.isRunning = False
        self.isParamUpdated = False
        self.isInputChecked = False

        self.optimType = ""
        self.minimizeMethodList = ["L-BFGS-B", "SLSQP", "Bayes"]
        self.minimizeMethod = "Bayes"
        self.maxIter = 100
        self.isBound = True
        self.tolerance = 1e-3
        self.ftolerance = 0.005
        self.jaceps = np.array([])

        # Optimization cache
        self.lastParam = list()
        self.lastOutput = list()
        self.lastParamLimit = 12

        self.mainTitle = ""
        self.pythonInterpreter = ""
        self.inputFilename = ""
        self.paramCount = 0
        self.paramName = []
        self.paramFormat = []
        self.paramFormatShort = []
        self.paramFormatNormalized = []
        self.paramNorm = np.array([])
        self.paramStart = np.array([])
        self.paramEnd = np.array([])
        self.paramInit = np.array([])
        self.paramPoints = []
        self.paramBounds = None
        # Tukey Window (Parameters Weight: decreases near the bounds)
        # If optimum is near the bounds, disable this feature or enlarge domain
        self.paramWeight = False
        self.weightFunc = None
        self.weightAlpha = 0.20
        self.weightPoints = 101
        # optimPoints is used to approximate the jacobian. If increased, the optimisation time will dramatically increase. The default value is 51 and the maximum value is 201.
        self.optimPoints = 51
        self.paramLogscale = []
        self.modelFilename = None

        self.paramOptim = np.array([])
        self.paramNatural = np.array([])
        self.paramCountTotal = 0
        self.paramFormatOutput = ""
        self.outputOptimized = 0.0
        self.outputOptimizedx = 0.0
        self.outputOptimizedy = 0.0
        self.outputOptimizedz = 0.0
        self.optimCounter = 1
        self.funcCounter = 1
        self.jacCounter = 0
        self.elapsedTime = 0
        self.delayMin = 0
        self.delayMax = 0
        self.delayMean = 0
        self.counterFormat = '{0:02d}'

        self.guessParam = False
        self.bruteSimul = False

        self.inJac = False

        # output filenames:
        # * these names should be the same than the simulator output filenames
        # * for the following three files, the position should be kept the same:
        # * >> second file (position 1) = J(V) from V = 0 to V = VOC
        # * >> before last file (position 6) = efficiency calculated by simulator
        # * >> last file (position 7) = J(V) (for efficiency calculation)
        # * the output file names remain unchanged since at every 
        #   optimization a new output directory is created
        self.outputFilename = [ "simuloutput_all.log",
                                "simuloutput_jvp.log",
                                "simuloutput_pv.log",
                                "simuloutput.log",
                                "simuloutput_spectralresponse_eqe.log",
                                "simuloutput_popt.log",
                                "simuloutput_efficiency.log",
                                "simuloutput_jv.log" ]
        self.outputFilenameJVPposition = 1
        self.outputFilenameEFFposition = 6
        self.outputFilenameJVposition = 7

        # output filenames description (each output file should have a description)
        self.outputComment = [  "Voltage Sweep Data",
                                "J(V) Characteristic from 0V to VOC (J in mA/cm2)",
                                "P(V) Characteristic (P in mW/cm2)",
                                "PV Performances Data (JSC (mA/cm2), VOC (V), FF, EFF)",
                                "External quantum efficiency",
                                "Optical Power (in mW/cm2)",
                                "PV Efficiency",
                                "J(V) Characteristic (J in mA/cm2)" ]

        self.outputCount = len(self.outputComment)

        # pipe filename (used for the simulator standard output redirection)
        self.verboseFilename = "simuloutput_stdout.txt"

        # log filename: used for the internal optimizer logging
        self.logFilename = "simuloutput_log.txt"

        # weight filename: not used yet
        self.weightFilename = "simuloutput_weight.txt"

        # log filename:
        # * contain the calculated efficiency at every optimization step along with 
        #   the corresponding set of parameters
        self.outputOptimizedFilename = "simuloutput_optimized.txt"

        self.commandFilename = "Optimize.bat" if (os.name == "nt") else "Optimize.sh"

        # internal filenames
        self.stopFilename = "stop.txt"
        self.stoppedFilename = "stopped.txt"
        self.stoppedDone = False
        self.delayFilename = "delay.txt"

        self.currentDir = ""
        self.outputDir = ""
        self.outputRoot = ""
        self.outputDirShort = ""
        self.dirSepChar = '/'

        if (Device is not None) and (pythonInterpreter is not None):
           self.setParam(Device, pythonInterpreter)
        # end if

        return

    # end __init__

    def log(self, strT):
        """ internal logging routine (the significant events are logged in the log file) """

        try:
            print strT
            fileT = open(self.outputDir + self.logFilename, "a")
            fileT.write(strT)
            fileT.close()
        except:
            pass
        # end try

    # end log

    def setCounterFormat(self, maxcount):
        if maxcount < 100:
            self.counterFormat = '{0:02d}'
        elif maxcount >= 100 and maxcount < 1000:
            self.counterFormat = '{0:03d}'
        elif maxcount >= 1000 and maxcount < 10000:
            self.counterFormat = '{0:04d}'
        else:
            self.counterFormat = '{0:07d}'
        # endif
    # end setCounterFormat

    def setInterpreter(self, pythonInterpreter):
        """ set the Python interpreter (usually just 'python') """
        self.pythonInterpreter = pythonInterpreter
    # end setInterpreter

    @staticmethod
    def chmodExec(strFilename):
        """ chmod a+x the simulator launcher """
        statT = os.stat(strFilename)
        os.chmod(strFilename, statT.st_mode | stat.S_IEXEC)
        return
    # end chmodExec

    def checkInput(self):
        """ check if the simulator files (input, C models, etc.) can be accessed """
        if (self.outputCount > 0):
            pathIn = ""
            # Check if the simulator input file contains the correct output filenames
            try:
                pathIn = os.path.join(self.currentDir, self.inputFilename)
                if not os.path.isfile(pathIn):
                    dispError("cannot open input: file not found '%s'" % pathIn,
                        doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
                # end if
                fileT = open("{0}{1}".format(self.currentDir, self.inputFilename), "r")
                foundCounter = 0
                for lineT in fileT:
                    for ii in range(0, self.outputCount):
                        for ll in range(0, len(self.simulator.filedecl)):
                            statementT = self.simulator.filedecl[ll] % self.outputFilename[ii]
                            # syntax should be the same than what defined in slalomSimulator
                            # for Silvaco: outfile=filename (without spaces aroud '=')
                            if (statementT in lineT):
                                foundCounter += 1
                                break
                            # end if
                        # end for
                    # end for
                    if foundCounter == self.outputCount:
                        break
                    # end for
                # end for
                fileT.close()
                if foundCounter != self.outputCount:
                    dispError("Output statements not found in the simulator input file",
                        doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
                # end if
            except Exception as excT:
                # catch only Exception (since sys.exit raise BaseException)
                dispError("Cannot open the simulator input file '%s' %s" % (pathIn, str(excT)),
                    doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
            # end try
        # end if

        self.isInputChecked = True

        return self.isInputChecked

    # end checkInput

    def updatePath(self, bCreateCommand = True):
        """ update/normalize input files """

        # Format simulator input file
        fileT = open(self.currentDir + self.inputFilename, "r")
        fileContent = ""
        for lineT in fileT:
            # Normalize line ending (Silvaco do not run input file if contains CRLF terminated lines)
            lineT = lineT.rstrip("\r\n")
            fileContent += (lineT + "\n")
        # end for
        fileT.close()
        fileT = open(self.outputDir + self.inputFilename, "w")
        fileT.write(fileContent)
        fileT.close()

        # Format model files
        if self.modelCount > 0:
            for ii in range(0, self.modelCount):
                if self.modelFilename[ii] == "":
                    continue
                # end if
                fileT = open(self.currentDir + self.modelFilename[ii], "r")
                fileContent = ""
                for lineT in fileT:
                    # Normalize line ending (Silvaco do not run input file if contains CRLF terminated lines)
                    lineT = lineT.rstrip("\r\n")
                    fileContent += (lineT + "\n")
                # end for
                fileT.close()
                fileT = open(self.outputDir + self.modelFilename[ii], "w")
                fileT.write(fileContent)
                fileT.close()
            # end for
        # end if

        if bCreateCommand == True:
            self.simulator.update(self.simulator.name, self.inputFilename, self.currentDir, self.outputDir, self.verboseFilename)
            strT = ""
            lenT = len(self.simulator.command)
            for ii in range(0, lenT):
                strT += self.simulator.command[ii]
                if (ii < (lenT - 1)):
                    strT += "\n"
                # end if
            # end for
            fileT = open(self.outputDir + self.commandFilename, "w")
            fileT.write(strT)
            fileT.close()
            # chmod a+x self.commandFilename
            self.chmodExec(self.outputDir + self.commandFilename)
        # end if

        return True

    # end updatePath

    def isChecked(self):
        """ input files successfully checked? """
        return (self.isInputChecked and self.isParamUpdated)
    # end isChecked

    @staticmethod
    def printTime(secondsT, short=False):
        """ print the time in a readable form """

        strT = ""
        if (secondsT < 60.0):
            strT = ("%02d" % secondsT) + (" seconds" if not short else " s")
        elif (secondsT < 3600.0):
            strT = ("%.2f" % (secondsT / 60.0)) + (" minutes" if not short else " m")
        else:
            strT = ("%.2f" % (secondsT / 3600.0)) + (" hours" if not short else " h")
        # end if

        return strT

    # end printTime

    def finish(self, errorOccured=True, userStopped=True, x=None, success=None, message=None, nit=None, nlfev=None, xl=None, funl=None):
        """ print out results at the optimization end """

        try:
            dateT = datetime.datetime.now()
            dateStr = dateT.strftime("%Y-%m-%d %H:%M:%S")

            if errorOccured == False:
                strT = "\n---------------------------------------------------------------\n"
                strT += ("Optimization ended @ " if (userStopped == False) else "Optimization interrupted @ ") + dateStr
                strT += ("\nMAXIMUM Efficiency: %g %%" % self.outputOptimized)
                strT += ("\nWITH Jsc = %g mA/cm2" % self.outputOptimizedx) + (" ; Voc = %.4f" % self.outputOptimizedy) + (
                " ; FF = %.3f %%" % self.outputOptimizedz) + "\nOBTAINED FOR:\n"
                for ii in range(0, self.paramCount - 1):
                    strT += self.paramName[ii] + "\t"
                # end for
                strT += self.paramName[self.paramCount - 1] + "\n"
                for ii in range(0, self.paramCount - 1):
                    strT += (self.paramFormat[ii] % self.paramOptim[ii]) + "\t"
                # end for
                strT += (self.paramFormat[self.paramCount - 1] % self.paramOptim[self.paramCount - 1])
                strT += "\nTotal duration: " + self.printTime(float(self.elapsedTime))
                strT += ("\nNumber of function evaluations: %d" % self.funcCounter)
                if (self.jacCounter >= self.paramCount):
                    strT += (" (%d for the Jacobian approximation)" % self.jacCounter)
                # end if
                strT += "\n---------------------------------------------------------------\n"

                if (x is not None) and (success is not None) and (message is not None):
                    xt = np.zeros(self.paramCount)
                    for ii in range(0, self.paramCount):
                        if self.paramLogscale[ii]:
                            xt[ii] = math.pow(10.0, (x[ii] * math.log10(self.paramNorm[ii])))
                        else:
                            xt[ii] = x[ii] * self.paramNorm[ii]
                        # end if
                    # end for
                    strT += "\n---------------------------------------------------------------\n"
                    strT += "Optimization function (" + self.minimizeMethod + ") output:\n"

                    strT += "Parameter:\t"
                    for ii in range(0, self.paramCount - 1):
                        strT += self.paramName[ii] + "\t"
                    # end for
                    strT += self.paramName[self.paramCount - 1] + "\n"

                    strT += "x (natural):\t"
                    for ii in range(0, self.paramCount - 1):
                        strT += (self.paramFormat[ii] % xt[ii]) + "\t"
                    # end for
                    strT += (self.paramFormat[self.paramCount - 1] % xt[self.paramCount - 1]) + "\n"

                    strT += "x (normalized):\t"
                    for ii in range(0, self.paramCount - 1):
                        strT += (self.paramFormatNormalized[ii] % x[ii]) + "\t"
                    # end for
                    strT += (self.paramFormatNormalized[self.paramCount - 1] % x[self.paramCount - 1]) + "\n"

                    strT += "\nsuccess: " + str(success) + "\n"
                    strT += "\nmessage: " + str(message) + "\n"
                    strT += "\nevaluations: " + str(self.funcCounter)
                    strT += "\n---------------------------------------------------------------\n"
                # end if

                if (xl is not None) and (funl is not None) and (success is not None) and (message is not None):
                    nxl = len(xl)
                    nfunl = len(funl)
                    if (nxl == nfunl):
                        if (nxl > 10):
                            nxl = 10
                            nfunl = 10
                        # end if
                        ll = 0
                        for xll in xl:
                            effl = 100.0 * (1.0 - funl[ll])
                            ll = ll + 1
                            if (effl < 0.0) or (effl > 90.0):
                                # should never happen
                                continue
                            # end if
                            xt = np.zeros(self.paramCount)
                            for ii in range(0, self.paramCount):
                                if self.paramLogscale[ii]:
                                    xt[ii] = math.pow(10.0, (xll[ii] * math.log10(self.paramNorm[ii])))
                                else:
                                    xt[ii] = xll[ii] * self.paramNorm[ii]
                                # end if
                            # end for
                            strT += "\n---------------------------------------------------------------\n"
                            strT += "Optimization function (" + self.minimizeMethod + (") local output #%d:\n" % ll)

                            strT += "Parameter:\t"
                            for ii in range(0, self.paramCount - 1):
                                strT += self.paramName[ii] + "\t"
                            # end for
                            strT += self.paramName[self.paramCount - 1] + "\n"

                            strT += "xl (natural):\t"
                            for ii in range(0, self.paramCount - 1):
                                strT += (self.paramFormat[ii] % xt[ii]) + "\t"
                            # end for
                            strT += (self.paramFormat[self.paramCount - 1] % xt[self.paramCount - 1]) + "\n"

                            strT += "xl (normalized):\t"
                            for ii in range(0, self.paramCount - 1):
                                strT += (self.paramFormatNormalized[ii] % xll[ii]) + "\t"
                            # end for
                            strT += (self.paramFormatNormalized[self.paramCount - 1] % xll[self.paramCount - 1]) + "\n"

                            strT += "\nefficiency: %06.3f %%" % effl
                            strT += "\n---------------------------------------------------------------\n"
                        # end if
                    # end if
                # end if

                self.log(strT)

            # end if

            strLog = ("# Optimization ended @ " if (userStopped == False) else "# Optimization interrupted @ ") + dateStr + "\n"

            fileOptim = open(self.outputDir + self.outputOptimizedFilename, "a")
            fileOptim.write(strLog)
            fileOptim.close()

            self.log(strLog)

            self.stopSet()

            if self.stoppedDone == False:
                pathStopped = os.path.join(self.outputDir, self.stoppedFilename)
                fileT = open(pathStopped, "w")
                fileT.write(strLog)
                fileT.close()
                self.stoppedDone = True
            # end if

            self.log("\nZipping optimization result files...")
            zipFilename = self.outputRoot + self.outputDirShort + ".zip"
            outFile = zipfile.ZipFile(zipFilename, "w", compression=zipfile.ZIP_DEFLATED)
            dirToZip = self.outputDir.rstrip(self.dirSepChar)
            for (dirPath, dirNames, fileNames) in os.walk(dirToZip):
                for fileName in fileNames:
                    fileAbsolutePath = os.path.join(dirPath, fileName)
                    fileRelativePath = fileAbsolutePath.replace(dirToZip + self.dirSepChar, '')
                    outFile.write(fileAbsolutePath, fileRelativePath)
                # end for
            # end for
            outFile.close()
            self.log("\nZipping done (File: " + self.outputDirShort + ".zip" + ").")

            self.isRunning = False
        except:
            pass
        # end try

        self.isRunning = False

        if self.stoppedDone == False:
            try:
                pathStopped = os.path.join(self.outputDir, self.stoppedFilename)
                fileT = open(pathStopped, "w")
                fileT.write("SLALOM\nStopped\n")
                fileT.close()
                self.stoppedDone = True
            except:
                pass
            # end try
        # end if

        sys.exit(0 if (errorOccured == False) else 1)

    # end finish

    def guess(self):
        """ get the initial parameters set """

        self.guessParam = True
        self.bruteSimul = False

        paramNormalized0 = np.zeros(self.paramCount)
        for ii in range(0, self.paramCount):
            if self.paramLogscale[ii]:
                paramNormalized0[ii] = math.log10(self.paramInit[ii]) / math.log10(self.paramNorm[ii])
            else:
                paramNormalized0[ii] = self.paramInit[ii] / self.paramNorm[ii]
            # end if
        # end for

        self.guessParam = False
        return paramNormalized0

    # end guess

    def isErrorOccurred(self):
        """ check if a simulator error has occurred """

        maxLines = 16383
        curLine = 0
        simulatorError = None
        try:
            pathV = os.path.join(self.outputDir + self.verboseFilename)
            if not os.path.isfile(pathV):
                return  None
            # end if
            iLines = 0;
            fileT = open(self.outputDir + self.verboseFilename, "r")
            for lineT in fileT:
                if (simulatorError is not None):
                    simulatorError += lineT + '\n'
                    iLines += 1
                    if iLines >= 12:
                        return simulatorError
                    # end if
                else:
                    for (name, descr) in self.simulator.error:
                        if lineT.find(name) != -1:
                            simulatorError = name + ': ' + descr + '\n'
                        # end if
                    # end for
                # end if
                curLine += 1
                if curLine >= maxLines:
                    return simulatorError
                # end if
            # end for
            fileT.close()
        except:
            pass
        # end try

        return simulatorError

    # end isErrorOccurred

    def printOutput(self):
        """ print out the simulator output """

        maxLines = 16383
        curLine = 0
        try:
            pathV = os.path.join(self.outputDir + self.verboseFilename)
            if not os.path.isfile(pathV):
                return
            # end if
            fileT = open(self.outputDir + self.verboseFilename, "r")
            strT = "\n# ---------------------- SIMULATOR OUTPUT: ----------------------\n\n"
            print strT
            for lineT in fileT:
                strT += "# " + lineT
                print lineT
                curLine += 1
                if curLine >= maxLines:
                    strT += "\n# SIMULATOR OUTPUT TOO LONG\n"
                    break
                # end if
            # end for
            fileT.close()
            strTT = "\n\n# ---------------------------------------------------------------\n"
            print strTT
            strT += strTT
        except:
            strT = "\n# ----------------------- SIMULATOR ERROR: -----------------------\n"
            strT += "# Check the simulator output for details.\n"
            strT += "\n# ---------------------------------------------------------------\n"
            pass
        # end try

        return strT

    # end printOutput

    def stopSet(self):
        """ check is the user has required the optimization to stop """

        isStopSet = False
        pathStop = os.path.join(self.outputDir, self.stopFilename)
        try:
            if os.path.isfile(pathStop):
                isStopSet = True
                shutil.move(self.outputDir + self.stopFilename, self.outputDir + "_" + self.stopFilename)
            # end if
            if isStopSet:
                pathOf = os.path.join(self.currentDir, "ofname.txt")
                if os.path.isfile(pathOf):
                    os.unlink(pathOf)
                # end if
            # end if
        except:
            pass
        # end try
        return isStopSet

    # end stopSet

    def getOptimizeJac(self, optimFunc):
        """ Construct the Jacobian approximation function. Adapted from the SLSQSP code source (scipy/optimize/slsqp.py) """

        def optimizeJac(x, *args):
            x0 = np.asfarray(x)
            self.inJac = False
            f0 = np.atleast_1d(optimFunc(*((x0,)+args)))
            self.inJac = True
            ixcount = len(x0)
            ifcount = len(f0)
            jac = np.zeros([ixcount, ifcount])
            dx = np.zeros(ixcount)
            for ii in range(ixcount):
                self.log("\nJacobian approximation [%d / %d]..." % (ii + 1, ixcount))
                dx[ii] = self.jaceps[ii]
                jac[ii] = (optimFunc(*((x0+dx,)+args)) - f0) / self.jaceps[ii]
                dx[ii] = 0.0
            # end for
            self.jacCounter += ixcount
            self.inJac = False
            return jac.transpose()
        # end optimizeJac

        return optimizeJac
    # end getOptimizeJac

    def getWeight(self, paramIndex, paramNormalized):
        """ weight/cost function for future use (giving each parameter a weight...) """
        if (self.paramWeight == False) or (self.weightFunc is None) or (self.paramBounds is None) or (paramIndex < 0) or (paramIndex >= self.paramCount):
            return 1.0
        # end if
        icw = len(self.weightFunc)
        if (icw < 7):
            return 1.0
        # end if
        (paramMin, paramMax) = self.paramBounds[paramIndex]
        if (paramMax <= paramMin) or (paramNormalized < paramMin) or (paramNormalized > paramMax):
            return 0.0
        # end if
        tdw = (paramMax - paramMin)
        idx = int(((paramNormalized - paramMin) * float(icw)) / tdw)
        if (idx < 0):
            idx = 0
        elif (idx >= icw):
            idx = icw - 1
        # end if
        return self.weightFunc[idx]
    # end if

    def optimizeFuncBayesian(self, **paramNormalizedBayesian):
        """ the optimizer maximization function for the Bayesian method """
        paramCount = len(paramNormalizedBayesian)
        if (self.paramCount != paramCount):
            # should never happen
            try:
                self.finish(errorOccured=True, userStopped=True)
            except:
                self.isRunning = False
                sys.exit(1)
            # end try
            return 0.0
        # end if
        paramNormalized = np.zeros(self.paramCount)
        for paramT in paramNormalizedBayesian:
            for ii in range(0, self.paramCount):
                if (self.paramName[ii] == paramT):
                    paramNormalized[ii] = float(paramNormalizedBayesian[paramT])
                    break
                # end if
            # end for
        # end for
        return self.optimizeFunc(paramNormalized)
    # end optimizeFuncBayesian

    def optimizeFunc(self, paramNormalized):
        """ the optimizer minimization function """

        paramCount = len(paramNormalized)
        if (self.paramCount != paramCount):
            # should never happen
            try:
                self.finish(errorOccured=True, userStopped=True)
            except:
                self.isRunning = False
                sys.exit(1)
            # end try
            return 0.0
        # end if

        bShowOutput = ((self.inJac == False) or (self.optimType == "Brute"))

        # If stopFilename exists, stop optimization
        if self.stopSet():
            try:
                self.finish(errorOccured=False, userStopped=True)
            except:
                self.isRunning = False
                sys.exit(1)
            # end try
            return 0.0
        # end if

        for ii in range(0, self.paramCount):
            if self.paramLogscale[ii]:
                self.paramNatural[ii] = math.pow(10.0, (paramNormalized[ii] * math.log10(self.paramNorm[ii])))
            else:
                self.paramNatural[ii] = paramNormalized[ii] * self.paramNorm[ii]
            # end if
        # end for

        # A cache strategy is implemented to avoid redundant calculation.
        try:
            if (self.funcCounter >= 1) and (len(self.lastParam) >= 1):
                tParam = ""
                for ii in range(0, self.paramCount - 1):
                    tParam += (self.paramFormat[ii] % self.paramNatural[ii]) + "\t"
                # end for
                tParam += (self.paramFormat[self.paramCount - 1] % self.paramNatural[self.paramCount - 1])
                if (tParam in self.lastParam):
                    return self.lastOutput[self.lastParam.index(tParam)]
                # end if
            # end if
        except:
            pass
        # end try

        strT = ""

        if (bShowOutput == True):
            if self.guessParam:
                strT = "\n-------------------- GUESS " + (self.counterFormat.format(self.optimCounter)) + " RUNNING -----------------------\n"
            else:
                strT = "\n----------------- OPTIMIZATION " + (self.counterFormat.format(self.optimCounter)) + " RUNNING ---------------------\n"
            # end if

            strT += self.title + ": Optimization (" + self.optimType
            if self.optimType == "Optim":
                strT += " " + self.minimizeMethod
            # end if

            strT += ") "
            dateT = datetime.datetime.now()
            dateStr = dateT.strftime("%Y-%m-%d %H:%M:%S")
            strT += (dateStr + "\n")

            strT += "Parameter:\t"
            for ii in range(0, self.paramCount - 1):
                if ((self.paramPoints[ii] > 1) or (self.bruteSimul == False)):
                    strT += "@" + self.paramName[ii] + "\t"
                else:
                    strT += self.paramName[ii] + "\t"
                # end if
            # end for
            if ((self.paramPoints[self.paramCount - 1] > 1) or (self.bruteSimul == False)):
                strT += "@" + self.paramName[self.paramCount - 1] + "\n"
            else:
                strT += self.paramName[self.paramCount - 1] + "\n"
            # end if

            strT += "Natural:\t"
            for ii in range(0, self.paramCount - 1):
                strT += (self.paramFormat[ii] % self.paramNatural[ii]) + "\t"
            # end for
            strT += (self.paramFormat[self.paramCount - 1] % self.paramNatural[self.paramCount - 1]) + "\n"

            strT += "Normalized:\t"
            for ii in range(0, self.paramCount - 1):
                strT += (self.paramFormatNormalized[ii] % paramNormalized[ii]) + "\t"
            # end for
            strT += (self.paramFormatNormalized[self.paramCount - 1] % paramNormalized[self.paramCount - 1])

            strT += "\n---------------------------------------------------------------\n"

            self.log(strT)
        # end if bShowOutput

        ticT = time.time()

        pathIn = os.path.join(self.outputDir, self.inputFilename)
        if not os.path.isfile(pathIn):
            dispError("cannot open input: file not found", doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
        # end if

        fileContent = ""

        fileT = open(self.outputDir + self.inputFilename, "r")
        for lineT in fileT:
            # Normalize line ending (Silvaco do not run input file if contains CRLF terminated lines)
            lineT = lineT.rstrip("\r\n")
            lineX = lineT.lstrip("\t ")
            nSpaces = len(lineT) - len(lineX)
            prefixT = lineT[0:nSpaces]

            if (self.simulator.name == "atlas") and lineX.startswith("tonyplot"):
                # skip tonyplot commands
                continue
            # endif

            if not lineX.startswith("#"):
                for ii in range(0, self.paramCount):
                    setparamT = self.simulator.vardeclpre % self.paramName[ii]
                    if lineX.startswith(setparamT):
                        # Need to format parameter to match simulator floating representation
                        strT = self.paramFormatShort[ii] % self.paramNatural[ii]
                        fT = float(strT)
                        lineT = self.simulator.vardecl % (self.paramName[ii], fT)
                        break
                    # end if
                # end for
            # end if

            fileContent += (lineT + "\n")
        # end for

        fileT.close()

        fileT = open(self.outputDir + self.inputFilename, "w")
        fileT.write(fileContent)
        fileT.close()

        # format model files
        if self.modelCount > 0:
            for ii in range(0, self.modelCount):
                if self.modelFilename[ii] == "":
                    break
                # end if
                pathCC = os.path.join(self.outputDir, self.modelFilename[ii])
                if not os.path.isfile(pathCC):
                    dispError("cannot open model file: " + self.modelFilename[ii], doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
                # end if

                fileContent = ""

                fileT = open(self.outputDir + self.modelFilename[ii], "r")
                for lineT in fileT:
                    # Normalize line ending (Silvaco do not run input file if contains CRLF terminated lines)
                    lineT = lineT.rstrip("\r\n")
                    lineX = lineT.lstrip("\t ")
                    nSpaces = len(lineT) - len(lineX)
                    prefixT = lineT[0:nSpaces]

                    bFound = False

                    nn = len(self.paramName)
                    bFound = False
                    if nn > 0:
                        for jj in range(0, nn):
                            setparamT = "double " + self.paramName[jj] + " = "
                            if lineX.startswith(setparamT):
                                strT = self.paramFormat[jj] % self.paramNatural[jj]
                                fT = float(strT)
                                lineT = prefixT + setparamT + ("%g" % fT) + ";"
                                bFound = True
                                break
                            # end if
                        # end for
                    # end if

                    fileContent += (lineT + "\n")
                # end while
                fileT.close()

                fileT = open(self.outputDir + self.modelFilename[ii], "w")
                fileT.write(fileContent)
                fileT.close()
            # end for
        # end if

        # remove the simulator verbose output file before starting optimization
        pathT = os.path.join(self.outputDir, self.verboseFilename)
        try:
            if os.path.isfile(pathT):
                os.unlink(pathT)
            # end if
        except:
            pass
        # end try

        # run optimization
        try:
            tEnv = dict(os.environ)
            subprocess.check_call([self.outputDir + self.commandFilename, ""], shell=True, env=tEnv)
        except subprocess.CalledProcessError, excT:
            try:
                strT = self.printOutput()
                fileOptim = open(self.outputDir + self.outputOptimizedFilename, "a")
                fileOptim.write(strT)
                fileOptim.close()
            except:
                pass
            # end try
            dispError(traceback.format_exc(), doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
            return 0.0
        # end try

        simulatorError = self.isErrorOccurred()
        if (simulatorError is not None):
            try:
                fileOptim = open(self.outputDir + self.outputOptimizedFilename, "a")
                fileOptim.write(simulatorError)
                fileOptim.close()
            except:
                pass
            # end try
            dispError(simulatorError, doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
            return 0.0
        # end if

        # Calculate the efficiency (Very important to be precise for the optimization algorithm)
        LinesToSkip = 4
        arrVoltage = np.array([])
        arrCurrent = np.array([])
        arrPower = np.array([])
        iLine = 0
        iPoints = 0
        iPVPoints = 0

        pathJV = os.path.join(self.outputDir, self.outputFilename[self.outputFilenameJVposition])
        if not os.path.isfile(pathJV):
            # do not necessarily exit, since the simulator can sometimes diverge for a set of parameters choosen by the optimizer
            dispError("cannot evaluate efficiency: J-V file not found: check the simulator output file (%s)" % self.verboseFilename, doExit = False)
            return 0.0
        # end if

        # :REV:1:20181115: J(V) from V = 0 to V = VOC (the photovoltaic part of the I(V) characteristic)
        pathJVP = os.path.join(self.outputDir, self.outputFilename[self.outputFilenameJVPposition])
        JVPcontent = ""

        try:

            iVpos = 0
            fVocx = 0.0
            fV = 0.0
            fVprev = 0.0
            fJ = 0.0
            fJprev = 0.0
            fP = 0.0
            fPprev = 0.0
            DblPrecision = 1e-13
            bStarted = False
            bFirstV = False

            fileT = open(self.outputDir + self.outputFilename[self.outputFilenameJVposition], "r")
            for lineT in fileT:

                if (lineT.startswith("#")):
                    JVPcontent += lineT
                    continue
                # end if

                if iLine < LinesToSkip:
                    iLine += 1
                    JVPcontent += lineT
                    continue
                # end if

                arrLine = []
                try:
                    arrLine = lineT.split(self.simulator.dataSeparator)
                except:
                    break
                # end try

                if (len(arrLine) < 2):
                    continue
                # end if

                fV = float(arrLine[0])
                fJ = float(arrLine[1])
                fP = math.fabs(fV * fJ)

                if (False == bStarted):
                    bStarted = True
                    fVprev = fV
                    fJprev = fJ
                    fPprev = fP
                    continue
                # end if

                # voltage should be in increasing order
                if (fV <= fVprev):
                    fVprev = fV
                    fJprev = fJ
                    fPprev = fP
                    continue
                # end if

                if (math.fabs(fV) < DblPrecision):
                    fV = 0.0
                # end if

                if (False == bFirstV):
                    bFirstV = True
                    arrVoltage = np.append(arrVoltage, fVprev)
                    arrCurrent = np.append(arrCurrent, fJprev)
                    arrPower = np.append(arrPower, fPprev)
                    continue
                # end if

                arrVoltage = np.append(arrVoltage, fV)
                arrCurrent = np.append(arrCurrent, fJ)
                arrPower = np.append(arrPower, fP)
                iPoints = len(arrVoltage)

                if (fV * fJ) <= 0.0:
                    JVPcontent += lineT
                # end if

                if (iPoints != len(arrCurrent)) or (iPoints != len(arrPower)):
                    # do not necessarily exit, since the simulator can sometimes diverge for a set of parameters choosen by the optimizer
                    dispError("cannot evaluate efficiency: J-V file content not valid: check the simulator output file (%s)" % self.verboseFilename, doExit = False)
                    return 0.0
                # end if

                if ((fV > 0.0) and (fJ > 0.0)):
                    if (iVpos == 0):
                        fVocx = fVprev
                    # end if
                    iVpos += 1
                    if (iVpos > 2):
                        break
                    # end if
                # end if

                if ((fV * fJ) < 0.0):
                    iPVPoints += 1
                #end if

                fVprev = fV

                iLine += 1
            # end for

            fileT.close()

        except:
            dispError("cannot evaluate efficiency: check the simulator output file (%s)" % self.verboseFilename, doExit = True)
            pass
        # end try

        # :REV:1:20181115: J(V) from V = 0 to V = VOC
        fileJVP = open(pathJVP, "w")
        fileJVP.write(JVPcontent)
        fileJVP.close()

        outputT = 0.0
        outputO = 0.0

        # max current in mA/cm2
        fJm = 0.0

        # max voltage in Volts
        fVm = 0.0

        # Fill Factor
        fFF = 0.0

        # Short-circuit current in mA/cm2
        fJsc = 0.0

        # Open-circuit voltage in Volts
        fVoc = 0.0

        # Maximal power in mW/cm2
        Pmax = 0.0

        doCalc = True

        if (iPVPoints < 12):
            doCalc = False
            # do not necessarily exit, since the simulator can sometimes diverge for a set of parameters choosen by the optimizer
            dispError("Cannot evaluate efficiency: J-V curve has less than 12 points with V*J < 0", doExit = False)
            return 0.0
        # end if

        try:
            if doCalc == True:

                windowLen = 8

                # interpolate the J-V data to accurately calculate the efficiency
                funcCurrent = interpolate.interp1d(arrVoltage, arrCurrent, kind='slinear')
                funcPower = interpolate.interp1d(arrVoltage, arrPower, kind='cubic')
                iPointsNew = iPoints * windowLen
                #

                dV = (arrVoltage[iPoints - 1] - arrVoltage[0]) / float(iPointsNew - 1)
                arrVoltageNew = np.arange(arrVoltage[0], arrVoltage[iPoints - 1] + dV, dV)
                iPointsNew = len(arrVoltageNew)

                iC = iPointsNew
                for ii in range(iPointsNew - 1, iPointsNew - windowLen - 1, -1):
                    if (arrVoltageNew[ii] > arrVoltage[iPoints - 1]):
                        iC -= 1
                    else:
                        break
                    # end if
                # end for
                if iC < iPointsNew:
                    arrVoltageNew = np.delete(arrVoltageNew, np.arange(iC, iPointsNew, 1))
                    iPointsNew = len(arrVoltageNew)
                # end for

                arrCurrentNew = funcCurrent(arrVoltageNew)
                arrPowerNew = funcPower(arrVoltageNew)
                dV = arrVoltageNew[1] - arrVoltageNew[0]

                bFoundJsc = False
                bFoundVoc = False
                bFoundPmax = False

                # Find PV parameters
                for ii in range(0, iPointsNew - 1):

                    # Short-circuit current (mA/cm2)
                    if (bFoundJsc == False):
                        if ((arrVoltageNew[ii] < 0.0) and (arrVoltageNew[ii + 1] > 0.0)):
                            fJsc = 0.5 * (arrCurrentNew[ii] + arrCurrentNew[ii + 1])
                            bFoundJsc = True
                            if (bFoundJsc == True) and (bFoundVoc == True) and (bFoundPmax == True):
                                break
                            # end if
                        elif ((arrVoltageNew[ii] >= 0.0) and (arrVoltageNew[ii] <= dV)):
                            fJsc = arrCurrentNew[ii]
                            bFoundJsc = True
                            if (bFoundJsc == True) and (bFoundVoc == True) and (bFoundPmax == True):
                                break
                            # end if
                        # end if
                    # end if

                    # Open-circuit voltage (V)
                    if (bFoundVoc == False):
                        if ((arrCurrentNew[ii] < 0.0) and (arrCurrentNew[ii + 1] > 0.0)):
                            fVoc = 0.5 * (arrVoltageNew[ii] + arrVoltageNew[ii + 1])
                            bFoundVoc = True
                            if (bFoundJsc == True) and (bFoundVoc == True) and (bFoundPmax == True):
                                break
                        elif ((arrCurrentNew[ii] < 0.0) and (arrCurrentNew[ii + 1] >= 0.0)):
                            fVoc = arrVoltageNew[ii + 1]
                            bFoundVoc = True
                            if (bFoundJsc == True) and (bFoundVoc == True) and (bFoundPmax == True):
                                break
                            # end if
                        # end if
                    # end if

                    # Maximum power (mW/cm2)
                    if ((bFoundPmax == False) and (ii >= windowLen) and (ii <= (iPointsNew - windowLen))):
                        if ((arrPowerNew[ii - 2] < arrPowerNew[ii - 1]) and (arrPowerNew[ii - 1] < arrPowerNew[ii]) and (arrPowerNew[ii] > arrPowerNew[ii + 1]) and (arrPowerNew[ii + 1] > arrPowerNew[ii + 2])):
                            fJm = arrCurrentNew[ii]
                            fVm = arrVoltageNew[ii]
                            Pmax = arrPowerNew[ii]
                            bFoundPmax = True
                            if (bFoundJsc == True) and (bFoundVoc == True) and (bFoundPmax == True):
                                break
                            # end if
                        # end if
                    # end if

                    # Direct polarization
                    if (((arrVoltageNew[ii] > 0.0) and (arrCurrentNew[ii] > 0.0)) or ((arrVoltageNew[ii] < 0.0) and (arrCurrentNew[ii] < 0.0))) and bFoundPmax:
                        break
                    # end if
                # end for (Find PV parameters)

                outputT = 0.0

                if (bFoundJsc == False):
                    if ((arrCurrentNew[0] < 0.0) and (arrVoltageNew[0] > 0.0)):
                        fJsc = -arrCurrentNew[0]
                    # end if
                else:
                    fJsc = -fJsc
                # end if

                if (bFoundVoc == False):
                    if ((arrCurrentNew[iPointsNew - 1] < 0.0) and (arrVoltageNew[iPointsNew - 1] > 0.0)):
                        fVoc = arrVoltageNew[iPointsNew - 1]
                    else:
                        if (fVocx > 0.01):
                            fVoc = fVocx
                            bFoundVoc = True
                        # end if
                    # end if
                # end if

                if ((bFoundJsc == True) and (bFoundVoc == True) and (bFoundPmax == True)):
                    fFF = 100.0 * Pmax / math.fabs(fJsc * fVoc)
                    if (fFF >= 95.0):
                        bFoundPmax = False
                    # end if
                # end if

                if ((bFoundJsc == False) or (bFoundVoc == False) or (bFoundPmax == False)):
                    strT = "Cannot evaluate efficiency: "
                    if bFoundJsc:
                        strT += ("  JSC = %.5f mA/cm2" % math.fabs(fJsc))
                    else:
                        strT += "  JSC not found"
                    # end if
                    if bFoundVoc:
                        strT += ("  VOC = %.5f V" % math.fabs(fVoc))
                    else:
                        strT += "  VOC not found"
                    # end if
                    if bFoundPmax:
                        strT += ("  Pmax = %.5f mW/cm2" % Pmax)
                    else:
                        strT += "  Pmax not found"
                        Pmax = 0.0
                    # end if
                    strT += "\n -> increase V-range and/or decrease V-step"
                    dispError(strT, doExit = False)
                # end if

                # AM 1.5 power density ~ 100 mW/cm2 (Atlas default AM 1.5 spectrum gives 100.037 mW/cm2)
                Psolar = 100.037
                outputT = 100.0 * Pmax / Psolar
                if (outputT == 0.0):
                    fJsc = 0.0
                    fVoc = 0.0
                    fFF = 0.0
                # end if

                # efficiency as calculated by the simulator
                if ((bFoundJsc == True) and (bFoundVoc == True) and (bFoundPmax == True)):
                    pathEE = os.path.join(self.outputDir, self.outputFilename[self.outputFilenameEFFposition])
                    if not os.path.isfile(pathEE):
                        dispError("cannot evaluate efficiency: '%s' file not found" % pathEE, doExit = False)
                    # end if
                    outputO = 0.0
                    try:
                        fileO = open(self.outputDir + self.outputFilename[self.outputFilenameEFFposition], "r")
                        lineO = ""
                        iLT = len("Efficiency=20.123456789123456789123456789")
                        for lineOT in fileO:
                            if lineOT.startswith("Efficiency=") and (len(lineOT) <= iLT):
                                lineO = lineOT
                            # end if
                        # end for
                        fileO.close()
                        if lineO.startswith("Efficiency=") and (len(lineO) <= iLT):
                            outputO = float(lineO.split("=")[1].rstrip(" \t\r\n").lstrip(" \t\r\n"))
                        # end if
                        os.unlink(self.outputDir + self.outputFilename[self.outputFilenameEFFposition])
                    except:
                        outputO = 0.0
                        pass
                    # end try
                # end if

                if outputT > self.outputOptimized:
                    self.outputOptimized = outputT
                    self.paramOptim = np.zeros(self.paramCount)
                    for ii in range(0, self.paramCount):
                        self.paramOptim[ii] = self.paramNatural[ii]
                    # end if
                # end if

                if math.fabs(fJsc) > self.outputOptimizedx:
                    self.outputOptimizedx = math.fabs(fJsc)
                # end if

                if math.fabs(fVoc) > self.outputOptimizedy:
                    self.outputOptimizedy = math.fabs(fVoc)
                # end if

                if fFF > self.outputOptimizedz:
                    self.outputOptimizedz = fFF
                # end if

            # end if doCalc
        except:
            dispError(traceback.format_exc(), doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
            pass
        # end try

        durationT = time.time() - ticT
        self.elapsedTime += durationT
        if self.funcCounter == 1:
            self.delayMean = float(durationT)
        else:
            self.delayMean = float(self.elapsedTime) / float(self.funcCounter)
        # end if

        if (self.delayMin == 0) or (durationT < self.delayMin):
            self.delayMin = durationT
        if (self.delayMax == 0) or (durationT > self.delayMax):
            self.delayMax = durationT

        try:
            # Timing information
            pathDelay = os.path.join(self.outputDir, self.delayFilename)
            fileT = open(pathDelay, "w")
            fileT.write("DelayMin = %.1f" % self.delayMin)
            fileT.write("\nDelayMax = %.1f" % self.delayMax)
            fileT.write("\nDelayMean = %.1f" % self.delayMean)
            fileT.close()
        except:
            pass

        strT = ""

        if (bShowOutput == True):
            if self.guessParam:
                strT = "\n---------------------- GUESS " + (self.counterFormat.format(self.optimCounter)) + " DONE --------------------------\n"
            else:
                strT = "\n------------------- OPTIMIZATION " + (self.counterFormat.format(self.optimCounter)) + " DONE ----------------------\n"
            # end if

            strT += self.title + ": Optimization (" + self.optimType
            if self.optimType == "Optim":
                strT += " " + self.minimizeMethod
            # end if

            strT += ") "
            dateT = datetime.datetime.now()
            dateStr = dateT.strftime("%Y-%m-%d %H:%M:%S")
            strT += (dateStr + "\n")

            strT += "Parameter:\t"
            for ii in range(0, self.paramCount - 1):
                strT += self.paramName[ii] + "\t"
            # end for
            strT += self.paramName[self.paramCount - 1] + "\n"

            strT += "Natural:\t"
            tParam = ""
            for ii in range(0, self.paramCount - 1):
                strT += (self.paramFormat[ii] % self.paramNatural[ii]) + "\t"
                tParam += (self.paramFormat[ii] % self.paramNatural[ii]) + "\t"
            # end for
            strT += (self.paramFormat[self.paramCount - 1] % self.paramNatural[self.paramCount - 1]) + "\n"
            tParam += (self.paramFormat[self.paramCount - 1] % self.paramNatural[self.paramCount - 1])
            if (len(self.lastParam) >= self.lastParamLimit):
                self.lastParam.pop(0)
            # end if
            self.lastParam.append(tParam)

            strT += "Normalized:\t"
            for ii in range(0, self.paramCount - 1):
                strT += (self.paramFormatNormalized[ii] % paramNormalized[ii]) + "\t"
            # end for
            strT += (self.paramFormatNormalized[self.paramCount - 1] % paramNormalized[self.paramCount - 1])

            strT += "\nPRESENT Efficiency: " + ("%g %%" % outputT) + " (Simulator: " + ("%g %%" % outputO) + ")"
            strT += "\nPRESENT " + ("FF = %08.5f %%" % fFF) + (" ; Jsc = %08.5f mA/cm2" % math.fabs(fJsc)) + (" ; Voc = %08.5f V" % math.fabs(fVoc))
            strT += "\nMAXIMUM Efficiency: %g %%" % self.outputOptimized
            strT += "\nThis run duration: " + self.printTime(float(durationT)) + " (mean: " + self.printTime(self.delayMean) + ")"
            strT += "\nElapsed time: " + self.printTime(float(self.elapsedTime))
            strT += "\nNumber of function evaluations: %d" % self.funcCounter
            if self.bruteSimul:
                if self.funcCounter < (self.paramCountTotal - 1):
                    remainingT = (float(self.paramCountTotal) * self.delayMean) - float(self.elapsedTime)
                    strT += "\nEstimated remaining time: " + self.printTime(remainingT)
                # end if
            # end if
            strT += "\n---------------------------------------------------------------\n"

            self.log(strT)

            dateStrCompact = None

            if not self.guessParam:
                dateT = datetime.datetime.now()
                dateStrCompact = dateT.strftime("%Y%m%d-%H%M%S")

                strT = (self.counterFormat.format(self.optimCounter)) + "\t" + dateStrCompact + "\t"

                for ii in range(0, self.paramCount):
                    strT += (self.paramFormat[ii] % self.paramNatural[ii]) + "\t"
                # end for

                strT += ("%08.5f\t" % math.fabs(fJm)) + ("%08.5f\t" % fVm) + ("%08.5f\t" % fFF) + ("%08.5f\t" % math.fabs(fJsc)) + ("%08.5f\t" % math.fabs(fVoc)) + ("%08.5f" % outputT) + "\n"
                fileOptim = open(self.outputDir + self.outputOptimizedFilename, "a")
                fileOptim.write(strT)
                fileOptim.close()
            # end if

            # in updateOutput, output files are moved
            self.updateOutput(dateStrCompact)
        else:
            # delete output files before the next run
            self.deleteOutput()
        # end if bShowOutput

        if self.inJac == False:
            self.optimCounter += 1
        # end if

        self.funcCounter += 1

        if not self.guessParam:

            if self.paramWeight and self.isBound:
                # Parameters Weight: decreases near the bounds
                fa = 0.0
                fb = 0.0
                for ii in range(0, self.paramCount):
                    ft = (paramNormalized[ii] * paramNormalized[ii])
                    fa += ft
                    fw = self.getWeight(ii, paramNormalized[ii])
                    fb += ft * (fw * fw)
                # end for
                if (fa > 0.0):
                    outputT = outputT * (fb / fa)
                # end if
            # end if

            # all methods minimize except the Bayesian method that maximizes
            if (self.minimizeMethod == "Bayes"):
                tOutput = outputT
            else:
                tOutput = (1.0 - (outputT / 100.0))
            # end if

            if (len(self.lastOutput) >= self.lastParamLimit):
                self.lastOutput.pop(0)
            # end if
            self.lastOutput.append(tOutput)

            return tOutput

        else:
            return outputT
        # end if

    # end optimizeFunc

    @staticmethod
    def removeOutputFiles(outputDirT):
        """ delete the output directory content (everything in that directory will be deleted!) """
        for fileT in os.listdir(outputDirT):
            pathT = os.path.join(outputDirT, fileT)
            try:
                if os.path.isfile(pathT):
                    os.unlink(pathT)
                elif os.path.isdir(pathT):
                    shutil.rmtree(pathT)
                # end if
            except:
                pass
            # end try
        # end for

        return

    # end removeOutputFiles

    def updateOutputFile(self, outputFilenameOld, outputComment, outputFilenameSuffix):
        """ update/rename the output files"""

        outputFilenameNew = ""

        # Files in the output directory
        try:
            pathOld = os.path.join(self.outputDir, outputFilenameOld)
            if not os.path.isfile(pathOld):
                return
            # end if
            fileT = open(self.outputDir + outputFilenameOld, "r")
            fileT.close()
            # Exists... change filename and shutil.move it to the outpur dir
            strT1 = outputFilenameOld.split(".")[0]
            strT2 = outputFilenameOld.split(".")[1]
            outputFilenameNew = strT1 + "_" + (self.counterFormat.format(self.optimCounter)) + "_" + outputFilenameSuffix + "." + strT2
            shutil.move(self.outputDir + outputFilenameOld, self.outputDir + outputFilenameNew)
        except:
            return
        # end try

        try:
            # Save optimization infos
            fileHeader = "# " + self.title + "\n# "
            fileHeader += outputComment + "\n# "
            for ii in range(0, self.paramCount):
                fileHeader += self.paramName[ii]
                if (ii < (self.paramCount - 1)):
                    fileHeader += "\t"
                # end if
            # end for
            fileHeader += "\n# "
            for ii in range(0, self.paramCount):
                fileHeader += (self.paramFormat[ii] % self.paramNatural[ii])
                if (ii < (self.paramCount - 1)):
                    fileHeader += "\t"
                # end if
            # end for
            fileHeader += "\n"

            fileT = open(self.outputDir + outputFilenameNew, "r")
            lll = False
            fileContent = ""
            for lineT in fileT:
                if lll == False:
                    if lineT.startswith(self.simulator.header):
                        fileContent = lineT + fileHeader
                    else:
                        fileContent = fileHeader + lineT
                    lll = True
                    continue
                # end if
                fileContent += lineT
            # end for
            fileT.close()
            fileContent += "\n"
            fileT = open(self.outputDir + outputFilenameNew, "w")
            fileT.write(fileContent)
            fileT.close()
        except Exception as excT:
            # catch only Exception (since sys.exit raise BaseException)
            dispError(traceback.format_exc(), doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
            pass
        # end try

        return

    # end updateOutputFile

    def updateOutput(self, dateStrCompact):
        """ update the simulator output files """

        if dateStrCompact is None:
            dateT = datetime.datetime.now()
            dateStrCompact = dateT.strftime("%Y%m%d-%H%M%S")
        # end if

        for ii in range(0, self.outputCount):
            self.updateOutputFile(self.outputFilename[ii], self.outputComment[ii], dateStrCompact)
        # end for

        return

    # end updateOutput

    def deleteOutput(self):
        """ delete the simulator output files """

        for ii in range(0, self.outputCount):
            pathT = os.path.join(self.outputDir, self.outputFilename[ii])
            try:
                if os.path.isfile(pathT):
                    os.unlink(pathT)
                # end if
            except:
                pass
            # end try
        # end for

        return

    # end deleteOutput

    def prepare(self):
        """ prepare the optimization """

        if self.isRunning:
            return False
        # end if

        if not self.isChecked():
            dispError("Simulator path and parameters not up to date",
                        doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
        # end if

        if not self.updatePath():
            dispError("Simulator path cannot be updated", doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
        # end if

        self.optimCounter = 1
        self.funcCounter = 1
        self.jacCounter = 0
        self.elapsedTime = 0
        self.isRunning = True

        self.stopSet()

        # remove the simulator verbose output file
        pathT = os.path.join(self.outputDir, self.verboseFilename)
        try:
            if os.path.isfile(pathT):
                os.unlink(pathT)
            # end if
        except:
            pass
        # end try

        # remove the simulator output files
        for ii in range(0, self.outputCount):
            pathT = os.path.join(self.outputDir, self.outputFilename[ii])
            try:
                if os.path.isfile(pathT):
                    os.unlink(pathT)
                # end if
            except:
                pass
            # end try
        # end for

        self.optimCounter = 1
        self.funcCounter = 1
        self.elapsedTime = 0
        dateT = datetime.datetime.now()
        dateStr = dateT.strftime("%Y-%m-%d %H:%M:%S")

        strT = "# ---------------------------------------------------------------\n"
        strT += "# " + self.title + "\n# Optimization (" + self.optimType
        if self.optimType == "Optim":
            strT += " " + self.minimizeMethod
        # end if
        strT += ") started @ "
        strT += dateStr
        if self.optimType == "Optim":
            strT += ("\n# With tolerance = %g" % self.tolerance)
            if (self.minimizeMethod != "Bayes"):
                strT += (" and jaceps = [ %.5f" % self.jaceps[0])
                if self.paramCount > 1:
                    strT += ("  %.5f" % self.jaceps[1])
                # end if
                if self.paramCount > 2:
                    strT += ("  %.5f" % self.jaceps[2])
                # end if
                if self.paramCount > 3:
                    strT += ("  %.5f" % self.jaceps[3])
                # end if
                if self.paramCount > 4:
                    strT += ("  %.5f" % self.jaceps[4])
                # end if
                if self.paramCount > 5:
                    strT += " ..."
                # end if
                strT += " ]"
            # end if
            if self.paramWeight and self.isBound:
                strT += " Weighted"
            # end if
        # end if
        strT += "\n# Parameter:\t"
        for ii in range(0, self.paramCount - 1):
            strT += self.paramName[ii] + "\t"
        # end for
        strT += self.paramName[self.paramCount - 1] + "\n"
        strT += "# StartValue:\t"
        for ii in range(0, self.paramCount - 1):
            strT += (self.paramFormat[ii] % self.paramStart[ii]) + "\t"
        # end for
        strT += (self.paramFormat[self.paramCount - 1] % self.paramStart[self.paramCount - 1]) + "\n"
        strT += "# EndValue:  \t"
        for ii in range(0, self.paramCount - 1):
            strT += (self.paramFormat[ii] % self.paramEnd[ii]) + "\t"
        # end for
        strT += (self.paramFormat[self.paramCount - 1] % self.paramEnd[self.paramCount - 1]) + "\n"
        strT += "# InitValue: \t"
        for ii in range(0, self.paramCount - 1):
            strT += (self.paramFormat[ii] % self.paramInit[ii]) + "\t"
        # end for
        strT += (self.paramFormat[self.paramCount - 1] % self.paramInit[self.paramCount - 1]) + "\n"
        strT += "# NormValue: \t"
        for ii in range(0, self.paramCount - 1):
            strT += (self.paramFormat[ii] % self.paramNorm[ii]) + "\t"
        # end for
        strT += (self.paramFormat[self.paramCount - 1] % self.paramNorm[self.paramCount - 1])
        if self.optimType == "Brute":
            strT += "\n# Points:   \t"
            for ii in range(0, self.paramCount - 1):
                strT += ("%9s" % self.paramPoints[ii]) + "\t"
            # end for
            strT += ("%9s" % self.paramPoints[self.paramCount - 1])
        # end if
        strT += "\n# ---------------------------------------------------------------\n\n"
        self.log(strT)

        fileOptim = open(self.outputDir + self.outputOptimizedFilename, "w")
        fileOptim.write(strT)
        fileOptim.close()

        return True

    # end prepare

    def start(self, optimType):
        """ start the optimization """

        if optimType == "Snap":
            self.startSnapshot()
        elif optimType == "Brute":
            self.startBrute()
        elif optimType == "Optim":
            self.startOptim()
        else:
            dispError("Specify the optimType to \"Brute\", \"Snap\" or \"Optim\"", doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
        #end if

    # end start

    def startOptim(self):
        """ start the optimization """

        self.optimType = "Optim"

        self.setCounterFormat(maxcount = 2 * self.optimPoints)

        tStart = 0.0
        tEnd = 0.0
        tRangeMin = 0.0
        tEps = 5.0 * self.tolerance

        self.jaceps = np.zeros(self.paramCount)

        self.paramBounds = list() if self.isBound else None

        if self.paramWeight and self.isBound:
            # Tukey Window (Parameters Weight: decreases near the bounds)
            # If optimum is near the bounds, disable this feature or enlarge domain
            nn = self.weightPoints
            aa = self.weightAlpha
            wn = np.arange(0, nn)
            wm = nn % 2
            ww = int(np.floor(aa * (float(nn - 1)) / 2.0))
            wn1 = wn[0 : ww + 1]
            wn2 = wn[ww + 1 : nn - ww - 1]
            wn3 = wn[nn - ww - 1 :]
            ww1 = 0.5 * (1.0 + np.cos(np.pi * (-1.0 + (2.0 * wn1 / aa / (nn - 1)))))
            ww2 = np.ones(wn2.shape)
            ww3 = 0.5 * (1.0 + np.cos(np.pi * ((-2.0 / aa) + 1.0 + (2.0 * wn3 / aa / (nn - 1)))))
            self.weightFunc = np.concatenate((ww1, ww2, ww3))

            # Save the weight window
            try:
                icw = len(self.weightFunc)
                if (icw >= 7):
                    fileT = open(self.outputDir + self.weightFilename, "w")
                    strT = "# " + self.title + "\n"
                    strT += "# Tukey Window (Parameters Weight)\n"
                    for ii in range(0, icw):
                        strT += ("\n%d\t%g" % (ii, self.weightFunc[ii]))
                    # end for
                    fileT.write(strT)
                    fileT.close()
                # end if
            except:
                pass
            # end try

        else:
            self.weightFunc = None
        # end if

        strT = "Index\tTime\t"

        for ii in range(0, self.paramCount):
            if self.paramLogscale[ii]:
                tStart = math.log10(self.paramStart[ii]) / math.log10(self.paramNorm[ii])
                tEnd = math.log10(self.paramEnd[ii]) / math.log10(self.paramNorm[ii])
            else:
                tStart = self.paramStart[ii] / self.paramNorm[ii]
                tEnd = self.paramEnd[ii] / self.paramNorm[ii]
            # end if
            if (ii == 0) or ((tEnd - tStart) < tRangeMin):
                tRangeMin = tEnd - tStart
            # end if

            self.jaceps[ii] = (tEnd - tStart) / float(self.optimPoints - 1)

            # tolerance should be kept less than eps
            if self.jaceps[ii] <= self.tolerance:
                self.tolerance = 0.2 * self.jaceps[ii]
            # end if

            if self.isBound:
                self.paramBounds.append((tStart, tEnd))
            # end if
            strT += self.paramName[ii] + "\t"
        # end for

        if tRangeMin < 1e-9:
            return False
        # end if

        tEps = tRangeMin / float(self.optimPoints - 1)

        # tolerance should be kept less than eps
        if tEps <= self.tolerance:
            self.tolerance = 0.2 * tEps
        # end if

        if not self.prepare():
            return False
        # end if

        strT += "Jm(mA/cm2)\tVm(V)\tFF(%)\tJsc(mA/cm2)\tVoc(V)\tEfficiency\n"
        fileOptim = open(self.outputDir + self.outputOptimizedFilename, "a")
        fileOptim.write(strT)
        fileOptim.close()

        self.optimCounter = 1
        self.funcCounter = 1
        self.jacCounter = 0
        self.guessParam = False
        self.bruteSimul = False

        outX = None
        outSuccess = None
        outMessage = None
        outNit = None
        outFun = None
        outNFev = None
        outNJev = None
        outNHev = None
        outNLFev = None
        outXl = None
        outFunl = None

        # Choose the initial values
        paramNormalized0 = self.guess()

        if self.isBound:
            try:
                self.isBound = True
                if (self.minimizeMethod == "Bayes"):
                    BayesianBbounds = {}
                    for ii in range(0, self.paramCount):
                        BayesianBbounds[self.paramName[ii]] = self.paramBounds[ii]
                    # end for
                    BayesianOptimizer = BayesianOptimization(
                        f=self.optimizeFuncBayesian,
                        pbounds=BayesianBbounds,
                        verbose=0
                    )
                    BayesianOptimizer.maximize(
                        init_points=self.paramCount if (self.paramCount <= 5) else 5,
                        n_iter=self.maxIter,
                    )
                    outFun = BayesianOptimizer.max['target']
                    params = BayesianOptimizer.max['params']
                    outX = np.array([])
                    for ii in range(0, len(params)):
                        outX = np.append(outX, params[self.paramName[ii]])
                    # end for
                    outSuccess = True
                    outMessage = 'Done.'
                    outNit = self.maxIter
                else:
                    outResult = optimize.minimize(self.optimizeFunc, paramNormalized0, method=self.minimizeMethod, jac=self.getOptimizeJac(self.optimizeFunc), bounds=self.paramBounds, tol=self.tolerance, options={ 'eps': tEps, 'maxiter': self.maxIter, 'disp': False, 'ftol': self.tolerance })
                # end if
                try:
                    if (self.minimizeMethod != "Bayes"):
                        outX = outResult.x
                        outSuccess = outResult.success
                        outMessage = outResult.message
                        outNit = outResult.nit
                        outFun = outResult.fun
                        outNFev = outResult.nfev
                        outNJev = outResult.njev
                        outNHev = outResult.nhev
                        outNLFev = outResult.nlfev
                        outXl = outResult.xl
                        outFunl = outResult.funl
                    # end if
                except:
                    pass
                # end try
            except Exception as excT:
                # catch only Exception (since sys.exit raise BaseException)
                if self.stoppedDone is False:
                    dispError(traceback.format_exc(), doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
                # end if
            # end try
        else:
            # not bound methods
            try:
                outResult = optimize.minimize(self.optimizeFunc, paramNormalized0, method=self.minimizeMethod, jac=False, tol=self.tolerance, options={ 'maxiter': self.maxIter, 'disp': False })
                outX = outResult.x
                outSuccess = outResult.success
                outMessage = outResult.message
                outNit = outResult.nit
            except Exception as excT:
                # catch only Exception (since sys.exit raise BaseException)
                dispError(traceback.format_exc(), doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
            # end try
        # end if

        self.finish(errorOccured=False, userStopped=False, x=outX, success=outSuccess, message=outMessage, nit=outNit, nlfev=outNLFev, xl=outXl, funl=outFunl)

        return True

    # end startOptim

    def startSnapshot(self):
        """ start the optimization (Snap: one calculation for the initial parameters set) """

        self.optimType = "Snap"

        self.setCounterFormat(maxcount = 2)

        if not self.prepare():
            return False
        # end if

        strT = "Index\tTime\t"
        for ii in range(0, self.paramCount):
            strT += self.paramName[ii] + "\t"
        # end for

        strT += "Jm(mA/cm2)\tVm(V)\tFF(%)\tJsc(mA/cm2)\tVoc(V)\tEfficiency\n"
        fileOptim = open(self.outputDir + self.outputOptimizedFilename, "a")
        fileOptim.write(strT)
        fileOptim.close()

        self.optimCounter = 1
        self.funcCounter = 1
        self.jacCounter = 0
        self.guessParam = False
        self.bruteSimul = True

        paramNormalized = np.zeros(self.paramCount)

        for ii in range(0, self.paramCount):
            self.paramNatural[ii] = self.paramInit[ii]
            if self.paramLogscale[ii]:
                paramNormalized[ii] = math.log10(self.paramNatural[ii]) / math.log10(self.paramNorm[ii])
            else:
                paramNormalized[ii] = self.paramNatural[ii] / self.paramNorm[ii]
            # end if
        # end for

        # Run the optimization
        self.optimizeFunc(paramNormalized)

        self.finish(errorOccured=False, userStopped=False)

    # end startSnapshot

    # create the grid for the brute force iterations
    def doGrid(self, arrP, tR, tGrid):
        ns = arrP.shape[0]
        ms = tGrid.shape[0] // (tR * ns)
        for n in range(ns):
            tVal = arrP[n]
            for k in range(tR):
                for m in range(ms):
                    idx = (k * ns * ms) + (n * ms) + m
                    tGrid[idx] = tVal
                # end for
            # end for
        # end for
    # end doGrid

    def getGrid(self, arrParams):
        arrParams = [np.array(tt) for tt in arrParams]
        arrShapes = [tt.shape[0] for tt in arrParams]
        tType = arrParams[0].dtype
        na = len(arrParams)
        ns = np.prod(arrShapes)
        tGrid = np.zeros((ns, na), dtype=tType)
        tRep = np.cumprod([1] + arrShapes[:-1])
        for ii in range(na):
            self.doGrid(arrParams[ii], tRep[ii], tGrid[:, ii])
        # end for
        iCount = 10 * ns * na
        if iCount < 10:
            iCount = 10
        # end if
        self.setCounterFormat(iCount)
        return tGrid
    # end getGrid

    def startBrute(self):
        """ start the optimization (brute force) """

        self.optimType = "Brute"

        if not self.prepare():
            return False
        # end if

        strT = "Index\tTime\t"
        for ii in range(0, self.paramCount):
            strT += self.paramName[ii] + "\t"
        # end for

        strT += "Jm(mA/cm2)\tVm(V)\tFF(%)\tJsc(mA/cm2)\tVoc(V)\tEfficiency\n"
        fileOptim = open(self.outputDir + self.outputOptimizedFilename, "a")
        fileOptim.write(strT)
        fileOptim.close()

        self.optimCounter = 1
        self.funcCounter = 1
        self.jacCounter = 0
        self.guessParam = False
        self.bruteSimul = True

        paramNormalized = np.zeros(self.paramCount)

        dateT = datetime.datetime.now()
        dateStr = dateT.strftime("%Y-%m-%d %H:%M:%S")

        # For ... for all parameters, if not fixed
        # Limited to 5 parameters
        if self.paramCount > 5:
            strT = "\n---------------------------------------------------------------\n"
            strT += " The number of parameters is limited to 5 in the Brute optimization"
            strT += dateStr
            strT += "\n---------------------------------------------------------------\n"
            self.log(strT)
            try:
                pathStopped = os.path.join(self.outputDir, self.stoppedFilename)
                fileT = open(pathStopped, "w")
                fileT.write("SLALOM\nStopped\n")
                fileT.close()
            except:
                pass
            # end try
            sys.exit(0)
        # end if

        self.paramBounds = list()
        tStart = 0.0
        tEnd = 0.0
        tInit = 0.0
        tStep = 0.0
        for ii in range(0, self.paramCount):
            if self.paramLogscale[ii]:
                tStart = math.log10(self.paramStart[ii]) / math.log10(self.paramNorm[ii])
                tEnd = math.log10(self.paramEnd[ii]) / math.log10(self.paramNorm[ii])
                tInit = math.log10(self.paramInit[ii]) / math.log10(self.paramNorm[ii])
            else:
                tStart = self.paramStart[ii] / self.paramNorm[ii]
                tEnd = self.paramEnd[ii] / self.paramNorm[ii]
                tInit = self.paramInit[ii] / self.paramNorm[ii]
            # end if
            if self.paramPoints[ii] > 1:
                tStep = (tEnd - tStart) / float(self.paramPoints[ii] - 1)
                arr = np.array([])
                for jj in range(0, self.paramPoints[ii]):
                    arr = np.append(arr, tStart + float(jj) * tStep)
                # end for
                self.paramBounds.append(arr)
            else:
                self.paramBounds.append(np.array([tInit]))
            # end if
        # end for

        paramNormalizedGrid = self.getGrid(self.paramBounds)
        for paramNormalized in paramNormalizedGrid:
            self.optimizeFunc(paramNormalized)
        # end for

        self.finish(errorOccured=False, userStopped=False)
        self.bruteSimul = False

        return True

    # end startBrute

    def getMinimizeMethod(self):
        return self.minimizeMethod
    # end getMinimizeMethod

    def setMinimizeMethod(self, minimizeMethod, maxIter = 10, tolerance = 1e-3, optimPoints = 51):
        """ set the optimization method ('L-BFGS-B' or 'SLSQP') """
        
        if (maxIter >= 1) and (maxIter <= 1024):
            self.maxIter = maxIter
        # end if

        if (tolerance >= 1e-6) and (tolerance <= 1.0):
            self.tolerance = tolerance
        # end if

        # optimPoints is used to approximate the jacobian. If increased, the optimisation time will dramatically increase. The default value is 51 and the maximum value is 201.
        if (optimPoints < 11):
            self.optimPoints = 11
        elif (optimPoints > 201):
            self.optimPoints = 201
        else:
            self.optimPoints = optimPoints
        # end if

        self.weightPoints = self.optimPoints * 2

        if minimizeMethod in self.minimizeMethodList:
            self.minimizeMethod = minimizeMethod
            if (self.minimizeMethod == "L-BFGS-B") or (self.minimizeMethod == "SLSQP") or (self.minimizeMethod == "Bayes"):
                self.isBound = True
            else:
                self.isBound = False
            # end if
        else:
            strT = minimizeMethod + " unknown. Supported algorithms: "
            for ii in range(0, len(self.minimizeMethodList)):
                strT += self.minimizeMethodList[ii] + "  "
            # end for
            dispError(strT, doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
        # end if
    # end setMinimizeMethod

    def getRunning(self):
        return self.isRunning
    # end getRunning

    def getParamOptim(self):
        return self.paramOptim
    # end getParamOptim

    def getOutputOptimized(self):
        return self.outputOptimized
    # end getOutputOptimized

    def setTitle(self, title):
        """ set the optimization title """

        if self.isRunning:
            return
        # end if

        self.title = title
        return

    # end setTitle

    def setPath(self, Device):
        """ set directory and optimization files information """

        if self.isRunning:
            return False
        #end if

        self.currentDir = Device.currentDir
        dirT = os.path.dirname(self.currentDir)
        if not os.path.exists(dirT):
            dispError("Directory not found: " + self.currentDir, doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
        # end if

        # Output directory
        self.outputDir = Device.outputDir
        self.outputRoot = self.outputDir
        self.outputDirSuffix = "_" + (Device.deviceType if Device.deviceType else "User")
        dateT = datetime.datetime.now()
        outputDirName = dateT.strftime("%Y%m%d_%H%M")
        if self.outputDirSuffix is not None:
            outputDirName += self.outputDirSuffix
        # end if
        self.outputDirShort = outputDirName
        self.outputDir += outputDirName + self.dirSepChar
        dirT = os.path.dirname(self.outputDir)
        if not os.path.exists(dirT):
            os.makedirs(dirT)
        # end if
        if not os.path.exists(dirT):
            dispError("Directory not found and cannot be created: " + self.outputDir, doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
        # end if

        self.inputFilename = Device.inputFilename
        if not os.path.exists(self.currentDir + self.inputFilename):
            dispError("File not found: " + (self.currentDir + self.inputFilename), doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
        # end if

        fileT = open(Device.currentDir + "ofname.txt", "w")
        fileT.write(self.outputDir + self.outputOptimizedFilename)
        fileT.close()

        return self.checkInput()

    # end setPath

    def getOutputDir(self):
        return self.outputDir
    # end getOutputDir

    # outputOptimizedFilename is used to plot in 'realtime' the variation of the efficiency during the optimization
    def getOptimizedFilename(self):
        return os.path.join(self.outputDir, self.outputOptimizedFilename)
    # end getOptimizedFilename

    def setParam(self, Device, pythonInterpreter):
        """ set the main optimization parameters """

        if self.isRunning:
            return False
        # end if

        self.setInterpreter(pythonInterpreter)

        self.setTitle(Device.mainTitle)

        if self.setPath(Device) == False:
            return False
        # end if

        lenT = len(Device.paramName)
        if (lenT < 1) or (lenT > 20) or (lenT != len(Device.paramFormat)) or (lenT != len(Device.paramStart)) or (
            lenT != len(Device.paramEnd)) or (lenT != len(Device.paramInit)) or (lenT != len(Device.paramLogscale)):
            dispError("Invalid parameters (setParam)", doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
        # end if

        self.paramCount = lenT

        self.paramOptim = np.zeros(self.paramCount)
        self.paramNatural = np.zeros(self.paramCount)

        self.paramName = []
        self.paramUnit = []
        self.paramFormat = []
        self.paramFormatShort = []
        self.paramFormatNormalized = []
        self.paramNorm = np.array([])
        self.paramStart = np.array([])
        self.paramEnd = np.array([])
        self.paramPoints = []
        self.paramInit = np.array([])
        self.paramLogscale = []
        for ii in range(0, self.paramCount):
            self.paramName.append(Device.paramName[ii])
            self.paramUnit.append(Device.paramUnit[ii])
            self.paramFormat.append(Device.paramFormat[ii])
            self.paramFormatShort.append(Device.paramFormatShort[ii])
            self.paramFormatNormalized.append(Device.paramFormatNormalized[ii])
            self.paramNorm = np.append(self.paramNorm, Device.paramNorm[ii])
            self.paramStart = np.append(self.paramStart, Device.paramStart[ii])
            self.paramEnd = np.append(self.paramEnd, Device.paramEnd[ii])
            if (Device.paramPoints[ii] >= 1) and (Device.paramPoints[ii] <= 100):
                self.paramPoints.append(Device.paramPoints[ii])
            # end if
            self.paramInit = np.append(self.paramInit, Device.paramInit[ii])
            self.paramLogscale.append(Device.paramLogscale[ii])
        # end for

        self.paramWeight = Device.paramWeight

        self.paramFormatOutput = Device.paramFormatOutput

        if Device.modelFilename:
            self.modelCount = len(Device.modelFilename)
            if self.modelCount < 1:
                dispError("modelFilename array size not valid", doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
            # end if
            self.modelFilename = []
            nn = 0
            for ii in range(0, self.modelCount):
                self.modelFilename.append(Device.modelFilename[ii])
                if self.modelFilename[ii] == "":
                    continue
                # end if
                if not os.path.exists(self.currentDir + self.modelFilename[ii]):
                    dispError("File not found: " + (self.currentDir + self.modelFilename[ii]), doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
                # end if
            # end for
        else:
            self.modelFilename = None
            self.modelCount = 0
        # end if

        self.isParamUpdated = True

        return self.isParamUpdated

    # end setParam

    def setParamInit(self, paramInit):
        """ set main initial parameters """

        if self.isRunning:
            return False
        # end if

        lenT = len(paramInit)
        if (lenT < 1) or (lenT > 20) or (lenT != len(self.paramFormat)) or (lenT != len(self.paramStart)) or (
            lenT != len(self.paramEnd)) or (lenT != len(self.paramName)) or (lenT != len(self.paramLogscale)):
            dispError("Invalid parameters (setParamInit)", doExit = True, atExit = self.finish, errFilename = self.currentDir + 'errlog.txt')
        # end if

        self.paramInit = np.array([])
        for ii in range(0, self.paramCount):
            self.paramInit = np.append(self.paramInit, paramInit[ii])
        # end for

        return True

    # end setParam

    @staticmethod
    def maxDim():
        return 1024
    # end maxDim

# end slalomCore

# class to disable standard output buffering
class UnbufferedStdout(object):

    def __init__(self, stream):
        self.stream = stream
    # end __init__

    def __getattr__(self, attr):
        return getattr(self.stream, attr)
    # end __getattr__

    def write(self, data):
        self.stream.write(data)
        self.stream.flush()
    # end write

# end UnbufferedStdout
back to top