diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/models/Electric.jl b/models/Electric.jl index 1240456..cc7096a 100644 --- a/models/Electric.jl +++ b/models/Electric.jl @@ -34,6 +34,8 @@ Inductor = OnePort | Model( L = 1.0u"H", i=Var(init=0.0u"A"), equations = :[ L*d ConstantVoltage = OnePort | Model( V = 1.0u"V", equations = :[ v = V ] ) +ConstantCurrent = OnePort | Model( I = 1.0u"A", equations = :[ i = I ] ) + Ground = Model( p = Pin, equations = :[ p.v = 0.0u"V" ] ) # Ideal operational amplifier (norator-nullator pair), but 3 pins diff --git a/models/ElectricLib.jl b/models/ElectricLib.jl new file mode 100644 index 0000000..97fa7ce --- /dev/null +++ b/models/ElectricLib.jl @@ -0,0 +1,187 @@ +""" +Modia module with electric component models (inspired from Modelica Standard Library). + +* Developer: Hilding Elmqvist, Mogram AB +* Copyright (c) 2016-2021: Hilding Elmqvist +* License: MIT (expat) + +""" +module Electric + +using Modia + +@define Pin = Model( v = potential, i = flow, + icon="Modelica.Electrical.Analog.Interfaces.Pin" ) + +@define OnePort = Model( + p = Pin(annotation=:left), + n = Pin(annotation=:right), + icon="Modelica.Electrical.Analog.Interfaces.OnePort", + equations = :[ + v = p.v - n.v + 0 = p.i + n.i + i = p.i ] + ) + +""" + Resistor(R=1.0u"Ω") + +Electrical resistor + + +`R` - Resistance Ω +""" +@define Resistor = OnePort | Model( R = 1.0u"Ω", icon="Modelica.Electrical.Analog.Basic.Resistor", equations = :[ R*i = v ] ) + +# @showModel(Resistor) + +#@define Capacitor = OnePort | Model( C = 1.0u"F", v=Var(init=0.0u"V"), equations = :[ C*der(v) = i ] ) +@define Capacitor = OnePort | Model( C = 1.0u"F", v=Var(init=0.0), + icon="Modelica.Electrical.Analog.Basic.Capacitor", + equations = :[ C*der(v) = i ] ) + +#@define Inductor = OnePort | Model( L = 1.0u"H", i=Var(init=0.0u"A"), equations = :[ L*der(i) = v ] ) +@define Inductor = OnePort | Model( L = 1.0u"H", i=Var(init=0), + icon="Modelica.Electrical.Analog.Basic.Inductor", + equations = :[ L*der(i) = v ] ) + +@define ConstantVoltage = OnePort | Model( V = 1.0u"V", + icon="Modelica.Electrical.Analog.Sources.ConstantVoltage", + equations = :[ v = V ] ) + +@define ConstantCurrent = OnePort | Model( I = 1.0u"A", + icon="Modelica.Electrical.Analog.Sources.ConstantCurrent", + equations = :[ i = I ] ) + +@define Ground = Model( p = Pin, + icon="Modelica.Electrical.Analog.Basic.Ground", + equations = :[ p.v = 0.0u"V" ] ) + +#= +# Ideal operational amplifier (norator-nullator pair), but 3 pins +IdealOpAmp3Pin = Model( + in_p = Pin, + in_n = Pin, + out = Pin, + equations = :[ + in_p.v = in_n.v + in_p.i = 0u"A" + in_n.i = 0u"A" ] +) + +# Partial generic voltage source using the input signal as source voltage +PartialSignalVoltage = Model( + v = input, + p = Pin, + n = Pin +) + +# Generic voltage source using the input signal (without and with unit) as source voltage +SignalVoltage = PartialSignalVoltage | Model( + equations = :[ + p.v - n.v = v + 0 = p.i + n.i + i = p.i ] +) +UnitlessSignalVoltage = PartialSignalVoltage | Model( + equations = :[ + p.v - n.v = v*u"V" + 0 = p.i + n.i + i = p.i ] +) + + +# Partial sensor to measure the current in a branch +PartialCurrentSensor = Model( + i = output, + p = Pin, # (info = "Positive pin") + n = Pin, # (info = "Negative pin") +) + +# Sensor to measure the current in a branch +CurrentSensor = PartialCurrentSensor | Model( + equations = :[ + p.v = n.v + 0 = p.i + n.i + i = p.i] +) +UnitlessCurrentSensor = PartialCurrentSensor | Model( + equations = :[ + p.v = n.v + 0 = p.i + n.i + i = p.i/u"A"] +) +=# + +#= +# Step voltage source +@model StepVoltage begin + V = Parameter(1.0, start = 1.0, info = "Voltage") #, T = Unitful.V) + startTime = Parameter(0.0, start = 0.0, info = "Start time") # , T = Unitful.s) + OnePort() +equations + v = if time < startTime; 0 else V end +end + +@model VoltageSource begin + OnePort() + offset = Par(0.0) # Voltage offset + startTime = Par(0.0) # Time offset + signalSource = SignalSource(offset=offset, startTime=startTime) +equations + v = signalSource.y +end + +#= +@model SineVoltage1 begin + # Sine voltage source + V = Parameter() # Amplitude of sine wave + phase = Par(0.0) # Phase of sine wave + freqHz = Parameter() # Frequency of sine wave + VoltageSource(signalSource=Sine(amplitude=V, freqHz=freqHz, phase=phase)) +end +=# + +# Sinusoidal voltage source +@model SineVoltage begin + V = Parameter() # Amplitude of sine wave + phase = Par(0.0) # Phase of sine wave + freqHz = Parameter() # Frequency of sine wave + VoltageSource() +equations + v = V*sin(10*time) +end + + +# Ideal diode +@model IdealDiode begin # Ideal diode + OnePort() + Ron = Par(1.0E-2) # Forward state-on differential resistance (closed diode resistance) + Goff = Par(1.0E-2) # Backward state-off conductance (opened diode conductance) + Vknee = Par(0) # Forward threshold voltage + # off = Variable(start=true) # Switching state + s = Float(start=0.0) # Auxiliary variable for actual position on the ideal diode characteristic + #= + s = 0: knee point + s < 0: below knee point, diode conducting + s > 0: above knee point, diode locking + =# +equations + # off := s < 0 +# v = s * if !positive(s); 1 else Ron end + Vknee +# i = s * if !positive(s); Goff else 1 end + Goff * Vknee + v = s * if !(s>0); 1 else Ron end + Vknee + i = s * if !(s>0); Goff else 1 end + Goff * Vknee +end + +@model Diode begin + OnePort() + Ids=Par(1.e-6) # "Saturation current"; + Vt=Par(0.04) # "Voltage equivalent of temperature (kT/qn)"; + Maxexp = Par(15) # "Max. exponent for linear continuation"; + R=Par(1.e8) # "Parallel ohmic resistance"; +equations + i = if v/Vt > Maxexp; Ids*(exp(Maxexp)*(1 + v/Vt - Maxexp) - 1) else Ids*(exp(v/Vt) - 1) end + v/R +end +=# +end diff --git a/models/RotationalLib.jl b/models/RotationalLib.jl new file mode 100644 index 0000000..137afa1 --- /dev/null +++ b/models/RotationalLib.jl @@ -0,0 +1,202 @@ +""" +Modia module with rotational component models (inspired from Modelica Standard Library). + +* Developer: Hilding Elmqvist, Mogram AB, Martin Otter, DLR +* Copyright (c) 2016-2021: Hilding Elmqvist, Martin Otter +* License: MIT (expat) + +""" +module Rotational + +#export Flange, Inertia, Spring, SpringDamper, EMF, IdealGear, Torque, CurrentSensor, Fixed, Damper, IdealGear_withSupport, SpeedSensor + +using Modia + +# Connector for 1D rotational systems +@define Flange = Model( + phi = potential, + tau = flow, + icon="Modelica.Mechanics.Rotational.Interfaces.Flange_a" +) + +# Flange fixed in housing at a given angle +#= +Fixed = Model( + flange = Flange, + phi0 = 0.0u"rad", + equations = :[ + flange.phi = 0.0u"rad"] +) +=# +@define Fixed = Model( + flange = Flange, + icon="Modelica.Mechanics.Rotational.Components.Fixed", + equations = :[ + flange.phi = 0.0] +) + +# 1D-rotational component with inertia +@define Inertia = Model( + flange_a = Flange, # (info = "Left flange of shaft") + flange_b = Flange, # (info = "Right flange of shaft") + J = 1.0u"kg*m^2", # (0, min=0, info = "Moment of inertia") #, T = u"kg*m^2") + phi = Var(init=0.0u"rad"), + w = Var(init=0.0u"rad/s"), + icon="Modelica.Mechanics.Rotational.Components.Inertia", + equations = :[ + phi = flange_a.phi + phi = flange_b.phi + w = der(phi) + a = der(w) + J * a = flange_a.tau + flange_b.tau ] +) + +# Partial model for the compliant connection of two rotational 1-dim. shaft flanges +PartialCompliant = Model( + flange_a = Flange, + flange_b = Flange, + equations = :[ + phi_rel = flange_b.phi - flange_a.phi + flange_b.tau = tau + flange_a.tau = -tau ] +) + +# Linear 1D rotational spring +@define Spring = PartialCompliant | Model( + c = 1.0u"N*m/rad", # (min = 0, info = "Spring constant") + phi_rel0 = 0.0u"rad", # (info = "Unstretched spring angle") + icon="Modelica.Mechanics.Rotational.Components.Spring", + equations = :[ + tau = c * (phi_rel - phi_rel0) ] +) + +# Linear 1D rotational spring with damper +@define SpringDamper = PartialCompliant | Model( + c = 1.0*u"N*m/rad", # (min = 0, info = "Spring constant") + d = 0.0u"N*m*s/rad", # (info = "Damping constant") + phi_rel0 = 0.0u"rad", # (info = "Unstretched spring angle") + icon="Modelica.Mechanics.Rotational.Components.SpringDamper", + equations = :[ + tau = c * (phi_rel - phi_rel0) + d * der(phi_rel) ] +) + +#= +# Electromotoric force (electric/mechanic) transformer +@define EMF = Model( + k = 1.0u"N*m/A", # (info = "Transformation coefficient") + p = Electric.Pin, + n = Electric.Pin, + flange = Flange, + + equations = :[ + v = p.v - n.v + 0 = p.i + n.i + i = p.i + + phi = flange.phi + w = der(phi) + k * w = v + flange.tau = -k * i ] +) +=# + +# Ideal gear +@define IdealGear = Model( + flange_a = Flange, # "Left flange of shaft" + flange_b = Flange, # "Right flange of shaft" + ratio = 1.0, # "Transmission ratio (flange_a.phi/flange_b.phi)" + icon="Modelica.Mechanics.Rotational.Components.IdealGear", + equations = :[ + phi_a = flange_a.phi + phi_b = flange_b.phi + phi_a = ratio * phi_b + 0 = ratio * flange_a.tau + flange_b.tau ] +) + +# Ideal gear with support +IdealGear_withSupport = Model( + flange_a = Flange, # "Left flange of shaft" + flange_b = Flange, # "Right flange of shaft" + support = Flange, # "Support flange" + ratio = 1.0, # "Transmission ratio" + equations = :[ + phi_a = flange_a.phi - support.phi + phi_b = flange_b.phi - support.phi + phi_a = ratio * phi_b + 0 = ratio * flange_a.tau + flange_b.tau + 0 = flange_a.tau + flange_b.tau + support.tau ] +) + + +# Partial input signal acting as external torque on a flange +@define PartialTorque = Model( + tau = input, + flange = Flange, + icon="Modelica.Mechanics.Rotational.Interfaces.PartialTorque", +) + +# Input signal acting as external torque on a flange +@define Torque = PartialTorque | Model( + icon="Modelica.Mechanics.Rotational.Sources.Torque", + equations = :[flange.tau = -tau]) + +UnitlessTorque = PartialTorque | Model(equations = :[flange.tau = -tau*u"N*m"]) + +#= +# Partial model for the compliant connection of two rotational 1-dim. shaft flanges where the relative angle and speed are used as preferred states +PartialCompliantWithRelativeStates = Model( + flange_a = Flange, + flange_b = Flange, + init = Map(phi_rel=0.0u"rad", w_rel=0.0u"rad/s"), + equations = :[ + phi_rel = flange_b.phi - flange_a.phi + w_rel = der(phi_rel) + a_rel = der(w_rel) + flange_b.tau = tau + flange_a.tau = -tau ] +) + +# Linear 1D rotational damper +Damper = PartialCompliantWithRelativeStates | Model( + d = 1.0u"N*m*s/rad", # (info = "Damping constant"), + equations = :[ + tau = d * w_rel ] +) +=# +# Linear 1D rotational damper +@define Damper = Model( + flange_a = Flange, + flange_b = Flange, + phi_rel = Var(start=0.0u"rad"), + d = 1.0u"N*m*s/rad", # (info = "Damping constant"), + icon="Modelica.Mechanics.Rotational.Components.Damper", + equations = :[ + phi_rel = flange_b.phi - flange_a.phi + w_rel = der(phi_rel) + flange_b.tau = tau + flange_a.tau = -tau + tau = d * w_rel ] +) + +# Partial model to measure a single absolute flange variable +@define PartialAbsoluteSensor = Model( + flange = Flange, + icon="Modelica.Mechanics.Rotational.Interfaces.PartialAbsoluteSensor", + equations = :[flange.tau = 0] +) + +# Ideal sensor to measure the absolute flange angular velocity +@define AngleSensor = PartialAbsoluteSensor | Model(phi = output, + icon="Modelica.Mechanics.Rotational.Sensors.AngleSensor", + equations = :[phi = flange.phi]) + +UnitlessAngleSensor = PartialAbsoluteSensor | Model(phi = output, equations = :[phi = flange.phi*u"1/rad"]) + +@define SpeedSensor = PartialAbsoluteSensor | Model(w = output, + icon="Modelica.Mechanics.Rotational.Sensors.SpeedSensor", + equations = :[w = der(flange.phi)]) + +UnitlessSpeedSensor = PartialAbsoluteSensor | Model(w = output, equations = :[w = der(flange.phi)*u"s/rad"]) + + +end diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/.vscode/settings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/ModelCollections.jl b/src/ModelCollections.jl index 9f256a2..719ae43 100644 --- a/src/ModelCollections.jl +++ b/src/ModelCollections.jl @@ -8,7 +8,8 @@ Handles models and variables defined as dictionaries. =# export mergeModels, recursiveMerge, redeclare, outer, showModel, @showModel, drawModel, Model, Map, Par, Var, setLogMerge, - constant, parameter, input, output, potential, flow, interval, @info_str, Boolean, Integ, @define, stringifyDefinition + constant, parameter, input, output, potential, flow, interval, @info_str, Boolean, Integ, @define, stringifyDefinition, Lookup, + resetLibrary, printLibrary, makePlacement using Base.Meta: isexpr using OrderedCollections: OrderedDict @@ -23,6 +24,7 @@ function setLogMerge(val) logMerge = val end +Lookup(model) = (:Lookup, model) function stringifyDefinition(v) if typeof(v) in [Symbol, Expr] @@ -53,6 +55,302 @@ macro define(modelDef) return esc(res) end +#Base.:<(m::AbstractDict, n::AbstractDict) = m # OrderedDict() + +# Convert hierchical modifiers and dot notation to merge operator +mergify(ex, model, kwvalue=false, level=1) = ex +function mergify(ex::Expr, model, kwvalue=true, level=1) + println("\nmergify: ") + level += 1 + @show ex model +# showModel(ex) + res = nothing +# dump(ex) + if ex.args[1] == :equations || ex.args[1] == :* || ex.head == :ncat # TODO: refine + res = ex + elseif isexpr(ex, :call) && !(ex.args[1] in [:Model, :Map, :Par, :Var, :|, :connect]) +# println("Instantiation") + mapArguments = [mergify(e, model, ex.head == :kw, level) for e in ex.args[2:end]] + if length(mapArguments) > 0 + maps = foldl( (x,y) -> :($x | Map($y)), mapArguments[2:end], init=:(Map($(mapArguments[1])))) + println("\nconverted: ", showModel(mapArguments)) + println(" to: ", showModel(maps)) + end + if kwvalue + redec = level > 4 # TODO: Better criteria + if redec + if length(mapArguments) > 0 + res = :(redeclare | $(ex.args[1]) | $maps) # Map($(mapArguments...)) ) + else + res = :(redeclare | $(ex.args[1]) ) + end + else + if length(mapArguments) > 0 +# @show mapArguments + res = :($(ex.args[1]) | $maps) # Map($(mapArguments...)) ) + else +# @show ex.args[1] +# dump(model) + modelDict = OrderedDict([kv.args[1] => kv.args[2] for kv in model.args[2:end]]...) +# @show modelDict + compType = ex.args[1] +# @show compType + if compType in keys(modelDict) +#= + newType = modelDict[compType] + @show newType + dump(newType) + compType = :($(newType.args[2])) +=# +# res = :((:Lookup, $(Meta.quot(compType)))) + res = :(Lookup($(Meta.quot(compType)))) +# println("mergified: ") +# showModel(res) + return res + end + res = :($(compType) ) + end + end + else + func = ex.args[1] +# @show ex func + if func != :(=>) + res = Expr(:kw, func, :(Map($(mapArguments...))) ) + else + dump(ex) +# :($(ex.args[2].args[1]) = 0) + res = Expr(:kw, ex.args[2].args[1], Expr(:kw, ex.args[2].args[1], 0) ) + end + end + elseif ex.head == :kw && typeof(ex.args[1]) == Expr && length(ex.args) == 2 && ex.args[1].head == :. +# println("Dot notation") + sequence = [] + unpackPath(ex.args[1], sequence) + nest = foldr( (x,y) -> :(Map($x=$y)), sequence[2:end], init=ex.args[2]) + res = Expr(:kw, sequence[1], nest) +# println("converted dot notation: ", ex) +# println(" to: ", res) +# Expr(:call, :(=>), [mergify(e, model, ex.head == :kw, level) for e in ex.args]...) + else +# println("Model, Map, Par, Var, |, connect or :kw: ", ex.args[1]) + res = Expr(ex.head, [mergify(e, model, ex.head == :kw, level) for e in ex.args]...) + end +# println("mergified: ") +# showModel(res) + res +end + +newLibrary = OrderedDict{Symbol, Any}() + +function makeVar(type, type_prefix = "") + v = OrderedDict{Symbol, Any}() + v[:type_specifier] = type + if type_prefix != "" + v[:type_prefix] = type_prefix + end + v +end + + +MakeMap(; kwargs...) = (; kwargs...) + +function makeComp(constructor) + @show constructor + dump(constructor) + class_prefixes = [] + if constructor == :potential + makeVar("Real") + elseif constructor == :flow + makeVar("Real", ["flow"]) + elseif typeof(constructor) == Float64 + v = makeVar("Real") + v[:type_prefix] = ["parameter"] + v[:binding] = constructor + v + elseif typeof(constructor) == String + v = makeVar("String") + v[:type_prefix] = ["parameter"] + v[:binding] = constructor + v + elseif typeof(constructor) == Expr && constructor.args[1] == :* && constructor.args[3].head == :macrocall + v = makeVar("Real") + v[:type_prefix] = ["parameter"] + v[:binding] = constructor.args[2] + v[:class_modification] = OrderedDict(:unit => OrderedDict(:binding => constructor.args[3].args[3])) + v + elseif hasproperty(constructor, :head) && constructor.head == :call && constructor.args[1] == :| + makeVar(constructor.args[3]) + elseif hasproperty(constructor, :head) && constructor.head == :call && constructor.args[1] == :Var + # class_modification!!! + OrderedDict(constructor.args[2].args[1] => constructor.args[2].args[2]) + elseif hasproperty(constructor, :head) && constructor.head == :call && constructor.args[2].head == :kw && constructor.args[2].args[2].value == :left + v = makeVar(constructor.args[1]) + v[:annotation] = MakeMap( + annotation = MakeMap( + Placement = MakeMap( + class_modification = MakeMap( + transformation = MakeMap( + class_modification = MakeMap( + extent = MakeMap( + binding = "[[-110,-10],[-90,10]]" + ) + ) + ) + ) + ) + ) + ) + v + elseif hasproperty(constructor, :head) && constructor.head == :call && constructor.args[2].head == :kw && constructor.args[2].args[2].value == :right + v = makeVar(constructor.args[1]) + v[:annotation] = MakeMap( + annotation = MakeMap( + Placement = MakeMap( + class_modification = MakeMap( + transformation = MakeMap( + class_modification = MakeMap( + extent = MakeMap( + binding = "[[110,-10],[90,10]]" + ) + ) + ) + ) + ) + ) + ) + v + else + makeVar(constructor) + end +end + +function makeAST(JuliaAST) + @show JuliaAST + ast = OrderedDict{Symbol, Any}() + class_prefixes = [] + elements = OrderedDict{Symbol, Any}() + if hasproperty(JuliaAST, :head) && JuliaAST.head == :call && JuliaAST.args[1] == :| + println("\n\n\nHEJSAN") + @show JuliaAST.args[2] + ast = makeAST(JuliaAST.args[3]) + ast[:extends] = OrderedDict(:type_specifier => JuliaAST.args[2]) + else + for a in JuliaAST.args[2:end] + @show a + n = a.args[1] + if n != :equations + constructor = a.args[2] + elements[n] = makeComp(constructor) + if :type_prefix in keys(elements[n]) && elements[n][:type_prefix] == ["flow"] + class_prefixes = ["connector"] + end + end + @show elements + end + if class_prefixes != [] + ast[:class_prefixes] = class_prefixes + end + ast[:elements] = elements + end + ast +end + +function storeModelAST(modelDef) + dump(modelDef) + modelName = modelDef.args[1] + JuliaAST = modelDef.args[2] + newLibrary[modelName] = makeAST(JuliaAST) +end + +function resetLibrary() + global newLibrary + newLibrary = OrderedDict{Symbol, Any}() +end + +function printLibrary() + println("newLibrary") + println(JSON.json(newLibrary, 2)) +end + +macro defineNEW(modelDef) +# storeModelAST(modelDef) + println() + modelName = modelDef.args[1] + println("Transforming: ", modelName) + model = modelDef.args[2] + println("Original model:") + print("@define ", modelName, " = ") + showModel(model) + println() +# dump(model, maxdepth=14) +# model = selquote(model) + model = mergify(model, model) + println("Modified model:") + print(modelName, " = ") + showModel(model) + println() +# dump(model, maxdepth=14) + res = :($modelName = $model) # Meta.quot($model)) + return esc(res) +end + +function substituteGenerics(model) + for (k,v) in model + if isCollection(v) + model[k] = substituteGenerics(v) + elseif typeof(v) <: Tuple && v[1] == :Lookup + # (:_lookup, name [, modifier]) + name = v[2] + # lookup name and set componenttype +# @show model[name] + componentType = deepcopy(model[name]) +# @show name k componentType +# println("Redeclare $k = ") +# showModel(componentType) + componentType[:_class] = :Model + delete!(componentType, :value) +# model[name] = OrderedDict() + model[name][:_class] = :Par + if length(v) == 3 + model[k] = componentType | v[3] + else + model[k] = componentType + end +# @show name model[k] + end + end + model +end + +# Remove generic parameters after all lookups have been performed +function removeGenerics(model) + for (k,v) in model + if isCollection(v) + if v[:_class] == :Par + model[k] = OrderedDict() # Replace by empty OrderedCollection +# delete!(model, k) + else + model[k] = removeGenerics(v) + end + end + end + model +end + +function showModel(m::Expr) + if isexpr(m, :call) && m.args[1] == :Model + println("Model(") + for kw in m.args[2:end] + println(" ", kw.args[1], " = ", kw.args[2]) + end + println(")") + elseif isexpr(m, :kw) + println(" ", m.args[1], " = ", m.args[2]) + else + println(m) + end +end + isCollection(v) = (typeof(v) <: AbstractDict) && :_class in keys(v) showModel(m, level=0) = println(m) @@ -163,6 +461,7 @@ function mergeModels(m1::AbstractDict, m2::AbstractDict, env=Symbol()) result[k] = m else # result[k] = v + result[k] = result[k] | v # Fails with @define on LinearSystem end elseif :_redeclare in keys(v) if logMerge; println("Redeclaring: $k = $v") end @@ -216,6 +515,10 @@ Par(; kwargs...) = newCollection(kwargs, :Par) Var(;kwargs...) = newCollection(kwargs, :Var) +par = Par() +var = Var() +type = Map() + function Var(values...; kwargs...) res = newCollection(kwargs, :Var) for v in values @@ -235,6 +538,7 @@ Integ(;kwargs...) = Var(kwargs) Boolean(;kwargs...) = Var(kwargs) redeclare = Model( _redeclare = true) +redefine = Par( _redeclare = true) outer = Var( _outer = true) constant = Var(constant = true) @@ -253,6 +557,7 @@ end Base.:|(m::AbstractDict, n::AbstractDict) = mergeModels(m, n) Base.:|(m::Symbol, n::AbstractDict) = mergeModels(eval(m), n) Base.:|(m, n) = if !(typeof(n) <: AbstractDict); mergeModels(m, Var(value=n)) else mergeModels(n, Var(value=m)) end +Base.:|(m::Tuple, n::AbstractDict) = (m[1], m[2], n) #mergeModels(x, ::Nothing) = x #mergeModels(x, y) = y diff --git a/src/Modia.jl b/src/Modia.jl index 9b637ba..2fcfe87 100644 --- a/src/Modia.jl +++ b/src/Modia.jl @@ -9,8 +9,8 @@ Main module of Modia. module Modia const path = dirname(dirname(@__FILE__)) # Absolute path of package directory -const Version = "0.12.0" -const Date = "2023-06-04" +const Version = "0.13.0" +const Date = "2023-08-22" const modelsPath = joinpath(Modia.path, "models") print(" \n\nWelcome to ") diff --git a/src/ModiaLang.jl b/src/ModiaLang.jl index bc82495..09edd0c 100644 --- a/src/ModiaLang.jl +++ b/src/ModiaLang.jl @@ -950,6 +950,9 @@ function instantiateModel(model; modelName="", modelModule=nothing, source=nothi resetEventCounters() global to = TimerOutput() +# substituteGenerics(model) +# removeGenerics(model) + modelStructure = ModelStructure() if isexpr(model, :quote) diff --git a/test/Slides.jl b/test/Slides.jl new file mode 100644 index 0000000..cde554a --- /dev/null +++ b/test/Slides.jl @@ -0,0 +1,56 @@ +module Slides + +using Modia + +@define FirstOrder = Model( + T = 0.5u"s", + x = Var(init=0.3), + equations = :[ + u = 2 + T * der(x) + x = u] +) + +firstOrder = @instantiateModel(FirstOrder, log=true) +simulate!(firstOrder, stopTime=3.0) + +@usingModiaPlot +plot(firstOrder, ("u", "x", "der(x)"), figure=1) + + + + +@define Pin = Model(v = potential, i = flow) + +@define OnePort = Model( + p = Pin, + n = Pin, + equations = :[ + 0 = p.i + n.i + v = p.v - n.v + i = p.i ]) + +@define Resistor = OnePort(R = missing, + equations = :[ R*i = v ] ) + +@define Capacitor = OnePort(C = missing, + v = Var(init=0.0u"V"), + equations = :[ C*der(v) = i ] ) + +@define ConstantVoltage = OnePort(V = 1.0u"V", equations = :[ v = V ] ) + +@define Circuit = Model( + R = Resistor(R=0.5u"Ω"), + C = Capacitor(C=2.0u"F"), + V = ConstantVoltage(V=10.0u"V"), + equations = :[ + connect(V.p, R.p) + connect(R.n, C.p) + connect(C.n, V.n)]) + +circuit = @instantiateModel(Circuit, unitless=true) +simulate!(circuit, stopTime=4.0) +@usingModiaPlot +plot(circuit, [("R.v", "C.v"); ("R.i", "C.i")], figure=2) + + +end \ No newline at end of file diff --git a/test/SlidesNoSimulation.jl b/test/SlidesNoSimulation.jl new file mode 100644 index 0000000..5334260 --- /dev/null +++ b/test/SlidesNoSimulation.jl @@ -0,0 +1,41 @@ +module Slides + +using Modia + +@define FirstOrder = Model( + T = 0.5u"s", + x = Var(init=0.3), + equations = :[ + u = 2 + T * der(x) + x = u] +) + +@define Pin = Model(v = potential, i = flow) + +@define OnePort = Model( + p = Pin, + n = Pin, + equations = :[ + 0 = p.i + n.i + v = p.v - n.v + i = p.i ]) + +@define Resistor = OnePort(R = missing, + equations = :[ R*i = v ] ) + +@define Capacitor = OnePort(C = missing, + v = Var(init=0.0u"V"), + equations = :[ C*der(v) = i ] ) + +@define ConstantVoltage = OnePort(V = 1.0u"V", equations = :[ v = V ] ) + +@define Circuit = Model( + R = Resistor(R=0.5u"Ω"), + C = Capacitor(C=2.0u"F"), + V = ConstantVoltage(V=10.0u"V"), + equations = :[ + connect(V.p, R.p) + connect(R.n, C.p) + connect(C.n, V.n)]) + +end \ No newline at end of file diff --git a/test/TestFundamentals.jl b/test/TestFundamentals.jl new file mode 100644 index 0000000..73f1df0 --- /dev/null +++ b/test/TestFundamentals.jl @@ -0,0 +1,209 @@ +module TestFundamentals + +using Modia +@usingModiaPlot + +setLogMerge(false) + +include("../models/Electric.jl") + +#= +@define Mod1 = Model( + R1 = Resistor, + R2 = Resistor | Map(R=10), + R3 = Resistor(), + R4 = Resistor(R=20), + R5 = Resistor(R=10), # <: OnePort(v=0), # replaceable Resistor R5(R=10) constrainedby OnePort(v=0) + CompType = Par(Resistor), + R6 = CompType(), + R7 = CompType(), # (v.init=1), + equations = :[connect(R1.p, R2.n), connect(R2.p, R3.n), connect(R3.p, R4.n), + connect(R4.p, R5.n), connect(R5.p, R6.n), connect(R6.p, R7.n), connect(R7.p, R1.n)] +) + +model = @instantiateModel(Mod1, logModel=true, aliasReduction=true, log=true, unitless=true, logCode=true) +simulate!(model, stopTime=1) +plot(model, ["R1.v", "R5.v"]) + +n = 10 + +@define Mod2 = Model( +# p = Par(5), +# q = Par(sin(p)), +# u = Var(), +# v = Var(cos(u)+n), +# R = Resistor(R=p), # R = Resistor | Map(R=10) +## m1 = Mod1(R1(R=100)), # m1 = Mod1(R1=Map(R=100)) +# m2 = Mod1(R1 = Capacitor), # m2 = Mod1(R1=redeclare | Capacitor) +## m2 = Mod1(R1 = Capacitor(C=5)), # m2 = Mod1(R1=redeclare | Capacitor | Map(C=5)) +# MyResistor = Resistor | Model(equations=:[P=v*i]), # extends Resistor + m3 = Mod1A(CompType = Capacitor), # Mod1 m3(redeclare model Comp=Resistor) + + m4 = [Resistor(R=i) for i in 1:5], + m5 = if q > 0.5; Capacitor end + +) + +@define ModA = Model( + CompType = Par(Resistor), + comp = CompType() +) + +@define ModB = Model( + m = ModA(CompType = Capacitor), # ModA m(redeclare model CompType=Capacitor) +) + +@instantiateModel(ModB, logModel=true, log=true) + +@define Circuit = Model( + DeviceType = Par(Resistor), + device = DeviceType(), + current = ConstantCurrent(I=3), + equations = :[ + connect(current.p, device.p) + connect(current.n, device.n) +# current.n.v = 0 + ] +) + +@define CapacitorCircuit = Model( + c = Circuit(DeviceType = Capacitor(C=2)) # ModA m(redeclare model CompType=Capacitor) +) + +model = @instantiateModel(CapacitorCircuit, logModel=true, aliasReduction=true, log=true, unitless=true, logCode=true) +simulate!(model, stopTime=1) +plot(model, ["c.device.v"]) + +=# + + +@define RCCircuit = Model( + r = Resistor(R=1), + c = Capacitor(C=1), + source = ConstantVoltage(V=1), + equations = :[ + connect(source.p, r.p) + connect(r.n, c.p) + connect(source.n, c.n) + ] +) + +@define RCCircuits = Model( # From slides + rc1 = RCCircuit(r(R=4), c(C=10)), + rc2 = RCCircuit(c(C=10, v(init=5)), source(V=-10)) +) + +@define RCCircuits2 = Model( + rc1 = RCCircuit, + rc2 = RCCircuit(), + rc3 = RCCircuit | Map(r = Map(R = 2)) | Map(c = Map(C = 0.5)), + rc4 = RCCircuit(r(R=2), c(C=0.5)), # Hierarchical modifiers (preferred) +# rc5 = RCCircuit(r(R=2)) | Map(c = Map(C = 0.5)), # Mixed hierarchical and merge + rc6 = RCCircuit(r.R=2, c.C=0.5), # Dot notation + rc7 = RCCircuit(r.R=2, c(C=0.5)), # Mixed + + rc8 = RCCircuit(c(v(init=1)), source.V=-1), # Modifier for variable attribute + rc9 = RCCircuit(c.v.init=1, source.V=-1), + rc10 = RCCircuit(c(v.init=1), source.V=-1), # Mixed +) + +model = @instantiateModel(RCCircuits2, logModel=true, aliasReduction=true, log=true, unitless=true, logCode=true) +simulate!(model, stopTime=10) +plot(model, ["rc1.c.v", "rc2.c.v", "rc3.c.v", "rc4.c.v", "rc5.c.v", "rc6.c.v", "rc7.c.v", "rc8.c.v", "rc9.c.v", "rc10.c.v"]) + + +@define RCCircuits3 = Model( + rc3 = RCCircuit(r(R=2), c(C=0.5)), # Hierarchical modifiers + rc3a = RCCircuit(r(R=2, ERR=5), r.R=10), # Multiple modifications, +) + +model = @instantiateModel(RCCircuits3, logModel=true, aliasReduction=true, log=true, unitless=true, logCode=true) +simulate!(model, stopTime=10) +plot(model, ["rc3.c.v", "rc3a.c.v"], figure=2) + + + +@define Circuit = Model( + DeviceType = Par(Resistor), + device1 = DeviceType(), + device2 = DeviceType(), + source = ConstantVoltage(V=10), + equations = :[ + connect(source.p, device1.p) + connect(device1.n, device2.p) + connect(source.n, device2.n) + ] +) + +#= +@define AlteredCircuit = Circuit(DeviceType = Resistor(R=2), device1(R=40)) +@define AlteredCircuit2 = Circuit(device1(R=40)) + +@define CapacitorCircuit = Circuit(DeviceType = Capacitor, source=ConstantCurrent) + +@define MixedCircuits1 = Model( + r1 = Circuit(), + r2 = Circuit(DeviceType(R=2), device1(R=8)), + r3 = Circuit(DeviceType = Resistor(R=2), device1(R=8)), + + c1 = Circuit(DeviceType = Capacitor(), source=ConstantCurrent(I=10)), + c2 = Circuit(DeviceType = Capacitor(C=2), source=ConstantCurrent(I=10)), + c3 = Circuit(DeviceType = Capacitor(C=2), device2(C=8), source=ConstantCurrent(I=10)), + + rc = Circuit(DeviceType = Capacitor(), device1=Resistor(R=2), device2(C=5), source(V=10)) + + #= + c1 = CapacitorCircuit(source(I=10)), + c2 = CapacitorCircuit(DeviceType = Capacitor(C=2), source(I=10)), + c3 = CapacitorCircuit(DeviceType = Capacitor(C=3), device2(C=30), source(I=10)) +=# + ) +=# + +@define MixedCircuits = Model( + c = Circuit(DeviceType = Capacitor, device2(C=8), source=ConstantCurrent(I=10)), + rc = Circuit(DeviceType = Capacitor, device1=Resistor(R=2), device2(C=5), source(V=10)), + rc1 = Circuit(DeviceType = Capacitor(C=5), device1=Resistor(R=2), source(V=10)), + rc2 = Circuit(device1=Capacitor(C=5), device2=Resistor(R=2), source(V=10)) + +) + +#= +@define GenericCircuits = Model( + DevType = Capacitor, + mc = MixedCircuits(rc(DeviceType=DevType)) +) +=# + +#= +@define CCCircuit = Model( + c1 = Capacitor(C=2), + c2 = Capacitor(C=4), + source = ConstantCurrent(I=10), + equations = :[ + connect(source.p, c1.p) + connect(c1.n, c2.p) + connect(source.n, c2.n) + ] +) +=# + +#= +model = @instantiateModel(MixedCircuits, logModel=true, aliasReduction=true, log=true, unitless=true, logCode=true) +simulate!(model, stopTime=100) +plot(model, ["c.device1.v", "c.device2.v"]) +#plot(model, ["r3.device1.v", "r3.device2.v", "c3.device1.v", "c3.device2.v", "rc.device1.v", "rc.device2.v"]) +#plot(model, ["rc.device1.v", "rc.device2.v"]) +# plot(model, ["c1.v", "c2.v"]) +=# + +#= +@define Dummy = Model( + DeviceType = Par(Resistor), +) + +model = @instantiateModel(Dummy, logModel=true, aliasReduction=true, log=true, unitless=true, logCode=true) +simulate!(model, stopTime=100) +=# + +end \ No newline at end of file diff --git a/test/TestLinearSystems.jl b/test/TestLinearSystems.jl index 0eb9c3c..3ea648e 100644 --- a/test/TestLinearSystems.jl +++ b/test/TestLinearSystems.jl @@ -194,14 +194,15 @@ end # T*der(x) + x = u T = 0.2; +# @define SSTest = Model( ss = LinearStateSpace(A=[-1.0/T;;], B=[1.0/T;;], C=[0.9;;], W=[1.1;;], x_init=[0.2]), # one state equations = :[ss.u = [2.0], y = ss.y[1]] ) -ssTest = @instantiateModel(SSTest, logCode=true) -simulate!(ssTest, stopTime=1.0, log=false, logStates=true, requiredFinalStates = [1.987867388853733]) +ssTest = @instantiateModel(SSTest, log=true, logCode=true) +simulate!(ssTest, stopTime=1.0, logParameters=true, log=true, logStates=true, requiredFinalStates = [1.987867388853733]) showInfo(ssTest) plot(ssTest, ("ss.x", "ss.u","ss.w", "y"), figure=1) diff --git a/test/TestModifierMode.jl b/test/TestModifierMode.jl new file mode 100644 index 0000000..277686c --- /dev/null +++ b/test/TestModifierMode.jl @@ -0,0 +1,92 @@ +module TestModifierMode + +using Modia +@usingModiaPlot + +include("../models/Electric.jl") + +@define RCCircuit = Model( + r = Resistor(R=1), + c = Capacitor(C=1), + source = ConstantVoltage(V=1), + equations = :[ + connect(source.p, r.p) + connect(r.n, c.p) + connect(source.n, c.n) + ] +) + +@define RCCircuits = Model( # From slides + rc1 = RCCircuit(r(R=4), c(C=10)), + rc2 = RCCircuit(c(C=10, v(init=5)), source(V=-10)) +) + +model = @instantiateModel(RCCircuits, unitless=true) +simulate!(model, stopTime=10, requiredFinalStates=[0.22119922721253596, -4.48181591672524]) +plot(model, ["rc1.c.v", "rc2.c.v"], figure=1) + + +@define RCCircuits2 = Model( + rc1 = RCCircuit, + rc2 = RCCircuit(), + rc3 = RCCircuit | Map(r = Map(R = 2)) | Map(c = Map(C = 0.5)), + rc4 = RCCircuit(r(R=2), c(C=0.5)), # Hierarchical modifiers (preferred) +# rc5 = RCCircuit(r(R=2)) | Map(c = Map(C = 0.5)), # Mixed hierarchical and merge + rc6 = RCCircuit(r.R=2, c.C=0.5), # Dot notation + rc7 = RCCircuit(r.R=2, c(C=0.5)), # Mixed + + rc8 = RCCircuit(c(v(init=1)), source.V=-1), # Modifier for variable attribute + rc9 = RCCircuit(c.v.init=1, source.V=-1), + rc10 = RCCircuit(c(v.init=1), source.V=-1), # Mixed +) + +model = @instantiateModel(RCCircuits2, unitless=true) +simulate!(model, stopTime=10, requiredFinalStates=[0.9999543756364957, 0.9999543756364957, 0.9999543756364957, 0.9999543756364957, 0.9999543756364957, 0.9999543756364957, -0.9999087910440049, -0.9999087910440049, -0.9999087910440049]) +plot(model, ["rc1.c.v", "rc2.c.v", "rc3.c.v", "rc4.c.v", "rc6.c.v", "rc7.c.v", "rc8.c.v", "rc9.c.v", "rc10.c.v"], figure=2) + + +@define RCCircuits3 = Model( + rc3 = RCCircuit(r(R=2), c(C=0.5)), # Hierarchical modifiers + rc3a = RCCircuit(r(R=2, ERR=5), r.R=10), # Multiple modifications, +) + +model = @instantiateModel(RCCircuits3, unitless=true) +simulate!(model, stopTime=10, requiredFinalStates=[0.9999565762239844, 0.632120613764388]) +plot(model, ["rc3.c.v", "rc3a.c.v"], figure=3) + + +@define Circuit = Model( + DeviceType = Resistor, # Generic model parameter + device1 = DeviceType(), + device2 = DeviceType(), + source = ConstantVoltage(V=10), + equations = :[ + connect(source.p, device1.p) + connect(device1.n, device2.p) + connect(source.n, device2.n) + ] +) + +@define MixedCircuits = Model( + c1 = Circuit(), + c2 = Circuit(device1(R=1), device2(R=4)), + c3 = Circuit(DeviceType=Resistor(R=2), device2(R=8)), + +# cc1 = Circuit(device1=Capacitor(C=5), device2=Capacitor(C=20), source=ConstantCurrent(I=10)), + cc2 = Circuit(DeviceType = Capacitor(C=0.5), device2(C=2), source=ConstantCurrent(I=10)), + +# rc1 = Circuit(device1=Capacitor(C=5), device2=Resistor(R=2), source(V=10)), + rc2 = Circuit(DeviceType = Capacitor(C=5), device1=Resistor(R=2), source(V=10)), + rc3 = Circuit(DeviceType = Capacitor(), device1=Resistor(R=2), device2(C=5), source(V=10)), +) + +model = @instantiateModel(MixedCircuits, unitless=true) +simulate!(model, stopTime=100) #, requiredFinalStates=[-999.9999999999999, -124.99999999999999]) +plot(model, ["c1.device1.v", "c1.device2.v"], figure=4) +plot(model, ["c2.device1.v", "c2.device2.v"], figure=5) +plot(model, ["c3.device1.v", "c3.device2.v"], figure=6) +plot(model, ["cc2.device1.v", "cc2.device2.v"], figure=7) +plot(model, ["rc2.device1.v", "rc2.device2.v"], figure=8) +plot(model, ["rc3.device1.v", "rc3.device2.v"], figure=9) + +end \ No newline at end of file