Ejemplo n.º 1
0
    def DeclareEquations(self):
        dae.daeModel.DeclareEquations(self)
        ndD = self.ndD
        N = ndD["N"]  # number of grid points in particle
        T = self.ndD_s["T"]  # nondimensional temperature
        r_vec, volfrac_vec = geo.get_unit_solid_discr(ndD['shape'], N)

        # Prepare the Ideal Solution log ratio terms
        self.ISfuncs = None
        if ndD["logPad"]:
            self.ISfuncs = np.array([
                extern_funcs.LogRatio("LR", self, dae.unit(), self.c(k))
                for k in range(N)
            ])

        # Prepare noise
        self.noise = None
        if ndD["noise"]:
            numnoise = ndD["numnoise"]
            noise_prefac = ndD["noise_prefac"]
            tvec = np.linspace(0., 1.05 * self.ndD_s["tend"], numnoise)
            noise_data = noise_prefac * np.random.randn(numnoise, N)
            # Previous_output is common for all external functions
            previous_output = []
            self.noise = [
                extern_funcs.InterpTimeVector("noise", self, dae.unit(),
                                              dae.Time(), tvec, noise_data,
                                              previous_output, _position_)
                for _position_ in range(N)
            ]

        # Figure out mu_O, mu of the oxidized state
        mu_O, act_lyte = calc_mu_O(self.c_lyte, self.phi_lyte, self.phi_m, T,
                                   self.ndD_s["elyteModelType"])

        # Define average filling fraction in particle
        eq = self.CreateEquation("cbar")
        eq.Residual = self.cbar()
        for k in range(N):
            eq.Residual -= self.c(k) * volfrac_vec[k]

        # Define average rate of filling of particle
        eq = self.CreateEquation("dcbardt")
        eq.Residual = self.dcbardt()
        for k in range(N):
            eq.Residual -= self.c.dt(k) * volfrac_vec[k]

        c = np.empty(N, dtype=object)
        c[:] = [self.c(k) for k in range(N)]
        if ndD["type"] in ["ACR", "diffn", "CHR"]:
            # Equations for 1D particles of 1 field varible
            self.sld_dynamics_1D1var(c, mu_O, act_lyte, self.ISfuncs,
                                     self.noise)
        elif ndD["type"] in ["homog", "homog_sdn"]:
            # Equations for 0D particles of 1 field variables
            self.sld_dynamics_0D1var(c, mu_O, act_lyte, self.ISfuncs,
                                     self.noise)

        for eq in self.Equations:
            eq.CheckUnitsConsistency = False
Ejemplo n.º 2
0
    def DeclareEquations(self):
        self.stnSpikeSource = self.STN("SpikeSource")

        for i, t in enumerate(self.spiketimes):
            self.STATE('State_{0}'.format(i))
            eq = self.CreateEquation("event")
            eq.Residual = self.event() - t
            self.ON_CONDITION(daet.Time() >= t,
                              switchTo='State_{0}'.format(i + 1),
                              triggerEvents=[(self.spikeoutput, daet.Time())])

        self.STATE('State_{0}'.format(len(self.spiketimes)))

        eq = self.CreateEquation("event")
        eq.Residual = self.event()

        self.END_STN()
Ejemplo n.º 3
0
def getEquationsExpressionParserIdentifiers(model):
    dictIdentifiers = {}

    dictIdentifiers['pi'] = daet.Constant(math.pi)
    dictIdentifiers['e'] = daet.Constant(math.e)
    dictIdentifiers['t'] = daet.Time()
    if model:
        dictIdentifiers = addIdentifiers(model, model, dictIdentifiers)

    return dictIdentifiers
Ejemplo n.º 4
0
    def DeclareEquations(self):
        dae.daeModel.DeclareEquations(self)
        ndD = self.ndD
        N = ndD["N"]  # number of grid points in particle
        T = self.ndD_s["T"]  # nondimensional temperature
        r_vec, volfrac_vec = geo.get_unit_solid_discr(ndD['shape'], N)

        # Prepare the Ideal Solution log ratio terms
        self.ISfuncs1 = self.ISfuncs2 = None
        if ndD["logPad"]:
            self.ISfuncs1 = np.array([
                extern_funcs.LogRatio("LR1", self, dae.unit(), self.c1(k))
                for k in range(N)
            ])
            self.ISfuncs2 = np.array([
                extern_funcs.LogRatio("LR2", self, dae.unit(), self.c2(k))
                for k in range(N)
            ])
        ISfuncs = (self.ISfuncs1, self.ISfuncs2)

        # Prepare noise
        self.noise1 = self.noise2 = None
        if ndD["noise"]:
            numnoise = ndD["numnoise"]
            noise_prefac = ndD["noise_prefac"]
            tvec = np.linspace(0., 1.05 * self.ndD_s["tend"], numnoise)
            noise_data1 = noise_prefac * np.random.randn(numnoise, N)
            noise_data2 = noise_prefac * np.random.randn(numnoise, N)
            # Previous_output is common for all external functions
            previous_output1 = []
            previous_output2 = []
            self.noise1 = [
                extern_funcs.InterpTimeVector("noise1", self, dae.unit(),
                                              dae.Time(), tvec, noise_data1,
                                              previous_output1, _position_)
                for _position_ in range(N)
            ]
            self.noise2 = [
                extern_funcs.InterpTimeVector("noise2", self, dae.unit(),
                                              dae.Time(), tvec, noise_data2,
                                              previous_output2, _position_)
                for _position_ in range(N)
            ]
        noises = (self.noise1, self.noise2)

        # Figure out mu_O, mu of the oxidized state
        mu_O, act_lyte = calc_mu_O(self.c_lyte(), self.phi_lyte(),
                                   self.phi_m(), T,
                                   self.ndD_s["elyteModelType"])

        # Define average filling fractions in particle
        eq1 = self.CreateEquation("c1bar")
        eq2 = self.CreateEquation("c2bar")
        eq1.Residual = self.c1bar()
        eq2.Residual = self.c2bar()
        for k in range(N):
            eq1.Residual -= self.c1(k) * volfrac_vec[k]
            eq2.Residual -= self.c2(k) * volfrac_vec[k]
        eq = self.CreateEquation("cbar")
        eq.Residual = self.cbar() - utils.mean_linear(self.c1bar(),
                                                      self.c2bar())

        # Define average rate of filling of particle
        eq = self.CreateEquation("dcbardt")
        eq.Residual = self.dcbardt()
        for k in range(N):
            eq.Residual -= utils.mean_linear(self.c1.dt(k),
                                             self.c2.dt(k)) * volfrac_vec[k]

        c1 = np.empty(N, dtype=object)
        c2 = np.empty(N, dtype=object)
        c1[:] = [self.c1(k) for k in range(N)]
        c2[:] = [self.c2(k) for k in range(N)]
        if ndD["type"] in ["diffn2", "CHR2"]:
            # Equations for 1D particles of 1 field varible
            self.sld_dynamics_1D2var(c1, c2, mu_O, act_lyte, ISfuncs, noises)
        elif ndD["type"] in ["homog2", "homog2_sdn"]:
            # Equations for 0D particles of 1 field variables
            self.sld_dynamics_0D2var(c1, c2, mu_O, act_lyte, ISfuncs, noises)

        for eq in self.Equations:
            eq.CheckUnitsConsistency = False
Ejemplo n.º 5
0
    def DeclareEquations(self):
        dae.daeModel.DeclareEquations(self)

        # Some values of domain lengths
        trodes = self.trodes
        ndD = self.ndD
        Nvol = ndD["Nvol"]
        Npart = ndD["Npart"]
        Nlyte = np.sum(list(Nvol.values()))

        # Define the overall filling fraction in the electrodes
        for trode in trodes:
            eq = self.CreateEquation("ffrac_{trode}".format(trode=trode))
            eq.Residual = self.ffrac[trode]()
            dx = 1. / Nvol[trode]
            # Make a float of Vtot, total particle volume in electrode
            # Note: for some reason, even when "factored out", it's a bit
            # slower to use Sum(self.psd_vol_ac[l].array([], [])
            tmp = 0
            for vInd in range(Nvol[trode]):
                for pInd in range(Npart[trode]):
                    Vj = ndD["psd_vol_FracVol"][trode][vInd, pInd]
                    tmp += self.particles[trode][vInd, pInd].cbar() * Vj * dx
            eq.Residual -= tmp

        # Define dimensionless R_Vp for each electrode volume
        for trode in trodes:
            for vInd in range(Nvol[trode]):
                eq = self.CreateEquation("R_Vp_trode{trode}vol{vInd}".format(
                    vInd=vInd, trode=trode))
                # Start with no reaction, then add reactions for each
                # particle in the volume.
                RHS = 0
                # sum over particle volumes in given electrode volume
                for pInd in range(Npart[trode]):
                    # The volume of this particular particle
                    Vj = ndD["psd_vol_FracVol"][trode][vInd, pInd]
                    RHS += -(ndD["beta"][trode] *
                             (1 - ndD["poros"][trode]) * ndD["P_L"][trode] *
                             Vj * self.particles[trode][vInd, pInd].dcbardt())
                eq.Residual = self.R_Vp[trode](vInd) - RHS

        # Define output port variables
        for trode in trodes:
            for vInd in range(Nvol[trode]):
                eq = self.CreateEquation(
                    "portout_c_trode{trode}vol{vInd}".format(vInd=vInd,
                                                             trode=trode))
                eq.Residual = (self.c_lyte[trode](vInd) -
                               self.portsOutLyte[trode][vInd].c_lyte())
                eq = self.CreateEquation(
                    "portout_p_trode{trode}vol{vInd}".format(vInd=vInd,
                                                             trode=trode))
                phi_lyte = self.phi_lyte[trode](vInd)
                eq.Residual = (phi_lyte -
                               self.portsOutLyte[trode][vInd].phi_lyte())
                for pInd in range(Npart[trode]):
                    eq = self.CreateEquation(
                        "portout_pm_trode{trode}v{vInd}p{pInd}".format(
                            vInd=vInd, pInd=pInd, trode=trode))
                    eq.Residual = (
                        self.phi_part[trode](vInd, pInd) -
                        self.portsOutBulk[trode][vInd, pInd].phi_m())

            # Simulate the potential drop along the bulk electrode
            # solid phase
            simBulkCond = ndD['simBulkCond'][trode]
            if simBulkCond:
                # Calculate the RHS for electrode conductivity
                phi_tmp = utils.add_gp_to_vec(
                    utils.get_var_vec(self.phi_bulk[trode], Nvol[trode]))
                porosvec = utils.pad_vec(
                    utils.get_const_vec(
                        (1 -
                         self.ndD["poros"][trode])**(1 -
                                                     ndD["BruggExp"][trode]),
                        Nvol[trode]))
                poros_walls = utils.mean_harmonic(porosvec)
                if trode == "a":  # anode
                    # Potential at the current collector is from
                    # simulation
                    phi_tmp[0] = self.phi_cell()
                    # No current passes into the electrolyte
                    phi_tmp[-1] = phi_tmp[-2]
                else:  # cathode
                    phi_tmp[0] = phi_tmp[1]
                    # Potential at current at current collector is
                    # reference (set)
                    phi_tmp[-1] = ndD["phi_cathode"]
                dx = ndD["L"][trode] / Nvol[trode]
                dvg_curr_dens = np.diff(-poros_walls * ndD["sigma_s"][trode] *
                                        np.diff(phi_tmp) / dx) / dx
            # Actually set up the equations for bulk solid phi
            for vInd in range(Nvol[trode]):
                eq = self.CreateEquation("phi_ac_trode{trode}vol{vInd}".format(
                    vInd=vInd, trode=trode))
                if simBulkCond:
                    eq.Residual = -dvg_curr_dens[vInd] - self.R_Vp[trode](vInd)
                else:
                    if trode == "a":  # anode
                        eq.Residual = self.phi_bulk[trode](
                            vInd) - self.phi_cell()
                    else:  # cathode
                        eq.Residual = self.phi_bulk[trode](
                            vInd) - ndD["phi_cathode"]

            # Simulate the potential drop along the connected
            # particles
            simPartCond = ndD['simPartCond'][trode]
            for vInd in range(Nvol[trode]):
                phi_bulk = self.phi_bulk[trode](vInd)
                for pInd in range(Npart[trode]):
                    G_l = ndD["G"][trode][vInd, pInd]
                    phi_n = self.phi_part[trode](vInd, pInd)
                    if pInd == 0:  # reference bulk phi
                        phi_l = phi_bulk
                    else:
                        phi_l = self.phi_part[trode](vInd, pInd - 1)
                    if pInd == (Npart[trode] -
                                1):  # No particle at end of "chain"
                        G_r = 0
                        phi_r = phi_n
                    else:
                        G_r = ndD["G"][trode][vInd, pInd + 1]
                        phi_r = self.phi_part[trode](vInd, pInd + 1)
                    # charge conservation equation around this particle
                    eq = self.CreateEquation(
                        "phi_ac_trode{trode}vol{vInd}part{pInd}".format(
                            vInd=vInd, trode=trode, pInd=pInd))
                    if simPartCond:
                        # -dcsbar/dt = I_l - I_r
                        eq.Residual = (
                            self.particles[trode][vInd, pInd].dcbardt() +
                            ((-G_l * (phi_n - phi_l)) - (-G_r *
                                                         (phi_r - phi_n))))
                    else:
                        eq.Residual = self.phi_part[trode](vInd,
                                                           pInd) - phi_bulk

        # If we have a single electrode volume (in a perfect bath),
        # electrolyte equations are simple
        if self.SVsim:
            eq = self.CreateEquation("c_lyte")
            eq.Residual = self.c_lyte["c"].dt(0) - 0
            eq = self.CreateEquation("phi_lyte")
            eq.Residual = self.phi_lyte["c"](0) - self.phi_cell()
        else:
            disc = geom.get_elyte_disc(Nvol, ndD["L"], ndD["poros"],
                                       ndD["BruggExp"])
            cvec = utils.get_asc_vec(self.c_lyte, Nvol)
            dcdtvec = utils.get_asc_vec(self.c_lyte, Nvol, dt=True)
            phivec = utils.get_asc_vec(self.phi_lyte, Nvol)
            Rvvec = utils.get_asc_vec(self.R_Vp, Nvol)
            # Apply concentration and potential boundary conditions
            # Ghost points on the left and no-gradients on the right
            ctmp = np.hstack((self.c_lyteGP_L(), cvec, cvec[-1]))
            phitmp = np.hstack((self.phi_lyteGP_L(), phivec, phivec[-1]))
            # If we don't have a porous anode:
            # 1) the total current flowing into the electrolyte is set
            # 2) assume we have a Li foil with BV kinetics and the specified rate constant
            eqC = self.CreateEquation("GhostPointC_L")
            eqP = self.CreateEquation("GhostPointP_L")
            if Nvol["a"] == 0:
                # Concentration BC from mass flux
                Nm_foil = get_lyte_internal_fluxes(ctmp[0:2], phitmp[0:2],
                                                   disc["dxd1"][0],
                                                   disc["eps_o_tau_edges"][0],
                                                   ndD)[0]
                eqC.Residual = Nm_foil[0]
                # Phi BC from BV at the foil
                # We assume BV kinetics with alpha = 0.5,
                # exchange current density, ecd = k0_foil * c_lyte**(0.5)
                cWall = utils.mean_harmonic(ctmp[0], ctmp[1])
                ecd = ndD["k0_foil"] * cWall**0.5
                # -current = ecd*(exp(-eta/2) - exp(eta/2))
                # note negative current because positive current is
                # oxidation here
                # -current = ecd*(-2*sinh(eta/2))
                # eta = 2*arcsinh(-current/(-2*ecd))
                BVfunc = -self.current() / ecd
                eta_eff = 2 * np.arcsinh(-BVfunc / 2.)
                eta = eta_eff + self.current() * ndD["Rfilm_foil"]
                #                # Infinitely fast anode kinetics
                #                eta = 0.
                # eta = mu_R - mu_O = -mu_O (evaluated at interface)
                # mu_O = [T*ln(c) +] phiWall - phi_cell = -eta
                # phiWall = -eta + phi_cell [- T*ln(c)]
                phiWall = -eta + self.phi_cell()
                if ndD["elyteModelType"] == "dilute":
                    phiWall -= ndD["T"] * np.log(cWall)
                # phiWall = 0.5 * (phitmp[0] + phitmp[1])
                eqP.Residual = phiWall - utils.mean_linear(
                    phitmp[0], phitmp[1])
            # We have a porous anode -- no flux of charge or anions through current collector
            else:
                eqC.Residual = ctmp[0] - ctmp[1]
                eqP.Residual = phitmp[0] - phitmp[1]

            Nm_edges, i_edges = get_lyte_internal_fluxes(
                ctmp, phitmp, disc["dxd1"], disc["eps_o_tau_edges"], ndD)
            dvgNm = np.diff(Nm_edges) / disc["dxd2"]
            dvgi = np.diff(i_edges) / disc["dxd2"]
            for vInd in range(Nlyte):
                # Mass Conservation (done with the anion, although "c" is neutral salt conc)
                eq = self.CreateEquation(
                    "lyte_mass_cons_vol{vInd}".format(vInd=vInd))
                eq.Residual = disc["porosvec"][vInd] * dcdtvec[vInd] + (
                    1. / ndD["num"]) * dvgNm[vInd]
                # Charge Conservation
                eq = self.CreateEquation(
                    "lyte_charge_cons_vol{vInd}".format(vInd=vInd))
                eq.Residual = -dvgi[vInd] + ndD["zp"] * Rvvec[vInd]

        # Define the total current. This must be done at the capacity
        # limiting electrode because currents are specified in
        # C-rates.
        eq = self.CreateEquation("Total_Current")
        eq.Residual = self.current()
        limtrode = ("c" if ndD["z"] < 1 else "a")
        dx = 1. / Nvol[limtrode]
        rxn_scl = ndD["beta"][limtrode] * (
            1 - ndD["poros"][limtrode]) * ndD["P_L"][limtrode]
        for vInd in range(Nvol[limtrode]):
            if limtrode == "a":
                eq.Residual -= dx * self.R_Vp[limtrode](vInd) / rxn_scl
            else:
                eq.Residual += dx * self.R_Vp[limtrode](vInd) / rxn_scl
        # Define the measured voltage, offset by the "applied" voltage
        # by any series resistance.
        # phi_cell = phi_applied - I*R
        eq = self.CreateEquation("Measured_Voltage")
        eq.Residual = self.phi_cell() - (self.phi_applied() -
                                         ndD["Rser"] * self.current())

        if self.profileType == "CC":
            # Total Current Constraint Equation
            eq = self.CreateEquation("Total_Current_Constraint")
            if ndD["tramp"] > 0:
                eq.Residual = self.current() - (
                    ndD["currPrev"] + (ndD["currset"] - ndD["currPrev"]) *
                    (1 - np.exp(-dae.Time() / (ndD["tend"] * ndD["tramp"]))))
            else:
                eq.Residual = self.current() - ndD["currset"]
        elif self.profileType == "CV":
            # Keep applied potential constant
            eq = self.CreateEquation("applied_potential")
            if ndD["tramp"] > 0:
                eq.Residual = self.phi_applied() - (
                    ndD["phiPrev"] + (ndD["Vset"] - ndD["phiPrev"]) *
                    (1 - np.exp(-dae.Time() / (ndD["tend"] * ndD["tramp"]))))
            else:
                eq.Residual = self.phi_applied() - ndD["Vset"]
        elif self.profileType == "CCsegments":
            if ndD["tramp"] > 0:
                ndD["segments_setvec"][0] = ndD["currPrev"]
                self.segSet = extern_funcs.InterpTimeScalar(
                    "segSet", self, dae.unit(), dae.Time(),
                    ndD["segments_tvec"], ndD["segments_setvec"])
                eq = self.CreateEquation("Total_Current_Constraint")
                eq.Residual = self.current() - self.segSet()

            #CCsegments implemented as discontinuous equations
            else:
                #First segment
                time = ndD["segments"][0][1]
                self.IF(dae.Time() < dae.Constant(time * s), 1.e-3)
                eq = self.CreateEquation("Total_Current_Constraint")
                eq.Residual = self.current() - ndD["segments"][0][0]

                #Middle segments
                for i in range(1, len(ndD["segments"]) - 1):
                    time = time + ndD["segments"][i][1]
                    self.ELSE_IF(dae.Time() < dae.Constant(time * s), 1.e-3)
                    eq = self.CreateEquation("Total_Current_Constraint")
                    eq.Residual = self.current() - ndD["segments"][i][0]

                #Last segment
                self.ELSE()
                eq = self.CreateEquation("Total_Current_Constraint")
                eq.Residual = self.current() - ndD["segments"][-1][0]
                self.END_IF()

        elif self.profileType == "CVsegments":
            if ndD["tramp"] > 0:
                ndD["segments_setvec"][0] = ndD["phiPrev"]
                self.segSet = extern_funcs.InterpTimeScalar(
                    "segSet", self, dae.unit(), dae.Time(),
                    ndD["segments_tvec"], ndD["segments_setvec"])
                eq = self.CreateEquation("applied_potential")
                eq.Residual = self.phi_applied() - self.segSet()

            #CVsegments implemented as discontinuous equations
            else:
                #First segment
                time = ndD["segments"][0][1]
                self.IF(dae.Time() < dae.Constant(time * s), 1.e-3)
                eq = self.CreateEquation("applied_potential")
                eq.Residual = self.phi_applied() - ndD["segments"][0][0]

                #Middle segments
                for i in range(1, len(ndD["segments"]) - 1):
                    time = time + ndD["segments"][i][1]
                    self.ELSE_IF(dae.Time() < dae.Constant(time * s), 1.e-3)
                    eq = self.CreateEquation("applied_potential")
                    eq.Residual = self.phi_applied() - ndD["segments"][i][0]

                #Last segment
                self.ELSE()
                eq = self.CreateEquation("applied_potential")
                eq.Residual = self.phi_applied() - ndD["segments"][-1][0]
                self.END_IF()

        for eq in self.Equations:
            eq.CheckUnitsConsistency = False

        #Ending conditions for the simulation
        if self.profileType in ["CC", "CCsegments"]:
            #Vmax reached
            self.ON_CONDITION((self.phi_applied() <= ndD["phimin"])
                              & (self.endCondition() < 1),
                              setVariableValues=[(self.endCondition, 1)])

            #Vmin reached
            self.ON_CONDITION((self.phi_applied() >= ndD["phimax"])
                              & (self.endCondition() < 1),
                              setVariableValues=[(self.endCondition, 2)])
Ejemplo n.º 6
0
    def DeclareEquations(self):
        """
        Iterates over *aliases*, *reduce analogue ports* and *regimes*, parses mathematical and logical expressions
        and generates daetools equation objects and state transition networks.
        
        :rtype: None
        :raises: RuntimeError
        """
        # Add identifiers for
        __equation_parser__.dictIdentifiers = getEquationsExpressionParserIdentifiers(
            self)

        # 1) Create aliases (algebraic equations)
        aliases = list(self.ninemlComponent.aliases)
        if len(aliases) > 0:
            for i, alias in enumerate(aliases):
                eq = self.CreateEquation(alias.lhs, "")
                eq.Residual = self.nineml_aliases[i](
                ) - __equation_parser__.parse_and_evaluate(alias.rhs)

        # 1a) Create equations for reduce ports (algebraic equations)
        for port in self.nineml_reduce_ports:
            port.generateEquation()

        # 2) Create regimes
        regimes = list(self.ninemlComponent.regimes)
        state_variables = list(self.ninemlComponent.state_variables)
        if len(regimes) > 0:
            # 2a) Create STN for model
            self.STN(nineml_daetools_bridge.ninemlSTNRegimesName)

            for regime in regimes:
                # 2b) Create State for each regime
                self.STATE(regime.name)

                # 2c) Create equations for all state variables/time derivatives
                # Sometime a time_derivative equation is not given and in that case the derivative is equal to zero
                # We have to discover which variables do not have a corresponding ODE and
                # we do that by creating a map {'state_var' : 'RHS'} which initially has
                # set rhs to '0'. RHS will be set later while iterating through ODEs
                map_statevars_timederivs = {}
                for state_var in state_variables:
                    map_statevars_timederivs[state_var.name] = 0

                time_derivatives = list(regime.time_derivatives)
                for time_deriv in time_derivatives:
                    map_statevars_timederivs[
                        time_deriv.dependent_variable] = time_deriv.rhs
                #print map_statevars_timederivs

                for var_name, rhs in list(map_statevars_timederivs.items()):
                    variable = self._findVariable(var_name)
                    if variable == None:
                        raise RuntimeError(
                            'Cannot find state variable {0}'.format(var_name))

                    # Create equation
                    eq = self.CreateEquation(var_name, "")

                    # If right-hand side expression is 0 do not parse it
                    if rhs == 0:
                        eq.Residual = variable.dt()
                    else:
                        eq.Residual = variable.dt(
                        ) - __equation_parser__.parse_and_evaluate(rhs)

                # 2d) Create on_condition actions
                for on_condition in regime.on_conditions:
                    condition = __equation_parser__.parse_and_evaluate(
                        on_condition.trigger.rhs)
                    switchTo = on_condition.target_regime.name
                    triggerEvents = []
                    setVariableValues = []

                    for state_assignment in on_condition.state_assignments:
                        variable = getObjectFromCanonicalName(
                            self,
                            state_assignment.lhs,
                            look_for_variables=True)
                        if variable == None:
                            raise RuntimeError(
                                'Cannot find variable {0}'.format(
                                    state_assignment.lhs))
                        expression = __equation_parser__.parse_and_evaluate(
                            state_assignment.rhs)
                        setVariableValues.append((variable, expression))

                    for event_output in on_condition.event_outputs:
                        event_port = getObjectFromCanonicalName(
                            self,
                            event_output.port_name,
                            look_for_eventports=True)
                        if event_port == None:
                            raise RuntimeError(
                                'Cannot find event port {0}'.format(
                                    event_output.port_name))
                        triggerEvents.append((event_port, daet.Time()))

                    # ACHTUNG!!!
                    # Check the order of switchTo, triggerEvents and setVariableValues arguments in daetools 1.2.0+!!!
                    self.ON_CONDITION(condition,
                                      switchTo=switchTo,
                                      setVariableValues=setVariableValues,
                                      triggerEvents=triggerEvents)

                # 2e) Create on_event actions
                for on_event in regime.on_events:
                    source_event_port = getObjectFromCanonicalName(
                        self, on_event.src_port_name, look_for_eventports=True)
                    if source_event_port == None:
                        raise RuntimeError('Cannot find event port {0}'.format(
                            on_event.src_port_name))

                    switchToStates = []
                    triggerEvents = []
                    setVariableValues = []

                    for state_assignment in on_event.state_assignments:
                        variable = getObjectFromCanonicalName(
                            self,
                            state_assignment.lhs,
                            look_for_variables=True)
                        if variable == None:
                            raise RuntimeError(
                                'Cannot find variable {0}'.format(
                                    state_assignment.lhs))
                        expression = __equation_parser__.parse_and_evaluate(
                            state_assignment.rhs)
                        setVariableValues.append((variable, expression))

                    for event_output in on_event.event_outputs:
                        event_port = getObjectFromCanonicalName(
                            self,
                            event_output.port_name,
                            look_for_eventports=True)
                        if event_port == None:
                            raise RuntimeError(
                                'Cannot find event port {0}'.format(
                                    event_output.port_name))
                        triggerEvents.append((event_port, daet.Time()))

                    # ACHTUNG!!!
                    # Check the order of switchTo, triggerEvents and setVariableValues arguments in daetools 1.2.0+!!!
                    self.ON_EVENT(source_event_port,
                                  switchToStates=switchToStates,
                                  setVariableValues=setVariableValues,
                                  triggerEvents=triggerEvents)

            self.END_STN()

        # 3) Create equations for outlet analog-ports: port.value() - variable() = 0
        for analog_port in self.nineml_analog_ports:
            if analog_port.Type == daet.eOutletPort:
                eq = self.CreateEquation(analog_port.Name + '_portequation',
                                         "")
                var_to = findObjectInModel(self,
                                           analog_port.Name,
                                           look_for_variables=True)
                if var_to == None:
                    raise RuntimeError('Cannot find variable/alias {0}'.format(
                        analog_port.Name))
                eq.Residual = analog_port.value() - var_to()