fmu-manipulation-toolbox 1.9rc6__tar.gz → 1.9rc8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/PKG-INFO +1 -1
  2. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/README.md +49 -20
  3. fmu_manipulation_toolbox-1.9rc8/fmu_manipulation_toolbox/__version__.py +1 -0
  4. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/cli/fmucontainer.py +8 -6
  5. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/cli/fmusplit.py +8 -5
  6. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/cli/fmutool.py +15 -4
  7. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/container.py +19 -16
  8. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/operations.py +10 -6
  9. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/remoting.py +4 -2
  10. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/darwin64/container.dylib +0 -0
  11. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/linux32/client_sm.so +0 -0
  12. fmu_manipulation_toolbox-1.9rc8/fmu_manipulation_toolbox/resources/linux32/server_sm +0 -0
  13. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/linux64/client_sm.so +0 -0
  14. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/linux64/container.so +0 -0
  15. fmu_manipulation_toolbox-1.9rc8/fmu_manipulation_toolbox/resources/linux64/server_sm +0 -0
  16. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/win32/client_sm.dll +0 -0
  17. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/win32/server_sm.exe +0 -0
  18. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/win64/client_sm.dll +0 -0
  19. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/win64/container.dll +0 -0
  20. fmu_manipulation_toolbox-1.9rc8/fmu_manipulation_toolbox/resources/win64/server_sm.exe +0 -0
  21. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/split.py +8 -3
  22. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox.egg-info/PKG-INFO +1 -1
  23. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/tests/test_suite.py +75 -5
  24. fmu_manipulation_toolbox-1.9rc6/fmu_manipulation_toolbox/__version__.py +0 -1
  25. fmu_manipulation_toolbox-1.9rc6/fmu_manipulation_toolbox/resources/linux32/server_sm +0 -0
  26. fmu_manipulation_toolbox-1.9rc6/fmu_manipulation_toolbox/resources/linux64/server_sm +0 -0
  27. fmu_manipulation_toolbox-1.9rc6/fmu_manipulation_toolbox/resources/win64/server_sm.exe +0 -0
  28. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/LICENSE.txt +0 -0
  29. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/__init__.py +0 -0
  30. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/__main__.py +0 -0
  31. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/assembly.py +0 -0
  32. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/checker.py +0 -0
  33. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/cli/__init__.py +0 -0
  34. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/cli/utils.py +0 -0
  35. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/gui.py +0 -0
  36. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/gui_style.py +0 -0
  37. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/help.py +0 -0
  38. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/checkbox-checked-disabled.png +0 -0
  39. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/checkbox-checked-hover.png +0 -0
  40. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/checkbox-checked.png +0 -0
  41. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/checkbox-unchecked-disabled.png +0 -0
  42. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/checkbox-unchecked-hover.png +0 -0
  43. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/checkbox-unchecked.png +0 -0
  44. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/container.png +0 -0
  45. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/drop_fmu.png +0 -0
  46. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Annotation.xsd +0 -0
  47. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2AttributeGroups.xsd +0 -0
  48. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2ModelDescription.xsd +0 -0
  49. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2ScalarVariable.xsd +0 -0
  50. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Type.xsd +0 -0
  51. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Unit.xsd +0 -0
  52. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2VariableDependency.xsd +0 -0
  53. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3Annotation.xsd +0 -0
  54. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3AttributeGroups.xsd +0 -0
  55. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3BuildDescription.xsd +0 -0
  56. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3InterfaceType.xsd +0 -0
  57. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3LayeredStandardManifest.xsd +0 -0
  58. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3ModelDescription.xsd +0 -0
  59. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3Terminal.xsd +0 -0
  60. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3TerminalsAndIcons.xsd +0 -0
  61. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3Type.xsd +0 -0
  62. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3Unit.xsd +0 -0
  63. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3Variable.xsd +0 -0
  64. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3VariableDependency.xsd +0 -0
  65. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/fmu.png +0 -0
  66. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/fmu_manipulation_toolbox.png +0 -0
  67. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/help.png +0 -0
  68. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/icon-round.png +0 -0
  69. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/icon.png +0 -0
  70. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/icon_fmu.png +0 -0
  71. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/license.txt +0 -0
  72. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/mask.png +0 -0
  73. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/resources/model.png +0 -0
  74. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox/version.py +0 -0
  75. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox.egg-info/SOURCES.txt +0 -0
  76. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox.egg-info/dependency_links.txt +0 -0
  77. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox.egg-info/entry_points.txt +0 -0
  78. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox.egg-info/requires.txt +0 -0
  79. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/fmu_manipulation_toolbox.egg-info/top_level.txt +0 -0
  80. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/setup.cfg +0 -0
  81. {fmu_manipulation_toolbox-1.9rc6 → fmu_manipulation_toolbox-1.9rc8}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fmu_manipulation_toolbox
3
- Version: 1.9rc6
3
+ Version: 1.9rc8
4
4
  Summary: FMU Manipulation Toolbox is a python application for modifying Functional Mock-up Units (FMUs) without recompilation or bundling them into FMU Containers
5
5
  Home-page: https://github.com/grouperenault/fmu_manipulation_toolbox/
6
6
  Author: Nicolas.LAURENT@Renault.com
@@ -1,12 +1,23 @@
1
- ![Title](fmu_manipulation_toolbox/resources/fmu_manipulation_toolbox.png)
1
+ ![](fmu_manipulation_toolbox/resources/fmu_manipulation_toolbox.png)
2
2
 
3
- FMU Manipulation Toolbox is a python package which help to analyse and modify a [Functional Mock-up Units (FMUs)](http://fmi-standard.org/)
4
- without recompilation. It is highly customizable. It can even modify the `modelDescription.xml` file.
3
+ FMU Manipulation Toolbox is a python package which help to analyse, modify or combine
4
+ [Functional Mock-up Units (FMUs)](http://fmi-standard.org/) without recompilation. It is highly customizable as it comes with
5
+ a Python API.
5
6
 
6
- Manipulating the `modelDescription.xml` can be a dangerous thing! Communicating with the FMU-developer and adapting
7
- the way the FMU is generated, is preferable when possible.
7
+ FMU Manipulation Toolbox can be used in different ways :
8
+ - Using a Graphical User Interface: suitable for end user
9
+ - Using a Command Line Interface: useful in
10
+ - Using a python API: the most efficient for automation (CI/CD, transformation scripts, ...)
8
11
 
9
- FMU Manipulation Toolbox also allows to group FMU's inside FMU Containers. (see [doc/container.md](doc/container.md))
12
+ Major features:
13
+ - Analyse FMU content: list ports and their attributes, check compliance of `ModelDescription.xml` with XSD, ...
14
+ - Alter FMU by modifying its `modelDescription.xml` file. NOTE: manipulating this file can be a dangerous
15
+ thing! Communicating with the FMU-developer and adapting the way the FMU is generated is preferable when
16
+ possible.
17
+ - Add binary interface. Typical use case is porting a 32bits FMUs to 64bits (or vice et versa).
18
+ - Nest FMUs in a [FMU Container](doc/container.md)
19
+
20
+ FMI versions 2.0 and 3.0 are supported.
10
21
 
11
22
  ## Installation
12
23
 
@@ -17,29 +28,39 @@ Two options available to install FMU Manipulation Toolbox:
17
28
  - Compile and install from [github repository](https://github.com/grouperenault/fmu_manipulation_toolbox). You will need
18
29
  - Python required packages. See [`requirements.txt`](requirements.txt).
19
30
  - C compiler (C99 or later)
20
- - CMake (>= 3.16)
31
+ - CMake (>= 3.20)
32
+
21
33
 
22
34
  ### Supported platforms
23
- FMU Manipulation Toolbox is tested on:
24
- - Windows 10/11
35
+
36
+ FMU Manipulation Toolbox is packaged for:
37
+ - Windows 10/11 (primary platform)
25
38
  - Linux (Ubuntu 22.04)
39
+ - Darwin
26
40
 
27
- Compilation is reported to work on:
28
- - MacOS (Apple Silicon and Intel)
29
41
 
30
42
  ## Graphical User Interface
31
43
 
32
- FMU Manipulation Toolbox is released with a GUI. You can launch it with the following command `fmutool`
33
- (without any option)
44
+ FMU Manipulation Toolbox is released with a GUI. You can launch it with the following command `fmutool-gui`
34
45
 
35
46
  ![GUI](doc/gui.png "GUI")
36
47
 
48
+ Button colors descriptions:
49
+ - red: remove information from the `modelDescription.xml`
50
+ - orange: alter `modelDescription.xml`
51
+ - green: add component into the FMU or check it
52
+ - violet: extract and save
53
+ - blue: filter actions scope or exit
54
+
55
+ **Original FMU is never modified**. Use `Save` button to get modified copy of the original FMU.
56
+
37
57
 
38
58
  ## Command Line Interface
39
59
 
40
60
  FMU Manipulation Toolbox comes with 2 commands:
41
61
  - `fmutool`: a versatile analysis and manipulation tool for FMU.
42
- - `fmucontainer`: group FMU's inside FMU Containers. (see [container/README.md](container/README.md))
62
+ - `fmucontainer`: group FMUs inside FMU Containers. (see [container/README.md](container/README.md))
63
+ - `fmusplit: to extract FMUs from a FMU Container.
43
64
 
44
65
 
45
66
  ### Analysis and Manipulation tool:
@@ -163,14 +184,13 @@ You can write your own FMU Manipulation scripts. Once you downloaded fmutool mod
163
184
  adding the `import` statement lets you access the API :
164
185
 
165
186
  ```python
166
- from fmu_manipulation_toolbox.operations import FMU, OperationExtractNames, OperationStripTopLevel,
167
-
168
- OperationRenameFromCSV
187
+ from fmu_manipulation_toolbox.operations import ...
169
188
  ```
170
189
 
190
+
171
191
  ### remove toplevel bus (if any)
172
192
 
173
- Give a FMU with the following I/O structure
193
+ Given a FMU with the following I/O structure
174
194
  ```
175
195
  ├── Parameters
176
196
  │ ├── Foo
@@ -194,6 +214,8 @@ The following transformation will lead into:
194
214
 
195
215
  The following code will do this transformation:
196
216
  ```python
217
+ from fmu_manipulation_toolbox.operations import FMU, OperationStripTopLevel
218
+
197
219
  fmu = FMU(r"bouncing_ball.fmu")
198
220
  operation = OperationStripTopLevel()
199
221
  fmu.apply_operation(operation)
@@ -205,8 +227,10 @@ fmu.repack(r"bouncing_ball-modified.fmu")
205
227
  The following code will dump all FMU's Scalars names into a CSV:
206
228
 
207
229
  ```python
230
+ from fmu_manipulation_toolbox.operations import FMU, OperationSaveNamesToCSV
231
+
208
232
  fmu = FMU(r"bouncing_ball.fmu")
209
- operation = OperationExtractNames()
233
+ operation = OperationSaveNamesToCSV()
210
234
  fmu.apply_operation(operation)
211
235
  operation.write_csv(r"bouncing_ball.csv")
212
236
  ```
@@ -224,15 +248,20 @@ g;g;4;parameter;fixed
224
248
  e;e;5;parameter;tunable
225
249
  ```
226
250
 
251
+
227
252
  ### Read CSV and rename FMU ports
228
253
 
229
- CSV file should contain- 2 columns:
254
+ CSV file should contain 2 columns:
230
255
  1. the current name
231
256
  2. the new name
232
257
 
233
258
  ```python
259
+ from fmu_manipulation_toolbox.operations import FMU, OperationRenameFromCSV
260
+
234
261
  fmu = FMU(r"bouncing_ball.fmu")
235
262
  operation = OperationRenameFromCSV(r"bouncing_ball-modified.csv")
236
263
  fmu.apply_operation(operation)
237
264
  fmu.repack(r"bouncing_ball-renamed.fmu")
238
265
  ```
266
+
267
+ More operations exist in [`Operation.py`](fmu_manipulation_toolbox/operations.py)
@@ -1,6 +1,8 @@
1
1
  import argparse
2
2
  import logging
3
+ import sys
3
4
 
5
+ from typing import *
4
6
  from pathlib import Path
5
7
 
6
8
  from .utils import setup_logger, make_wide
@@ -9,7 +11,7 @@ from ..container import FMUContainerError
9
11
  from ..version import __version__ as version
10
12
 
11
13
 
12
- def fmucontainer():
14
+ def fmucontainer(command_options: Sequence[str]):
13
15
  logger = setup_logger()
14
16
 
15
17
  logger.info(f"FMUContainer version {version}")
@@ -63,7 +65,7 @@ def fmucontainer():
63
65
  parser.add_argument("-dump-json", action="store_true", dest="dump", default=False,
64
66
  help="Dump a JSON file for each container.")
65
67
 
66
- config = parser.parse_args()
68
+ config = parser.parse_args(command_options)
67
69
 
68
70
  if config.debug:
69
71
  logger.setLevel(logging.DEBUG)
@@ -88,16 +90,16 @@ def fmucontainer():
88
90
  auto_parameter=config.auto_parameter)
89
91
  except FileNotFoundError as e:
90
92
  logger.fatal(f"Cannot read file: {e}")
91
- continue
93
+ sys.exit(-1)
92
94
  except (FMUContainerError, AssemblyError) as e:
93
95
  logger.fatal(f"{filename}: {e}")
94
- continue
96
+ sys.exit(-2)
95
97
 
96
98
  try:
97
99
  assembly.make_fmu(dump_json=config.dump, fmi_version=int(config.fmi_version))
98
100
  except FMUContainerError as e:
99
101
  logger.fatal(f"{filename}: {e}")
100
- continue
102
+ sys.exit(-3)
101
103
 
102
104
  if __name__ == "__main__":
103
- fmucontainer()
105
+ fmucontainer(sys.argv[1:])
@@ -1,12 +1,15 @@
1
1
  import argparse
2
2
  import logging
3
+ import sys
4
+
5
+ from typing import *
3
6
 
4
7
  from .utils import setup_logger, make_wide
5
8
  from ..split import FMUSplitter, FMUSplitterError
6
9
  from ..version import __version__ as version
7
10
 
8
11
 
9
- def fmusplit():
12
+ def fmusplit(command_options: Sequence[str]):
10
13
  logger = setup_logger()
11
14
 
12
15
  logger.info(f"FMUSplit version {version}")
@@ -24,7 +27,7 @@ def fmusplit():
24
27
  metavar="filename.fmu", required=True,
25
28
  help="Description of the FMU container to split.")
26
29
 
27
- config = parser.parse_args()
30
+ config = parser.parse_args(command_options)
28
31
 
29
32
  if config.debug:
30
33
  logger.setLevel(logging.DEBUG)
@@ -35,11 +38,11 @@ def fmusplit():
35
38
  splitter.split_fmu()
36
39
  except FMUSplitterError as e:
37
40
  logger.fatal(f"{fmu_filename}: {e}")
38
- continue
41
+ sys.exit(-1)
39
42
  except FileNotFoundError as e:
40
43
  logger.fatal(f"Cannot read file: {e}")
41
- continue
44
+ sys.exit(-2)
42
45
 
43
46
 
44
47
  if __name__ == "__main__":
45
- fmusplit()
48
+ fmusplit(sys.argv[1:])
@@ -1,6 +1,8 @@
1
1
  import argparse
2
2
  import sys
3
3
 
4
+ from typing import *
5
+
4
6
  from .utils import setup_logger, make_wide
5
7
  from ..operations import (OperationSummary, OperationError, OperationRemoveRegexp,
6
8
  OperationRemoveSources, OperationTrimUntil, OperationKeepOnlyRegexp, OperationMergeTopLevel,
@@ -12,7 +14,7 @@ from ..version import __version__ as version
12
14
  from ..help import Help
13
15
 
14
16
 
15
- def fmutool():
17
+ def fmutool(command_options: Sequence[str]):
16
18
  logger = setup_logger()
17
19
 
18
20
  logger.info(f"FMU Manipulation Toolbox version {version}")
@@ -68,7 +70,7 @@ def fmutool():
68
70
  add_option('-summary', action='append_const', dest='operations_list', const=OperationSummary())
69
71
  add_option('-check', action='append_const', dest='operations_list', const=[checker() for checker in checker_list])
70
72
 
71
- cli_options = parser.parse_args()
73
+ cli_options = parser.parse_args(command_options)
72
74
  # handle the "no operation" use case
73
75
  if not cli_options.operations_list:
74
76
  cli_options.operations_list = []
@@ -89,7 +91,16 @@ def fmutool():
89
91
  for causality in cli_options.apply_on:
90
92
  logger.info(f" - causality = {causality}")
91
93
 
92
- for operation in cli_options.operations_list:
94
+ # Checker operations are added as a list into operations_list
95
+ def operation_iterator():
96
+ for op in cli_options.operations_list:
97
+ if isinstance(op, list):
98
+ for sub_op in op:
99
+ yield sub_op
100
+ else:
101
+ yield op
102
+
103
+ for operation in operation_iterator():
93
104
  logger.info(f" => {operation}")
94
105
  try:
95
106
  fmu.apply_operation(operation, cli_options.apply_on)
@@ -112,4 +123,4 @@ def fmutool():
112
123
  logger.info(f"INFO Modified FMU is not saved. If necessary use '-output' option.")
113
124
 
114
125
  if __name__ == "__main__":
115
- fmutool()
126
+ fmutool(sys.argv[1:])
@@ -796,23 +796,26 @@ class FMUContainer:
796
796
  def make_fmu_xml_epilog_2(self, xml_file, index_offset):
797
797
  xml_file.write(" </ModelVariables>\n"
798
798
  "\n"
799
- " <ModelStructure>\n"
800
- " <Outputs>\n")
799
+ " <ModelStructure>\n")
801
800
 
802
- index = index_offset
803
- for output in self.outputs.values():
804
- if output.port.type_name in EmbeddedFMUPort.CONTAINER_TO_FMI[2]:
805
- print(f' <Unknown index="{index}"/>', file=xml_file)
806
- index += 1
807
- xml_file.write(" </Outputs>\n"
808
- " <InitialUnknowns>\n")
809
- index = index_offset
810
- for output in self.outputs.values():
811
- if output.port.type_name in EmbeddedFMUPort.CONTAINER_TO_FMI[2]:
812
- print(f' <Unknown index="{index}"/>', file=xml_file)
813
- index += 1
814
- xml_file.write(" </InitialUnknowns>\n"
815
- " </ModelStructure>\n"
801
+
802
+ if self.outputs:
803
+ xml_file.write(" <Outputs>\n")
804
+ index = index_offset
805
+ for output in self.outputs.values():
806
+ if output.port.type_name in EmbeddedFMUPort.CONTAINER_TO_FMI[2]:
807
+ print(f' <Unknown index="{index}"/>', file=xml_file)
808
+ index += 1
809
+ xml_file.write(" </Outputs>\n"
810
+ " <InitialUnknowns>\n")
811
+ index = index_offset
812
+ for output in self.outputs.values():
813
+ if output.port.type_name in EmbeddedFMUPort.CONTAINER_TO_FMI[2]:
814
+ print(f' <Unknown index="{index}"/>', file=xml_file)
815
+ index += 1
816
+ xml_file.write(" </InitialUnknowns>\n")
817
+
818
+ xml_file.write(" </ModelStructure>\n"
816
819
  "\n"
817
820
  "</fmiModelDescription>")
818
821
 
@@ -416,8 +416,8 @@ class OperationSummary(OperationAbstract):
416
416
  hash_md5.update(chunk)
417
417
  digest = hash_md5.hexdigest()
418
418
  logger.info(f"| MD5Sum = {digest}")
419
-
420
- logger.info(f"|\n| FMI properties: ")
419
+ logger.info(f"|")
420
+ logger.info(f"| FMI properties: ")
421
421
  for (k, v) in attrs.items():
422
422
  logger.info(f"| - {k} = {v}")
423
423
  logger.info(f"|")
@@ -457,21 +457,25 @@ class OperationSummary(OperationAbstract):
457
457
 
458
458
  resource_dir = os.path.join(self.fmu.tmp_directory, "resources")
459
459
  if os.path.isdir(resource_dir):
460
- logger.info("|\n| Embedded resources:")
460
+ logger.info("|")
461
+ logger.info("| Embedded resources:")
461
462
  for resource in os.listdir(resource_dir):
462
463
  logger.info(f"| - {resource}")
463
464
 
464
465
  extra_dir = os.path.join(self.fmu.tmp_directory, "extra")
465
466
  if os.path.isdir(extra_dir):
466
- logger.info("|\n| Additional (meta-)data:")
467
+ logger.info("|")
468
+ logger.info("| Additional (meta-)data:")
467
469
  for extra in os.listdir(extra_dir):
468
470
  logger.info(f"| - {extra}")
469
471
 
470
- logger.info("|\n| Number of ports")
472
+ logger.info("|")
473
+ logger.info("| Number of ports")
471
474
  for causality, nb_ports in self.nb_port_per_causality.items():
472
475
  logger.info(f"| {causality} : {nb_ports}")
473
476
 
474
- logger.info("|\n| [End of report]")
477
+ logger.info("|")
478
+ logger.info("| [End of report]")
475
479
 
476
480
 
477
481
  class OperationRemoveSources(OperationAbstract):
@@ -60,9 +60,11 @@ class OperationAddRemotingWinAbstract(OperationAbstract):
60
60
  fmu_bin[self.bitness_to] / "license.txt")
61
61
 
62
62
  def port_attrs(self, fmu_port) -> int:
63
+ vr = int(fmu_port["valueReference"])
64
+ causality = fmu_port.get("causality", "local")
63
65
  try:
64
- self.vr[fmu_port.fmi_type].append(int(fmu_port["valueReference"]))
65
- if fmu_port["causality"] in ("input", "parameter"):
66
+ self.vr[fmu_port.fmi_type].append(vr)
67
+ if causality in ("input", "parameter"):
66
68
  self.nb_input += 1
67
69
  else:
68
70
  self.nb_output += 1
@@ -6,6 +6,8 @@ import xml.parsers.expat
6
6
  from typing import *
7
7
  from pathlib import Path
8
8
 
9
+ from .container import EmbeddedFMUPort
10
+
9
11
  logger = logging.getLogger("fmu_manipulation_toolbox")
10
12
 
11
13
 
@@ -66,7 +68,7 @@ class FMUSplitter:
66
68
  def split_fmu(self):
67
69
  logger.info(f"Splitting...")
68
70
  config = self._split_fmu(fmu_filename=str(self.fmu_filename), relative_path="")
69
- config_filename = self.directory / self.fmu_filename.with_suffix(".json")
71
+ config_filename = self.directory / self.fmu_filename.with_suffix(".json").name
70
72
  with open(config_filename, "w") as file:
71
73
  json.dump(config, file, indent=2)
72
74
  logger.info(f"Container definition saved to '{config_filename}'")
@@ -77,7 +79,7 @@ class FMUSplitter:
77
79
  if txt_filename in self.filenames_list:
78
80
  description = FMUSplitterDescription(self.zip)
79
81
  config = description.parse_txt_file(txt_filename)
80
- config["name"] = fmu_filename
82
+ config["name"] = Path(fmu_filename).name
81
83
  for i, fmu_filename in enumerate(config["candidate_fmu"]):
82
84
  directory = f"{relative_path}resources/{i:02x}/"
83
85
  if directory not in self.dir_set:
@@ -134,6 +136,7 @@ class FMUSplitterDescription:
134
136
  self.current_vr = None
135
137
  self.current_name = None
136
138
  self.current_causality = None
139
+ self.supported_fmi_types: Tuple[str] = ()
137
140
 
138
141
  @staticmethod
139
142
  def get_line(file):
@@ -182,10 +185,12 @@ class FMUSplitterDescription:
182
185
  def parse_txt_file_header(self, file, txt_filename):
183
186
  flags = self.get_line(file).split(" ")
184
187
  if len(flags) == 1:
188
+ self.supported_fmi_types = ("Real", "Integer", "Boolean", "String")
185
189
  self.config["mt"] = flags[0] == "1"
186
190
  self.config["profiling"] = self.get_line(file) == "1"
187
191
  self.config["sequential"] = False
188
192
  elif len(flags) == 3:
193
+ self.supported_fmi_types = EmbeddedFMUPort.ALL_TYPES
189
194
  self.config["mt"] = flags[0] == "1"
190
195
  self.config["profiling"] = flags[1] == "1"
191
196
  self.config["sequential"] = flags[2] == "1"
@@ -242,7 +247,7 @@ class FMUSplitterDescription:
242
247
  logger.debug(f"Adding container port {causality}: {definition}")
243
248
 
244
249
  def parse_txt_file_ports(self, file):
245
- for fmi_type in ("Real", "Integer", "Boolean", "String"):
250
+ for fmi_type in self.supported_fmi_types:
246
251
  nb_port_variables = self.get_line(file).split(" ")[0]
247
252
  for i in range(int(nb_port_variables)):
248
253
  tokens = self.get_line(file).split(" ")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fmu_manipulation_toolbox
3
- Version: 1.9rc6
3
+ Version: 1.9rc8
4
4
  Summary: FMU Manipulation Toolbox is a python application for modifying Functional Mock-up Units (FMUs) without recompilation or bundling them into FMU Containers
5
5
  Home-page: https://github.com/grouperenault/fmu_manipulation_toolbox/
6
6
  Author: Nicolas.LAURENT@Renault.com
@@ -1,24 +1,42 @@
1
1
  import unittest
2
2
  import sys
3
3
  from pathlib import Path
4
+ from fmpy.simulation import simulate_fmu
5
+ import numpy as np
4
6
 
5
7
  sys.path.insert(0, str(Path(__file__).parent.parent))
6
8
  from fmu_manipulation_toolbox.operations import *
7
9
  from fmu_manipulation_toolbox.remoting import *
8
10
  from fmu_manipulation_toolbox.container import *
9
11
  from fmu_manipulation_toolbox.assembly import *
10
-
12
+ from fmu_manipulation_toolbox.cli.fmutool import fmutool
13
+ from fmu_manipulation_toolbox.cli.fmusplit import fmusplit
14
+ from fmu_manipulation_toolbox.cli.fmucontainer import fmucontainer
11
15
 
12
16
  class FMUManipulationToolboxTestSuite(unittest.TestCase):
13
17
  def __init__(self, *args, **kwargs):
14
18
  super().__init__(*args, **kwargs)
15
19
  self.fmu_filename = "operations/bouncing_ball.fmu"
16
20
 
21
+ def assert_simulation(self, filename: Union[Path, str], step_size: Optional[float] = None):
22
+ if isinstance(filename, str):
23
+ filename = Path(filename)
24
+ result_filename = filename.with_name("results-" + filename.with_suffix(".csv").name)
25
+ ref_filename = result_filename.with_stem("REF-" + result_filename.stem)
26
+
27
+ result = simulate_fmu(filename, step_size=step_size, stop_time=10, relative_tolerance=1e-9,
28
+ output_interval=step_size, validate=True)
29
+
30
+ np.savetxt(result_filename, result, delimiter=',', fmt="%.5e")
31
+
32
+ self.assert_identical_files(result_filename, ref_filename)
33
+
17
34
  def assert_identical_files(self, filename1, filename2):
18
35
  with open(filename1, mode="rt", newline=None) as a, open(filename2, mode="rt", newline=None) as b:
19
36
  self.assertTrue(all(lineA == lineB for lineA, lineB in zip(a, b)))
20
37
 
21
- def assert_identical_files_but_guid(self, filename1, filename2):
38
+ @staticmethod
39
+ def assert_identical_files_but_guid(filename1, filename2):
22
40
  with open(filename1, mode="rt", newline=None) as a, open(filename2, mode="rt", newline=None) as b:
23
41
  for lineA, lineB in zip(a, b):
24
42
  if not "guid" in lineA and not lineA == lineB:
@@ -51,10 +69,12 @@ class FMUManipulationToolboxTestSuite(unittest.TestCase):
51
69
 
52
70
  @unittest.skipUnless(sys.platform.startswith("win"), "Supported only on Windows")
53
71
  def test_add_remoting_win32(self):
54
- fmu = FMU(self.fmu_filename)
55
- operation = OperationAddRemotingWin32()
72
+ fmu = FMU("remoting/bouncing_ball-win32.fmu")
73
+ operation = OperationAddRemotingWin64()
56
74
  fmu.apply_operation(operation)
57
- fmu.repack("operations/bouncing_ball-win32.fmu")
75
+ fmu.repack("remoting/bouncing_ball-win64.fmu")
76
+ self.assert_simulation("remoting/bouncing_ball-win32.fmu")
77
+ self.assert_simulation("remoting/bouncing_ball-win64.fmu")
58
78
 
59
79
  def test_remove_regexp(self):
60
80
  self.assert_operation_match_ref("operations/bouncing_ball-removed.fmu",
@@ -72,6 +92,8 @@ class FMUManipulationToolboxTestSuite(unittest.TestCase):
72
92
  "containers/bouncing_ball/bouncing/resources/container.txt")
73
93
  self.assert_identical_files("containers/bouncing_ball/REF-bouncing.json",
74
94
  "containers/bouncing_ball/bouncing.json")
95
+ if os.name == 'nt':
96
+ self.assert_simulation("containers/bouncing_ball/bouncing.fmu")
75
97
 
76
98
  def test_container_bouncing_ball_seq(self):
77
99
  assembly = Assembly("bouncing-seq.csv", fmu_directory=Path("containers/bouncing_ball"), mt=True, debug=True,
@@ -82,6 +104,8 @@ class FMUManipulationToolboxTestSuite(unittest.TestCase):
82
104
  "containers/bouncing_ball/bouncing-seq/resources/container.txt")
83
105
  self.assert_identical_files("containers/bouncing_ball/REF-bouncing-seq.json",
84
106
  "containers/bouncing_ball/bouncing-seq.json")
107
+ if os.name == 'nt':
108
+ self.assert_simulation("containers/bouncing_ball/bouncing-seq.fmu")
85
109
 
86
110
  def test_container_bouncing_ball_profiling(self):
87
111
  assembly = Assembly("bouncing-profiling.csv", fmu_directory=Path("containers/bouncing_ball"), profiling=True,
@@ -94,6 +118,8 @@ class FMUManipulationToolboxTestSuite(unittest.TestCase):
94
118
  "containers/bouncing_ball/bouncing-profiling.json")
95
119
  self.assert_identical_files_but_guid("containers/bouncing_ball/REF-modelDescription-profiling.xml",
96
120
  "containers/bouncing_ball/bouncing-profiling/modelDescription.xml")
121
+ if os.name == 'nt':
122
+ self.assert_simulation("containers/bouncing_ball/bouncing-profiling.fmu")
97
123
 
98
124
  def test_container_bouncing_ball_profiling_3(self):
99
125
  assembly = Assembly("bouncing-3.csv", fmu_directory=Path("containers/bouncing_ball"), profiling=True,
@@ -103,18 +129,24 @@ class FMUManipulationToolboxTestSuite(unittest.TestCase):
103
129
  "containers/bouncing_ball/bouncing-3/resources/container.txt")
104
130
  self.assert_identical_files_but_guid("containers/bouncing_ball/REF-modelDescription-3.xml",
105
131
  "containers/bouncing_ball/bouncing-3/modelDescription.xml")
132
+ if os.name == 'nt':
133
+ self.assert_simulation("containers/bouncing_ball/bouncing-3.fmu")
106
134
 
107
135
  def test_container_ssp(self):
108
136
  assembly = Assembly("bouncing.ssp", fmu_directory=Path("containers/ssp"))
109
137
  assembly.make_fmu(dump_json=True)
110
138
  self.assert_identical_files("containers/ssp/REF-bouncing-dump.json",
111
139
  "containers/ssp/bouncing-dump.json")
140
+ if os.name == 'nt':
141
+ self.assert_simulation("containers/ssp/bouncing.fmu")
112
142
 
113
143
  def test_container_json_flat(self):
114
144
  assembly = Assembly("flat.json", fmu_directory=Path("containers/arch"))
115
145
  assembly.make_fmu(dump_json=True)
116
146
  self.assert_identical_files("containers/arch/REF-flat-dump.json",
117
147
  "containers/arch/flat-dump.json")
148
+ if os.name == 'nt':
149
+ self.assert_simulation("containers/arch/flat.fmu")
118
150
 
119
151
  def test_container_subdir_flat(self):
120
152
  container = FMUContainer("sub.fmu", fmu_directory=Path("containers/arch"))
@@ -123,18 +155,24 @@ class FMUManipulationToolboxTestSuite(unittest.TestCase):
123
155
  container.get_fmu("sine.fmu")
124
156
  container.add_implicit_rule()
125
157
  container.make_fmu("sub.fmu", step_size=0.5)
158
+ if os.name == 'nt':
159
+ self.assert_simulation("containers/arch/sub.fmu")
126
160
 
127
161
  def test_container_json_hierarchical(self):
128
162
  assembly = Assembly("hierarchical.json", fmu_directory=Path("containers/arch"))
129
163
  assembly.make_fmu(dump_json=True)
130
164
  self.assert_identical_files("containers/arch/REF-hierarchical-dump.json",
131
165
  "containers/arch/hierarchical-dump.json")
166
+ if os.name == 'nt':
167
+ self.assert_simulation("containers/arch/hierarchical.fmu")
132
168
 
133
169
  def test_container_json_reversed(self):
134
170
  assembly = Assembly("reversed.json", fmu_directory=Path("containers/arch"))
135
171
  assembly.make_fmu(dump_json=True)
136
172
  self.assert_identical_files("containers/arch/REF-reversed-dump.json",
137
173
  "containers/arch/reversed-dump.json")
174
+ if os.name == 'nt':
175
+ self.assert_simulation("containers/arch/reversed.fmu")
138
176
 
139
177
  def test_container_start(self):
140
178
  assembly = Assembly("slx.json", fmu_directory=Path("containers/start"), debug=True)
@@ -143,12 +181,22 @@ class FMUManipulationToolboxTestSuite(unittest.TestCase):
143
181
  "containers/start/container-slx/resources/container.txt")
144
182
  self.assert_identical_files_but_guid("containers/start/REF-modelDescription.xml",
145
183
  "containers/start/container-slx/modelDescription.xml")
184
+ if os.name == 'nt':
185
+ self.assert_simulation("containers/start/slx.fmu")
186
+
187
+ def test_container_vanderpol(self):
188
+ self.assert_simulation("containers/VanDerPol/VanDerPol.fmu", 0.1)
189
+ assembly = Assembly("VanDerPol.json", fmu_directory=Path("containers/VanDerPol"))
190
+ assembly.make_fmu()
191
+ self.assert_simulation("containers/VanDerPol/VanDerPol-Container.fmu", 0.1)
146
192
 
147
193
  def test_fmi3_pt2(self):
148
194
  assembly = Assembly("passthrough.json", fmu_directory=Path("fmi3/passthrough"), debug=True)
149
195
  assembly.make_fmu(fmi_version=2)
150
196
  self.assert_identical_files("fmi3/passthrough/REF-container.txt",
151
197
  "fmi3/passthrough/container-passthrough/resources/container.txt")
198
+ if os.name == 'nt':
199
+ self.assert_simulation("fmi3/passthrough/container-passthrough.fmu")
152
200
 
153
201
  def test_container_move(self):
154
202
  #bb = Assembly("bouncing.csv", fmu_directory=Path("containers/bouncing_ball"))
@@ -168,6 +216,28 @@ class FMUManipulationToolboxTestSuite(unittest.TestCase):
168
216
  for link in links_fmu0a:
169
217
  print(f"{link}")
170
218
 
219
+ def test_fmutool(self):
220
+ fmutool(['-input', 'operations/bouncing_ball.fmu', '-summary', '-check', '-dump-csv',
221
+ 'operations/cli-bouncing_ball.csv'])
222
+ self.assert_identical_files("operations/cli-bouncing_ball.csv", "operations/REF-bouncing_ball.csv")
223
+
224
+ def test_fmucontainer_csv(self):
225
+ fmucontainer(['-container', 'cli-bouncing.csv', '-fmu-directory', 'containers/bouncing_ball', '-mt', '-debug'])
226
+ self.assert_identical_files("containers/bouncing_ball/REF-container.txt",
227
+ "containers/bouncing_ball/cli-bouncing/resources/container.txt")
228
+
229
+ def test_fmucontainer_json(self):
230
+ fmucontainer(['-fmu-directory', 'containers/arch', '-container', 'cli-flat.json', '-dump'])
231
+ self.assert_identical_files("containers/arch/REF-cli-flat-dump.json",
232
+ "containers/arch/cli-flat-dump.json")
233
+
234
+ def test_fmusplit(self):
235
+ fmusplit(["-fmu", "containers/ssp/bouncing.fmu"])
236
+ self.assertTrue(Path("containers/ssp/bouncing.dir/bb_position.fmu").exists())
237
+ self.assertTrue(Path("containers/ssp/bouncing.dir/bb_velocity.fmu").exists())
238
+ self.assert_identical_files("containers/ssp/REF-split-bouncing.json",
239
+ "containers/ssp/bouncing.dir/bouncing.json")
240
+
171
241
 
172
242
  if __name__ == '__main__':
173
243
  unittest.main()