Migration from trano's FastModel, phase 0: trials¶
Phase 0 of the migration of trano's FastModel package (trano/library/FastModel.mo) to the FastSmoothBuildings
library tried the split into the directory layout and its use from OpenModelica and rumoca before anything was
pushed. Source: trano commit c70f8d2 (branch feat/smooth-controller-parameters, PR #60), OpenModelica 1.26.9,
rumoca 0.10.3.
1. The split¶
scripts/migration/split_library.py renames the package (FastModel → FastSmoothBuildings; the library refers to itself
by its full name once, extends FastSmoothBuildings.Icons.Package, and two description strings name trano's
configurations, now FastSmoothBuildings and FastSmoothBuildingsMPC) and writes the directory layout of the
Modelica specification (13.4):
- every package that holds a model, block, function, connector or record (directly or below) is a folder with
package.moandpackage.order: 15 folders; - every other class of such a package is a file
<name>.mo: 92 files;IconsandTypes(constants only) stay one file each, as in Buildings; - constants and short classes (
connector RealInput = input Real) stay inpackage.mo;package.orderlists classes and constants in the order of the source.
fastsmoothbuildings.bundle.bundle() writes the folder back as one file (to embed it in a generated model). The
split checks that the bundle gives back the renamed source character for character.
2. OpenModelica¶
| Check | Single file | Folder (MODELICAPATH) |
|---|---|---|
messages while loading (missing or extra package.order entries) |
none | none |
classes (getClassNames(recursive=true)) |
112 | the same 112 |
checkModel of the 38 models and blocks that are not partial |
31 pass | the same output, word for word |
The 7 models that do not check are the walls, the window and the zones: their construction records have no
defaults on purpose (nLay, nPan, nSurf must be literal for rumoca), so they check only inside a model that
gives them a record; phase 1 adds such examples.
The runnable models of four trano inputs, generated with the library embedded and with the library loaded from the folder, simulated for two days (tolerance 1e-6, 576 intervals) give the same values for every variable, bit for bit:
| Input | Variables |
|---|---|
| tests/models/single_zone_hydronic.yaml | 1634 |
| BESTEST 960 (sun-space) | 2142 |
| examples/residential_before_renovation.yaml | 8116 |
| examples/residential_after_renovation.yaml | 9249 |
3. rumoca and CasADi¶
For six inputs, each as MPC model and as closed-loop model (12 exports), rumoca.Session().loads of the model with
the library embedded (as trano writes it) and rumoca.Session(roots=[...]).loads of the model with the library
referenced from the folder give the same CasADi model: the same names of states, inputs and parameters, and no
difference in the start states, the parameters and the derivatives at four times of the day.
| Input | MPC: states / inputs / parameters | Closed loop: states / inputs / parameters | Translation, embedded / roots (s) |
|---|---|---|---|
| BESTEST 600FF | 25 / 9 / 550 | 54 / 9 / 782 | 2.9–3.2 / 2.7 |
| BESTEST 630 (overhang, fins) | 27 / 10 / 667 | 60 / 9 / 923 | 4.0–4.3 / 4.0 |
| BESTEST 960 | 42 / 12 / 835 | 96 / 11 / 1259 | 4.1–4.8 / 4.0–4.3 |
| single_zone_hydronic | 27 / 12 / 758 | 52 / 9 / 903 | 6.3–7.0 / 6.8–6.9 |
| residential_after_renovation | 189 / 28 / 4165 | 278 / 19 / 4770 | 43–50 / 45 |
| residential_before_renovation | 160 / 27 / 3544 | 224 / 19 / 3955 | 38 / 40–41 |
Loading from roots costs no time.
Found: the records name the embedded copy¶
trano's three data templates (library.json, construction twice and glazing) write
record x = <package>.FastModel.Data.Construction(...): the qualified name of the copy inside the generated
package, the one thing that stops a reference to the library. Written as FastSmoothBuildings.Data.Construction,
the name finds the embedded copy first (Modelica looks in the enclosing packages) and the library otherwise: with
the library embedded, the 6 exports tried and the OpenModelica simulation of single_zone_hydronic are identical to
the qualified names. Phase 2 writes the plain name, so that the switch to a reference (phase 3) needs no change of
these templates.
4. Documentation¶
Utilities.regStep and Envelope.Functions.gapConductance carry a sample of the documentation of phase 4
(purpose, equations as implemented, smoothing, relation to Buildings, validation, references, revisions) in HTML
with tables, sub- and superscripts, entities, escaped quotes and modelica:// links; phase 1 kept it in both
classes. The documentation of gapConductance as a browser shows it:
phase0/gap_conductance_documentation.png.
- OpenModelica loads it without a message and
getDocumentationAnnotationreturns it whole (3687 characters forgapConductance, the quotes unescaped, the links kept); - rumoca translates the models with it, from the folder and embedded, to the same CasADi model.
A copy with a 3 kB documentation on each of the 107 files (a bundle of 494 896 characters, three times the 163 390 of the library today) measures the cost: OpenModelica loads it, as folder and as one file, and checks every class as without it; rumoca translates it to the same CasADi model, from the folder and embedded, in the same time (best of three):
| MPC model | No documentation (roots) | Sample (roots / embedded) | 3 kB on every file (roots / embedded / embedded, stripped) |
|---|---|---|---|
| single_zone_hydronic | 6.3 s | 6.5 / 6.4 s | 6.4 / 6.2 / 6.6 s |
| residential_after_renovation | 42.2 s | 43.3 / 42.5 s | 44.4 / 43.1 / 42.6 s |
strip_documentation (the documentation removed from the bundle) gives the same model too, but is not needed for
speed.
Found: the bundle must put the classes before the annotation of their package¶
The first bundle put the classes of a folder at the end of its package.mo, after the annotation of the package:
not valid Modelica (an annotation ends a class), which rumoca refused once the packages had a Documentation.
bundle() now inserts them before the annotation; the folder itself was never affected.
Result¶
The split, the bundle and both ways of using the library (embedded or referenced) give the same models in OpenModelica and in CasADi. Phase 1 can create the repository from the split.
Scripts of the trials (outside the repository): equivalence.py (rumoca), om_simulate.py and the check_*.mos
scripts (OpenModelica), documentation.py (documentation and timing).