From c98af69d1b20f3398b3b1ab7ad9ddb9e750749ba Mon Sep 17 00:00:00 2001 From: Jan Breuer Date: Fri, 20 Mar 2026 15:21:54 +0100 Subject: [PATCH 1/3] Add multiple C-API version infrastructure and C-API v1.1.0a.1 --- cadet/cadet_dll.py | 202 +++++++++++++++++++++++++++------------------ 1 file changed, 122 insertions(+), 80 deletions(-) diff --git a/cadet/cadet_dll.py b/cadet/cadet_dll.py index 162e87f..2f84341 100644 --- a/cadet/cadet_dll.py +++ b/cadet/cadet_dll.py @@ -4,6 +4,7 @@ from pathlib import Path from typing import Any, Optional +from typing import Union import addict import numpy @@ -26,7 +27,7 @@ _CDT_DATA_NOT_STORED = -3 -class CADETAPIV010000_DATA: +class CADET_API_V1_SIGNATURES: """ Definition of CADET-C-API v1.0 function signatures and type mappings. @@ -42,65 +43,68 @@ class CADETAPIV010000_DATA: # API function signatures # Note, order is important, it has to match the cdtAPIv010000 struct of the C-API - signatures = {} - - signatures['getFileFormat'] = ('return', 'fileFormat') - - signatures['createDriver'] = ('drv',) - signatures['deleteDriver'] = (None, 'drv') - signatures['runSimulation'] = ('return', 'drv', 'parameterProvider') - - signatures['getNumUnitOp'] = ('return', 'drv', 'nUnits') - signatures['getNumParTypes'] = ('return', 'drv', 'unitOpId', 'nParTypes') - signatures['getNumSensitivities'] = ('return', 'drv', 'nSens') - - signatures['getSolutionInlet'] = ('return', 'drv', 'unitOpId', 'time', 'data', 'nTime', 'nPort', 'nComp') - signatures['getSolutionOutlet'] = ('return', 'drv', 'unitOpId', 'time', 'data', 'nTime', 'nPort', 'nComp') - signatures['getSolutionBulk'] = ('return', 'drv', 'unitOpId', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nComp', 'keepAxialSingletonDimension') - signatures['getSolutionParticle'] = ('return', 'drv', 'unitOpId', 'parType', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParShells', 'nComp', 'keepAxialSingletonDimension', 'keepParticleSingletonDimension') - signatures['getSolutionSolid'] = ('return', 'drv', 'unitOpId', 'parType', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParShells', 'nBound', 'keepAxialSingletonDimension', 'keepParticleSingletonDimension') - signatures['getSolutionFlux'] = ('return', 'drv', 'unitOpId', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParTypes', 'nComp', 'keepAxialSingletonDimension') - signatures['getSolutionVolume'] = ('return', 'drv', 'unitOpId', 'time', 'data', 'nTime') - - signatures['getSolutionDerivativeInlet'] = ('return', 'drv', 'unitOpId', 'time', 'data', 'nTime', 'nPort', 'nComp') - signatures['getSolutionDerivativeOutlet'] = ('return', 'drv', 'unitOpId', 'time', 'data', 'nTime', 'nPort', 'nComp') - signatures['getSolutionDerivativeBulk'] = ('return', 'drv', 'unitOpId', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nComp', 'keepAxialSingletonDimension') - signatures['getSolutionDerivativeParticle'] = ('return', 'drv', 'unitOpId', 'parType', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParShells', 'nComp', 'keepAxialSingletonDimension', 'keepParticleSingletonDimension') - signatures['getSolutionDerivativeSolid'] = ('return', 'drv', 'unitOpId', 'parType', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParShells', 'nBound', 'keepAxialSingletonDimension', 'keepParticleSingletonDimension') - signatures['getSolutionDerivativeFlux'] = ('return', 'drv', 'unitOpId', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParTypes', 'nComp', 'keepAxialSingletonDimension') - signatures['getSolutionDerivativeVolume'] = ('return', 'drv', 'unitOpId', 'time', 'data', 'nTime') - - signatures['getSensitivityInlet'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'time', 'data', 'nTime', 'nPort', 'nComp') - signatures['getSensitivityOutlet'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'time', 'data', 'nTime', 'nPort', 'nComp') - signatures['getSensitivityBulk'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nComp', 'keepAxialSingletonDimension') - signatures['getSensitivityParticle'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'parType', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParShells', 'nComp', 'keepAxialSingletonDimension', 'keepParticleSingletonDimension') - signatures['getSensitivitySolid'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'parType', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParShells', 'nBound', 'keepAxialSingletonDimension', 'keepParticleSingletonDimension') - signatures['getSensitivityFlux'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParTypes', 'nComp', 'keepAxialSingletonDimension') - signatures['getSensitivityVolume'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'time', 'data', 'nTime') - - signatures['getSensitivityDerivativeInlet'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'time', 'data', 'nTime', 'nPort', 'nComp') - signatures['getSensitivityDerivativeOutlet'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'time', 'data', 'nTime', 'nPort', 'nComp') - signatures['getSensitivityDerivativeBulk'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nComp', 'keepAxialSingletonDimension') - signatures['getSensitivityDerivativeParticle'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'parType', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParShells', 'nComp', 'keepAxialSingletonDimension', 'keepParticleSingletonDimension') - signatures['getSensitivityDerivativeSolid'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'parType', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParShells', 'nBound', 'keepAxialSingletonDimension', 'keepParticleSingletonDimension') - signatures['getSensitivityDerivativeFlux'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParTypes', 'nComp', 'keepAxialSingletonDimension') - signatures['getSensitivityDerivativeVolume'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'time', 'data', 'nTime') - - signatures['getLastState'] = ('return', 'drv', 'state', 'nStates') - signatures['getLastStateTimeDerivative'] = ('return', 'drv', 'state', 'nStates') - signatures['getLastUnitState'] = ('return', 'drv', 'unitOpId', 'state', 'nStates') - signatures['getLastUnitStateTimeDerivative'] = ('return', 'drv', 'unitOpId', 'state', 'nStates') - signatures['getLastSensitivityState'] = ('return', 'drv', 'sensIdx', 'state', 'nStates') - signatures['getLastSensitivityStateTimeDerivative'] = ('return', 'drv', 'sensIdx', 'state', 'nStates') - signatures['getLastSensitivityUnitState'] = ('return', 'drv', 'sensIdx', 'unitOpId', 'state', 'nStates') - signatures['getLastSensitivityUnitStateTimeDerivative'] = ('return', 'drv', 'sensIdx', 'unitOpId', 'state', 'nStates') - - signatures['getPrimaryCoordinates'] = ('return', 'drv', 'unitOpId', 'coords', 'nCoords') - signatures['getSecondaryCoordinates'] = ('return', 'drv', 'unitOpId', 'coords', 'nCoords') - signatures['getParticleCoordinates'] = ('return', 'drv', 'unitOpId', 'parType', 'coords', 'nCoords') - signatures['getSolutionTimes'] = ('return', 'drv', 'time', 'nTime') - - signatures['getTimeSim'] = ('return', 'drv', 'timeSim') + signatures_1_0_0 = {} + + signatures_1_0_0['getFileFormat'] = ('return', 'fileFormat') + + signatures_1_0_0['createDriver'] = ('drv',) + signatures_1_0_0['deleteDriver'] = (None, 'drv') + signatures_1_0_0['runSimulation'] = ('return', 'drv', 'parameterProvider') + + signatures_1_0_0['getNumUnitOp'] = ('return', 'drv', 'nUnits') + signatures_1_0_0['getNumParTypes'] = ('return', 'drv', 'unitOpId', 'nParTypes') + signatures_1_0_0['getNumSensitivities'] = ('return', 'drv', 'nSens') + + signatures_1_0_0['getSolutionInlet'] = ('return', 'drv', 'unitOpId', 'time', 'data', 'nTime', 'nPort', 'nComp') + signatures_1_0_0['getSolutionOutlet'] = ('return', 'drv', 'unitOpId', 'time', 'data', 'nTime', 'nPort', 'nComp') + signatures_1_0_0['getSolutionBulk'] = ('return', 'drv', 'unitOpId', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nComp', 'keepAxialSingletonDimension') + signatures_1_0_0['getSolutionParticle'] = ('return', 'drv', 'unitOpId', 'parType', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParShells', 'nComp', 'keepAxialSingletonDimension', 'keepParticleSingletonDimension') + signatures_1_0_0['getSolutionSolid'] = ('return', 'drv', 'unitOpId', 'parType', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParShells', 'nBound', 'keepAxialSingletonDimension', 'keepParticleSingletonDimension') + signatures_1_0_0['getSolutionFlux'] = ('return', 'drv', 'unitOpId', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParTypes', 'nComp', 'keepAxialSingletonDimension') + signatures_1_0_0['getSolutionVolume'] = ('return', 'drv', 'unitOpId', 'time', 'data', 'nTime') + + signatures_1_0_0['getSolutionDerivativeInlet'] = ('return', 'drv', 'unitOpId', 'time', 'data', 'nTime', 'nPort', 'nComp') + signatures_1_0_0['getSolutionDerivativeOutlet'] = ('return', 'drv', 'unitOpId', 'time', 'data', 'nTime', 'nPort', 'nComp') + signatures_1_0_0['getSolutionDerivativeBulk'] = ('return', 'drv', 'unitOpId', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nComp', 'keepAxialSingletonDimension') + signatures_1_0_0['getSolutionDerivativeParticle'] = ('return', 'drv', 'unitOpId', 'parType', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParShells', 'nComp', 'keepAxialSingletonDimension', 'keepParticleSingletonDimension') + signatures_1_0_0['getSolutionDerivativeSolid'] = ('return', 'drv', 'unitOpId', 'parType', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParShells', 'nBound', 'keepAxialSingletonDimension', 'keepParticleSingletonDimension') + signatures_1_0_0['getSolutionDerivativeFlux'] = ('return', 'drv', 'unitOpId', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParTypes', 'nComp', 'keepAxialSingletonDimension') + signatures_1_0_0['getSolutionDerivativeVolume'] = ('return', 'drv', 'unitOpId', 'time', 'data', 'nTime') + + signatures_1_0_0['getSensitivityInlet'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'time', 'data', 'nTime', 'nPort', 'nComp') + signatures_1_0_0['getSensitivityOutlet'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'time', 'data', 'nTime', 'nPort', 'nComp') + signatures_1_0_0['getSensitivityBulk'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nComp', 'keepAxialSingletonDimension') + signatures_1_0_0['getSensitivityParticle'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'parType', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParShells', 'nComp', 'keepAxialSingletonDimension', 'keepParticleSingletonDimension') + signatures_1_0_0['getSensitivitySolid'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'parType', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParShells', 'nBound', 'keepAxialSingletonDimension', 'keepParticleSingletonDimension') + signatures_1_0_0['getSensitivityFlux'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParTypes', 'nComp', 'keepAxialSingletonDimension') + signatures_1_0_0['getSensitivityVolume'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'time', 'data', 'nTime') + + signatures_1_0_0['getSensitivityDerivativeInlet'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'time', 'data', 'nTime', 'nPort', 'nComp') + signatures_1_0_0['getSensitivityDerivativeOutlet'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'time', 'data', 'nTime', 'nPort', 'nComp') + signatures_1_0_0['getSensitivityDerivativeBulk'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nComp', 'keepAxialSingletonDimension') + signatures_1_0_0['getSensitivityDerivativeParticle'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'parType', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParShells', 'nComp', 'keepAxialSingletonDimension', 'keepParticleSingletonDimension') + signatures_1_0_0['getSensitivityDerivativeSolid'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'parType', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParShells', 'nBound', 'keepAxialSingletonDimension', 'keepParticleSingletonDimension') + signatures_1_0_0['getSensitivityDerivativeFlux'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'time', 'data', 'nTime', 'nAxialCells', 'nRadialCells', 'nParTypes', 'nComp', 'keepAxialSingletonDimension') + signatures_1_0_0['getSensitivityDerivativeVolume'] = ('return', 'drv', 'unitOpId', 'sensIdx', 'time', 'data', 'nTime') + + signatures_1_0_0['getLastState'] = ('return', 'drv', 'state', 'nStates') + signatures_1_0_0['getLastStateTimeDerivative'] = ('return', 'drv', 'state', 'nStates') + signatures_1_0_0['getLastUnitState'] = ('return', 'drv', 'unitOpId', 'state', 'nStates') + signatures_1_0_0['getLastUnitStateTimeDerivative'] = ('return', 'drv', 'unitOpId', 'state', 'nStates') + signatures_1_0_0['getLastSensitivityState'] = ('return', 'drv', 'sensIdx', 'state', 'nStates') + signatures_1_0_0['getLastSensitivityStateTimeDerivative'] = ('return', 'drv', 'sensIdx', 'state', 'nStates') + signatures_1_0_0['getLastSensitivityUnitState'] = ('return', 'drv', 'sensIdx', 'unitOpId', 'state', 'nStates') + signatures_1_0_0['getLastSensitivityUnitStateTimeDerivative'] = ('return', 'drv', 'sensIdx', 'unitOpId', 'state', 'nStates') + + signatures_1_0_0['getPrimaryCoordinates'] = ('return', 'drv', 'unitOpId', 'coords', 'nCoords') + signatures_1_0_0['getSecondaryCoordinates'] = ('return', 'drv', 'unitOpId', 'coords', 'nCoords') + signatures_1_0_0['getParticleCoordinates'] = ('return', 'drv', 'unitOpId', 'parType', 'coords', 'nCoords') + signatures_1_0_0['getSolutionTimes'] = ('return', 'drv', 'time', 'nTime') + + signatures_1_0_0['getTimeSim'] = ('return', 'drv', 'timeSim') + + signatures_1_1_0a_1 = {} + signatures_1_1_0a_1['timeout'] = ('return', 'drv', 'timeout') # Mappings for common ctypes parameters lookup_prototype = { @@ -131,6 +135,7 @@ class CADETAPIV010000_DATA: 'keepAxialSingletonDimension': point_bool, 'keepParticleSingletonDimension': point_bool, 'timeSim': point_double, + 'timeout': point_double, } lookup_output_argument_type = { @@ -154,31 +159,57 @@ class CADETAPIV010000_DATA: 'keepAxialSingletonDimension': ctypes.c_bool, 'keepParticleSingletonDimension': ctypes.c_bool, 'timeSim': ctypes.c_double, + 'timeout': ctypes.c_double, } - -def _setup_api() -> list[tuple[str, ctypes.CFUNCTYPE]]: +def _get_api_signatures(api: Any) -> dict[str, tuple[str, ...]]: + if isinstance(api, CADETAPI_V1_1_0a_1): + sigs = dict(CADET_API_V1_SIGNATURES.signatures_1_0_0) + sigs.update(CADET_API_V1_SIGNATURES.signatures_1_1_0a_1) + return sigs + elif isinstance(api, CADETAPI_V1_0_0): + return CADET_API_V1_SIGNATURES.signatures_1_0_0 + else: + raise TypeError(f"Unsupported API type: {type(api).__name__}") + +def _setup_api(signatures: dict[str, tuple[str, ...]]) -> list[tuple[str, ctypes.CFUNCTYPE]]: """ - Set up the API function prototypes for CADETAPIV010000. - - Returns - ------- - list of tuple - List of function names and corresponding ctypes function prototypes. + Set up the API function prototypes for a given CADET API signature table. """ - _fields_ = [] - for key, value in CADETAPIV010000_DATA.signatures.items(): - args = tuple(CADETAPIV010000_DATA.lookup_prototype[key] for key in value) - _fields_.append((key, ctypes.CFUNCTYPE(*args))) + fields = [] + + for name, value in signatures.items(): + args = tuple(CADET_API_V1_SIGNATURES.lookup_prototype[arg_name] for arg_name in value) + fields.append((name, ctypes.CFUNCTYPE(*args))) + + return fields + +SIGNATURES_V1_0_0 = dict(CADET_API_V1_SIGNATURES.signatures_1_0_0) + +SIGNATURES_V1_1_0A_1 = dict(CADET_API_V1_SIGNATURES.signatures_1_0_0) +SIGNATURES_V1_1_0A_1.update(CADET_API_V1_SIGNATURES.signatures_1_1_0a_1) - return _fields_ +class CADETAPI_V1_0_0(ctypes.Structure): + """Mimic cdtAPIv1.0.0 struct of CADET C-API in ctypes.""" + _signatures_ = SIGNATURES_V1_0_0 + _fields_ = _setup_api(_signatures_) -class CADETAPIV010000(ctypes.Structure): - """Mimic cdtAPIv010000 struct of CADET C-API in ctypes.""" - _fields_ = _setup_api() +CADETAPIV010000 = CADETAPI_V1_0_0 + +class CADETAPI_V1_1_0a_1(ctypes.Structure): + """Mimic cdtAPIv1.1.0a.1 struct of CADET C-API in ctypes.""" + _signatures_ = SIGNATURES_V1_1_0A_1 + _fields_ = _setup_api(_signatures_) + + +class CADETAPI_V1_0_0(ctypes.Structure): + """Mimic cdtAPIv1.0.0 struct of CADET C-API in ctypes.""" + _fields_ = _setup_api("1.0.0") + + class SimulationResult: """ Handles reading results from a CADET simulation. @@ -192,7 +223,10 @@ class SimulationResult: """ - def __init__(self, api: CADETAPIV010000, driver: CadetDriver) -> None: + def __init__( + self, + api: Union[CADETAPI_V1_0_0, CADETAPI_V1_1_0a_1], driver: CadetDriver + ) -> None: self._api = api self._driver = driver @@ -228,21 +262,29 @@ def _load_data( call_args = [] call_outputs = {} + signatures = _get_api_signatures(self._api) + # Construct API call function arguments - for key in CADETAPIV010000_DATA.signatures[get_solution_str]: + for key in signatures[get_solution_str]: if key == 'return': # Skip, this is the actual return value of the API function continue elif key == 'drv': call_args.append(self._driver) - elif key == 'unitOpId' and unitOpId is not None: + elif key == 'unitOpId': + if unitOpId is None: + raise ValueError(f"{get_solution_str} requires unitOpId") call_args.append(unitOpId) elif key == 'sensIdx': + if sensIdx is None: + raise ValueError(f"{get_solution_str} requires sensIdx") call_args.append(sensIdx) elif key == 'parType': + if parType is None: + raise ValueError(f"{get_solution_str} requires parType") call_args.append(parType) else: - _obj = CADETAPIV010000_DATA.lookup_output_argument_type[key]() + _obj = CADET_API_V1_SIGNATURES.lookup_output_argument_type[key]() call_outputs[key] = _obj call_args.append(ctypes.byref(_obj)) From a71a6948b812e7d4761406a8b0a26af982c4edb0 Mon Sep 17 00:00:00 2001 From: AntoniaBerger Date: Mon, 30 Mar 2026 17:09:21 +0200 Subject: [PATCH 2/3] Add VERSION_SIGNATURES and update init --- cadet/cadet_dll.py | 66 ++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/cadet/cadet_dll.py b/cadet/cadet_dll.py index 2f84341..86c7d63 100644 --- a/cadet/cadet_dll.py +++ b/cadet/cadet_dll.py @@ -162,20 +162,22 @@ class CADET_API_V1_SIGNATURES: 'timeout': ctypes.c_double, } +_VERSION_SIGNATURES: dict[Version, dict] = {} +_VERSION_SIGNATURES[Version("1.0.0")] = dict(CADET_API_V1_SIGNATURES.signatures_1_0_0) + +_sigs_1_1_0a_1 = dict(CADET_API_V1_SIGNATURES.signatures_1_0_0) +_sigs_1_1_0a_1.update(CADET_API_V1_SIGNATURES.signatures_1_1_0a_1) +_VERSION_SIGNATURES[Version("1.1.0a1")] = _sigs_1_1_0a_1 + def _get_api_signatures(api: Any) -> dict[str, tuple[str, ...]]: - if isinstance(api, CADETAPI_V1_1_0a_1): - sigs = dict(CADET_API_V1_SIGNATURES.signatures_1_0_0) - sigs.update(CADET_API_V1_SIGNATURES.signatures_1_1_0a_1) - return sigs - elif isinstance(api, CADETAPI_V1_0_0): - return CADET_API_V1_SIGNATURES.signatures_1_0_0 - else: - raise TypeError(f"Unsupported API type: {type(api).__name__}") + return _VERSION_SIGNATURES[api._version] -def _setup_api(signatures: dict[str, tuple[str, ...]]) -> list[tuple[str, ctypes.CFUNCTYPE]]: +def _setup_api(version: Version) -> list[tuple[str, ctypes.CFUNCTYPE]]: """ Set up the API function prototypes for a given CADET API signature table. """ + + signatures = _VERSION_SIGNATURES[version] fields = [] for name, value in signatures.items(): @@ -184,30 +186,19 @@ def _setup_api(signatures: dict[str, tuple[str, ...]]) -> list[tuple[str, ctypes return fields -SIGNATURES_V1_0_0 = dict(CADET_API_V1_SIGNATURES.signatures_1_0_0) - -SIGNATURES_V1_1_0A_1 = dict(CADET_API_V1_SIGNATURES.signatures_1_0_0) -SIGNATURES_V1_1_0A_1.update(CADET_API_V1_SIGNATURES.signatures_1_1_0a_1) - class CADETAPI_V1_0_0(ctypes.Structure): """Mimic cdtAPIv1.0.0 struct of CADET C-API in ctypes.""" - _signatures_ = SIGNATURES_V1_0_0 - _fields_ = _setup_api(_signatures_) - + _version = Version("1.0.0") + _fields_ = _setup_api(_version) CADETAPIV010000 = CADETAPI_V1_0_0 class CADETAPI_V1_1_0a_1(ctypes.Structure): """Mimic cdtAPIv1.1.0a.1 struct of CADET C-API in ctypes.""" - _signatures_ = SIGNATURES_V1_1_0A_1 - _fields_ = _setup_api(_signatures_) - - -class CADETAPI_V1_0_0(ctypes.Structure): - """Mimic cdtAPIv1.0.0 struct of CADET C-API in ctypes.""" - _fields_ = _setup_api("1.0.0") + _version = Version("1.1.0a1") + _fields_ = _setup_api(_version) class SimulationResult: @@ -1714,23 +1705,35 @@ def _initialize_dll(self): # Query API try: - cdtGetLatestCAPIVersion = self._lib.cdtGetLatestCAPIVersion# + cdtGetLatestCAPIVersion = self._lib.cdtGetLatestCAPIVersion except AttributeError: raise ValueError( "CADET-Python does not support CADET-CAPI at all." ) cdtGetLatestCAPIVersion.restype = ctypes.c_char_p - self._cadet_capi_version = cdtGetLatestCAPIVersion().decode('utf-8') + self._cadet_capi_version = Version(cdtGetLatestCAPIVersion().decode('utf-8')) # Check which C-API is provided by CADET (given the current install path) - if self._cadet_capi_version == "1.0.0": - cdtGetAPIv010000 = self._lib.cdtGetAPIv010000 + #current supported versions are: 1.0.0 and 1.1.0a1 + #TODO: write in developer guide to update LATEST_CAPI_VERSION in VersionInfo.cpp.in (Cadet-Core) + if self._cadet_capi_version == Version("1.1.0a1"): + cdtGetAPIv1_1_0a_1 = self._lib.cdtGetAPIv1_1_0a1 + cdtGetAPIv1_1_0a_1.argtypes = [ctypes.POINTER(CADETAPI_V1_1_0a_1)] + cdtGetAPIv1_1_0a_1.restype = c_cadet_result + self._api = CADETAPI_V1_1_0a_1() + cdtGetAPIv1_1_0a_1(ctypes.byref(self._api)) + elif self._cadet_capi_version < Version("1.1.0a1"): + cdtGetAPIv010000 = self._lib.cdtGetAPIv1_0_0 cdtGetAPIv010000.argtypes = [ctypes.POINTER(CADETAPIV010000)] cdtGetAPIv010000.restype = c_cadet_result self._api = CADETAPIV010000() cdtGetAPIv010000(ctypes.byref(self._api)) + #TODO if version 1.1.0 is realeased + #raise Warning ( + # "A newer C-API version is available you are using the fallback version 1.0.0" + #) else: - raise ValueError( + raise TypeError( "CADET-Python does not support CADET-CAPI version " f"({self._cadet_capi_version})." ) @@ -2017,11 +2020,6 @@ def load_state(self, sim: "Cadet") -> None: soldot_last_unit = self.res.last_state_ydot_unit(unit) solution[unit_index]['last_state_ydot'] = soldot_last_unit - @staticmethod - def _get_index_string(prefix: str, index: int) -> str: - """Get a formatted string index (e.g., ('unit', 0) -> 'unit_000').""" - return f'{prefix}_{index:03d}' - def _checks_if_write_is_true(func): """Decorator to check if unit operation solution should be written out.""" def wrapper(self, sim, unitOpId, solution_str, *args, **kwargs): From 59130ee0f1b19508f42f69f20738d39bc4657cb2 Mon Sep 17 00:00:00 2001 From: AntoniaBerger Date: Mon, 30 Mar 2026 17:10:08 +0200 Subject: [PATCH 3/3] Add time out warnings and change type of timeout from int to float --- cadet/cadet.py | 8 ++++---- cadet/cadet_dll.py | 12 +++++++++--- cadet/runner.py | 7 ++++--- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/cadet/cadet.py b/cadet/cadet.py index 5a63636..e814198 100644 --- a/cadet/cadet.py +++ b/cadet/cadet.py @@ -518,7 +518,7 @@ def run_load( def run_simulation( self, - timeout: Optional[int] = None, + timeout: Optional[float] = None, clear: bool = True ) -> ReturnInformation: """ @@ -526,7 +526,7 @@ def run_simulation( Parameters ---------- - timeout : Optional[int] + timeout : Optional[float] Maximum time allowed for the simulation to run, in seconds. clear : bool If True, clear the simulation results from the current runner instance. @@ -551,14 +551,14 @@ def run_simulation( def run( self, - timeout: Optional[int] = None, + timeout: Optional[float] = None, ) -> ReturnInformation: """ Run the CADET simulation. Parameters ---------- - timeout : Optional[int] + timeout : Optional[float] Maximum time allowed for the simulation to run, in seconds. Returns diff --git a/cadet/cadet_dll.py b/cadet/cadet_dll.py index 86c7d63..aad359e 100644 --- a/cadet/cadet_dll.py +++ b/cadet/cadet_dll.py @@ -2,9 +2,9 @@ import io import os from pathlib import Path -from typing import Any, Optional +from typing import Any, Optional, Union -from typing import Union +from packaging.version import Version import addict import numpy @@ -1811,7 +1811,7 @@ def log_handler(file, func, line, level, level_name, message): def run( self, simulation: Optional["Cadet"] = None, - timeout: Optional[int] = None, + timeout: Optional[float] = None, ) -> ReturnInformation: """ Run a CADET simulation using the DLL interface. @@ -1828,6 +1828,12 @@ def run( RuntimeError If the simulation process returns a non-zero exit code. """ + if timeout is not None: + if(self._cadet_capi_version < Version("1.1.0a1")): + raise TypeError( + "timeout is not support CADET-CAPI version: " + f"({self._cadet_capi_version})." + ) pp = cadet_dll_parameterprovider.PARAMETERPROVIDER(simulation) log_buffer = self.setup_log_buffer() diff --git a/cadet/runner.py b/cadet/runner.py index 602e681..ec0afa6 100644 --- a/cadet/runner.py +++ b/cadet/runner.py @@ -6,6 +6,7 @@ from dataclasses import dataclass from pathlib import Path from typing import Optional +from packaging.version import Version @dataclass @@ -38,7 +39,7 @@ class CadetRunnerBase(ABC): def run( self, simulation: "Cadet", - timeout: Optional[int] = None, + timeout: Optional[float] = None, ) -> ReturnInformation: """ Run a CADET simulation. @@ -126,7 +127,7 @@ def __init__(self, cadet_path: str | os.PathLike) -> None: def run( self, simulation: "Cadet", - timeout: Optional[int] = None, + timeout: Optional[float] = None, ) -> ReturnInformation: """ Run a CADET simulation using the CLI executable. @@ -135,7 +136,7 @@ def run( ---------- simulation : Cadet Not used in this runner. - timeout : Optional[int] + timeout : Optional[float] Maximum time allowed for the simulation to run, in seconds. Raises