def solve_bvp_casadi(self):
        """
        Uses casadi's interface to sundials to solve the boundary value
        problem using a single-shooting method with automatic differen-
        tiation.
        
        Related to PCSJ code. 
        """

        self.bvpint = cs.Integrator('cvodes', self.modlT)
        self.bvpint.setOption('abstol', self.intoptions['bvp_abstol'])
        self.bvpint.setOption('reltol', self.intoptions['bvp_reltol'])
        self.bvpint.setOption('tf', 1)
        self.bvpint.setOption('disable_internal_warnings', True)
        self.bvpint.setOption('fsens_err_con', True)
        self.bvpint.init()

        # Vector of unknowns [y0, T]
        V = cs.MX.sym("V", self.neq + 1)
        y0 = V[:-1]
        T = V[-1]
        param = cs.vertcat([self.param, T])
        yf = self.bvpint.call(cs.integratorIn(x0=y0, p=param))[0]
        fout = self.modlT.call(cs.daeIn(t=T, x=y0, p=param))[0]

        # objective: continuity
        obj = (yf -
               y0)**2  # yf and y0 are the same ..i.e. 2 ends of periodic fcn
        obj.append(
            fout[0])  # y0 is a peak for state 0, i.e. fout[0] is slope state 0

        #set up the matrix we want to solve
        F = cs.MXFunction([V], [obj])
        F.init()
        guess = np.append(self.y0, self.T)
        solver = cs.ImplicitFunction('kinsol', F)
        solver.setOption('abstol', self.intoptions['bvp_ftol'])
        solver.setOption('strategy', 'linesearch')
        solver.setOption('exact_jacobian', False)
        solver.setOption('pretype', 'both')
        solver.setOption('use_preconditioner', True)
        if self.intoptions['constraints'] == 'positive':
            solver.setOption('constraints', (2, ) * (self.neq + 1))
        solver.setOption('linear_solver_type', 'dense')
        solver.init()
        solver.setInput(guess)
        solver.evaluate()

        sol = solver.output().toArray().squeeze()

        self.y0 = sol[:-1]
        self.T = sol[-1]
Esempio n. 2
0
    def solveBVP_casadi(self):
        """
        Uses casadi's interface to sundials to solve the boundary value
        problem using a single-shooting method with automatic differen-
        tiation. 
        """

        # Here we create and initialize the integrator SXFunction
        self.bvpint = cs.CVodesIntegrator(self.modlT)
        self.bvpint.setOption('abstol',self.intoptions['bvp_abstol'])
        self.bvpint.setOption('reltol',self.intoptions['bvp_reltol'])
        self.bvpint.setOption('tf',1)
        self.bvpint.setOption('disable_internal_warnings', True)
        self.bvpint.setOption('fsens_err_con', True)
        self.bvpint.init()

        # Vector of unknowns [y0, T]
        V = cs.msym("V",self.NEQ+1)
        y0 = V[:-1]
        T = V[-1]
        t = cs.msym('t')
        param = cs.vertcat([self.paramset, T])

        yf = self.bvpint.call(cs.integratorIn(x0=y0,p=param))[0]
        fout = self.modlT.call(cs.daeIn(t=t, x=y0,p=param))[0]

        obj = (yf - y0)**2
        obj.append(fout[0])

        F = cs.MXFunction([V],[obj])
        F.init()

        solver = cs.KinsolSolver(F)
        solver.setOption('abstol',self.intoptions['bvp_ftol'])
        solver.setOption('ad_mode', "forward")
        solver.setOption('strategy','linesearch')
        solver.setOption('numeric_jacobian', True)
        solver.setOption('exact_jacobian', False)
        solver.setOption('pretype', 'both')
        solver.setOption('use_preconditioner', True)
        solver.setOption('numeric_hessian', True)
        solver.setOption('constraints', (2,)*(self.NEQ+1))
        solver.setOption('verbose', False)
        solver.setOption('sparse', False)
        solver.setOption('linear_solver', 'dense')
        solver.init()
        solver.output().set(self.y0)
        solver.solve()

        self.y0 = solver.output().toArray().squeeze()
Esempio n. 3
0
    def defineOCP(self,
                  ocp,
                  DT=20,
                  controlCost=0,
                  xOpt=[],
                  uOpt=[],
                  finalStateCost=1,
                  deltaUCons=[]):

        self.ocp = ocp
        ocp = self.ocp
        self.DT = DT
        self.n_k = int(self.ocp.tf / self.DT)
        self.controlCost = controlCost

        stateScaling = C.vertcat([
            ocp.variable(ocp.x[k].getName()).nominal
            for k in range(ocp.x.size())
        ])
        algStateScaling = C.vertcat([
            ocp.variable(ocp.z[k].getName()).nominal
            for k in range(ocp.z.size())
        ])
        controlScaling = C.vertcat([
            ocp.variable(ocp.u[k].getName()).nominal
            for k in range(ocp.u.size())
        ])

        xOpt = xOpt / stateScaling
        uOpt = uOpt / controlScaling
        self.xOpt = xOpt
        self.uOpt = uOpt

        self.stateScaling = C.vertcat([
            ocp.variable(ocp.x[k].getName()).nominal
            for k in range(ocp.x.size())
        ])
        self.algStateScaling = C.vertcat([
            ocp.variable(ocp.z[k].getName()).nominal
            for k in range(ocp.z.size())
        ])
        self.controlScaling = C.vertcat([
            ocp.variable(ocp.u[k].getName()).nominal
            for k in range(ocp.u.size())
        ])

        odeS = C.substitute(
            ocp.ode(ocp.x), C.vertcat([ocp.x, ocp.z, ocp.u]),
            C.vertcat([
                stateScaling * ocp.x, algStateScaling * ocp.z,
                controlScaling * ocp.u
            ])) / stateScaling
        algS = C.substitute(
            ocp.alg, C.vertcat([ocp.x, ocp.z, ocp.u]),
            C.vertcat([
                stateScaling * ocp.x, algStateScaling * ocp.z,
                controlScaling * ocp.u
            ]))
        ltermS = C.substitute(
            ocp.lterm, C.vertcat([ocp.x, ocp.z, ocp.u]),
            C.vertcat([
                stateScaling * ocp.x, algStateScaling * ocp.z,
                controlScaling * ocp.u
            ]))

        sysIn = C.daeIn(x=ocp.x, z=ocp.z, p=ocp.u, t=ocp.t)
        sysOut = C.daeOut(ode=odeS, alg=algS, quad=ltermS)
        odeF = C.SXFunction(sysIn, sysOut)
        odeF.init()

        C.Integrator.loadPlugin("idas")
        G = C.Integrator("idas", odeF)
        G.setOption("reltol", self.INTG_REL_TOL)  #for CVODES and IDAS
        G.setOption("abstol", self.INTG_ABS_TOL)  #for CVODES and IDAS
        G.setOption("max_multistep_order", 5)  #for CVODES and IDAS
        G.setOption("max_step_size", self.IDAS_MAX_STEP_SIZE)  #for IDAS only
        G.setOption("tf", self.DT)
        self.G = G

        #==============================================================================
        #        G.setOption('verbose',True)
        #        G.addMonitor('res')
        #        G.addMonitor('inputs')
        #        G.addMonitor('outputs')
        #G.addMonitor('djacB')
        #         G.addMonitor('bjacB')
        #         G.addMonitor('jtimesB')
        #         G.addMonitor('psetup')
        #         G.addMonitor('psetupB')
        #         G.addMonitor('psolveB')
        #         G.addMonitor('resB')
        #         G.addMonitor('resS')
        #         G.addMonitor('rhsQB')
        #==============================================================================
        G.init()

        self.n_u = self.ocp.u.size()
        self.n_x = self.ocp.x.size()
        self.n_v = self.n_u * self.n_k + self.n_x * self.n_k

        self.V = C.MX.sym("V", int(self.n_v), 1)
        self.U, self.X = self.splitVariables(self.V)

        uMin = C.vertcat([
            self.ocp.variable(self.ocp.u[i].getName()).min.getValue()
            for i in range(self.n_u)
        ]) / controlScaling
        uMax = C.vertcat([
            self.ocp.variable(self.ocp.u[i].getName()).max.getValue()
            for i in range(self.n_u)
        ]) / controlScaling
        UMIN = C.vertcat([uMin for k in range(self.n_k)])
        UMAX = C.vertcat([uMax for k in range(self.n_k)])

        xMin = C.vertcat([
            self.ocp.variable(self.ocp.x[i].getName()).min.getValue()
            for i in range(self.n_x)
        ]) / stateScaling
        xMax = C.vertcat([
            self.ocp.variable(self.ocp.x[i].getName()).max.getValue()
            for i in range(self.n_x)
        ]) / stateScaling
        XMIN = C.vertcat([xMin for k in range(self.n_k)])
        XMAX = C.vertcat([xMax for k in range(self.n_k)])

        if len(deltaUCons) > 0:
            addDeltaUCons = True
            deltaUCons = deltaUCons / self.controlScaling
        else:
            addDeltaUCons = False

        pathIn = C.daeIn(x=ocp.x, z=ocp.z, p=ocp.u, t=ocp.t)

        pathVarNames = [sv.getName() for sv in ocp.beq(ocp.path)]
        pathScaling = C.vertcat([ocp.nominal(pv) for pv in pathVarNames])

        pathS = C.substitute(
            ocp.beq(ocp.beq(ocp.path)), C.vertcat([ocp.x, ocp.z, ocp.u]),
            C.vertcat([
                stateScaling * ocp.x, algStateScaling * ocp.z,
                controlScaling * ocp.u
            ])) / pathScaling
        pathConstraints = C.SXFunction(pathIn, [pathS])

        pathMax = C.vertcat([
            ocp.variable(pathVarNames[i]).max.getValue()
            for i in range(ocp.path.size())
        ]) / pathScaling
        pathMin = C.vertcat([
            ocp.variable(pathVarNames[i]).min.getValue()
            for i in range(ocp.path.size())
        ]) / pathScaling
        pathConstraints.setOption("name", "PATH")
        pathConstraints.init()

        pathConstraints.setInput(xOpt, 'x')
        pathConstraints.setInput([], 'z')
        pathConstraints.setInput(uOpt, 'p')
        pathConstraints.setInput(0, 't')
        pathConstraints.evaluate()
        pathOpt = pathConstraints.getOutput()

        optimalValues = {}

        print 'min <= (name,optimal,nominal) <= max'
        for i in range(self.n_x):
            print ocp.variable(
                ocp.x[i].getName()).min.getValue(), ' <= (', ocp.x[i].getName(
                ), ',', xOpt[i] * stateScaling[i], ',', stateScaling[
                    i], ') <= ', ocp.variable(
                        ocp.x[i].getName()).max.getValue()
            optimalValues[ocp.x[i].getName()] = xOpt[i] * stateScaling[i]

        for i in range(self.n_u):
            print ocp.variable(
                ocp.u[i].getName()).min.getValue(), ' <= (', ocp.u[i].getName(
                ), ',', uOpt[i] * controlScaling[i], ',', controlScaling[
                    i], ')  <= ', ocp.variable(
                        ocp.u[i].getName()).max.getValue()
            if addDeltaUCons:
                print -deltaUCons[i] * controlScaling[i], ' <= (Delta(', ocp.u[
                    i].getName(), ')/DTMPC,', 0, ',', controlScaling[
                        i], ')  <= ', deltaUCons[i] * controlScaling[i]

            optimalValues[ocp.u[i].getName()] = uOpt[i] * controlScaling[i]

        for i in range(len(pathVarNames)):
            print ocp.variable(pathVarNames[i]).min.getValue(
            ), ' <= (', pathVarNames[i], ',', pathOpt[i] * pathScaling[
                i], ',', pathScaling[i], ') <= ', ocp.variable(
                    pathVarNames[i]).max.getValue()
            optimalValues[pathVarNames[i]] = pathOpt[i] * pathScaling[i]

        plotTags = [ocp.x[i].getName() for i in range(ocp.x.size())]
        plotTags = plotTags + [ocp.u[i].getName() for i in range(ocp.u.size())]
        plotTags = plotTags + [sv.getName() for sv in ocp.beq(ocp.path)]

        self.plotTags = plotTags
        self.optimalValues = optimalValues

        # Constraint functions
        g = []
        g_min = []
        g_max = []

        self.XU0 = C.MX.sym("XU0", self.n_x + self.n_u, 1)
        Z = self.XU0[0:self.n_x]
        U0 = self.XU0[self.n_x:self.n_x + self.n_u]

        # Build up a graph of integrator calls
        obj = 0
        zf = C.vertcat([
            ocp.variable(ocp.z[k].getName()).start for k in range(ocp.z.size())
        ]) / algStateScaling
        for k in range(self.n_k):
            Z, QF, zf = C.integratorOut(
                G(C.integratorIn(x0=Z, p=self.U[k], z0=zf)), "xf", "qf", "zf")

            errU = self.U[k] - U0
            obj = obj + QF + C.mul(C.mul(errU.T, controlCost), errU)
            U0 = self.U[k]

            #          include MS constraints!
            g.append(Z - self.X[k])
            g_min.append(NP.zeros(self.n_x))
            g_max.append(NP.zeros(self.n_x))
            Z = self.X[k]

            [pathCons] = pathConstraints.call(
                C.daeIn(t=[], x=self.X[k], z=zf, p=self.U[k]))
            g.append(pathCons)  ## be carefull on giving all inputs
            g_max.append(pathMax)
            g_min.append(pathMin)
            if addDeltaUCons:
                g.append(errU)
                g_max.append(deltaUCons * DT)
                g_min.append(-deltaUCons * DT)

        #errU = (self.U[-1]-uOpt)
        #errX = self.X[-1]-xOpt
        #obj = obj + finalStateCost*C.mul((errX).trans(),(errX))+C.mul(C.mul(errU.T,controlCost),errU)
        self.obj = obj

        ### Constrains
        g = C.vertcat(g)

        nlp = C.MXFunction(C.nlpIn(x=self.V, p=self.XU0), C.nlpOut(f=obj, g=g))
        nlp.init()
        self.odeF = odeF
        self.nlp = nlp

        solver = C.NlpSolver('ipopt', nlp)

        # remove the comment to implement the hessian
        solver.setOption('hessian_approximation',
                         'limited-memory')  # comment for exact hessian
        solver.setOption('print_user_options', 'no')

        solver.setOption("tol", self.IPOPT_tol)  # IPOPT tolerance
        solver.setOption("dual_inf_tol",
                         self.IPOPT_dual_inf_tol)  #  dual infeasibility
        solver.setOption("constr_viol_tol",
                         self.IPOPT_constr_viol_tol)  # primal infeasibility
        solver.setOption("compl_inf_tol",
                         self.IPOPT_compl_inf_tol)  # complementarity
        #        solver.setOption("acceptable_tol",0.01)
        #        solver.setOption("acceptable_obj_change_tol",1e-6)
        #        solver.setOption("acceptable_constr_viol_tol",1e-6)
        solver.setOption("max_iter",
                         self.IPOPT_max_iter)  # IPOPT maximum iterations
        solver.setOption("print_level", self.IPOPT_print_level)
        solver.setOption("max_cpu_time",
                         self.IPOPT_max_cpu_time)  # IPOPT maximum iterations

        solver.init()

        ### Variable Bounds and initial guess
        solver.setInput(C.vertcat([UMIN, XMIN]), 'lbx')  # u_L
        solver.setInput(C.vertcat([UMAX, XMAX]), 'ubx')  # u_U
        solver.setInput(C.vertcat(g_min), 'lbg')  # g_L
        solver.setInput(C.vertcat(g_max), 'ubg')  # g_U

        self.solver = solver

        u0N = C.vertcat([
            self.ocp.variable(self.ocp.u[i].getName()).initialGuess.getValue()
            for i in range(self.n_u)
        ]) / controlScaling
        x0N = C.vertcat([
            self.ocp.variable(self.ocp.x[i].getName()).initialGuess.getValue()
            for i in range(self.n_x)
        ]) / stateScaling

        USOL, XSOL = self.forwardSimulation(x0N, u0N)

        self.USOL = USOL
        self.XSOL = XSOL