Skip to content

could the @structure_parameters of descendant models the main model calls be assigned simultaneously during @mtkbuild? Add the global variable feature? #2848

New issue

Have a question about this project? No Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “No Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? No Sign in to your account

Open
wang890 opened this issue Jul 6, 2024 · 10 comments

Comments

@wang890
Copy link

wang890 commented Jul 6, 2024

The MTK modeling process seems to be as follows:
@structural_parameters determine the model structure. Only after the model structure is determined can a specific ODESystem be obtained, so structural parameters can not be assined by @parameters. The @parameter assignment of ODEsystem is very easy and can be done after @mtkbuild, but structural parameters should be assigned during @mtkbuild.

# for @parameter params, assignment is easy.
u0s = [sys.capacitor.v=>0.0, sys.resistor.i=>0.0]
params = [sys.resistor.alpha=>0.01]
tspan = (0, 20.0)
saveat = 0.1
prob = ODEProblem(sys, u0s, tspan, params)

Is your feature request related to a problem? Please describe.

For the case of a hierarchical ModelingToolkit.model, that is, model A calls B, B calls C, @structural_parameters assignent is a bit difficult. Take the RC system code of Issue #2758 as example, The current solution is as follows.

@mtkmodel Rc begin
    @structural_parameters begin        
        resistorUseHeatPort = false
    end
    @components begin                
        resistor = Resistor(; R=1.0, use_Heat_Port=resistorUseHeatPort)        
        ......(Ignored)
        if resistorUseHeatPort
            fixedTemperature = FixedTemperature(T=400.15)
        end
    end
    @equations begin
        connect.... (Ignored)
        if resistorUseHeatPort
             connect(resistor.conditionalHeatPort.heatPort, fixedTemperature.port)
        end  
    end
end
@mtkmodel Resistor begin   
    @structural_parameters begin        
        use_Heat_Port = false       
    end   
    @extend i,v,p,n = onePort = OnePort()
    
    # only the last @extend takes effect, so conditionalHeatPort was defined as @components
    @components begin        
         conditionalHeatPort = ConditionalHeatPort(; useHeatPort=use_Heat_Port)
    end
    ...... (Ignored) 
end
@mtkmodel ConditionalHeatPort begin
    @structural_parameters begin        
        useHeatPort = false  # = true, if heatPort is enabled
    end    
    ...... Ignore @parameters  @variables
           
    @components begin
        heatPort = HeatPort_a()
    end 
    @equations begin
        if useHeatPort     
            ......
        else ...... 
end
# when @mtkbuild using Rc
@mtkbuild sys = Rc(; resistorUseHeatPort=true)

resistorUseHeatPort, use_Heat_Port, useHeatPort are the structural parameter of model Rc, Resistor, ConditionalHeatPort, respectively. In fact, these three structural parameters mean the same thing. In the current MTK version, they cannot use the same variable name, so I need to think about how to name them using different strings.

For this hierarchical model Rc, I must pass the structural_parameters step by step, like Resistor(; use_Heat_Port=resistorUseHeatPort), and ConditionalHeatPort(; useHeatPort=use_Heat_Port) in Resistor again, and then I can bulid ODESystem using @mtkbuild sys = Rc(; resistorUseHeatPort=true)

Describe the solution you’d like

For the @mtkmodel style, not @component function style:

(1) Is there a better way to pass @structural_parameters in hierarchical models? When the main model is instantiated using @mtkbuild, could the structure parameters of its child, grandchild, descendant models it calls be assigned simultaneously? like @mtkbuild sys = Rc(; structural_param=..., child's_structural_param=..., grandchild's__structural_param=...) , assigning all stucture parameters of the called descendant models at one time.

In current MTK version, it seems that:
the @parameters of child model the main model calls could be assigned by double _ , like @mtkbuild sys = Rc(; resistor__R=100), but it doesn't work for grandchild models and structure parameters, that is, resistor__conditionalHeatPort__T=300 Error, resistor__use_Heat_Port=true Error.

(2) Is it possible to add global variable features? If the structure parameter could be declared as a global variable, resistorUseHeatPort, use_Heat_Port, useHeatPort could use the same name such as use_heat_port, and we don't need to pass the structural parameter step by step, we could do it as follows: @mtkbuild sys = Rc(; use_head_port=true), the use_heat_port value in Resistor() ConditionalHeatPort() is automatically that of use_heat_port in Rc, because use_heat_port is a global variable. ---- other related keywords: protected, private, public, ...

@structural_parameters is useful. Sometimes, we can use the Julia function ifelse(), but sometimes if... else... statement is necessary for the model construction by @mtkmodel.

Thanks a lot for you all @ChrisRackauckas

@wang890 wang890 changed the title could the @structure_parameters of descendant models the main model calls assigned simultaneously during @mtkbuild? Add the global variable feature? could the @structure_parameters of descendant models the main model calls be assigned simultaneously during @mtkbuild? Add the global variable feature? Jul 6, 2024
@ChrisRackauckas
Copy link
Member

@AayushSabharwal or @ven-k just handled something of the sort?

@wang890
Copy link
Author

wang890 commented Oct 2, 2024

This feature is great, but will take time to implement. If it had been handled, how to use, where is the code modification in the corresponding section of src code, test code or docs url? I did not find. Thanks a lot @AayushSabharwal, @ven-k

# for @parameter params, assignment is easy.
u0s = [sys.capacitor.v=>0.0, sys.resistor.i=>0.0]
params = [sys.resistor.alpha=>0.01]
tspan = (0, 20.0)
saveat = 0.1
prob = ODEProblem(sys, u0s, tspan, params)

... ... like above parameter assignment,
(1) Is there a better way to pass @structural_parameters in hierarchical models? When the main model is instantiated using @mtkbuild, could the structure parameters of its child, grandchild, descendant models it calls be assigned simultaneously? like @mtkbuild sys = Rc(; structural_param=..., child's_structural_param=..., grandchild's__structural_param=...) , assigning all stucture parameters of the called descendant models at one time.
.... ....

@wang890
Copy link
Author

wang890 commented Oct 6, 2024

@AayushSabharwal or @ven-k just handled something of the sort?

Maybe, like:
@mtkbuild sys = Rc(; ConditionalHeatPort.useHeatPort=true),
OR
@mtkbuild sys = Rc(; Rc.Resistor.ConditionalHeatPort.useHeatPort=true)

@AayushSabharwal, @ven-k

@AayushSabharwal
Copy link
Member

(1) Is there a better way to pass @structural_parameters in hierarchical models? When the main model is instantiated using @mtkbuild, could the structure parameters of its child, grandchild, descendant models it calls be assigned simultaneously?

I can see this being possible, but specifically only for @mtkmodel whose children are also @mtkmodel (since otherwise we don't have the metadata).

(2) Is it possible to add global variable features? If the structure parameter could be declared as a global variable, resistorUseHeatPort, use_Heat_Port, useHeatPort could use the same name such as use_heat_port, and we don't need to pass the structural parameter step by step,

What if you have two resistors in a model, one of which has a heat port and one doesn't? Or if you have two child components which just so happen to have identically named structural parameters (think something trivial like n for the length of an array).

@AayushSabharwal or @ven-k just handled something of the sort?

I'm not aware of any such syntax 😅

Maybe, like:

@mtkbuild sys = Rc(; ConditionalHeatPort.useHeatPort=true),

OR

@mtkbuild sys = Rc(; Rc.Resistor.ConditionalHeatPort.useHeatPort=true)

Interesting concept. Assuming we propagate structural parameters up this would lower to something like

Rc(; ConditionalHeatPort__useHeatPort = true)

@wang890
Copy link
Author

wang890 commented Oct 14, 2024

Interesting concept. Assuming we propagate structural parameters up this would lower to something like

Rc(; ConditionalHeatPort__useHeatPort = true)

Rc model call Resistor, Resistor call ConditionalHeatPort, and ConditionalHeatPort has a structural parameter of useHeatPort. They are all @mtkmodel , code is here
use current version MTK v9.46.1, Error message is following:

Closest candidates are:
__Rc__(; name, resistorUseHeatPort, resistor__R, resistor__use_Heat_Port, capacitor__C, source__V, fixedTemperature__T) 
got unsupported keyword arguments "resistor__conditionalHeatPort__useHeatPort", "conditionalHeatPort__useHeatPort"

that is, when @mtkbuild, MTK already allows assigning parameters and structural parameters to child models, but not to grandchild models. If support many levels like "resistor__conditionalHeatPort__ ... __useHeatPort", it would be very nice. @AayushSabharwal

@wang890
Copy link
Author

wang890 commented Oct 14, 2024

Because MTK does not support argument "resistor__conditionalHeatPort__useHeatPort" now,
I set resistorUseHeatPort, use_Heat_Port, useHeatPort as the structural parameter of model Rc, Resistor, ConditionalHeatPort, respectively, pass the structural_parameters step by step (Not a good way)

In fact, the three structural parameters mean the same thing, but in the current MTK version, they cannot use the same variable name, so I need to think about how to name them using different strings (resistorUseHeatPort, use_Heat_Port, useHeatPort ), this is not good. This problem no longer exists, if mtk could surrport multi-level argument "resistor__conditionalHeatPort__ ... __useHeatPort"

@AayushSabharwal

@mtkmodel Rc begin
    @structural_parameters begin        
        resistorUseHeatPort = false
    end
    @components begin                
        resistor = Resistor(; R=1.0, use_Heat_Port=resistorUseHeatPort)        
        ......(Ignored)
        if resistorUseHeatPort
            fixedTemperature = FixedTemperature(T=400.15)
        end
    end
    @equations begin
        connect.... (Ignored)
        if resistorUseHeatPort
             connect(resistor.conditionalHeatPort.heatPort, fixedTemperature.port)
        end  
    end
end
@mtkmodel Resistor begin   
    @structural_parameters begin        
        use_Heat_Port = false       
    end   
    @extend i,v,p,n = onePort = OnePort()
    
    # only the last @extend takes effect, so conditionalHeatPort was defined as @components
    @components begin        
         conditionalHeatPort = ConditionalHeatPort(; useHeatPort=use_Heat_Port)
    end
    ...... (Ignored) 
end
@mtkmodel ConditionalHeatPort begin
    @structural_parameters begin        
        useHeatPort = false  # = true, if heatPort is enabled
    end    
    ...... Ignore @parameters  @variables
           
    @components begin
        heatPort = HeatPort_a()
    end 
    @equations begin
        if useHeatPort     
            ......
        else ...... 
end
# when @mtkbuild using Rc
@mtkbuild sys = Rc(; resistorUseHeatPort=true)

@wang890
Copy link
Author

wang890 commented Oct 14, 2024

What if you have two resistors in a model, one of which has a heat port and one doesn't? Or if you have two child components which just so happen to have identically named structural parameters (think something trivial like n for the length of an array).

Yes, these are also possible application scenarios. I don't know whether MTK needs the keywords protected, private, public, ..., but Modelica has these keywords. If need, this issue could be solved in future. Now, support multi-level argument "resistor__conditionalHeatPort__ ... __useHeatPort" firstly.

Thank you very much @AayushSabharwal

@AayushSabharwal
Copy link
Member

that is, when @mtkbuild, MTK already allows assigning parameters and structural parameters to child models, but not to grandchild models. If support many levels like "resistor__conditionalHeatPort__ ... __useHeatPort", it would be very nice.

Yeah. The macro doesn't propagate down multiple levels of the hierarchy yet.

In fact, the three structural parameters mean the same thing, but in the current MTK version, they cannot use the same variable name,

That's interesting. What happens when you give all of them the same name?

@wang890
Copy link
Author

wang890 commented Oct 16, 2024

In fact, the three structural parameters mean the same thing, but in the current MTK version, they cannot use the same variable name,

That's interesting. What happens when you give all of them the same name?

It's OK now @AayushSabharwal .
I remember there was this error before, but now it is gone, No error under current Envrioments:

Julia 1.11.0, vscode 1.94.2, Julia extention v1.124.2 of vscode
⌃ [d360d2e6] ChainRulesCore v1.24.0
⌃ [2b5f629d] DiffEqBase v6.151.4
⌅ [06fc5a27] DynamicQuantities v0.13.2
⌃ [961ee093] ModelingToolkit v9.19.0
⌃ [1dea7af3] OrdinaryDiffEq v6.84.0
⌃ [91a5bcdd] Plots v1.40.4

Now, the same string "useHeatPort" as following is OK.

@mtkmodel Rc begin
    @structural_parameters begin        
        useHeatPort = false
    end
    @components begin                
        resistor = Resistor(; R=1.0, useHeatPort=useHeatPort)        
        ......(Ignored)
        if useHeatPort
            fixedTemperature = FixedTemperature(T=400.15)
        end
    end
    ......
end
@mtkmodel Resistor begin   
    @structural_parameters begin        
        useHeatPort = false       
    end   
    ...    
    @components begin        
         conditionalHeatPort = ConditionalHeatPort(; useHeatPort=useHeatPort)
    end
    ...... (Ignored) 
end
@mtkmodel ConditionalHeatPort begin
    @structural_parameters begin        
        useHeatPort = false  # = true, if heatPort is enabled
    end    
    ......     
end

@wang890
Copy link
Author

wang890 commented Oct 16, 2024

that is, when @mtkbuild, MTK already allows assigning parameters and structural parameters to child models, but not to grandchild models. If support many levels like "resistor__conditionalHeatPort__ ... __useHeatPort", it would be very nice.

Yeah. The macro doesn't propagate down multiple levels of the hierarchy yet.

propagating down multiple levels, is a required feature.

No Sign up for free to join this conversation on GitHub. Already have an account? No Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants