Skip to content

RFC - definition of a runner class using as much of the existing code as possible#404

Draft
syntron wants to merge 102 commits intoOpenModelica:masterfrom
syntron:syntron_RFC
Draft

RFC - definition of a runner class using as much of the existing code as possible#404
syntron wants to merge 102 commits intoOpenModelica:masterfrom
syntron:syntron_RFC

Conversation

@syntron
Copy link
Contributor

@syntron syntron commented Jan 11, 2026

Content (lots of changes / these will be splitted into several PRs if the general approach is approved):

  • OMCSession.py: rename real OMCPath* classes as privat (start with '_')
  • OMCSession: define OMCPathDummy and OMCSessionDummy => no OMC server
  • ModelExecution.py: include all code related to model execution (from OMCsession*; ModelicaSystemCmd)
  • ModelicaSystemDoE.py: seperate file for this class
  • ModelicaSystemDoE.py: use ModelicaSystem / reduce number of arguments to init
  • ModelicaSystem(Base).py: split getOutputs() and getContinuous() to Initial and Final data
  • ModelicaSystem(Base).py: getOutputs*() / getContinuous*() - always return np.float64
  • ModelicaSystemBase.py: define a basic version of ModelicaSystem
  • ModelicaSystemRunner.py: definition of a 'runner' class using ModelicaSystemBasic / OMCSessionDummy (see PR Enable the usage of a pre-built model executables by ModelicaSystemRunner #401)
  • test_ModelicaSystemRunner.py: test case for ModelicaSystemRunner
  • test_*: update of test cases based on above listed changes

Compared to PR #401 this solution:

  • still uses the existing OMPython requirements (dependencies of the Runner class; i.e. numpy, etc.)
  • shares nearly all code with the current definitions (by using OMCPathDummy and OMCSessionDummy)
  • uses | instead of Union

Further changes could do additional modifications such that the 3rd party packages (ZMQ) are not needed at all in the runner code path.

All tests are running without error / tested in https://github.com/syntron/OMPython/actions/runs/20865402893

Please comment!

@syntron
Copy link
Contributor Author

syntron commented Feb 9, 2026

Some status update on this PR - the main points are:

The code defined here updates the internal structure of OMPython such that:

  • generic code is separated in ABC classes
  • derived code exists for OMC usage and Runner usage
  • a compatibility layer exists such that the interface of OMPython 4.0.0 is (basically) supported; this is tested using the original unittests from v4.0.0

Furthermore, as OMCPath is working only for Python >= 3.12, another dimension exists. There is a dummy implementation of OMCPath for Python < 3.12 such that most of the functionality can also be used here. See PR #401 for a hint that even older Python version are used (Python 3.10 or even lower).

These changes could be the baseline for a new documentation (see https://openmodelica.org/doc/OpenModelicaUsersGuide/latest/ompython.html). My idea was to include these changes as 4.x.0 and create a cut at the next release (or keep some kind of compatibility layer alive).

(partly from the discussion in PR #387)

@syntron
Copy link
Contributor Author

syntron commented Feb 9, 2026

The failed test is due to Windows - it runs well for v4.0.0 (see: https://github.com/syntron/OMPython/actions/runs/21842400755) and 4.x.x (see: https://github.com/syntron/OMPython/actions/runs/21841418638)

@syntron
Copy link
Contributor Author

syntron commented Feb 13, 2026

This is a final version of my changes to get a 'runner' functionality to OMPython. The main parts:

  • Compatibility with OMPython v4.0.0
  • Split fucntions into ABC / OMC / Runner
  • Split the large files into smaller onces based on the usage

This PR will be splitted into smaller steps.

@syntron syntron marked this pull request as ready for review February 13, 2026 23:20
@syntron syntron force-pushed the syntron_RFC branch 2 times, most recently from be89d52 to b20568b Compare February 15, 2026 11:17
@adeas31 adeas31 requested review from adeas31 and arun3688 February 16, 2026 16:30
Copy link
Collaborator

@arun3688 arun3688 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good

@syntron
Copy link
Contributor Author

syntron commented Feb 16, 2026

looks good

OK; I will create small parts for review & merge; there are some changes on top to restructure the two main files

@adeas31
Copy link
Member

adeas31 commented Feb 18, 2026

looks good

OK; I will create small parts for review & merge; there are some changes on top to restructure the two main files

Sorry I haven't got time to look into this yet.
As I understand, you will break this into new smaller PRs for review & merge, right?

@syntron
Copy link
Contributor Author

syntron commented Feb 18, 2026

Sorry I haven't got time to look into this yet. As I understand, you will break this into new smaller PRs for review & merge, right?

yes - this is the plan; the one RFC is way too big / too much changes to get an easy overview ...

@syntron
Copy link
Contributor Author

syntron commented Feb 18, 2026

Summary of the changes in these RFC; the elements will be provided as single PRs.

Basic concept / plan:

  • Define a Runner variant which allows to run compiled (binary) models without usage of an OMC server
  • Split all code into 3 parts: ABC (generic code), OMC related, Runner related
  • Define a compatibility layer which provides OMPython v4.0.0 compatibility

This means for some period of time, the new interface and the compatibility layer can exist in parallel (versions
4.x.x). Thus, errors can be found & fixed and people can adapt to the new interface before it is the only option.

Steps for the implementation (see PRs below):

  • (A) independent PRs based on current master; together these build the baseline for (B) and further
  • (B) split classes into ABC / OMC related
  • (C) define Runner part
  • (D) use unittests from v4.0.0 as reference / prepare compatibility layer
  • (E) prepare & split files (move classes around)
  • (F) cleanup after changes
  • (G) move all depreciated code into the compatibility layer
  • (H) removal of compatibility layer for a OMPython v5.0.0 (merged later)

I tried to order the PRs based on the steps (A) to (H) but some will be out of order. If the PRs are merged, please try
to follow the numbering.

PRs based on this RFC (using the branch name in my fork syntron:OMPython as reference; I will use this comment to link the PRs step by step):

(step A)

  • (A001) simplify_workflow_Test
  • (A002) OMParser
  • (A003) OMTypedParser
  • (A004) ModelicaSystem_okey_oval
  • (A005) OMCPath_remove_stat
  • (A006) OMCSession_small_fixes
  • (A007) ModelicaSystem_cleanup
  • (A008) ModelicaSystem_getContinuous_getOutputs
  • (A009) ModelicaSystem_small_fixes
  • (A000) use_function_keyword_arguments
  • (A011) update_init_docstring
  • (A012) reorder_imports
  • (A013) ModelicaSystemDoE
  • (A014) ModelExecution
  • (A015) ModelicaSystem_check_model_executable

(step B)

  • (B001) ModelicaSystem_split
  • (B002) ModelicaSystemDoE_split
  • (B003) OMCSession_OMPathABC
  • (B004) OMCSession_OMSessionABC

(step C)

  • (C001) Runner_definitions

(step D)

  • (D001) OMSession_small_fixes
  • (D002) OMSessionZMQ_move
  • (D003) OMCPath_improve
  • (D004) OMSessionRunner
  • (D005) OMCSession_classes_update
  • (D006) ModelicaSystem_small_fixes2
  • (D007) test_v400
  • (D008) v400_compatibility_layer

(step E)

  • (E001) update_tests
  • (E002) prepare_restructure
  • (E003) restructure

(step F)

  • (F001) restructure_cleanup

=> status syntron_RFC

step G & H are still to be reordered

@syntron
Copy link
Contributor Author

syntron commented Feb 18, 2026

PRs of step A:

(A001) simplify_workflow_Test - PR #411 - UPDATED
(A002) OMParser - PR #412
DONE - (A003) OMTypedParser - PR #413
DONE - (A004) ModelicaSystem_okey_oval - PR #414
(A005) OMCPath_remove_stat - PR #415
(A006) OMCSession_small_fixes - PR #416
(A007) ModelicaSystem_cleanup - PR #417
(A008) ModelicaSystem_getContinuous_getOutputs - PR #418
(A009) ModelicaSystem_small_fixes - PR #419
(A011) update_init_docstring - PR #421
(A012) reorder_imports - PR #422
(A013) ModelicaSystemDoE - PR #423

moved to the end / rebase needed:
(A010) use_function_keyword_arguments - PR #420
(A014) ModelExecution - PR #424
(A015) ModelicaSystem_check_model_executable - PR #425 (on top of PR #424)

The final state can be found at https://github.com/syntron/OMPython/tree/mergeA

@syntron
Copy link
Contributor Author

syntron commented Feb 26, 2026

@joewa any feedback from your side?

@Sonyoyo could you test the runner functionality for WSL? I do not have this option available to me ...

@adeas31
Copy link
Member

adeas31 commented Feb 26, 2026

@adeas31 I'm working with smaller commits which build up the PR in serveral steps; however, if these are merged into one commit, were is a high risk for merge conflicts for later PRs especially if these build on top of each other. Would it be OK if I create single step PRs for section B to E (see #404 (comment))? I would keep the details locally but only push one-commit PRs ...

Yeah sounds good.

@syntron
Copy link
Contributor Author

syntron commented Feb 27, 2026

Yeah sounds good.

OK - working on it ...

@syntron
Copy link
Contributor Author

syntron commented Feb 27, 2026

@adeas31 would it make sence to have a unittest without windows for testing? (windows quite often fails on github) - see https://github.com/syntron/OMPython/tree/test_ubuntu_nightly for a definition I just to check the PRs listed below

@syntron
Copy link
Contributor Author

syntron commented Feb 27, 2026

Updated list of open PRs based on this RFC:

=> all are single comit PRs; if needed, the detailed version can be found at https://github.com/syntron/OMPython/
=> the final state (F001) is matching this PR

  • G & H definitions are to come ...

@syntron
Copy link
Contributor Author

syntron commented Feb 28, 2026

A preliminary state including steps G is available:

and also a version including G & H - a possible baseline for v5.0.0:

Feel free to test and/or provide feedback!

@adeas31
Copy link
Member

adeas31 commented Mar 2, 2026

@adeas31 would it make sence to have a unittest without windows for testing? (windows quite often fails on github) - see https://github.com/syntron/OMPython/tree/test_ubuntu_nightly for a definition I just to check the PRs listed below

No. We should fix the Windows github runner.

@syntron
Copy link
Contributor Author

syntron commented Mar 2, 2026

No. We should fix the Windows github runner.

OK; regarding this I'm out ... It fails at the step of downloading OMC / setup OMC

@adeas31
Copy link
Member

adeas31 commented Mar 3, 2026

No. We should fix the Windows github runner.

OK; regarding this I'm out ... It fails at the step of downloading OMC / setup OMC

This should work fine with #445. The actual fix is done here OpenModelica/setup-openmodelica#471

@joewa
Copy link

joewa commented Mar 3, 2026

@joewa any feedback from your side?

Overall, the direction looks great @syntron ! However, I’m running into a regression when trying to run a pre-compiled BouncingBall example. It seems the way the command line is constructed has changed in a way that the executable no longer accepts.

The number in instantiatemodel refers to PR.

def instantiatemodel404(modelName):
    mod = ModelicaSystemRunner(work_directory='.')
    mod.setup(model_name=modelName)
    return mod

def instantiatemodel401(modelName):
    mod = ModelicaSystemRunner(
            modelname=modelName,
            runpath='.',
        )
    return mod

Then, we instantiate the model:

mod = instantiatemodelXXX(modelName='BouncingBall')

override_variables = {'e': 0.7,}
sim_options_override = {'stopTime': 3.0, 'stepSize': 0.03125}
resfilepathname = 'bounce_run001.mat'
mod.setParameters(**override_variables)
mod.setSimulationOptions(**sim_options_override)

mod.simulate(resultfile=resfilepathname, simflags=None,)

This fails because it builds a bad command line:

ModelExecutionException: Error running model executable ['/currentworkingdirectory/BouncingBall', '-overrideFile=/currentworkingdirectory/bounce_run001_override.txt', '-r=/currentworkingdirectory/bounce_run001.mat', '-stepSize=0.03125', '-stopTime=3.0']:
Command '['/currentworkingdirectory/BouncingBall', '-overrideFile=/currentworkingdirectory/bounce_run001_override.txt', '-r=/currentworkingdirectory/bounce_run001.mat', '-stepSize=0.03125', '-stopTime=3.0']' returned non-zero exit status 1.

But it worked in PR #401

['/currentworkingdirectory/BouncingBall', '-override=e=0.7,stopTime=3.0,stepSize=0.03125', '-r=bounce_run001.mat']
b'LOG_SUCCESS       | info    | The initialization finished successfully without homotopy method.\nLOG_SUCCESS       | info    | The simulation finished successfully.\n'

Could you please clarify if the usage of ModelicaSystemRunner has changed, or if this is an unintended side effect of the new command-line construction? I’m happy to do more testing once we get this "Hello World" case running!

Thanks.

@syntron
Copy link
Contributor Author

syntron commented Mar 4, 2026

@joewa the problem is the change of sim command line handling in OpenModelia (see PR #400 and PR #402). This is NOT included in PR #401.

You should be able to solve this for older versions < 1.26 if you use oms = OMSessionRunner(version="1.25.0") to force the older version and provide this session definition to the other classes, i.e. ModelicaSystemRunner(session = oms). Perhaps this could be automated if the compiled models provide a version string of the used OMC. Could you check?

I'm currently not able to test any of this; thus, please provide feedback!

@joewa
Copy link

joewa commented Mar 6, 2026

I missed that the interface has changed between omc 1.25 and 1.26.

So I did:

def instantiatemodel404(modelName):
    oms = OMSessionRunner(version="1.25.5")
    mod = ModelicaSystemRunner(session=oms, work_directory='.')
    mod.setup(model_name=modelName)
    return mod

That fixed it.

There is a key generationTool in BouncingBall_init.xml.

generationTool                      = "OpenModelica Compiler v1.25.5-cmake"

I think we should use it to switch automatically between the interfaces @syntron

Will do more testing in the next days...

@syntron
Copy link
Contributor Author

syntron commented Mar 6, 2026

I think we should use it to switch automatically between the interfaces @syntron

Could you please test PR #446?

@joewa
Copy link

joewa commented Mar 8, 2026

I think we should use it to switch automatically between the interfaces @syntron

Could you please test PR #446?

Perfect, it works with omc 1.25.5:

def instantiatemodel404(modelName):
    mod = ModelicaSystemRunner(work_directory='.')
    mod.setup(model_name=modelName)
    return mod

In the next step I'd like the simulation to read timeseries from a file.
So the command line has to have " -csvInput="+self.csvFile (works for models built with omc 1.25)
I cannot see how to make it happen with the updated OMPython (worked for me by setting inputFlag=True before).

@syntron
Copy link
Contributor Author

syntron commented Mar 8, 2026

In the next step I'd like the simulation to read timeseries from a file. So the command line has to have " -csvInput="+self.csvFile (works for models built with omc 1.25) I cannot see how to make it happen with the updated OMPython (worked for me by setting inputFlag=True before).

Please have a look in PR #447 - is it the code you are searching? (please keep in mind this is untested!)

The code for 1.25 is not working anymore as there is some internal handling of the data to allow to use something like docker or WSL as backend. Thus, the CSV file has to be read and the data merge to the input data used for the model simulation.

@joewa
Copy link

joewa commented Mar 9, 2026

Please have a look in PR #447 - is it the code you are searching? (please keep in mind this is untested!)

This is actually not what I wanted. I'm using -csvInput=myinputtimeseries.csv only, because the compiled models digest timeseries data in CSV easily. I would be even more happy if some other formats would be supported by the model executables in the future.

I'm calling the simulation like this:

mod.simulate(
            resultfile=resfilepathname,
            simargs={'csvInput': mod_csvFile},  # Old: simflags=f"-csvInput={mod_csvFile}",
            # overrideaux='variableFilter="'+'|'.join(list(res_vars))+'"'  # TODO: how to do this with ModelicaSystemRunner?
        )

but this was not working. The problem is that the simarg "csvInput" is overwritten in simulate_cmd (in modelica_system_abc.py)

So I added a check if "csvInput" was already provided in the simargs by the user:

        # always define the result file to use
        om_cmd.arg_set(key="r", val=result_file.as_posix())

        if simargs:
            om_cmd.args_set(args=simargs)

        ...

        if self._inputs:  # if model has input quantities
            for key, val in self._inputs.items():
                ...

            if "csvInput" not in simargs:  # NEW: csvInput provided by user
                # csvfile is based on name used for result file
                csvfile = result_file.parent / f"{result_file.stem}.csv"
                # write csv file and store the name
                csvfile = self._createCSVData(csvfile=csvfile)
    
                om_cmd.arg_set(key="csvInput", val=csvfile.as_posix())

        return om_cmd

Does this make sense for you @syntron ?

@syntron
Copy link
Contributor Author

syntron commented Mar 9, 2026

Does this make sense for you @syntron ?

Makes sense ...

But what against the following code:

    mod = ModelicaSystemRunner(work_directory='.')
    mod.setup(model_name=modelName)
    mod.setInputsCSV(csvfile=csvfile)
    mod.simulate(resultfile=resfilepathname)

This is the current definition which allows to run code also on docker or WSL. There, the csv file could not be read from the local system but the data has to be handled internally by the ModelicaSystem* class such that is available to the model.

Regarding the code you have, should the check not be on the level of if self._inputs:? Furthermore, a descission is needed which part is preferred - self._input or csvInput? If this is needed, I would go with something like this:

if self._input is not None:
    if "csvInput" in simargs
        logger.warning("...")  # <= add the corresponding message here
    else:  # if else is used, csvInput is preferred; if this line is removed, self._inputs is preferred

However, this code needs checks for the case of docker or WSL ... these will find the file / path only if it is available for them!

@syntron
Copy link
Contributor Author

syntron commented Mar 9, 2026

here are part G/H/I

G => cleanup / all compatibility code moved to v4.0.0 part

H => final cleanup / for v5.0.0 (set as draft PR)

I => additional changes; these could also be moved directly after part G

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants