Engee documentation
Notebook

Creating complex components in the Engee physics modeling language

The Engee physical modeling language allows you not only to develop your own components, but also to combine different components into a single whole. Such components are called composite.

It's like a constructor, when there are parts (components) and our task is to connect them in the right order.

Why make composite components?

  • "Packing" multiple components into one (as a subsystem in conventional modeling)
  • Arrays of components

Example of a composite component

As an example of a composite component, consider a DC motor with an external shaft load.

To make sure that the component was assembled correctly, let's take a reference implementation on primitives from the library of non-directional blocks.:

image.png

For testing, we will simulate the following scenario: in the first 0.1 seconds of operation, the engine runs without load on the shaft, and reaches maximum speed, and then a load occurs on the shaft.

Creating a composite component

Composite components are created in the same way as custom components using the Engee physics modeling language. Therefore, the component code must be contained in the *.ngpc file located along the Engee search path.

Let's look at the component code from the DCMotorComosite.ngpc file:

@engeemodel DCMotorComposite begin
    
    @components begin
        p = EngeePhysicalFoundation.Electrical.Pin(), [view = ("+", "left")]
        n = EngeePhysicalFoundation.Electrical.Pin(), [view = ("-", "left")]
        r = EngeePhysicalFoundation.Mechanical.Rotational.Flange(), [view = ("R", "right")]
        c = EngeePhysicalFoundation.Mechanical.Rotational.Flange(), [view = ("C", "right")]
    end

    @parameters begin
        rotor_resistance    = 3.9, 		[unit="Ohm"]
        rotor_inductance    = 12e-6, 	[unit="H"]
        motor_inertia       = 0.01, 	[unit="g*cm^2"]
        breakaway_torque    = 0.02e-3, 	[unit="N*m"]
        coulomb_torque      = 0.02e-3, 	[unit="N*m"]
        breakaway_velocity  = 0.03347, 	[unit="rad/s"]
        back_emf_constant   = 0.072e-3*60/(2*pi), [unit="V/(rad/s)"]
        viscous_coefficient = 0.0, [unit="N*m/(rad/s)"]
    end

    @components [gui = None] begin
        rotorResistor = EngeePhysicalFoundation.Electrical.Elements.Resistor(R=default(rotor_resistance))
        rotorInductor = EngeePhysicalFoundation.Electrical.Elements.Inductor(L=default(rotor_inductance))
        motorInertia = EngeePhysicalFoundation.Mechanical.Rotational.Elements.Inertia(I=default(motor_inertia))
        friction = EngeePhysicalFoundation.Mechanical.Rotational.Elements.Friction(w_breakaway=default(breakaway_velocity),T_breakaway=default(breakaway_torque),T_coulomb=default(coulomb_torque), viscous_coefficient=default(viscous_coefficient))
        rotEMechConverter = EngeePhysicalFoundation.Electrical.Elements.RotationalConverter(k=default(back_emf_constant))
    end

    @equations begin
        connect(p, rotorResistor.p)
        connect(rotorResistor.n, rotorInductor.p)
        connect(rotorInductor.n, rotEMechConverter.p)
        connect(rotEMechConverter.n, n)
        connect(rotEMechConverter.rod_flange, friction.rod_flange, motorInertia.flange, r)
        connect(rotEMechConverter.case_flange, friction.case_flange, c)
    end

end

Pay attention to how we create the sub-components - they are contained in the section @components. We initialize their parameters with values from the section @parameters

The assembly of the sub-components into a single component takes place in the section @equations using the function connect(). Please note that you can specify multiple connection ports at once!

Testing a composite component

Let's check the modeling results of our composite component relative to the same model, but using primitives.

In [ ]:
demoroot = @__DIR__
engee.addpath(demoroot)
mdl = engee.open(joinpath(demoroot,"DCComposite.engee"))
sim_res = engee.run(mdl);

Let's plot the simulation results.:

In [ ]:
using Plots

CustC = collect(sim_res["Cust"]);
Primitives = collect(sim_res["Prim"]);
plot(CustC.time,CustC.value, label = "Custom Component")
plot!(Primitives.time,Primitives.value, label = "The library of blocks")
plot!(legend=:outerbottom,legendcolumns=2)
Out[0]:

Let's find the maximum absolute error:

In [ ]:
err = CustC.value .- Primitives.value;
println("Maximum absolute error: $(maximum(err))")
Максимальная абсолютная ошибка: 2.093656803481281e-9

The simulation results actually match, which means our component is working correctly!

Conclusions

The Engee physical modeling language is not just a language for writing custom components for 1-d modeling, but also a language for creating and describing complex physical systems. At the same time, the readability of the code was preserved, which is important for complex systems!