Skip to content

Commit 72d7f23

Browse files
committed
Refactoring, adding empty docu
1 parent d76400d commit 72d7f23

23 files changed

+1076
-587
lines changed

Manifest.toml

+237-180
Large diffs are not rendered by default.

Project.toml

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ authors = ["TT <tobias.thummerer@informatik.uni-augsburg.de>", "LM <lars.mikelso
44
version = "0.1.1"
55

66
[deps]
7+
DiffEqCallbacks = "459566f4-90b8-5000-8ac3-15dfb0a30def"
78
DiffEqFlux = "aae7a2af-3d4f-5e19-a356-7da93b79d9d0"
89
FMI = "14a09403-18e3-468f-ad8a-74f8dda2d9ac"
910
Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c"

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# FMIFlux.jl
33

44
## What is FMIFlux.jl?
5-
FMIFlux.jl is a free-to-use software library for the Julia programming language, which offers the ability to setup NeuralFMUs. You can place FMUs simply inside any feed-forward NN topology and still keep the resulting hybrid model trainable with a standard AD training process.
5+
FMIFlux.jl is a free-to-use software library for the Julia programming language, which offers the ability to setup NeuralFMUs: You can place FMUs ([fmi-standard.org](http://fmi-standard.org/)) simply inside any feed-forward NN topology and still keep the resulting hybrid model trainable with a standard AD training process.
66

77
<!--- [![](https://img.shields.io/badge/docs-stable-blue.svg)](https://ThummeTo.github.io/FMIFlux.jl/stable) --->
88
[![](https://img.shields.io/badge/docs-dev-blue.svg)](https://ThummeTo.github.io/FMIFlux.jl/dev)
@@ -11,6 +11,7 @@ FMIFlux.jl is a free-to-use software library for the Julia programming language,
1111
1. open a Julia-Command-Window, activate your prefered environemnt
1212
1. goto package manager using ```]```
1313
1. type ```add FMIFlux``` or ```add "https://github.com/ThummeTo/FMIFlux.jl"```
14+
1. have a look in the ```example``` folder
1415

1516
## What is currently supported in FMIFlux.jl?
1617
- building and training ME-NeuralFMUs with the default Flux-Front-End

docs/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
build/

docs/Manifest.toml

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# This file is machine-generated - editing it directly is not advised
2+
3+
[[Base64]]
4+
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
5+
6+
[[Dates]]
7+
deps = ["Printf"]
8+
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"
9+
10+
[[Distributed]]
11+
deps = ["Random", "Serialization", "Sockets"]
12+
uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b"
13+
14+
[[DocStringExtensions]]
15+
deps = ["LibGit2", "Markdown", "Pkg", "Test"]
16+
git-tree-sha1 = "9d4f64f79012636741cf01133158a54b24924c32"
17+
uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
18+
version = "0.8.4"
19+
20+
[[Documenter]]
21+
deps = ["Base64", "Dates", "DocStringExtensions", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "REPL", "Test", "Unicode"]
22+
git-tree-sha1 = "3ebb967819b284dc1e3c0422229b58a40a255649"
23+
uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
24+
version = "0.26.3"
25+
26+
[[IOCapture]]
27+
deps = ["Logging"]
28+
git-tree-sha1 = "377252859f740c217b936cebcd918a44f9b53b59"
29+
uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89"
30+
version = "0.1.1"
31+
32+
[[InteractiveUtils]]
33+
deps = ["Markdown"]
34+
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
35+
36+
[[JSON]]
37+
deps = ["Dates", "Mmap", "Parsers", "Unicode"]
38+
git-tree-sha1 = "81690084b6198a2e1da36fcfda16eeca9f9f24e4"
39+
uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
40+
version = "0.21.1"
41+
42+
[[LibGit2]]
43+
deps = ["Printf"]
44+
uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"
45+
46+
[[Libdl]]
47+
uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
48+
49+
[[Logging]]
50+
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"
51+
52+
[[Markdown]]
53+
deps = ["Base64"]
54+
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
55+
56+
[[Mmap]]
57+
uuid = "a63ad114-7e13-5084-954f-fe012c677804"
58+
59+
[[Parsers]]
60+
deps = ["Dates"]
61+
git-tree-sha1 = "c8abc88faa3f7a3950832ac5d6e690881590d6dc"
62+
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
63+
version = "1.1.0"
64+
65+
[[Pkg]]
66+
deps = ["Dates", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"]
67+
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
68+
69+
[[Printf]]
70+
deps = ["Unicode"]
71+
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"
72+
73+
[[REPL]]
74+
deps = ["InteractiveUtils", "Markdown", "Sockets"]
75+
uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
76+
77+
[[Random]]
78+
deps = ["Serialization"]
79+
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
80+
81+
[[SHA]]
82+
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"
83+
84+
[[Serialization]]
85+
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
86+
87+
[[Sockets]]
88+
uuid = "6462fe0b-24de-5631-8697-dd941f90decc"
89+
90+
[[Test]]
91+
deps = ["Distributed", "InteractiveUtils", "Logging", "Random"]
92+
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
93+
94+
[[UUIDs]]
95+
deps = ["Random", "SHA"]
96+
uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
97+
98+
[[Unicode]]
99+
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"

docs/Project.toml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[deps]
2+
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"

docs/make.jl

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#
2+
# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons
3+
# Licensed under the MIT license. See LICENSE file in the project root for details.
4+
#
5+
6+
using Documenter, FMIFlux
7+
8+
makedocs(sitename="FMIFlux.jl",
9+
pages=Any["Home" => "index.md"])
10+
11+
deploydocs(repo = "github.com/ThummeTo/FMIFlux.jl.git", devbranch = "main")

docs/src/index.md

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
# FMIFlux.jl Documentation
3+
4+
```@contents
5+
Pages = ["index.md"]
6+
```
7+
8+
The documentation for FMIFlux is currently under construction and will be available soon.
9+
10+
# FMIFlux.jl Index
11+
12+
```@index
13+
```

example/advanced_hybrid_ME.jl

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#
2+
# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons
3+
# Licensed under the MIT license. See LICENSE file in the project root for details.
4+
#
5+
6+
################################## INSTALLATION ####################################################
7+
# (1) Enter Package Manager via ]
8+
# (2) Install FMI via add FMI or add "https://github.com/ThummeTo/FMI.jl"
9+
# (3) Install FMIFlux via add FMIFlux or add "https://github.com/ThummeTo/FMIFlux.jl"
10+
################################ END INSTALLATION ##################################################
11+
12+
# this example covers creation and training of a ME-NeuralFMU
13+
# here, solver step size is adaptive controlled for better training performance
14+
15+
using FMI
16+
using FMIFlux
17+
using Flux
18+
using DifferentialEquations: Tsit5
19+
import Plots
20+
using Zygote
21+
22+
modelFMUPath = joinpath(dirname(@__FILE__), "../model/SpringPendulum1D.fmu")
23+
realFMUPath = joinpath(dirname(@__FILE__), "../model/SpringFrictionPendulum1D.fmu")
24+
25+
t_start = 0.0
26+
t_step = 0.1
27+
t_stop = 5.0
28+
tData = collect(t_start:t_step:t_stop)
29+
30+
myFMU = fmiLoad(realFMUPath)
31+
fmiInstantiate!(myFMU; loggingOn=false)
32+
fmiSetupExperiment(myFMU, t_start, t_stop)
33+
34+
fmiEnterInitializationMode(myFMU)
35+
fmiExitInitializationMode(myFMU)
36+
37+
x0 = fmi2GetContinuousStates(myFMU)
38+
39+
realSimData = fmiSimulate(myFMU, t_start, t_stop; saveat=tData, recordValues=["mass.s", "mass.v", "mass.f", "mass.a"], setup=false)
40+
fmiUnload(myFMU)
41+
42+
fmiPlot(realSimData)
43+
44+
myFMU = fmiLoad(modelFMUPath)
45+
46+
fmiInstantiate!(myFMU; loggingOn=false)
47+
fmuSimData = fmiSimulate(myFMU, t_start, t_stop; saveat=tData, recordValues=["mass.s", "mass.v", "mass.a"])
48+
49+
posData = fmi2SimulationResultGetValues(realSimData, "mass.s")
50+
velData = fmi2SimulationResultGetValues(realSimData, "mass.v")
51+
52+
# loss function for training
53+
global integratorSteps
54+
function losssum()
55+
global integratorSteps, problem
56+
57+
solution = problem(x0)
58+
59+
tNet = collect(data[1] for data in solution.u)
60+
posNet = collect(data[2] for data in solution.u)
61+
#velNet = collect(data[3] for data in solution.u)
62+
63+
integratorSteps = length(tNet)
64+
65+
#mse_interpolate(tData, posData, tNet, posNet, tData) # mse_interpolate(tData, velData, tNet, velNet, tData)
66+
Flux.mse(posData, posNet)
67+
end
68+
69+
# callback function for training
70+
global iterCB = 0
71+
function callb()
72+
global iterCB += 1
73+
global integratorSteps
74+
75+
if iterCB % 10 == 1
76+
avg_ls = losssum()
77+
@info "Loss: $(round(avg_ls, digits=5)) Avg displacement in data: $(round(sqrt(avg_ls), digits=5)) Integ.Steps: $integratorSteps"
78+
end
79+
80+
if iterCB % 100 == 1
81+
fig = plotResults()
82+
println("Fig. update.")
83+
display(fig)
84+
end
85+
end
86+
87+
function plotResults()
88+
solutionAfter = problem(x0, t_start)
89+
fig = Plots.plot(xlabel="t [s]", ylabel="mass position [m]", linewidth=2,
90+
xtickfontsize=12, ytickfontsize=12,
91+
xguidefontsize=12, yguidefontsize=12,
92+
legendfontsize=12, legend=:bottomright)
93+
Plots.plot!(fig, tData, fmi2SimulationResultGetValues(fmuSimData, "mass.s"), label="FMU", linewidth=2)
94+
Plots.plot!(fig, tData, posData, label="reference", linewidth=2)
95+
Plots.plot!(fig, collect(data[1] for data in solutionAfter.u), collect(data[2] for data in solutionAfter.u), label="NeuralFMU", linewidth=2)
96+
fig
97+
end
98+
99+
# NeuralFMU setup
100+
numStates = fmiGetNumberOfStates(myFMU)
101+
additionalVRs = [fmi2String2ValueReference(myFMU, "mass.m")]
102+
numAdditionalVRs = length(additionalVRs)
103+
104+
net = Chain(inputs -> fmiDoStepME(myFMU, inputs, -1.0, [], [], additionalVRs),
105+
Dense(numStates+numAdditionalVRs, 16, tanh),
106+
Dense(16, 16, tanh),
107+
Dense(16, numStates))
108+
109+
problem = ME_NeuralFMU(myFMU, net, (t_start, t_stop), Tsit5(); saveat=tData)
110+
solutionBefore = problem(x0, t_start)
111+
fmiPlot(problem)
112+
113+
# train it ...
114+
p_net = Flux.params(problem)
115+
116+
optim = ADAM()
117+
# Feel free to increase training steps or epochs for better results
118+
Flux.train!(losssum, p_net, Iterators.repeated((), 1000), optim; cb=callb)
119+
120+
fmiUnload(myFMU)

example/modelica_conference_2021.jl

+10-39
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ realFMUPath = joinpath(dirname(@__FILE__), "../model/SpringFrictionPendulum1D.fm
2121
t_start = 0.0
2222
t_step = 0.01
2323
t_stop = 5.0
24+
tData = t_start:t_step:t_stop
2425

2526
myFMU = fmiLoad(realFMUPath)
2627
fmiInstantiate!(myFMU; loggingOn=false)
@@ -31,7 +32,7 @@ fmiExitInitializationMode(myFMU)
3132

3233
x0 = fmi2GetContinuousStates(myFMU)
3334

34-
realSimData = fmi2SimulateCS(myFMU, t_step, t_start, t_stop, ["mass.s", "mass.v", "mass.f", "mass.a"], false)
35+
realSimData = fmi2Simulate(myFMU, t_start, t_stop; recordValues=["mass.s", "mass.v", "mass.f", "mass.a"], saveat=tData, setup=false)
3536
fmiUnload(myFMU)
3637

3738
fmiPlot(realSimData)
@@ -40,7 +41,7 @@ displacement = 0.1
4041
myFMU = fmiLoad(modelFMUPath)
4142

4243
fmiInstantiate!(myFMU; loggingOn=false)
43-
fmuSimData = fmiSimulate(myFMU, t_step, t_start, t_stop, ["mass.s", "mass.v", "mass.a"])
44+
fmuSimData = fmiSimulate(myFMU, t_start, t_stop; recordValues=["mass.s", "mass.v", "mass.a"], saveat=tData)
4445
fmiReset(myFMU)
4546
fmiSetupExperiment(myFMU, 0.0)
4647

@@ -50,13 +51,12 @@ fmi2SetReal(myFMU, "mass_s0", 0.5 + displacement) # das sollte Wurst sein oder?
5051
fmiEnterInitializationMode(myFMU)
5152
fmiExitInitializationMode(myFMU)
5253
#x0 = fmi2GetContinuousStates(myFMU)
53-
tData = t_start:t_step:t_stop
5454
posData = fmi2SimulationResultGetValues(realSimData, "mass.s")
5555
velData = fmi2SimulationResultGetValues(realSimData, "mass.v")
5656

5757
# loss function for training
5858
function losssum()
59-
solution = problem(t_start, x0)
59+
solution = problem(x0)
6060

6161
posNet = collect(data[2] for data in solution.u)
6262
velNet = collect(data[3] for data in solution.u)
@@ -77,7 +77,7 @@ function callb()
7777

7878
if iterCB % 10 == 1
7979
avg_ls = losssum()
80-
display("Loss: $(round(avg_ls, digits=5)) Avg displacement in data: $(round(sqrt(avg_ls / 2.0), digits=5)) Weight/Scale: $(p_net[1][1]) Bias/Offset: $(p_net[1][5])")
80+
@info "Loss: $(round(avg_ls, digits=5)) Avg displacement in data: $(round(sqrt(avg_ls / 2.0), digits=5)) Weight/Scale: $(p_net[1][1]) Bias/Offset: $(p_net[1][5])"
8181
end
8282

8383
end
@@ -91,20 +91,20 @@ net = Chain(Dense(numStates, numStates, identity; initW = (out, in) -> [[1.0, 0.
9191
Dense(16, 16, tanh),
9292
Dense(16, numStates))
9393

94-
problem = ME_NeuralFMU(myFMU, net, (t_start, t_stop), Tsit5(), tData)
95-
solutionBefore = problem(t_start, x0)
94+
problem = ME_NeuralFMU(myFMU, net, (t_start, t_stop), Tsit5(); saveat=tData)
95+
solutionBefore = problem(x0)
9696
fmiPlot(problem)
9797

9898
# train it ...
9999
p_net = Flux.params(problem)
100100
optim = ADAM()
101101
for i in 1:3
102-
display("epoch: $i/3")
102+
@info "epoch: $i/3"
103103
Flux.train!(losssum, p_net, Iterators.repeated((), 1000), optim; cb=callb)
104104
end
105105

106106
###### plot results s
107-
solutionAfter = problem(t_start, x0)
107+
solutionAfter = problem(x0)
108108
fig = Plots.plot(xlabel="t [s]", ylabel="mass position [m]", linewidth=2,
109109
xtickfontsize=12, ytickfontsize=12,
110110
xguidefontsize=12, yguidefontsize=12,
@@ -115,7 +115,7 @@ Plots.plot!(fig, tData, collect(data[2] for data in solutionAfter.u), label="Neu
115115
Plots.savefig(fig, "exampleResult_s.pdf")
116116

117117
###### plot results v
118-
solutionAfter = problem(t_start, x0)
118+
solutionAfter = problem(x0)
119119
fig = Plots.plot(xlabel="t [s]", ylabel="mass velocity [m/s]", linewidth=2,
120120
xtickfontsize=12, ytickfontsize=12,
121121
xguidefontsize=12, yguidefontsize=12,
@@ -125,35 +125,6 @@ Plots.plot!(fig, tData, velData, label="reference", linewidth=2)
125125
Plots.plot!(fig, tData, collect(data[3] for data in solutionAfter.u), label="NeuralFMU", linewidth=2)
126126
Plots.savefig(fig, "exampleResult_v.pdf")
127127

128-
# write training parameters *p_net* back to *net* with data offset *c*
129-
function transferParams!(net, p_net, c=0)
130-
numLayers = length(net.layers)
131-
for l in 1:numLayers
132-
ni = size(net.layers[l].weight,2)
133-
no = size(net.layers[l].weight,1)
134-
135-
w = zeros(no, ni)
136-
b = zeros(no)
137-
138-
for i in 1:ni
139-
for o in 1:no
140-
w[o,i] = p_net[1][c + (i-1)*no + (o-1)]
141-
end
142-
end
143-
144-
c += ni*no
145-
146-
for o in 1:no
147-
b[o] = p_net[1][c + (o-1)]
148-
end
149-
150-
c += no
151-
152-
copy!(net.layers[l].weight, w)
153-
copy!(net.layers[l].bias, b)
154-
end
155-
end
156-
157128
###### friction model extraction
158129
layers = problem.neuralODE.model.layers[4:6]
159130
net_bottom = Chain(layers...)

0 commit comments

Comments
 (0)