MTK - struggling with initialization

I’m converting a model from Modelica to ModelingToolkit [MTK]. The Modelica-version works fine using OpenModelica, but the ModelingToolkit has problems wrt. initialization.

Here is the Julia+MTK code that is equivalent to the Modelica code (including initial conditions).

using ModelingToolkit
using ModelingToolkit: t_nounits as t, D_nounits as Dt
using DifferentialEquations
#
# Friction force computation
#
@register_symbolic fForce(v,rho,mu,D,epsilon,Aw)

function fForce(v,rho,mu,D,epsilon,Aw)
    # Friction force
    #= Function input arguments
    v : velocity, m/s 
    rho : density, kg/m3
    mu : viscositiy, Pa.s 
    D : pipe diameter, m 
    epsilon : pipe roughness heigth, m 
    Aw : pipe wetting surface, m2
    =#
    #= Function return (response) value
    F_f : Friction force, N
    =#
    #= Local (protected) quantities
    N_Re : Reynolds number
    arg : temporary variable
    fD : Darcy friction factor
    =#
    N_Re = rho*abs(v)*D/mu 
    if N_Re < 2.3e3
        F_f = 8mu*Aw/D*v
    else
        arg = epsilon/3.7/D + 5.74/(N_Re + 1e-3)^0.9
        if arg <= 0
            fD = 0
        else
            fD = 1/(2 * log10(arg))^2
        end
        F_f = rho*v*abs(v)*Aw*fD/8
    end
    return F_f
end
#
# Registering inputs
#
@register_symbolic u_u_v(t)   # Turbine valve signal function
@register_symbolic u_Wd_e(t)  # Electrical power usage, W
@register_symbolic u_H_t(t)   # Tailwater level, m
#
u_u_v(t) = 0.89062946
u_Wd_e(t) = 111.729e6
u_H_t(t) = 5
#
# MTK model
#
@mtkmodel HydropowerSystem begin
    # Model parameters
    # -- structure parameters
    @structural_parameters begin
        # :io = standard input-output, :zero = zero dynamics
        # :lin_io = linearizing input-output, :lin_zero = linearizing zero dynamics
        case = :io
    end
    #=
    # -- constants
    @constants begin
        # Constant to keep or kill surge tank
        keep_surge_tank = 1, [description = "Parameter to keep surge tank, -"]
        g = 9.81,           [description = "Acceleration of gravity, m/s2"]
    end
    =#
    #
    # -- parameters
    @parameters begin
        # Constant to keep or kill surge tank
        g = 9.81,               [description = "Acceleration of gravity, m/s2"]
        # General parameters
        gamma_sw = 1,           [description = "Swirl constant,"]
        k_g = 0.675
        gamma_f = 2,            [description = "Friction constant, -"]
        gamma_sh = 0.88,        [description = "shock constant, -"]
        rho = 997,              [description = "Water density, kg/m3"]
        mu = 8.9e-4,            [description = "Viscosity of water, Pa.s"]
        epsilon = 1.5e-5,       [description = "Pipe roughness height, m"]
        H_r = 48,               [description = "Reservoir level above intake, m"]
        p_a = 1.01325e5,        [description = "Atmospheric pressure, Pa"]
        #
        # Rated values
        Vd_n = 24.3,            [description = "Nominal flow rate, m3/s"]
        Np = 6,                 [description = "Number of pole pairs, -"]
        f_0 = 50,               [description = "Electric freqency, Hz"]
        w_n = f_0*2pi/Np,       [description = "Nominal angular velocity, rad/s"]
        W_nom = 104.4e6,        [description = "Nominal power output, MW"]
        #
        # Intake race
        H_i = 23,               [description = "Vertical drop of intake race, m"]
        L_i = 6600,             [description = "Length of intake race, m"]
        D_i = 5.8,              [description = "Diameter of intake race, m"]
        A_i = pi*D_i^2/4,       [description = "Cross section area of intake race, m2"]
        V_i = A_i*L_i,          [description = "Volume of intake race, m3"]
        m_i = rho*V_i,          [description = "Mass of intake race, kg"]
        A_wi = pi*D_i*L_i,      [description = "Wetting surface of intake race, m2"]
        p_i1 = p_a + rho*g*H_r, [description = "Intake race pressure, Pa"]
        #
        # Penstock
        H_p = 428.5,            [description = "Vertical drop of penstock, m"]
        L_p = 600,              [description = "Length of penstock, m"]
        D_p = 3,                [description = "Diameter of penstock, m"]
        A_p = pi*D_p^2/4,       [description = "Cross section area of penstock, m2"]
        V_p = A_p*L_p,          [description = "Volume of penstock, m3"]
        m_p = rho*V_p,          [description = "Mass of penstock, kg"]
        A_wp = pi*D_p*L_p,      [description = "Wetting surface of penstock, m2"]
        #
        # Discharge
        H_d = 0.5,              [description = "Vertical drop of discharge race, m"]
        L_d = 600,              [description = "Length of discharge race, m"]
        D_d = 5.8,              [description = "Diameter of discharge race, m"]
        A_d = pi*D_d^2/4,       [description = "Cross section area of discharge race, m2"]
        V_d = A_d*L_d,          [description = "Volume of discharge race, m3"]
        m_d = rho*V_d,          [description = "Mass of discharge race, kg"]
        A_wd = pi*D_d*L_d,      [description = "Wetting surface of discharge race, m2"]
        #
        # Aggregate
        J_a = 2e5,              [description = "Moment of inertia of aggregate, kg.m2"]
        k_ba = 1e3,             [description = "Friction factor in the aggregate bearing box, W.s3/rad3"]
        eta_e = 0.99,           [description = "Electricity generator efficiency, -"]
        #
        # Turbine
        w_1 = 0.25,             [description = "Width of the turbine/blades inlet, m"]
        beta_1 = 62.85,         [description = "Turbine inlet angle, degree"]
        beta_2 = 17.5,          [description = "Turbine outlet angles, degree"]
        r_1 = 1.32,             [description = "Radius of the turbine outer, m"]
        r_2 = 0.777,            [description = "Radius of the turbine inner, m"]
        r_v = 1.445,            [description = "Radius of the outer guide vanes, m"]
        A_1 = 2pi*r_1*w_1,      [description = "Maximal area of opening, m2"]
    end
    #
    # Model variables, with initial values needed
    @variables begin
        # Variables
        #
        md_i(t),            [description = "Inlet race mass flow rate, kg/s"]
        md_p(t),            [description = "Penstock mass flow rate, kg/s"]
        md_d(t),            [description = "Discharge mass flow rate, kg/s"]
        #
        Vd_i(t)=Vd_n,       [description = "Inlet race volumetric flow rate, m3/s"]
        Vd_p(t),            [description = "Penstock volumetric flow rate, m3/s"]
        Vd_d(t),            [description = "Discharge volumetric flow rate, m3/s"]
        #
        v_i(t),             [description = "Inlet race linear velocity, m/s"]
        v_p(t),             [description = "Penstock linear velocity, m/s"]
        v_d(t),             [description = "Discharge linear velocity, m/s"]
        #
        N_Re_i(t),          [description = "Inlet race Reynolds number, -"]
        N_Re_p(t),          [description = "Penstock Reynolds number, -"]
        N_Re_d(t),          [description = "Discharge Reynolds number, -"]
        #
        F_fi(t),            [description = "Inlet race tank friction force, N"]
        F_fp(t),            [description = "Penstock tank friction force, N"]
        F_fd(t),            [description = "Discharge tank friction force, N"]
        #
        F_gi(t),            [description = "Inlet race tank gravity force, N"]
        F_gp(t),            [description = "Penstock tank gravity force, N"]
        F_gd(t),            [description = "Discharge tank gravity force, N"]
        #
        M_i(t),             [description = "Inlet race tank linear momentum, kg.m/s"]
        M_p(t),             [description = "Penstock tank linear momentum, kg.m/s"]
        M_d(t),             [description = "Discharge tank linear momentum, kg.m/s"]
        #
        p_m(t),             [description = "Pressure in manifold, Pa"]
        p_i2(t),            [description = "Inlet race exit pressure, Pa"]
        p_p1(t),            [description = "Penstock inlet pressure, Pa"]
        p_p2(t),            [description = "Penstock outlet pressure, Pa"]
        p_tr2(t),           [description = "Turbine outlet pressure, Pa??"]
        p_d1(t),            [description = "Discharge inlet pressure, Pa"]
        p_d2(t),            [description = "Discharge outlet pressure, Pa"]
        #
        Dp_tt(t),           [description = "Turbine total pressure drop, Pa"]
        Dp_tf(t),           [description = "Turbine friction pressure drop, Pa"]
        Dp_sw(t),           [description = "Turbine swirl pressure drop, Pa"]
        Dp_sh(t),           [description = "Turbine shock pressure drop, Pa"]
        Dp_ts(t),           [description = "Turbine shaft pressure drop, Pa"]
        #
        alpha_1(t),         [description = "Guide vane opening angle, deg"]
        gamma(t),           [description = "Inlet flow angle, deg"]
        #
        v_1(t),             [description = "XX linear velocity, m/s"]
        v_w1(t),            [description = "XX linear velocity, m/s"]
        v_w2(t),            [description = "XX linear velocity, m/s"]
        # Variables related to height loss ex turbine
        Dp_i(t),            [description = "Intake race pressure drop, Pa"]
        Dp_p(t),            [description = "Penstock pressure drop, Pa"]
        Dp_d(t),            [description = "Discharge pressure drop, Pa"]
        Dp_xtr(t),          [description = "XX pressure drop, Pa"]
        H_xtr(t),           [description = "XX height drop, m"]
        H_tr(t),            [description = "XX heigth drop, m"]
        H_n(t),             [description = "XX nominal heigth drop, m"]  
        K_a(t),             [description = "Aggregate kinetic energy, J"]
        Wd_ts(t),           [description = "Turbine shaft power, W"]
        Wd_af(t),           [description = "Aggregate friction power, W"]
        # Real Wd_e; // Work powers
        w(t)=w_n,           [description = "Aggregate angular velocity, rad/s"]
        Wd_tt(t),            [description = "Turbine total power, W"]
        # Inputs
        u_v(t),             [description = "Input guide vane signal, -"]
        Wd_e(t),            [description = "Electrical power usage, W"]
        H_t(t),             [description = "Tail water level, m"]
    end
    #
    # Model equations
    @equations begin
        # Discharge counter pressure
        p_d2 ~ p_a + rho*g*H_t
        # Mass flow rates
        md_i ~ rho*Vd_i
        md_p ~ rho*Vd_p
        md_d ~ rho*Vd_d
        # Linear velocities
        Vd_i ~ A_i*v_i
        Vd_p ~ A_p*v_p
        Vd_d ~ A_d*v_d
        # Manifold
        p_i2 ~ p_m
        p_p1 ~ p_m
        Vd_i ~ Vd_p
        # Penstock-Discharge string
        Vd_d ~ Vd_p
        # Momentums
        M_i ~ m_i*v_i
        M_p ~ m_p*v_p
        M_d ~ m_d*v_d
        # Gravitational forces
        F_gi ~ m_i*g*H_i/L_i
        F_gp ~ m_p*g*H_p/L_p 
        F_gd ~ m_d*g*H_d/L_d
        # Reynold's numbers
        N_Re_i ~ rho*abs(v_i)*D_i/mu
        N_Re_p ~ rho*abs(v_p)*D_p/mu
        N_Re_d ~ rho*abs(v_d)*D_d/mu
        # Friction forces
        F_fi ~ fForce(v_i,rho,mu,D_i,epsilon,A_wi)
        F_fp ~ fForce(v_p,rho,mu,D_p,epsilon,A_wp)
        F_fd ~ fForce(v_d,rho,mu,D_d,epsilon,A_wd)
        # Turbine
        #alpha versus guide vane signal
        alpha_1 ~ (90-90*u_v)   # "relation between alpha and u_v"
        v_1 ~ Vd_p/(A_1*sind(alpha_1)) # "linear velocity"
        gamma ~ atand(1/((v_w1/(sind(alpha_1)*v_1)) - (1/tand(alpha_1) )))
        v_w1 ~ w*r_1
        v_w2 ~ w*r_2
        # Turbine shaft power
        Wd_ts ~ rho*Vd_p*w*( r_1*Vd_p/(A_1*tand(alpha_1))-gamma_sw*k_g*r_2^2*w*(1- (w_n*Vd_p/(w*Vd_n))) )
        # Turbine pressure losses
        Dp_tf ~ gamma_f*0.5*rho*(Vd_p/(pi*r_2^2))^2
        Dp_sh ~ gamma_sh*0.5*rho*((sind(gamma-beta_1)^2)/(sind(beta_1)^2))*(Vd_p/(A_1*sind(gamma)))^2
        Dp_sw ~ rho*(gamma_sw*k_g*v_w2)^2*(1-(w_n*Vd_p/(w*Vd_n)))^2
        Dp_ts ~ Wd_ts/Vd_p
        
        Dp_tt ~ Dp_tf + Dp_sh + Dp_sw + Dp_ts
        Wd_tt ~ Vd_p*Dp_tt
        Dp_tt ~ p_p2-p_tr2
        p_d1 ~ p_tr2
        #Pressure friction losses in pipes
        Dp_i ~ F_fi/A_i
        Dp_p ~ F_fp/A_p
        Dp_d ~ F_fd/A_d
        Dp_xtr ~ Dp_i + Dp_p + Dp_d
        H_xtr ~ Dp_xtr/rho/g
        H_n ~ H_i + H_p
        H_tr ~ H_n + H_r - H_t - H_xtr
        # Aggregate
        K_a ~ J_a*w^2/2
        Wd_af ~ k_ba*w*abs(w)/2   # k_ba*w^2/2
        # Balance laws
        Dt(M_i) ~ (p_i1 - p_i2)*A_i + F_gi - F_fi
        Dt(M_p) ~ (p_p1 - p_p2)*A_p + F_gp - F_fp
        Dt(M_d) ~ (p_d1 - p_d2)*A_d + F_gd - F_fd
        Dt(K_a) ~ Wd_ts - Wd_af - Wd_e
        # Injecting inputs
        #if case == :io 
            # Standard input-output simulation
            u_v ~ u_u_v(t)
        #elseif case == :lin_io 
            # Linearization of input-output model
            # u_v ~ u_u_v(t)
        #end 
        Wd_e ~ u_Wd_e(t)
        H_t ~ u_H_t(t)
    end
end
#
# Instantiation, etc.
#
@mtkbuild hps = HydropowerSystem()

tspan = (0,15)

prob_sys = ODEProblem(hps, [], tspan)

The model looks as follows:
image

The dynamics seems ok – one ode for rotational (kinetic) energy, and one for velocity + some algebraic equations.

In OpenModelica, it suffices to provide initial conditions for Vd_p which is a volumetric flowrate, directly proportional to v_d, and w which is the angular velocity, directly related to K_a. In other words, with Vd_p and w specified, it is trivial to find initial conditions for K_a and v_d.

Julia/MTK complains that

ArgumentError: SymbolicUtils.BasicSymbolic{Real}[p_d1(0)] are either missing from the variable map or missing from the system's unknowns/parameters list.

OK… if I try to specify an initial value for p_d1, I get another error message, related to over specified system …

Warning: Initialization system is overdetermined. 2 equations for 1 unknowns. Initialization will default to using least squares. To suppress this warning pass warn_initialize_determined = false

If I try to solve this overdetermined system (default solver or Rodas5()), there is no solution – sol.t contains the single element 0.0.

Question:

  • What is a good procedure to understand which initial conditions are needed?

Why not provide a guess for p_d1?

1 Like

I have tried that. I’m then warned that the system is overdetermined. When I still try to solve it, it comes out with a single element in sol.t (0), but no solution to the system.

I will try to rewrite the system into a simpler, but less general form

No, that would be impossible. Adding a new initial condition would cause it to go overdetermined, adding it as a guess does not change the number of equations.

1 Like

Ah. I see what you say. I didn’t see the word “guess”. I’ll check the manual to see how one can specify a guess.

OK – something like

@variables begin
    p_d1(t), [guess = 2e5]
end

Yepp, this did the trick! Thanks!