Currently, the openEMS command-line argument parsing has two limitations.
If a new module is added into openEMS and that module accepts runtime options, one must change the main openems.cpp to add one new API per option, decreasing code modularity - especially if an option is only used by a single module. This is unsustainable in the long run. The in-development Tiling engine is expected to accept 3 to 4 options.
When we're running as a shared library (used by Python binding), if one wants to pass runtime options from Python, one must change the main openems.cpp to add one new API per option. This is again unsustainable. For example, currently there's no way to change the simulation engine via Python because of the lack of APIs.
To overcome these limitations, this commit completely rewrites the command-line argument parsing logic, and a new API SetLibraryArguments() which can be used to set almost all options supported by the main openEMS executable.
The new logic works in the following way:
If a class accepts options, it provides a method optionDesc() that returns a options_description. This data structure contains a list of supported options, their text descriptions. Optional callback functions can also be registered, which may be used to set the internal state of a class. During early initialization, the main program openems.cpp collects these options_description from multiple classes, then registers them to g_settings by calling Global::appendOptionDesc(). The combined structure is stored as m_optionDesc. Boost can automatically validate arguments, generate help messages, provide default values, etc.
Before starting simulation, if we're running as an executable, parseCommandLineArguments() is called. If we're running as a shared library, parseOption() is called (by the user). Both functions calls Boost's program_options library to parse supplied string options automatically. Registered Callback functions are also executed at this moment, so the variables and states within a class is updated automatically.
The parsed options is also globally stored as a variables_map in m_options of g_settings, which can be accessed globally via Global::hasOption() and Global::getOption(). This is useful when a class is only newed after parsing all options (such as an engine-specific option), as callbacks can't be used.
A new API SetLibraryArguments() is provided for setting command-line arguments when running as a shared library:
void SetLibraryArguments(std::vector allOptions);
python: use new SetLibraryArguments() API
In the rewritten command-line argument parsing logic, a new API SetLibraryArguments() is provied, which can be used to set almost all options supported by the main openEMS executable by passing options as strings.
This commit change FDTD.Run() to use the new API. As a result, all command-line options, such as engine types and debugging options, are supported by the Python binding.
Outcomes
New help screen, automatically generated by Boost's program_options library:
----------------------------------------------------------------------
| openEMS 64bit -- version v0.0.36-17-gd56ff99
| (C) 2010-2023 Thorsten Liebig <thorsten.liebig@gmx.de> GPL license
----------------------------------------------------------------------
Used external libraries:
CSXCAD -- Version: v0.6.3-2-gc6a1587
hdf5 -- Version: 1.14.2
compiled against: HDF5 library version: 1.14.2
tinyxml -- compiled against: 2.6.2
fparser
boost -- compiled against: 1_83
vtk -- Version: 9.2.6
compiled against: 9.2.6
Usage: openEMS <FDTD_XML_FILE> [<options>...]
Options:
-h [ --help ] Show this help message and exit
--disable-dumps Disable all field dumps for faster simulation
--debug-material Dump material distribution to a vtk file for
debugging
--debug-PEC Dump metal distribution to a vtk file for
debugging
--debug-operator Dump operator to vtk file for debugging
--debug-boxes Dump e.g. probe boxes to vtk file for debugging
--debug-CSX Write CSX geometry file to debugCSX.xml
--engine arg (=fastest) Choose engine type
fastest: fastest available engine (default)
basic: basic FDTD engine
sse: engine using SSE vector extensions
sse-compressed: engine using compressed
operator + sse vector
extensions
multithreaded: engine using compressed
operator + sse vector
extensions + multithreading
--numThreads arg (=0) Force use n threads for multithreaded engine
(needs: --engine=multithreaded)
--no-simulation only run preprocessing; do not simulate
--dump-statistics dump simulation statistics to
'openEMS_run_stats.txt' and 'openEMS_stats.txt'
Additional global arguments:
--showProbeDiscretization Show probe discretization information
--nativeFieldDumps Dump all fields using the native field
components
-v [ --verbose ] [=arg(=1)] Verbose level, select debug level 1 to 3, also
accept -v, -vv, -vvv
Completely Rewrite Command-Line Argument Parsing
Currently, the openEMS command-line argument parsing has two limitations.
If a new module is added into openEMS and that module accepts runtime options, one must change the main openems.cpp to add one new API per option, decreasing code modularity - especially if an option is only used by a single module. This is unsustainable in the long run. The in-development Tiling engine is expected to accept 3 to 4 options.
When we're running as a shared library (used by Python binding), if one wants to pass runtime options from Python, one must change the main openems.cpp to add one new API per option. This is again unsustainable. For example, currently there's no way to change the simulation engine via Python because of the lack of APIs.
To overcome these limitations, this commit completely rewrites the command-line argument parsing logic, and a new API SetLibraryArguments() which can be used to set almost all options supported by the main openEMS executable.
The new logic works in the following way:
If a class accepts options, it provides a method optionDesc() that returns a options_description. This data structure contains a list of supported options, their text descriptions. Optional callback functions can also be registered, which may be used to set the internal state of a class. During early initialization, the main program openems.cpp collects these options_description from multiple classes, then registers them to g_settings by calling Global::appendOptionDesc(). The combined structure is stored as m_optionDesc. Boost can automatically validate arguments, generate help messages, provide default values, etc.
Before starting simulation, if we're running as an executable, parseCommandLineArguments() is called. If we're running as a shared library, parseOption() is called (by the user). Both functions calls Boost's program_options library to parse supplied string options automatically. Registered Callback functions are also executed at this moment, so the variables and states within a class is updated automatically.
The parsed options is also globally stored as a variables_map in m_options of g_settings, which can be accessed globally via Global::hasOption() and Global::getOption(). This is useful when a class is only newed after parsing all options (such as an engine-specific option), as callbacks can't be used.
A new API SetLibraryArguments() is provided for setting command-line arguments when running as a shared library:
void SetLibraryArguments(std::vector allOptions);
python: use new SetLibraryArguments() API
In the rewritten command-line argument parsing logic, a new API SetLibraryArguments() is provied, which can be used to set almost all options supported by the main openEMS executable by passing options as strings.
This commit change FDTD.Run() to use the new API. As a result, all command-line options, such as engine types and debugging options, are supported by the Python binding.
Outcomes
e.g.
produces: