From 4dd058044bfff3f19534d9c3d9114dc5025f1a44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Schm=C3=B6lder?= Date: Sat, 7 Dec 2024 09:35:06 +0100 Subject: [PATCH 1/8] Handle str type in ParameterProvider --- cadet/cadet_dll_utils.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cadet/cadet_dll_utils.py b/cadet/cadet_dll_utils.py index daa1933..204b52f 100644 --- a/cadet/cadet_dll_utils.py +++ b/cadet/cadet_dll_utils.py @@ -413,8 +413,15 @@ def param_provider_get_string_array_item( str_value = current_reader[name_str] if isinstance(str_value, bytes): bytes_val = str_value - else: + elif isinstance(str_value, str): + bytes_val = str_value.encode('utf-8') + elif isinstance(str_value, np.ndarray): bytes_val = str_value[index] + else: + raise TypeError( + "Unexpected type for str_value. " + "Must be of type bytes, str, or np.ndarray." + ) reader.buffer = bytes_val val[0] = ctypes.cast(reader.buffer, ctypes.c_char_p) From 9222d23a3e0da0cbe4c30db4f71bee96b6cdbd60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Schm=C3=B6lder?= Date: Fri, 6 Dec 2024 14:56:15 +0100 Subject: [PATCH 2/8] Fix inconsistencies in C-API - Added tests for coordinates - Added more combinations for splitting components / ports in SolutionRecorder - Added ids for test cases - Keep order of solution keys consistent across tests --- cadet/cadet_dll.py | 23 +- tests/test_dll.py | 841 ++++++++++++++++++++++++++++++++------------- 2 files changed, 623 insertions(+), 241 deletions(-) diff --git a/cadet/cadet_dll.py b/cadet/cadet_dll.py index 12f3561..3730c75 100644 --- a/cadet/cadet_dll.py +++ b/cadet/cadet_dll.py @@ -2037,51 +2037,62 @@ def _load_solution_io( solution = addict.Dict() _, out, dims = data + # Retrieve configuration options split_components_data = sim.root.input['return'].get('split_components_data', 1) split_ports_data = sim.root.input['return'].get('split_ports_data', 1) single_as_multi_port = sim.root.input['return'].get('single_as_multi_port', 0) + # Identify dimension indices and sizes nComp_idx = dims.index('nComp') nComp = out.shape[nComp_idx] - try: - nPort_idx = dims.index('nPort') - nPort = out.shape[nPort_idx] - except ValueError: - nPort_idx = None - nPort = 1 + nPort_idx = dims.index('nPort') if 'nPort' in dims else None + nPort = out.shape[nPort_idx] if nPort_idx is not None else 1 + # Process data based on the split settings if split_components_data: if split_ports_data: + # Case: Split both components and ports if nPort == 1: if single_as_multi_port: + # Treat single port as multiple ports for comp in range(nComp): comp_out = out[..., 0, comp] solution[f'{solution_str}_port_000_comp_{comp:03d}'] = comp_out else: + # Default case for single port for comp in range(nComp): comp_out = out[..., comp] solution[f'{solution_str}_comp_{comp:03d}'] = comp_out else: + # Multi-port case for port in range(nPort): for comp in range(nComp): comp_out = out[..., port, comp] solution[f'{solution_str}_port_{port:03d}_comp_{comp:03d}'] = comp_out else: + # Case: Split components only for comp in range(nComp): comp_out = out[..., comp] + if nPort == 1 and nPort_idx is not None: + comp_out = comp_out[:, 0] # Single-port adjustment solution[f'{solution_str}_comp_{comp:03d}'] = comp_out else: if split_ports_data: + # Case: Split ports only if nPort == 1: if single_as_multi_port: + if nPort_idx is not None: + out = out[:, 0] # Adjust for single-port case solution[f'{solution_str}_port_000'] = out else: solution[solution_str] = out[..., 0, :] else: + # Multi-port case for port in range(nPort): port_out = out[..., port, :] solution[f'{solution_str}_port_{port:03d}'] = port_out else: + # Default case: No splitting if nPort == 1 and nPort_idx is not None: solution[solution_str] = out[..., 0, :] else: diff --git a/tests/test_dll.py b/tests/test_dll.py index 4bfd13b..e222a30 100644 --- a/tests/test_dll.py +++ b/tests/test_dll.py @@ -120,27 +120,54 @@ def setup_model( if n_components < 4: unit_000 = cadet_model.root.input.model.unit_000 unit_000.update({ - 'adsorption_model': 'LINEAR', 'col_dispersion': 5.75e-08, - 'col_length': 0.014, 'col_porosity': 0.37, + 'adsorption_model': 'LINEAR', + 'col_dispersion': 5.75e-08, + 'col_length': 0.014, + 'col_porosity': 0.37, 'cross_section_area': 0.0003141592653589793, 'film_diffusion': [6.9e-06, ] * n_components, - 'film_diffusion_multiplex': 0, 'init_c': [0., ] * n_components, - 'init_q': [0., ] * n_components, 'nbound': [1, ] * n_components, - 'ncomp': 1, 'npartype': 1, 'par_coreradius': 0.0, - 'par_diffusion': [7.00e-10, ] * n_components, 'par_geom': 'SPHERE', - 'par_porosity': 0.75, 'par_radius': 4.5e-05, - 'par_surfdiffusion': [0., ] * n_components, 'unit_type': 'GENERAL_RATE_MODEL', + 'film_diffusion_multiplex': 0, + 'init_c': [0., ] * n_components, + 'init_q': [0., ] * n_components, + 'nbound': [1, ] * n_components, + 'ncomp': 1, + 'npartype': 1, + 'par_coreradius': 0.0, + 'par_diffusion': [7.00e-10, ] * n_components, + 'par_geom': 'SPHERE', + 'par_porosity': 0.75, + 'par_radius': 4.5e-05, + 'par_surfdiffusion': [0., ] * n_components, + 'unit_type': 'GENERAL_RATE_MODEL', 'velocity': 1.0, - 'adsorption': {'is_kinetic': 0, 'lin_ka': [0.] * n_components, 'lin_kd': [1.] * n_components}, + 'adsorption': { + 'is_kinetic': 0, + 'lin_ka': [0.] * n_components, + 'lin_kd': [1.] * n_components + }, }) - cadet_model.root.input.model.unit_001.update( - {'inlet_type': b'PIECEWISE_CUBIC_POLY', 'ncomp': 1, 'unit_type': b'INLET', - 'sec_000': {'const_coeff': [50., ], 'cube_coeff': [0., ], - 'lin_coeff': [0., ], 'quad_coeff': [0., ]}, - 'sec_001': {'const_coeff': [50., ], 'cube_coeff': [0., ], - 'lin_coeff': [0., ], 'quad_coeff': [0., ]}, - 'sec_002': {'const_coeff': [100., ], 'cube_coeff': [0., ], - 'lin_coeff': [0.2], 'quad_coeff': [0., ]}} + cadet_model.root.input.model.unit_001.update({ + 'inlet_type': b'PIECEWISE_CUBIC_POLY', + 'ncomp': 1, 'unit_type': b'INLET', + 'sec_000': { + 'const_coeff': [50., ], + 'cube_coeff': [0., ], + 'lin_coeff': [0., ], + 'quad_coeff': [0., ] + }, + 'sec_001': { + 'const_coeff': [50., ], + 'cube_coeff': [0., ], + 'lin_coeff': [0., ], + 'quad_coeff': [0., ] + }, + 'sec_002': { + 'const_coeff': [100., ], + 'cube_coeff': [0.2, ], + 'lin_coeff': [0., ], + 'quad_coeff': [0., ] + } + } ) # if we don't save and re-load the model we get windows access violations. # Interesting case for future tests, not what I want to test now. @@ -351,24 +378,37 @@ def run_simulation_with_options(use_dll, model_options, solution_recorder_option # %% Solution recorder templates -no_split_options = { +no_split = { 'split_components': 0, 'split_ports': 0, 'single_as_multi_port': 0, } -split_ports_options = { +split_components = { + 'split_components': 1, + 'split_ports': 0, + 'single_as_multi_port': 0, +} + +split_ports = { 'split_components': 0, 'split_ports': 1, 'single_as_multi_port': 0, } -split_all_options = { +split_ports_single_as_multi = { + 'split_components': 0, + 'split_ports': 1, + 'single_as_multi_port': 1, +} + +split_all = { 'split_components': 1, 'split_ports': 1, 'single_as_multi_port': 1, } + # %% Test cases class Case(): @@ -386,230 +426,368 @@ def __repr__(self): f"Case('{self.name}', {self.model_options}, " \ f"{self.solution_recorder_options}, {self.expected_results})" + # %% CSTR cstr = Case( name='cstr', model_options=cstr_template, - solution_recorder_options=no_split_options, + solution_recorder_options=no_split, expected_results={ + 'solution_times': (1501,), 'last_state_y': (21,), 'last_state_ydot': (21,), 'coordinates_unit_000': {}, - 'solution_times': (1501,), + 'coordinates_unit_001': {}, 'solution_unit_000': { + 'last_state_y': (13,), + 'last_state_ydot': (13,), + 'solution_inlet': (1501, 4), 'solution_outlet': (1501, 4), 'solution_bulk': (1501, 4), 'solution_solid': (1501, 4), 'solution_volume': (1501,), + 'soldot_inlet': (1501, 4), 'soldot_outlet': (1501, 4), 'soldot_bulk': (1501, 4), 'soldot_solid': (1501, 4), - 'soldot_volume': (1501, ), - 'last_state_y': (13,), - 'last_state_ydot': (13,), + 'soldot_volume': (1501,), }, 'solution_unit_001': { + 'last_state_y': (4,), + 'last_state_ydot': (4,), + 'solution_inlet': (1501, 4), 'solution_outlet': (1501, 4), + 'soldot_inlet': (1501, 4), 'soldot_outlet': (1501, 4), - 'last_state_y': (4,), - 'last_state_ydot': (4,), }, }, ) + # %% LRM lrm = Case( name='lrm', model_options=lrm_template, - solution_recorder_options=no_split_options, + solution_recorder_options=no_split, expected_results={ + 'solution_times': (1501,), 'last_state_y': (92,), 'last_state_ydot': (92,), 'coordinates_unit_000': { 'axial_coordinates': (10,), }, - 'solution_times': (1501,), + 'coordinates_unit_001': {}, 'solution_unit_000': { + 'last_state_y': (84,), + 'last_state_ydot': (84,), + 'solution_inlet': (1501, 4), 'solution_outlet': (1501, 4), 'solution_bulk': (1501, 10, 4), 'solution_solid': (1501, 10, 4), + 'soldot_inlet': (1501, 4), 'soldot_outlet': (1501, 4), 'soldot_bulk': (1501, 10, 4), 'soldot_solid': (1501, 10, 4), - 'last_state_y': (84,), - 'last_state_ydot': (84,), }, 'solution_unit_001': { + 'last_state_y': (4,), + 'last_state_ydot': (4,), + 'solution_inlet': (1501, 4), 'solution_outlet': (1501, 4), + 'soldot_inlet': (1501, 4), 'soldot_outlet': (1501, 4), - 'last_state_y': (4,), - 'last_state_ydot': (4,), + }, }, ) + # %% LRMP lrmp = Case( name='lrmp', model_options=lrmp_template, - solution_recorder_options=no_split_options, + solution_recorder_options=no_split, expected_results={ + 'solution_times': (1501,), 'last_state_y': (172,), 'last_state_ydot': (172,), 'coordinates_unit_000': { 'axial_coordinates': (10,), }, - 'solution_times': (1501,), + 'coordinates_unit_001': {}, 'solution_unit_000': { 'last_state_y': (164,), 'last_state_ydot': (164,), - 'soldot_bulk': (1501, 10, 4), - 'soldot_flux': (1501, 1, 10, 4), - 'soldot_inlet': (1501, 4), - 'soldot_outlet': (1501, 4), - 'soldot_particle': (1501, 10, 4), - 'soldot_solid': (1501, 10, 4), - 'solution_bulk': (1501, 10, 4), - 'solution_flux': (1501, 1, 10, 4), + 'solution_inlet': (1501, 4), 'solution_outlet': (1501, 4), + 'solution_bulk': (1501, 10, 4), 'solution_particle': (1501, 10, 4), 'solution_solid': (1501, 10, 4), + 'solution_flux': (1501, 1, 10, 4), + + 'soldot_inlet': (1501, 4), + 'soldot_outlet': (1501, 4), + 'soldot_bulk': (1501, 10, 4), + 'soldot_particle': (1501, 10, 4), + 'soldot_solid': (1501, 10, 4), + 'soldot_flux': (1501, 1, 10, 4), }, 'solution_unit_001': { 'last_state_y': (4,), 'last_state_ydot': (4,), - 'soldot_inlet': (1501, 4), - 'soldot_outlet': (1501, 4), + 'solution_inlet': (1501, 4), 'solution_outlet': (1501, 4), + + 'soldot_inlet': (1501, 4), + 'soldot_outlet': (1501, 4), }, }, ) -# %% GRM + +# %% GRM (no_split) grm = Case( name='grm', model_options=grm_template, - solution_recorder_options=no_split_options, + solution_recorder_options=no_split, expected_results={ + 'solution_times': (1501,), 'last_state_y': (412,), 'last_state_ydot': (412,), 'coordinates_unit_000': { 'axial_coordinates': (10,), 'particle_coordinates_000': (4,), }, - 'solution_times': (1501,), + 'coordinates_unit_001': {}, 'solution_unit_000': { 'last_state_y': (404,), 'last_state_ydot': (404,), - 'soldot_bulk': (1501, 10, 4), - 'soldot_flux': (1501, 1, 10, 4), + + 'solution_inlet': (1501, 4), + 'solution_outlet': (1501, 4), + 'solution_bulk': (1501, 10, 4), + 'solution_particle': (1501, 10, 4, 4), + 'solution_solid': (1501, 10, 4, 4), + 'solution_flux': (1501, 1, 10, 4), + 'soldot_inlet': (1501, 4), 'soldot_outlet': (1501, 4), + 'soldot_bulk': (1501, 10, 4), 'soldot_particle': (1501, 10, 4, 4), 'soldot_solid': (1501, 10, 4, 4), - 'solution_bulk': (1501, 10, 4), - 'solution_flux': (1501, 1, 10, 4), + 'soldot_flux': (1501, 1, 10, 4), + }, + 'solution_unit_001': { + 'last_state_y': (4,), + 'last_state_ydot': (4,), + 'solution_inlet': (1501, 4), 'solution_outlet': (1501, 4), + + 'soldot_inlet': (1501, 4), + 'soldot_outlet': (1501, 4), + }, + }, +) + + +# %% GRM (split_components) + +grm_split_components = Case( + name='grm_split_components', + model_options=grm_template, + solution_recorder_options=split_components, + expected_results={ + 'solution_times': (1501,), + 'last_state_y': (412,), + 'last_state_ydot': (412,), + 'coordinates_unit_000': { + 'axial_coordinates': (10,), + 'particle_coordinates_000': (4,), + }, + 'coordinates_unit_001': {}, + 'solution_unit_000': { + 'last_state_y': (404,), + 'last_state_ydot': (404,), + + 'solution_inlet_comp_000': (1501,), + 'solution_inlet_comp_001': (1501,), + 'solution_inlet_comp_002': (1501,), + 'solution_inlet_comp_003': (1501,), + 'solution_outlet_comp_000': (1501,), + 'solution_outlet_comp_001': (1501,), + 'solution_outlet_comp_002': (1501,), + 'solution_outlet_comp_003': (1501,), + 'solution_bulk': (1501, 10, 4), 'solution_particle': (1501, 10, 4, 4), 'solution_solid': (1501, 10, 4, 4), + 'solution_flux': (1501, 1, 10, 4), + + 'soldot_inlet_comp_000': (1501,), + 'soldot_inlet_comp_001': (1501,), + 'soldot_inlet_comp_002': (1501,), + 'soldot_inlet_comp_003': (1501,), + 'soldot_outlet_comp_000': (1501,), + 'soldot_outlet_comp_001': (1501,), + 'soldot_outlet_comp_002': (1501,), + 'soldot_outlet_comp_003': (1501,), + 'soldot_bulk': (1501, 10, 4), + 'soldot_particle': (1501, 10, 4, 4), + 'soldot_solid': (1501, 10, 4, 4), + 'soldot_flux': (1501, 1, 10, 4), }, 'solution_unit_001': { 'last_state_y': (4,), 'last_state_ydot': (4,), + + 'solution_inlet_comp_000': (1501,), + 'solution_inlet_comp_001': (1501,), + 'solution_inlet_comp_002': (1501,), + 'solution_inlet_comp_003': (1501,), + 'solution_outlet_comp_000': (1501,), + 'solution_outlet_comp_001': (1501,), + 'solution_outlet_comp_002': (1501,), + 'solution_outlet_comp_003': (1501,), + + 'soldot_inlet_comp_000': (1501,), + 'soldot_inlet_comp_001': (1501,), + 'soldot_inlet_comp_002': (1501,), + 'soldot_inlet_comp_003': (1501,), + 'soldot_outlet_comp_000': (1501,), + 'soldot_outlet_comp_001': (1501,), + 'soldot_outlet_comp_002': (1501,), + 'soldot_outlet_comp_003': (1501,), + }, + }, +) + + +# %% GRM (split_ports) + +grm_split_ports = Case( + name='grm_split_ports', + model_options=grm_template, + solution_recorder_options=split_ports, + expected_results={ + 'solution_times': (1501,), + 'last_state_y': (412,), + 'last_state_ydot': (412,), + 'coordinates_unit_000': { + 'axial_coordinates': (10,), + 'particle_coordinates_000': (4,), + }, + 'coordinates_unit_001': {}, + 'solution_unit_000': { + 'last_state_y': (404,), + 'last_state_ydot': (404,), + + 'solution_inlet': (1501, 4), + 'solution_outlet': (1501, 4), + 'solution_bulk': (1501, 10, 4), + 'solution_particle': (1501, 10, 4, 4), + 'solution_solid': (1501, 10, 4, 4), + 'solution_flux': (1501, 1, 10, 4), + 'soldot_inlet': (1501, 4), 'soldot_outlet': (1501, 4), + 'soldot_bulk': (1501, 10, 4), + 'soldot_particle': (1501, 10, 4, 4), + 'soldot_solid': (1501, 10, 4, 4), + 'soldot_flux': (1501, 1, 10, 4), + }, + 'solution_unit_001': { + 'last_state_y': (4,), + 'last_state_ydot': (4,), + 'solution_inlet': (1501, 4), 'solution_outlet': (1501, 4), + + 'soldot_inlet': (1501, 4), + 'soldot_outlet': (1501, 4), }, }, ) -# %% GRM 1 Comp -grm_1_comp = Case( - name='grm_1_comp', - model_options=grm_template_1_comp, - solution_recorder_options=no_split_options, +# %% GRM (split_ports_single_as_multi) + +grm_split_ports_single_as_multi = Case( + name='grm_split_ports_single_as_multi', + model_options=grm_template, + solution_recorder_options=split_ports_single_as_multi, expected_results={ - 'last_state_y': (103,), - 'last_state_ydot': (103,), + 'solution_times': (1501,), + 'last_state_y': (412,), + 'last_state_ydot': (412,), 'coordinates_unit_000': { 'axial_coordinates': (10,), 'particle_coordinates_000': (4,), }, - 'solution_times': (1501,), + 'coordinates_unit_001': {}, 'solution_unit_000': { - 'last_state_y': (101,), - 'last_state_ydot': (101,), - 'soldot_bulk': (1501, 10, 1), - 'soldot_flux': (1501, 1, 10, 1), - 'soldot_inlet': (1501, 1), - 'soldot_outlet': (1501, 1), - 'soldot_particle': (1501, 10, 4, 1), - 'soldot_solid': (1501, 10, 4, 1), - 'solution_bulk': (1501, 10, 1), - 'solution_flux': (1501, 1, 10, 1), - 'solution_inlet': (1501, 1), - 'solution_outlet': (1501, 1), - 'solution_particle': (1501, 10, 4, 1), - 'solution_solid': (1501, 10, 4, 1), + 'last_state_y': (404,), + 'last_state_ydot': (404,), + + 'solution_inlet_port_000': (1501, 4), + 'solution_outlet_port_000': (1501, 4), + 'solution_bulk': (1501, 10, 4), + 'solution_particle': (1501, 10, 4, 4), + 'solution_solid': (1501, 10, 4, 4), + 'solution_flux': (1501, 1, 10, 4), + + 'soldot_inlet_port_000': (1501, 4), + 'soldot_outlet_port_000': (1501, 4), + 'soldot_bulk': (1501, 10, 4), + 'soldot_particle': (1501, 10, 4, 4), + 'soldot_solid': (1501, 10, 4, 4), + 'soldot_flux': (1501, 1, 10, 4), }, 'solution_unit_001': { - 'last_state_y': (1,), - 'last_state_ydot': (1,), - 'soldot_inlet': (1501, 1), - 'soldot_outlet': (1501, 1), - 'solution_inlet': (1501, 1), - 'solution_outlet': (1501, 1), + 'last_state_y': (4,), + 'last_state_ydot': (4,), + + 'solution_inlet_port_000': (1501, 4), + 'solution_outlet_port_000': (1501, 4), + + 'soldot_inlet_port_000': (1501, 4), + 'soldot_outlet_port_000': (1501, 4), }, }, ) -# %% GRM Split -grm_split = Case( - name='grm_split', +# %% GRM (split_all) + +grm_split_all = Case( + name='grm_split_all', model_options=grm_template, - solution_recorder_options=split_all_options, + solution_recorder_options=split_all, expected_results={ + 'solution_times': (1501,), 'last_state_y': (412,), 'last_state_ydot': (412,), 'coordinates_unit_000': { 'axial_coordinates': (10,), 'particle_coordinates_000': (4,), }, - 'solution_times': (1501,), + 'coordinates_unit_001': {}, 'solution_unit_000': { 'last_state_y': (404,), 'last_state_ydot': (404,), - 'soldot_bulk': (1501, 10, 4), - 'soldot_flux': (1501, 1, 10, 4), - 'soldot_inlet_port_000_comp_000': (1501,), - 'soldot_inlet_port_000_comp_001': (1501,), - 'soldot_inlet_port_000_comp_002': (1501,), - 'soldot_inlet_port_000_comp_003': (1501,), - 'soldot_outlet_port_000_comp_000': (1501,), - 'soldot_outlet_port_000_comp_001': (1501,), - 'soldot_outlet_port_000_comp_002': (1501,), - 'soldot_outlet_port_000_comp_003': (1501,), - 'soldot_particle': (1501, 10, 4, 4), - 'soldot_solid': (1501, 10, 4, 4), - 'solution_bulk': (1501, 10, 4), - 'solution_flux': (1501, 1, 10, 4), + 'solution_inlet_port_000_comp_000': (1501,), 'solution_inlet_port_000_comp_001': (1501,), 'solution_inlet_port_000_comp_002': (1501,), @@ -618,12 +796,11 @@ def __repr__(self): 'solution_outlet_port_000_comp_001': (1501,), 'solution_outlet_port_000_comp_002': (1501,), 'solution_outlet_port_000_comp_003': (1501,), + 'solution_bulk': (1501, 10, 4), 'solution_particle': (1501, 10, 4, 4), 'solution_solid': (1501, 10, 4, 4), - }, - 'solution_unit_001': { - 'last_state_y': (4,), - 'last_state_ydot': (4,), + 'solution_flux': (1501, 1, 10, 4), + 'soldot_inlet_port_000_comp_000': (1501,), 'soldot_inlet_port_000_comp_001': (1501,), 'soldot_inlet_port_000_comp_002': (1501,), @@ -632,6 +809,15 @@ def __repr__(self): 'soldot_outlet_port_000_comp_001': (1501,), 'soldot_outlet_port_000_comp_002': (1501,), 'soldot_outlet_port_000_comp_003': (1501,), + 'soldot_bulk': (1501, 10, 4), + 'soldot_particle': (1501, 10, 4, 4), + 'soldot_solid': (1501, 10, 4, 4), + 'soldot_flux': (1501, 1, 10, 4), + }, + 'solution_unit_001': { + 'last_state_y': (4,), + 'last_state_ydot': (4,), + 'solution_inlet_port_000_comp_000': (1501,), 'solution_inlet_port_000_comp_001': (1501,), 'solution_inlet_port_000_comp_002': (1501,), @@ -640,67 +826,130 @@ def __repr__(self): 'solution_outlet_port_000_comp_001': (1501,), 'solution_outlet_port_000_comp_002': (1501,), 'solution_outlet_port_000_comp_003': (1501,), + + 'soldot_inlet_port_000_comp_000': (1501,), + 'soldot_inlet_port_000_comp_001': (1501,), + 'soldot_inlet_port_000_comp_002': (1501,), + 'soldot_inlet_port_000_comp_003': (1501,), + 'soldot_outlet_port_000_comp_000': (1501,), + 'soldot_outlet_port_000_comp_001': (1501,), + 'soldot_outlet_port_000_comp_002': (1501,), + 'soldot_outlet_port_000_comp_003': (1501,), + }, + }, +) + + +# %% GRM 1 Comp + +grm_1_comp = Case( + name='grm_1_comp', + model_options=grm_template_1_comp, + solution_recorder_options=no_split, + expected_results={ + 'solution_times': (1501,), + 'last_state_y': (103,), + 'last_state_ydot': (103,), + 'coordinates_unit_000': { + 'axial_coordinates': (10,), + 'particle_coordinates_000': (4,), + }, + 'coordinates_unit_001': {}, + 'solution_unit_000': { + 'last_state_y': (101,), + 'last_state_ydot': (101,), + + 'solution_inlet': (1501, 1), + 'solution_outlet': (1501, 1), + 'solution_bulk': (1501, 10, 1), + 'solution_particle': (1501, 10, 4, 1), + 'solution_solid': (1501, 10, 4, 1), + 'solution_flux': (1501, 1, 10, 1), + + 'soldot_inlet': (1501, 1), + 'soldot_outlet': (1501, 1), + 'soldot_bulk': (1501, 10, 1), + 'soldot_particle': (1501, 10, 4, 1), + 'soldot_solid': (1501, 10, 4, 1), + 'soldot_flux': (1501, 1, 10, 1), + }, + 'solution_unit_001': { + 'last_state_y': (1,), + 'last_state_ydot': (1,), + + 'solution_inlet': (1501, 1), + 'solution_outlet': (1501, 1), + 'soldot_inlet': (1501, 1), + 'soldot_outlet': (1501, 1), }, }, ) + # %% GRM Sens grm_sens = Case( name='grm_sens', model_options=grm_template_sens, - solution_recorder_options=no_split_options, + solution_recorder_options=no_split, expected_results={ + 'solution_times': (1501,), 'last_state_y': (412,), 'last_state_ydot': (412,), 'coordinates_unit_000': { 'axial_coordinates': (10,), 'particle_coordinates_000': (4,), }, - 'solution_times': (1501,), + 'coordinates_unit_001': {}, 'solution_unit_000': { 'last_state_y': (404,), 'last_state_ydot': (404,), - 'soldot_bulk': (1501, 10, 4), - 'soldot_flux': (1501, 1, 10, 4), - 'soldot_inlet': (1501, 4), - 'soldot_outlet': (1501, 4), - 'soldot_particle': (1501, 10, 4, 4), - 'soldot_solid': (1501, 10, 4, 4), - 'solution_bulk': (1501, 10, 4), - 'solution_flux': (1501, 1, 10, 4), + 'solution_inlet': (1501, 4), 'solution_outlet': (1501, 4), + 'solution_bulk': (1501, 10, 4), 'solution_particle': (1501, 10, 4, 4), 'solution_solid': (1501, 10, 4, 4), + 'solution_flux': (1501, 1, 10, 4), + + 'soldot_inlet': (1501, 4), + 'soldot_outlet': (1501, 4), + 'soldot_bulk': (1501, 10, 4), + 'soldot_particle': (1501, 10, 4, 4), + 'soldot_solid': (1501, 10, 4, 4), + 'soldot_flux': (1501, 1, 10, 4), }, 'solution_unit_001': { 'last_state_y': (4,), 'last_state_ydot': (4,), - 'soldot_inlet': (1501, 4), - 'soldot_outlet': (1501, 4), + 'solution_inlet': (1501, 4), 'solution_outlet': (1501, 4), + + 'soldot_inlet': (1501, 4), + 'soldot_outlet': (1501, 4), }, 'sens_param_000_unit_000': { - 'sensdot_bulk': (1501, 10, 4), - 'sensdot_flux': (1501, 1, 10, 4), - 'sensdot_inlet': (1501, 4), - 'sensdot_outlet': (1501, 4), - 'sensdot_particle': (1501, 10, 4, 4), - 'sensdot_solid': (1501, 10, 4, 4), - 'sens_bulk': (1501, 10, 4), - 'sens_flux': (1501, 1, 10, 4), 'sens_inlet': (1501, 4), 'sens_outlet': (1501, 4), + 'sens_bulk': (1501, 10, 4), 'sens_particle': (1501, 10, 4, 4), 'sens_solid': (1501, 10, 4, 4), - }, - 'sens_param_000_unit_001': { + 'sens_flux': (1501, 1, 10, 4), + 'sensdot_inlet': (1501, 4), 'sensdot_outlet': (1501, 4), + 'sensdot_bulk': (1501, 10, 4), + 'sensdot_particle': (1501, 10, 4, 4), + 'sensdot_solid': (1501, 10, 4, 4), + 'sensdot_flux': (1501, 1, 10, 4), + }, + 'sens_param_000_unit_001': { 'sens_inlet': (1501, 4), 'sens_outlet': (1501, 4), + + 'sensdot_inlet': (1501, 4), + 'sensdot_outlet': (1501, 4), }, }, ) @@ -710,8 +959,9 @@ def __repr__(self): grm_par_types = Case( name='grm_par_types', model_options=grm_template_partypes, - solution_recorder_options=no_split_options, + solution_recorder_options=no_split, expected_results={ + 'solution_times': (1501,), 'last_state_y': (772,), 'last_state_ydot': (772,), 'coordinates_unit_000': { @@ -719,34 +969,38 @@ def __repr__(self): 'particle_coordinates_000': (4,), 'particle_coordinates_001': (4,), }, - 'solution_times': (1501,), + 'coordinates_unit_001': {}, 'solution_unit_000': { 'last_state_y': (764,), 'last_state_ydot': (764,), - 'soldot_bulk': (1501, 10, 4), - 'soldot_flux': (1501, 2, 10, 4), - 'soldot_inlet': (1501, 4), - 'soldot_outlet': (1501, 4), - 'soldot_particle_partype_000': (1501, 10, 4, 4), - 'soldot_particle_partype_001': (1501, 10, 4, 4), - 'soldot_solid_partype_000': (1501, 10, 4, 4), - 'soldot_solid_partype_001': (1501, 10, 4, 4), - 'solution_bulk': (1501, 10, 4), - 'solution_flux': (1501, 2, 10, 4), + 'solution_inlet': (1501, 4), 'solution_outlet': (1501, 4), + 'solution_bulk': (1501, 10, 4), 'solution_particle_partype_000': (1501, 10, 4, 4), 'solution_particle_partype_001': (1501, 10, 4, 4), 'solution_solid_partype_000': (1501, 10, 4, 4), 'solution_solid_partype_001': (1501, 10, 4, 4), + 'solution_flux': (1501, 2, 10, 4), + + 'soldot_inlet': (1501, 4), + 'soldot_outlet': (1501, 4), + 'soldot_bulk': (1501, 10, 4), + 'soldot_particle_partype_000': (1501, 10, 4, 4), + 'soldot_particle_partype_001': (1501, 10, 4, 4), + 'soldot_solid_partype_000': (1501, 10, 4, 4), + 'soldot_solid_partype_001': (1501, 10, 4, 4), + 'soldot_flux': (1501, 2, 10, 4), }, 'solution_unit_001': { 'last_state_y': (4,), 'last_state_ydot': (4,), - 'soldot_inlet': (1501, 4), - 'soldot_outlet': (1501, 4), + 'solution_inlet': (1501, 4), 'solution_outlet': (1501, 4), + + 'soldot_inlet': (1501, 4), + 'soldot_outlet': (1501, 4), }, }, ) @@ -755,8 +1009,9 @@ def __repr__(self): _2dgrm = Case( name='_2dgrm', model_options=_2dgrm_template, - solution_recorder_options=no_split_options, + solution_recorder_options=no_split, expected_results={ + 'solution_times': (1501,), 'last_state_y': (1228,), 'last_state_ydot': (1228,), 'coordinates_unit_000': { @@ -764,7 +1019,7 @@ def __repr__(self): 'particle_coordinates_000': (4,), 'radial_coordinates': (3,), }, - 'solution_times': (1501,), + 'coordinates_unit_001': {}, 'solution_unit_000': { 'last_state_y': (1212,), 'last_state_ydot': (1212,), @@ -797,8 +1052,9 @@ def __repr__(self): _2dgrm_split_ports = Case( name='_2dgrm_split_ports', model_options=_2dgrm_template, - solution_recorder_options=split_ports_options, + solution_recorder_options=split_ports, expected_results={ + 'solution_times': (1501,), 'last_state_y': (1228,), 'last_state_ydot': (1228,), 'coordinates_unit_000': { @@ -806,38 +1062,43 @@ def __repr__(self): 'particle_coordinates_000': (4,), 'radial_coordinates': (3,), }, - 'solution_times': (1501,), + 'coordinates_unit_001': {}, 'solution_unit_000': { 'last_state_y': (1212,), 'last_state_ydot': (1212,), - 'soldot_bulk': (1501, 10, 3, 4), - 'soldot_flux': (1501, 1, 10, 3, 4), - 'soldot_inlet_port_000': (1501, 4), - 'soldot_inlet_port_001': (1501, 4), - 'soldot_inlet_port_002': (1501, 4), - 'soldot_outlet_port_000': (1501, 4), - 'soldot_outlet_port_001': (1501, 4), - 'soldot_outlet_port_002': (1501, 4), - 'soldot_particle': (1501, 10, 3, 4, 4), - 'soldot_solid': (1501, 10, 3, 4, 4), - 'solution_bulk': (1501, 10, 3, 4), - 'solution_flux': (1501, 1, 10, 3, 4), + 'solution_inlet_port_000': (1501, 4), 'solution_inlet_port_001': (1501, 4), 'solution_inlet_port_002': (1501, 4), 'solution_outlet_port_000': (1501, 4), 'solution_outlet_port_001': (1501, 4), 'solution_outlet_port_002': (1501, 4), + 'solution_bulk': (1501, 10, 3, 4), 'solution_particle': (1501, 10, 3, 4, 4), 'solution_solid': (1501, 10, 3, 4, 4), + 'solution_flux': (1501, 1, 10, 3, 4), + + 'soldot_inlet_port_000': (1501, 4), + 'soldot_inlet_port_001': (1501, 4), + 'soldot_inlet_port_002': (1501, 4), + 'soldot_outlet_port_000': (1501, 4), + 'soldot_outlet_port_001': (1501, 4), + 'soldot_outlet_port_002': (1501, 4), + 'soldot_bulk': (1501, 10, 3, 4), + 'soldot_particle': (1501, 10, 3, 4, 4), + 'soldot_solid': (1501, 10, 3, 4, 4), + 'soldot_flux': (1501, 1, 10, 3, 4), + }, 'solution_unit_001': { 'last_state_y': (4,), 'last_state_ydot': (4,), - 'soldot_inlet': (1501, 4), - 'soldot_outlet': (1501, 4), + 'solution_inlet': (1501, 4), 'solution_outlet': (1501, 4), + + 'soldot_inlet': (1501, 4), + 'soldot_outlet': (1501, 4), }, }, ) @@ -847,8 +1108,9 @@ def __repr__(self): _2dgrm_split_all = Case( name='_2dgrm_split_all', model_options=_2dgrm_template, - solution_recorder_options=split_all_options, + solution_recorder_options=split_all, expected_results={ + 'solution_times': (1501,), 'last_state_y': (1228,), 'last_state_ydot': (1228,), 'coordinates_unit_000': { @@ -856,40 +1118,11 @@ def __repr__(self): 'particle_coordinates_000': (4,), 'radial_coordinates': (3,), }, - 'solution_times': (1501,), + 'coordinates_unit_001': {}, 'solution_unit_000': { 'last_state_y': (1212,), 'last_state_ydot': (1212,), - 'soldot_bulk': (1501, 10, 3, 4), - 'soldot_flux': (1501, 1, 10, 3, 4), - 'soldot_inlet_port_000_comp_000': (1501,), - 'soldot_inlet_port_000_comp_001': (1501,), - 'soldot_inlet_port_000_comp_002': (1501,), - 'soldot_inlet_port_000_comp_003': (1501,), - 'soldot_inlet_port_001_comp_000': (1501,), - 'soldot_inlet_port_001_comp_001': (1501,), - 'soldot_inlet_port_001_comp_002': (1501,), - 'soldot_inlet_port_001_comp_003': (1501,), - 'soldot_inlet_port_002_comp_000': (1501,), - 'soldot_inlet_port_002_comp_001': (1501,), - 'soldot_inlet_port_002_comp_002': (1501,), - 'soldot_inlet_port_002_comp_003': (1501,), - 'soldot_outlet_port_000_comp_000': (1501,), - 'soldot_outlet_port_000_comp_001': (1501,), - 'soldot_outlet_port_000_comp_002': (1501,), - 'soldot_outlet_port_000_comp_003': (1501,), - 'soldot_outlet_port_001_comp_000': (1501,), - 'soldot_outlet_port_001_comp_001': (1501,), - 'soldot_outlet_port_001_comp_002': (1501,), - 'soldot_outlet_port_001_comp_003': (1501,), - 'soldot_outlet_port_002_comp_000': (1501,), - 'soldot_outlet_port_002_comp_001': (1501,), - 'soldot_outlet_port_002_comp_002': (1501,), - 'soldot_outlet_port_002_comp_003': (1501,), - 'soldot_particle': (1501, 10, 3, 4, 4), - 'soldot_solid': (1501, 10, 3, 4, 4), - 'solution_bulk': (1501, 10, 3, 4), - 'solution_flux': (1501, 1, 10, 3, 4), + 'solution_inlet_port_000_comp_000': (1501,), 'solution_inlet_port_000_comp_001': (1501,), 'solution_inlet_port_000_comp_002': (1501,), @@ -914,20 +1147,43 @@ def __repr__(self): 'solution_outlet_port_002_comp_001': (1501,), 'solution_outlet_port_002_comp_002': (1501,), 'solution_outlet_port_002_comp_003': (1501,), + 'solution_bulk': (1501, 10, 3, 4), 'solution_particle': (1501, 10, 3, 4, 4), 'solution_solid': (1501, 10, 3, 4, 4), - }, - 'solution_unit_001': { - 'last_state_y': (4,), - 'last_state_ydot': (4,), + 'solution_flux': (1501, 1, 10, 3, 4), + 'soldot_inlet_port_000_comp_000': (1501,), 'soldot_inlet_port_000_comp_001': (1501,), 'soldot_inlet_port_000_comp_002': (1501,), 'soldot_inlet_port_000_comp_003': (1501,), + 'soldot_inlet_port_001_comp_000': (1501,), + 'soldot_inlet_port_001_comp_001': (1501,), + 'soldot_inlet_port_001_comp_002': (1501,), + 'soldot_inlet_port_001_comp_003': (1501,), + 'soldot_inlet_port_002_comp_000': (1501,), + 'soldot_inlet_port_002_comp_001': (1501,), + 'soldot_inlet_port_002_comp_002': (1501,), + 'soldot_inlet_port_002_comp_003': (1501,), 'soldot_outlet_port_000_comp_000': (1501,), 'soldot_outlet_port_000_comp_001': (1501,), 'soldot_outlet_port_000_comp_002': (1501,), 'soldot_outlet_port_000_comp_003': (1501,), + 'soldot_outlet_port_001_comp_000': (1501,), + 'soldot_outlet_port_001_comp_001': (1501,), + 'soldot_outlet_port_001_comp_002': (1501,), + 'soldot_outlet_port_001_comp_003': (1501,), + 'soldot_outlet_port_002_comp_000': (1501,), + 'soldot_outlet_port_002_comp_001': (1501,), + 'soldot_outlet_port_002_comp_002': (1501,), + 'soldot_outlet_port_002_comp_003': (1501,), + 'soldot_bulk': (1501, 10, 3, 4), + 'soldot_particle': (1501, 10, 3, 4, 4), + 'soldot_solid': (1501, 10, 3, 4, 4), + 'soldot_flux': (1501, 1, 10, 3, 4), + }, + 'solution_unit_001': { + 'last_state_y': (4,), + 'last_state_ydot': (4,), 'solution_inlet_port_000_comp_000': (1501,), 'solution_inlet_port_000_comp_001': (1501,), 'solution_inlet_port_000_comp_002': (1501,), @@ -936,26 +1192,53 @@ def __repr__(self): 'solution_outlet_port_000_comp_001': (1501,), 'solution_outlet_port_000_comp_002': (1501,), 'solution_outlet_port_000_comp_003': (1501,), + + 'soldot_inlet_port_000_comp_000': (1501,), + 'soldot_inlet_port_000_comp_001': (1501,), + 'soldot_inlet_port_000_comp_002': (1501,), + 'soldot_inlet_port_000_comp_003': (1501,), + 'soldot_outlet_port_000_comp_000': (1501,), + 'soldot_outlet_port_000_comp_001': (1501,), + 'soldot_outlet_port_000_comp_002': (1501,), + 'soldot_outlet_port_000_comp_003': (1501,), }, }, ) -# %% Actual tests -use_dll = [False, True] -test_cases = [ - cstr, - lrm, - lrmp, - grm, - grm_1_comp, - grm_split, - grm_sens, - grm_par_types, - _2dgrm, - _2dgrm_split_ports, - _2dgrm_split_all -] + +# %% Testing utils + +def assert_keys(model_dict: dict, expected_dict: dict): + """ + Assert that the keys of two dictionaries are identical. + + Parameters + ---------- + model_dict : dict + The dictionary whose keys are to be compared. + expected_dict : dict + The dictionary containing the expected set of keys. + + Raises + ------ + AssertionError + If the keys of `model_dict` and `expected_dict` do not match. + + Examples + -------- + >>> assert_keys({"a": 1, "b": 2}, {"b": 3, "a": 4}) + True + >>> assert_keys({"a": 1, "b": 2}, {"b": 3, "c": 4}) + Traceback (most recent call last): + ... + AssertionError: Key mismatch. Expected {'b', 'c'}, but got {'b', 'a'}. + """ + model_keys = set(model_dict.keys()) + expected_keys = set(expected_dict.keys()) + assert model_keys == expected_keys, ( + f"Key mismatch. Expected {expected_keys}, but got {model_keys}." + ) def assert_shape(array_shape, expected_shape, context, key, unit_id=None): @@ -990,8 +1273,30 @@ def assert_shape(array_shape, expected_shape, context, key, unit_id=None): ) -@pytest.mark.parametrize("use_dll", use_dll) -@pytest.mark.parametrize("test_case", test_cases) +# %% Actual tests + +use_dll = [False, True] + +test_cases = [ + cstr, + lrm, + lrmp, + grm, + grm_split_components, + grm_split_ports, + grm_split_ports_single_as_multi, + grm_split_all, + grm_1_comp, + grm_sens, + grm_par_types, + _2dgrm, + _2dgrm_split_ports, + _2dgrm_split_all +] + + +@pytest.mark.parametrize("use_dll", use_dll, ids=[f"{case}" for case in use_dll]) +@pytest.mark.parametrize("test_case", test_cases, ids=[case.name for case in test_cases]) def test_simulator_options(use_dll, test_case): model_options = test_case.model_options solution_recorder_options = test_case.solution_recorder_options @@ -1001,6 +1306,14 @@ def test_simulator_options(use_dll, test_case): use_dll, model_options, solution_recorder_options ) + # Assert solution_times shape + assert_shape( + model.root.output.solution.solution_times.shape, + expected_results['solution_times'], + context="solution", + key="solution_times" + ) + # Assert last_state shapes assert_shape( model.root.output.last_state_y.shape, @@ -1015,40 +1328,98 @@ def test_simulator_options(use_dll, test_case): key="ydot" ) - # Check coordinates for unit_000 - for key, value in expected_results['coordinates_unit_000'].items(): - coordinates_shape = model.root.output.coordinates.unit_000[key].shape - assert_shape(coordinates_shape, value, context="coordinates", key=key, unit_id="unit_000") + # Check coordinates + unit = "unit_000" + excpected_coordinates = expected_results[f'coordinates_{unit}'] + coordinates_unit = model.root.output.coordinates[unit] + assert_keys(coordinates_unit, excpected_coordinates) + + for key, value in excpected_coordinates.items(): + coordinates_shape = coordinates_unit[key].shape + assert_shape( + coordinates_shape, + value, + context="coordinates", + key=key, + unit_id=unit, + ) - # Assert solution_times shape - assert_shape( - model.root.output.solution.solution_times.shape, - expected_results['solution_times'], - context="solution", - key="solution_times" - ) + unit = "unit_001" + excpected_coordinates = expected_results[f'coordinates_{unit}'] + coordinates_unit = model.root.output.coordinates[unit] + assert_keys(coordinates_unit, excpected_coordinates) + + for key, value in excpected_coordinates.items(): + coordinates_shape = coordinates_unit[key].shape + assert_shape( + coordinates_shape, + value, + context="coordinates", + key=key, + unit_id=unit, + ) - # Check solution for unit_000 - for key, value in expected_results['solution_unit_000'].items(): - shape = model.root.output.solution.unit_000[key].shape - assert_shape(shape, value, context="solution", key=key, unit_id="unit_000") + # Check solution + unit = "unit_000" + excpected_solution = expected_results[f'solution_{unit}'] + solution_unit = model.root.output.solution[unit] + assert_keys(excpected_solution, solution_unit) + + for key, value in excpected_solution.items(): + shape = solution_unit[key].shape + assert_shape( + shape, + value, + context="solution", + key=key, + unit_id=unit, + ) - # Check solution for unit_001 - for key, value in expected_results['solution_unit_001'].items(): - shape = model.root.output.solution.unit_001[key].shape - assert_shape(shape, value, context="solution", key=key, unit_id="unit_001") + unit = "unit_001" + excpected_solution = expected_results[f'solution_{unit}'] + solution_unit = model.root.output.solution[unit] + assert_keys(excpected_solution, solution_unit) + + for key, value in excpected_solution.items(): + shape = solution_unit[key].shape + assert_shape( + shape, + value, + context="solution", + key=key, + unit_id=unit, + ) + # Check sensitivity if model_options['include_sensitivity']: - for key, value in expected_results['sens_param_000_unit_000'].items(): - shape = model.root.output.sensitivity.param_000.unit_000[key].shape + unit = "unit_000" + excpected_sensitivity = expected_results[f'sens_param_000_{unit}'] + sensitivity_unit = model.root.output.sensitivity.param_000[unit] + assert_keys(excpected_sensitivity, sensitivity_unit) + + for key, value in excpected_sensitivity.items(): + shape = sensitivity_unit[key].shape assert_shape( - shape, value, context="sensitivity", key=key, unit_id="unit_000" + shape, + value, + context="sensitivity", + key=key, + unit_id=unit, ) - for key, value in expected_results['sens_param_000_unit_001'].items(): - shape = model.root.output.sensitivity.param_000.unit_001[key].shape + unit = "unit_001" + excpected_sensitivity = expected_results[f'sens_param_000_{unit}'] + sensitivity_unit = model.root.output.sensitivity.param_000[unit] + assert_keys(excpected_sensitivity, sensitivity_unit) + + for key, value in excpected_sensitivity.items(): + shape = sensitivity_unit[key].shape assert_shape( - shape, value, context="sensitivity", key=key, unit_id="unit_001" + shape, + value, + context="sensitivity", + key=key, + unit_id=unit, ) From 5957c164576ea099d5e14ddfc5cf646a203b0a97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Schm=C3=B6lder?= Date: Sat, 7 Dec 2024 15:56:31 +0100 Subject: [PATCH 3/8] Add option to specify ncol / npar in test cases --- tests/test_dll.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_dll.py b/tests/test_dll.py index e222a30..0063702 100644 --- a/tests/test_dll.py +++ b/tests/test_dll.py @@ -16,6 +16,8 @@ def setup_model( use_dll=True, model='GENERAL_RATE_MODEL', n_partypes=1, + ncol=10, + npar=4, include_sensitivity=False, file_name='LWE.h5', n_components=4 @@ -41,6 +43,10 @@ def setup_model( The model type to set up. The default is 'GENERAL_RATE_MODEL'. n_partypes : int, optional The number of particle types. The default is 1. + ncol : int, optional + The number of axial cells in the unit operation. The default is 10. + npar : int, optional + The number of particle cells in the unit operation. The default is 4. include_sensitivity : bool, optional If True, included parameter sensitivities in template. The default is False. file_name : str, optional @@ -94,6 +100,8 @@ def setup_model( f'--out {file_name}', f'--unit {model}', f'--parTypes {n_partypes}', + f'--col {ncol}', + f'--par {npar}', ] if include_sensitivity: From a495d23469ed05ece9a61440d3471493979ce890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Schm=C3=B6lder?= Date: Sat, 7 Dec 2024 15:57:52 +0100 Subject: [PATCH 4/8] Set npar=5 in tests to distinguish from ncomp in solution shape --- tests/test_dll.py | 248 ++++++++++++++++++++++++---------------------- 1 file changed, 132 insertions(+), 116 deletions(-) diff --git a/tests/test_dll.py b/tests/test_dll.py index 0063702..29f7c7a 100644 --- a/tests/test_dll.py +++ b/tests/test_dll.py @@ -342,18 +342,22 @@ def run_simulation_with_options(use_dll, model_options, solution_recorder_option lrm_template = { 'model': 'LUMPED_RATE_MODEL_WITHOUT_PORES', + 'ncol': 10, 'n_partypes': 1, 'include_sensitivity': False, } lrmp_template = { 'model': 'LUMPED_RATE_MODEL_WITH_PORES', + 'ncol': 10, 'n_partypes': 1, 'include_sensitivity': False, } grm_template = { 'model': 'GENERAL_RATE_MODEL', + 'ncol': 10, + 'npar': 5, 'n_partypes': 1, 'include_sensitivity': False, } @@ -361,6 +365,8 @@ def run_simulation_with_options(use_dll, model_options, solution_recorder_option grm_template_1_comp = { 'model': 'GENERAL_RATE_MODEL', 'n_partypes': 1, + 'ncol': 10, + 'npar': 5, 'include_sensitivity': False, 'n_components': 1, } @@ -368,18 +374,24 @@ def run_simulation_with_options(use_dll, model_options, solution_recorder_option grm_template_sens = { 'model': 'GENERAL_RATE_MODEL', 'n_partypes': 1, + 'ncol': 10, + 'npar': 5, 'include_sensitivity': True, } grm_template_partypes = { 'model': 'GENERAL_RATE_MODEL', 'n_partypes': 2, + 'ncol': 10, + 'npar': 5, 'include_sensitivity': False, } _2dgrm_template = { 'model': 'GENERAL_RATE_MODEL_2D', 'n_partypes': 1, + 'ncol': 10, + 'npar': 5, 'include_sensitivity': False, } @@ -574,29 +586,29 @@ def __repr__(self): solution_recorder_options=no_split, expected_results={ 'solution_times': (1501,), - 'last_state_y': (412,), - 'last_state_ydot': (412,), + 'last_state_y': (492,), + 'last_state_ydot': (492,), 'coordinates_unit_000': { 'axial_coordinates': (10,), - 'particle_coordinates_000': (4,), + 'particle_coordinates_000': (5,), }, 'coordinates_unit_001': {}, 'solution_unit_000': { - 'last_state_y': (404,), - 'last_state_ydot': (404,), + 'last_state_y': (484,), + 'last_state_ydot': (484,), 'solution_inlet': (1501, 4), 'solution_outlet': (1501, 4), 'solution_bulk': (1501, 10, 4), - 'solution_particle': (1501, 10, 4, 4), - 'solution_solid': (1501, 10, 4, 4), + 'solution_particle': (1501, 10, 5, 4), + 'solution_solid': (1501, 10, 5, 4), 'solution_flux': (1501, 1, 10, 4), 'soldot_inlet': (1501, 4), 'soldot_outlet': (1501, 4), 'soldot_bulk': (1501, 10, 4), - 'soldot_particle': (1501, 10, 4, 4), - 'soldot_solid': (1501, 10, 4, 4), + 'soldot_particle': (1501, 10, 5, 4), + 'soldot_solid': (1501, 10, 5, 4), 'soldot_flux': (1501, 1, 10, 4), }, 'solution_unit_001': { @@ -621,16 +633,16 @@ def __repr__(self): solution_recorder_options=split_components, expected_results={ 'solution_times': (1501,), - 'last_state_y': (412,), - 'last_state_ydot': (412,), + 'last_state_y': (492,), + 'last_state_ydot': (492,), 'coordinates_unit_000': { 'axial_coordinates': (10,), - 'particle_coordinates_000': (4,), + 'particle_coordinates_000': (5,), }, 'coordinates_unit_001': {}, 'solution_unit_000': { - 'last_state_y': (404,), - 'last_state_ydot': (404,), + 'last_state_y': (484,), + 'last_state_ydot': (484,), 'solution_inlet_comp_000': (1501,), 'solution_inlet_comp_001': (1501,), @@ -641,8 +653,8 @@ def __repr__(self): 'solution_outlet_comp_002': (1501,), 'solution_outlet_comp_003': (1501,), 'solution_bulk': (1501, 10, 4), - 'solution_particle': (1501, 10, 4, 4), - 'solution_solid': (1501, 10, 4, 4), + 'solution_particle': (1501, 10, 5, 4), + 'solution_solid': (1501, 10, 5, 4), 'solution_flux': (1501, 1, 10, 4), 'soldot_inlet_comp_000': (1501,), @@ -654,8 +666,8 @@ def __repr__(self): 'soldot_outlet_comp_002': (1501,), 'soldot_outlet_comp_003': (1501,), 'soldot_bulk': (1501, 10, 4), - 'soldot_particle': (1501, 10, 4, 4), - 'soldot_solid': (1501, 10, 4, 4), + 'soldot_particle': (1501, 10, 5, 4), + 'soldot_solid': (1501, 10, 5, 4), 'soldot_flux': (1501, 1, 10, 4), }, 'solution_unit_001': { @@ -692,29 +704,29 @@ def __repr__(self): solution_recorder_options=split_ports, expected_results={ 'solution_times': (1501,), - 'last_state_y': (412,), - 'last_state_ydot': (412,), + 'last_state_y': (492,), + 'last_state_ydot': (492,), 'coordinates_unit_000': { 'axial_coordinates': (10,), - 'particle_coordinates_000': (4,), + 'particle_coordinates_000': (5,), }, 'coordinates_unit_001': {}, 'solution_unit_000': { - 'last_state_y': (404,), - 'last_state_ydot': (404,), + 'last_state_y': (484,), + 'last_state_ydot': (484,), 'solution_inlet': (1501, 4), 'solution_outlet': (1501, 4), 'solution_bulk': (1501, 10, 4), - 'solution_particle': (1501, 10, 4, 4), - 'solution_solid': (1501, 10, 4, 4), + 'solution_particle': (1501, 10, 5, 4), + 'solution_solid': (1501, 10, 5, 4), 'solution_flux': (1501, 1, 10, 4), 'soldot_inlet': (1501, 4), 'soldot_outlet': (1501, 4), 'soldot_bulk': (1501, 10, 4), - 'soldot_particle': (1501, 10, 4, 4), - 'soldot_solid': (1501, 10, 4, 4), + 'soldot_particle': (1501, 10, 5, 4), + 'soldot_solid': (1501, 10, 5, 4), 'soldot_flux': (1501, 1, 10, 4), }, 'solution_unit_001': { @@ -738,29 +750,29 @@ def __repr__(self): solution_recorder_options=split_ports_single_as_multi, expected_results={ 'solution_times': (1501,), - 'last_state_y': (412,), - 'last_state_ydot': (412,), + 'last_state_y': (492,), + 'last_state_ydot': (492,), 'coordinates_unit_000': { 'axial_coordinates': (10,), - 'particle_coordinates_000': (4,), + 'particle_coordinates_000': (5,), }, 'coordinates_unit_001': {}, 'solution_unit_000': { - 'last_state_y': (404,), - 'last_state_ydot': (404,), + 'last_state_y': (484,), + 'last_state_ydot': (484,), 'solution_inlet_port_000': (1501, 4), 'solution_outlet_port_000': (1501, 4), 'solution_bulk': (1501, 10, 4), - 'solution_particle': (1501, 10, 4, 4), - 'solution_solid': (1501, 10, 4, 4), + 'solution_particle': (1501, 10, 5, 4), + 'solution_solid': (1501, 10, 5, 4), 'solution_flux': (1501, 1, 10, 4), 'soldot_inlet_port_000': (1501, 4), 'soldot_outlet_port_000': (1501, 4), 'soldot_bulk': (1501, 10, 4), - 'soldot_particle': (1501, 10, 4, 4), - 'soldot_solid': (1501, 10, 4, 4), + 'soldot_particle': (1501, 10, 5, 4), + 'soldot_solid': (1501, 10, 5, 4), 'soldot_flux': (1501, 1, 10, 4), }, 'solution_unit_001': { @@ -785,16 +797,16 @@ def __repr__(self): solution_recorder_options=split_all, expected_results={ 'solution_times': (1501,), - 'last_state_y': (412,), - 'last_state_ydot': (412,), + 'last_state_y': (492,), + 'last_state_ydot': (492,), 'coordinates_unit_000': { 'axial_coordinates': (10,), - 'particle_coordinates_000': (4,), + 'particle_coordinates_000': (5,), }, 'coordinates_unit_001': {}, 'solution_unit_000': { - 'last_state_y': (404,), - 'last_state_ydot': (404,), + 'last_state_y': (484,), + 'last_state_ydot': (484,), 'solution_inlet_port_000_comp_000': (1501,), 'solution_inlet_port_000_comp_001': (1501,), @@ -805,8 +817,8 @@ def __repr__(self): 'solution_outlet_port_000_comp_002': (1501,), 'solution_outlet_port_000_comp_003': (1501,), 'solution_bulk': (1501, 10, 4), - 'solution_particle': (1501, 10, 4, 4), - 'solution_solid': (1501, 10, 4, 4), + 'solution_particle': (1501, 10, 5, 4), + 'solution_solid': (1501, 10, 5, 4), 'solution_flux': (1501, 1, 10, 4), 'soldot_inlet_port_000_comp_000': (1501,), @@ -818,8 +830,8 @@ def __repr__(self): 'soldot_outlet_port_000_comp_002': (1501,), 'soldot_outlet_port_000_comp_003': (1501,), 'soldot_bulk': (1501, 10, 4), - 'soldot_particle': (1501, 10, 4, 4), - 'soldot_solid': (1501, 10, 4, 4), + 'soldot_particle': (1501, 10, 5, 4), + 'soldot_solid': (1501, 10, 5, 4), 'soldot_flux': (1501, 1, 10, 4), }, 'solution_unit_001': { @@ -856,29 +868,29 @@ def __repr__(self): solution_recorder_options=no_split, expected_results={ 'solution_times': (1501,), - 'last_state_y': (103,), - 'last_state_ydot': (103,), + 'last_state_y': (123,), + 'last_state_ydot': (123,), 'coordinates_unit_000': { 'axial_coordinates': (10,), - 'particle_coordinates_000': (4,), + 'particle_coordinates_000': (5,), }, 'coordinates_unit_001': {}, 'solution_unit_000': { - 'last_state_y': (101,), - 'last_state_ydot': (101,), + 'last_state_y': (121,), + 'last_state_ydot': (121,), 'solution_inlet': (1501, 1), 'solution_outlet': (1501, 1), 'solution_bulk': (1501, 10, 1), - 'solution_particle': (1501, 10, 4, 1), - 'solution_solid': (1501, 10, 4, 1), + 'solution_particle': (1501, 10, 5, 1), + 'solution_solid': (1501, 10, 5, 1), 'solution_flux': (1501, 1, 10, 1), 'soldot_inlet': (1501, 1), 'soldot_outlet': (1501, 1), 'soldot_bulk': (1501, 10, 1), - 'soldot_particle': (1501, 10, 4, 1), - 'soldot_solid': (1501, 10, 4, 1), + 'soldot_particle': (1501, 10, 5, 1), + 'soldot_solid': (1501, 10, 5, 1), 'soldot_flux': (1501, 1, 10, 1), }, 'solution_unit_001': { @@ -902,29 +914,29 @@ def __repr__(self): solution_recorder_options=no_split, expected_results={ 'solution_times': (1501,), - 'last_state_y': (412,), - 'last_state_ydot': (412,), + 'last_state_y': (492,), + 'last_state_ydot': (492,), 'coordinates_unit_000': { 'axial_coordinates': (10,), - 'particle_coordinates_000': (4,), + 'particle_coordinates_000': (5,), }, 'coordinates_unit_001': {}, 'solution_unit_000': { - 'last_state_y': (404,), - 'last_state_ydot': (404,), + 'last_state_y': (484,), + 'last_state_ydot': (484,), 'solution_inlet': (1501, 4), 'solution_outlet': (1501, 4), 'solution_bulk': (1501, 10, 4), - 'solution_particle': (1501, 10, 4, 4), - 'solution_solid': (1501, 10, 4, 4), + 'solution_particle': (1501, 10, 5, 4), + 'solution_solid': (1501, 10, 5, 4), 'solution_flux': (1501, 1, 10, 4), 'soldot_inlet': (1501, 4), 'soldot_outlet': (1501, 4), 'soldot_bulk': (1501, 10, 4), - 'soldot_particle': (1501, 10, 4, 4), - 'soldot_solid': (1501, 10, 4, 4), + 'soldot_particle': (1501, 10, 5, 4), + 'soldot_solid': (1501, 10, 5, 4), 'soldot_flux': (1501, 1, 10, 4), }, 'solution_unit_001': { @@ -941,15 +953,15 @@ def __repr__(self): 'sens_inlet': (1501, 4), 'sens_outlet': (1501, 4), 'sens_bulk': (1501, 10, 4), - 'sens_particle': (1501, 10, 4, 4), - 'sens_solid': (1501, 10, 4, 4), + 'sens_particle': (1501, 10, 5, 4), + 'sens_solid': (1501, 10, 5, 4), 'sens_flux': (1501, 1, 10, 4), 'sensdot_inlet': (1501, 4), 'sensdot_outlet': (1501, 4), 'sensdot_bulk': (1501, 10, 4), - 'sensdot_particle': (1501, 10, 4, 4), - 'sensdot_solid': (1501, 10, 4, 4), + 'sensdot_particle': (1501, 10, 5, 4), + 'sensdot_solid': (1501, 10, 5, 4), 'sensdot_flux': (1501, 1, 10, 4), }, 'sens_param_000_unit_001': { @@ -970,34 +982,34 @@ def __repr__(self): solution_recorder_options=no_split, expected_results={ 'solution_times': (1501,), - 'last_state_y': (772,), - 'last_state_ydot': (772,), + 'last_state_y': (932,), + 'last_state_ydot': (932,), 'coordinates_unit_000': { 'axial_coordinates': (10,), - 'particle_coordinates_000': (4,), - 'particle_coordinates_001': (4,), + 'particle_coordinates_000': (5,), + 'particle_coordinates_001': (5,), }, 'coordinates_unit_001': {}, 'solution_unit_000': { - 'last_state_y': (764,), - 'last_state_ydot': (764,), + 'last_state_y': (924,), + 'last_state_ydot': (924,), 'solution_inlet': (1501, 4), 'solution_outlet': (1501, 4), 'solution_bulk': (1501, 10, 4), - 'solution_particle_partype_000': (1501, 10, 4, 4), - 'solution_particle_partype_001': (1501, 10, 4, 4), - 'solution_solid_partype_000': (1501, 10, 4, 4), - 'solution_solid_partype_001': (1501, 10, 4, 4), + 'solution_particle_partype_000': (1501, 10, 5, 4), + 'solution_particle_partype_001': (1501, 10, 5, 4), + 'solution_solid_partype_000': (1501, 10, 5, 4), + 'solution_solid_partype_001': (1501, 10, 5, 4), 'solution_flux': (1501, 2, 10, 4), 'soldot_inlet': (1501, 4), 'soldot_outlet': (1501, 4), 'soldot_bulk': (1501, 10, 4), - 'soldot_particle_partype_000': (1501, 10, 4, 4), - 'soldot_particle_partype_001': (1501, 10, 4, 4), - 'soldot_solid_partype_000': (1501, 10, 4, 4), - 'soldot_solid_partype_001': (1501, 10, 4, 4), + 'soldot_particle_partype_000': (1501, 10, 5, 4), + 'soldot_particle_partype_001': (1501, 10, 5, 4), + 'soldot_solid_partype_000': (1501, 10, 5, 4), + 'soldot_solid_partype_001': (1501, 10, 5, 4), 'soldot_flux': (1501, 2, 10, 4), }, 'solution_unit_001': { @@ -1020,37 +1032,41 @@ def __repr__(self): solution_recorder_options=no_split, expected_results={ 'solution_times': (1501,), - 'last_state_y': (1228,), - 'last_state_ydot': (1228,), + 'last_state_y': (1468,), + 'last_state_ydot': (1468,), 'coordinates_unit_000': { 'axial_coordinates': (10,), - 'particle_coordinates_000': (4,), + 'particle_coordinates_000': (5,), 'radial_coordinates': (3,), }, 'coordinates_unit_001': {}, 'solution_unit_000': { - 'last_state_y': (1212,), - 'last_state_ydot': (1212,), - 'soldot_bulk': (1501, 10, 3, 4), - 'soldot_flux': (1501, 1, 10, 3, 4), - 'soldot_inlet': (1501, 3, 4), - 'soldot_outlet': (1501, 3, 4), - 'soldot_particle': (1501, 10, 3, 4, 4), - 'soldot_solid': (1501, 10, 3, 4, 4), - 'solution_bulk': (1501, 10, 3, 4), - 'solution_flux': (1501, 1, 10, 3, 4), + 'last_state_y': (1452,), + 'last_state_ydot': (1452,), + 'solution_inlet': (1501, 3, 4), 'solution_outlet': (1501, 3, 4), - 'solution_particle': (1501, 10, 3, 4, 4), - 'solution_solid': (1501, 10, 3, 4, 4), + 'solution_bulk': (1501, 10, 3, 4), + 'solution_particle': (1501, 10, 3, 5, 4), + 'solution_solid': (1501, 10, 3, 5, 4), + 'solution_flux': (1501, 1, 10, 3, 4), + + 'soldot_inlet': (1501, 3, 4), + 'soldot_outlet': (1501, 3, 4), + 'soldot_bulk': (1501, 10, 3, 4), + 'soldot_particle': (1501, 10, 3, 5, 4), + 'soldot_solid': (1501, 10, 3, 5, 4), + 'soldot_flux': (1501, 1, 10, 3, 4), }, 'solution_unit_001': { 'last_state_y': (4,), 'last_state_ydot': (4,), - 'soldot_inlet': (1501, 4), - 'soldot_outlet': (1501, 4), + 'solution_inlet': (1501, 4), 'solution_outlet': (1501, 4), + + 'soldot_inlet': (1501, 4), + 'soldot_outlet': (1501, 4), }, }, ) @@ -1063,17 +1079,17 @@ def __repr__(self): solution_recorder_options=split_ports, expected_results={ 'solution_times': (1501,), - 'last_state_y': (1228,), - 'last_state_ydot': (1228,), + 'last_state_y': (1468,), + 'last_state_ydot': (1468,), 'coordinates_unit_000': { 'axial_coordinates': (10,), - 'particle_coordinates_000': (4,), + 'particle_coordinates_000': (5,), 'radial_coordinates': (3,), }, 'coordinates_unit_001': {}, 'solution_unit_000': { - 'last_state_y': (1212,), - 'last_state_ydot': (1212,), + 'last_state_y': (1452,), + 'last_state_ydot': (1452,), 'solution_inlet_port_000': (1501, 4), 'solution_inlet_port_001': (1501, 4), @@ -1082,8 +1098,8 @@ def __repr__(self): 'solution_outlet_port_001': (1501, 4), 'solution_outlet_port_002': (1501, 4), 'solution_bulk': (1501, 10, 3, 4), - 'solution_particle': (1501, 10, 3, 4, 4), - 'solution_solid': (1501, 10, 3, 4, 4), + 'solution_particle': (1501, 10, 3, 5, 4), + 'solution_solid': (1501, 10, 3, 5, 4), 'solution_flux': (1501, 1, 10, 3, 4), 'soldot_inlet_port_000': (1501, 4), @@ -1093,8 +1109,8 @@ def __repr__(self): 'soldot_outlet_port_001': (1501, 4), 'soldot_outlet_port_002': (1501, 4), 'soldot_bulk': (1501, 10, 3, 4), - 'soldot_particle': (1501, 10, 3, 4, 4), - 'soldot_solid': (1501, 10, 3, 4, 4), + 'soldot_particle': (1501, 10, 3, 5, 4), + 'soldot_solid': (1501, 10, 3, 5, 4), 'soldot_flux': (1501, 1, 10, 3, 4), }, @@ -1119,17 +1135,17 @@ def __repr__(self): solution_recorder_options=split_all, expected_results={ 'solution_times': (1501,), - 'last_state_y': (1228,), - 'last_state_ydot': (1228,), + 'last_state_y': (1468,), + 'last_state_ydot': (1468,), 'coordinates_unit_000': { 'axial_coordinates': (10,), - 'particle_coordinates_000': (4,), + 'particle_coordinates_000': (5,), 'radial_coordinates': (3,), }, 'coordinates_unit_001': {}, 'solution_unit_000': { - 'last_state_y': (1212,), - 'last_state_ydot': (1212,), + 'last_state_y': (1452,), + 'last_state_ydot': (1452,), 'solution_inlet_port_000_comp_000': (1501,), 'solution_inlet_port_000_comp_001': (1501,), @@ -1156,8 +1172,8 @@ def __repr__(self): 'solution_outlet_port_002_comp_002': (1501,), 'solution_outlet_port_002_comp_003': (1501,), 'solution_bulk': (1501, 10, 3, 4), - 'solution_particle': (1501, 10, 3, 4, 4), - 'solution_solid': (1501, 10, 3, 4, 4), + 'solution_particle': (1501, 10, 3, 5, 4), + 'solution_solid': (1501, 10, 3, 5, 4), 'solution_flux': (1501, 1, 10, 3, 4), 'soldot_inlet_port_000_comp_000': (1501,), @@ -1185,8 +1201,8 @@ def __repr__(self): 'soldot_outlet_port_002_comp_002': (1501,), 'soldot_outlet_port_002_comp_003': (1501,), 'soldot_bulk': (1501, 10, 3, 4), - 'soldot_particle': (1501, 10, 3, 4, 4), - 'soldot_solid': (1501, 10, 3, 4, 4), + 'soldot_particle': (1501, 10, 3, 5, 4), + 'soldot_solid': (1501, 10, 3, 5, 4), 'soldot_flux': (1501, 1, 10, 3, 4), }, 'solution_unit_001': { From ad35d65a656a731dc7779b83337e4029da36dab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Schm=C3=B6lder?= Date: Sun, 8 Dec 2024 12:34:56 +0100 Subject: [PATCH 5/8] Formatting --- cadet/cadet.py | 31 ++-- cadet/cadet_dll_parameterprovider.py | 5 + cadet/cadet_dll_utils.py | 235 +++++++++++++++------------ 3 files changed, 154 insertions(+), 117 deletions(-) diff --git a/cadet/cadet.py b/cadet/cadet.py index 4de20f1..5a37840 100644 --- a/cadet/cadet.py +++ b/cadet/cadet.py @@ -474,55 +474,56 @@ def inverse_transform(self, x: str) -> str: """ return str.lower(x) - def run( + def run_load( self, timeout: Optional[int] = None, + clear: bool = True ) -> ReturnInformation: """ - Run the CADET simulation. + Run the CADET simulation and load the results. Parameters ---------- timeout : Optional[int] Maximum time allowed for the simulation to run, in seconds. + clear : bool + If True, clear previous results after loading new ones. Returns ------- ReturnInformation Information about the simulation run. """ - return_information = self.cadet_runner.run( - self, - timeout=timeout, - ) + return_information = self.run(timeout) + print(return_information) + self.load_results() + if clear: + self.clear() return return_information - def run_load( + def run( self, timeout: Optional[int] = None, - clear: bool = True ) -> ReturnInformation: """ - Run the CADET simulation and load the results. + Run the CADET simulation. Parameters ---------- timeout : Optional[int] Maximum time allowed for the simulation to run, in seconds. - clear : bool - If True, clear previous results after loading new ones. Returns ------- ReturnInformation Information about the simulation run. """ - return_information = self.run(timeout) - self.load_results() + return_information = self.cadet_runner.run( + self, + timeout=timeout, + ) - if clear: - self.clear() return return_information def load_results(self) -> None: diff --git a/cadet/cadet_dll_parameterprovider.py b/cadet/cadet_dll_parameterprovider.py index 9b95153..2615580 100644 --- a/cadet/cadet_dll_parameterprovider.py +++ b/cadet/cadet_dll_parameterprovider.py @@ -133,23 +133,28 @@ def __init__(self, simulation: "Cadet") -> None: self.popScope = self._fields_[17][1](utils.param_provider_pop_scope) _fields_ = [ + # 0 (Position must match indices in __init__ method.) ('userData', ctypes.py_object), + # 1 ('getDouble', ctypes.CFUNCTYPE(c_cadet_result, ctypes.py_object, ctypes.c_char_p, ctypes.POINTER(ctypes.c_double))), ('getInt', ctypes.CFUNCTYPE(c_cadet_result, ctypes.py_object, ctypes.c_char_p, point_int)), ('getBool', ctypes.CFUNCTYPE(c_cadet_result, ctypes.py_object, ctypes.c_char_p, ctypes.POINTER(ctypes.c_uint8))), ('getString', ctypes.CFUNCTYPE(c_cadet_result, ctypes.py_object, ctypes.c_char_p, ctypes.POINTER(ctypes.c_char_p))), + # 5 ('getDoubleArray', ctypes.CFUNCTYPE(c_cadet_result, ctypes.py_object, ctypes.c_char_p, point_int, array_double)), ('getIntArray', ctypes.CFUNCTYPE(c_cadet_result, ctypes.py_object, ctypes.c_char_p, point_int, ctypes.POINTER(point_int))), ('getBoolArray', ctypes.CFUNCTYPE(c_cadet_result, ctypes.py_object, ctypes.c_char_p, point_int, ctypes.POINTER(ctypes.POINTER(ctypes.c_uint8)))), ('getStringArray', ctypes.CFUNCTYPE(c_cadet_result, ctypes.py_object, ctypes.c_char_p, point_int, ctypes.POINTER(ctypes.POINTER(ctypes.c_char_p)))), + # 9 ('getDoubleArrayItem', ctypes.CFUNCTYPE(c_cadet_result, ctypes.py_object, ctypes.c_char_p, ctypes.c_int, ctypes.POINTER(ctypes.c_double))), ('getIntArrayItem', ctypes.CFUNCTYPE(c_cadet_result, ctypes.py_object, ctypes.c_char_p, ctypes.c_int, point_int)), ('getBoolArrayItem', ctypes.CFUNCTYPE(c_cadet_result, ctypes.py_object, ctypes.c_char_p, ctypes.c_int, ctypes.POINTER(ctypes.c_uint8))), ('getStringArrayItem', ctypes.CFUNCTYPE(c_cadet_result, ctypes.py_object, ctypes.c_char_p, ctypes.c_int, ctypes.POINTER(ctypes.c_char_p))), + # 13 ('exists', ctypes.CFUNCTYPE(ctypes.c_int, ctypes.py_object, ctypes.c_char_p)), ('isArray', ctypes.CFUNCTYPE(c_cadet_result, ctypes.py_object, ctypes.c_char_p, ctypes.POINTER(ctypes.c_uint8))), ('numElements', ctypes.CFUNCTYPE(ctypes.c_int, ctypes.py_object, ctypes.c_char_p)), diff --git a/cadet/cadet_dll_utils.py b/cadet/cadet_dll_utils.py index 204b52f..d61596a 100644 --- a/cadet/cadet_dll_utils.py +++ b/cadet/cadet_dll_utils.py @@ -1,14 +1,19 @@ import ctypes +from typing import Any + import numpy as np -from typing import Any, Optional def null(*args: Any) -> None: """Do nothing (used as a placeholder function).""" pass + log_print = print if 0 else null + +# %% Single entries + def param_provider_get_double( reader: Any, name: ctypes.c_char_p, @@ -34,18 +39,19 @@ def param_provider_get_double( n = name.decode('utf-8') c = reader.current() - if n in c: - o = c[n] - try: - float_val = float(o) - except TypeError: - float_val = float(o[0]) + if n not in c: + log_print(f"Parameter {n} not found.") + return -1 - val[0] = ctypes.c_double(float_val) - log_print(f"GET scalar [double] {n}: {float(val[0])}") - return 0 + o = c[n] + try: + float_val = float(o) + except TypeError: + float_val = float(o[0]) - return -1 + val[0] = ctypes.c_double(float_val) + log_print(f"GET scalar [double] {n}: {float(val[0])}") + return 0 def param_provider_get_int( @@ -73,18 +79,20 @@ def param_provider_get_int( n = name.decode('utf-8') c = reader.current() - if n in c: - o = c[n] - try: - int_val = int(o) - except TypeError: - int_val = int(o[0]) + if n not in c: + log_print(f"Parameter {n} not found.") + return -1 - val[0] = ctypes.c_int(int_val) - log_print(f"GET scalar [int] {n}: {int(val[0])}") - return 0 + o = c[n] + try: + int_val = int(o) + except TypeError: + int_val = int(o[0]) - return -1 + val[0] = ctypes.c_int(int_val) + + log_print(f"GET scalar [int] {n}: {int(val[0])}") + return 0 def param_provider_get_bool( @@ -112,18 +120,20 @@ def param_provider_get_bool( n = name.decode('utf-8') c = reader.current() - if n in c: - o = c[n] - try: - int_val = int(o) - except TypeError: - int_val = int(o[0]) + if n not in c: + log_print(f"Parameter {n} not found.") + return -1 - val[0] = ctypes.c_uint8(int_val) - log_print(f"GET scalar [bool] {n}: {bool(val[0])}") - return 0 + o = c[n] + try: + int_val = int(o) + except TypeError: + int_val = int(o[0]) - return -1 + val[0] = ctypes.c_uint8(int_val) + + log_print(f"GET scalar [bool] {n}: {bool(val[0])}") + return 0 def param_provider_get_string( @@ -151,25 +161,28 @@ def param_provider_get_string( n = name.decode('utf-8') c = reader.current() - if n in c: - o = c[n] + if n not in c: + log_print(f"Parameter {n} not found.") + return -1 - if hasattr(o, 'encode'): - bytes_val = o.encode('utf-8') - elif hasattr(o, 'decode'): - bytes_val = o - elif hasattr(o[0], 'encode'): - bytes_val = o[0].encode('utf-8') - elif hasattr(o[0], 'decode'): - bytes_val = o[0] - - reader.buffer = bytes_val - val[0] = ctypes.cast(reader.buffer, ctypes.c_char_p) - return 0 + o = c[n] - return -1 + if hasattr(o, 'encode'): + bytes_val = o.encode('utf-8') + elif hasattr(o, 'decode'): + bytes_val = o + elif hasattr(o[0], 'encode'): + bytes_val = o[0].encode('utf-8') + elif hasattr(o[0], 'decode'): + bytes_val = o[0] + + reader.buffer = bytes_val + val[0] = ctypes.cast(reader.buffer, ctypes.c_char_p) + return 0 +# %% Arrays + def param_provider_get_double_array( reader: Any, name: ctypes.c_char_p, @@ -256,10 +269,13 @@ def param_provider_get_int_array( return -1 +# %% Array items + def param_provider_get_double_array_item( reader: Any, name: ctypes.c_char_p, - index: int, val: ctypes.POINTER(ctypes.c_double) + index: int, + val: ctypes.POINTER(ctypes.c_double) ) -> int: """ Retrieve an item from a double array in the reader based on the provided name and index. @@ -283,25 +299,28 @@ def param_provider_get_double_array_item( n = name.decode('utf-8') c = reader.current() - if n in c: - o = c[n] + if n not in c: + log_print(f"Parameter {n} not found.") + return -1 - try: - float_val = float(o) - except TypeError: - float_val = float(o[index]) + o = c[n] - val[0] = ctypes.c_double(float_val) - log_print(f"GET array [double] ({index}) {n}: {val[0]}") - return 0 + try: + float_val = float(o) + except TypeError: + float_val = float(o[index]) - return -1 + val[0] = ctypes.c_double(float_val) + + log_print(f"GET array [double] ({index}) {n}: {val[0]}") + return 0 def param_provider_get_int_array_item( reader: Any, name: ctypes.c_char_p, - index: int, val: ctypes.POINTER(ctypes.c_int) + index: int, + val: ctypes.POINTER(ctypes.c_int) ) -> int: """ Retrieve an item from an integer array in the reader based on the provided name and index. @@ -325,25 +344,28 @@ def param_provider_get_int_array_item( n = name.decode('utf-8') c = reader.current() - if n in c: - o = c[n] + if n not in c: + log_print(f"Parameter {n} not found.") + return -1 - try: - int_val = int(o) - except TypeError: - int_val = int(o[index]) + o = c[n] - val[0] = ctypes.c_int(int_val) - log_print(f"GET array [int] ({index}) {n}: {val[0]}") - return 0 + try: + int_val = int(o) + except TypeError: + int_val = int(o[index]) - return -1 + val[0] = ctypes.c_int(int_val) + + log_print(f"GET array [int] ({index}) {n}: {val[0]}") + return 0 def param_provider_get_bool_array_item( reader: Any, name: ctypes.c_char_p, - index: int, val: ctypes.POINTER(ctypes.c_uint8) + index: int, + val: ctypes.POINTER(ctypes.c_uint8) ) -> int: """ Retrieve an item from a boolean array in the reader based on the provided name and index. @@ -367,25 +389,28 @@ def param_provider_get_bool_array_item( n = name.decode('utf-8') c = reader.current() - if n in c: - o = c[n] + if n not in c: + log_print(f"Parameter {n} not found.") + return -1 - try: - int_val = int(o) - except TypeError: - int_val = int(o[index]) + o = c[n] - val[0] = ctypes.c_uint8(int_val) - log_print(f"GET array [bool] ({index}) {n}: {bool(val[0])}") - return 0 + try: + int_val = int(o) + except TypeError: + int_val = int(o[index]) - return -1 + val[0] = ctypes.c_uint8(int_val) + + log_print(f"GET array [bool] ({index}) {n}: {bool(val[0])}") + return 0 def param_provider_get_string_array_item( reader: Any, name: ctypes.c_char_p, - index: int, val: ctypes.POINTER(ctypes.c_char_p) + index: int, + val: ctypes.POINTER(ctypes.c_char_p) ) -> int: """ Retrieve an item from a string array in the reader based on the provided name and index. @@ -406,30 +431,34 @@ def param_provider_get_string_array_item( int 0 if the value was found and retrieved successfully, -1 otherwise. """ - name_str = name.decode('utf-8') - current_reader = reader.current() - - if name_str in current_reader: - str_value = current_reader[name_str] - if isinstance(str_value, bytes): - bytes_val = str_value - elif isinstance(str_value, str): - bytes_val = str_value.encode('utf-8') - elif isinstance(str_value, np.ndarray): - bytes_val = str_value[index] - else: - raise TypeError( - "Unexpected type for str_value. " - "Must be of type bytes, str, or np.ndarray." - ) - - reader.buffer = bytes_val - val[0] = ctypes.cast(reader.buffer, ctypes.c_char_p) - log_print(f"GET array [string] ({index}) {name_str}: {reader.buffer.decode('utf-8')}") - return 0 + n = name.decode('utf-8') + c = reader.current() + + if n not in c: + log_print(f"Parameter {n} not found.") + return -1 + + o = c[n] + if isinstance(o, bytes): + bytes_val = o + elif isinstance(o, str): + bytes_val = o.encode('utf-8') + elif isinstance(o, np.ndarray): + bytes_val = o[index] + else: + raise TypeError( + "Unexpected type for name {n}: {type(o)}. " + "Must be of type bytes, str, or np.ndarray." + ) + + reader.buffer = bytes_val + val[0] = ctypes.cast(reader.buffer, ctypes.c_char_p) + + log_print(f"GET array [string] ({index}) {n}: {reader.buffer.decode('utf-8')}") + return 0 - return -1 +# %% Misc def param_provider_exists( reader: Any, @@ -484,6 +513,7 @@ def param_provider_is_array( c = reader.current() if n not in c: + log_print(f"Parameter {n} not found.") return -1 o = c[n] @@ -516,6 +546,7 @@ def param_provider_num_elements( c = reader.current() if n not in c: + log_print(f"Parameter {n} not found.") return -1 o = c[n] From 896343bcbaf22e17d08a8bc52a8d2514eda85859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Schm=C3=B6lder?= Date: Wed, 11 Dec 2024 15:16:44 +0100 Subject: [PATCH 6/8] Do not load results if return_code != 0 --- cadet/cadet.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cadet/cadet.py b/cadet/cadet.py index 5a37840..614bd21 100644 --- a/cadet/cadet.py +++ b/cadet/cadet.py @@ -495,8 +495,9 @@ def run_load( Information about the simulation run. """ return_information = self.run(timeout) - print(return_information) - self.load_results() + + if return_information.return_code == 0: + self.load_results() if clear: self.clear() From 2fc60e2453d31e75b91138ba82d9f25f028b14d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Schm=C3=B6lder?= Date: Thu, 12 Dec 2024 18:07:07 +0100 Subject: [PATCH 7/8] Require CADET-Core >=v5.0.3 in CI --- .github/workflows/pipeline.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index a7fb3c5..898eae8 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -62,5 +62,5 @@ jobs: - name: Test with pytest run: | pip install .[testing] - mamba install cadet>5.0.2 -c conda-forge + mamba install -c conda-forge cadet>=5.0.3 pytest tests --rootdir=tests -m "not slow and not local" From c8dada7a356fec1150d1939ab5646f7c92cbe577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Schm=C3=B6lder?= Date: Thu, 12 Dec 2024 18:40:21 +0100 Subject: [PATCH 8/8] Update the parameter reader's storage to avoid inconsistencies --- cadet/cadet_dll_utils.py | 64 +++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/cadet/cadet_dll_utils.py b/cadet/cadet_dll_utils.py index d61596a..82fda34 100644 --- a/cadet/cadet_dll_utils.py +++ b/cadet/cadet_dll_utils.py @@ -211,19 +211,28 @@ def param_provider_get_double_array( n = name.decode('utf-8') c = reader.current() - if n in c: - o = c[n] - if isinstance(o, list): - o = np.ascontiguousarray(o) - if not isinstance(o, np.ndarray) or o.dtype != np.double or not o.flags.c_contiguous: - return -1 - - n_elem[0] = ctypes.c_int(o.size) - val[0] = o.ctypes.data_as(ctypes.POINTER(ctypes.c_double)) - log_print(f"GET array [double] {n}: {o}") - return 0 + if n not in c: + log_print(f"Parameter {n} not found.") + return -1 + + o = c[n] - return -1 + # Ensure object is a properly aligned numpy array + if isinstance(o, list): # Convert lists to numpy arrays + o = np.array(o, dtype=np.double) + c[n] = o # Update the reader's storage + + # Validate the array + if not isinstance(o, np.ndarray) or o.dtype != np.double or not o.flags.c_contiguous: + log_print(f"Error: Parameter {n} is not a contiguous double array.") + return -1 + + # Provide array data to the caller + n_elem[0] = ctypes.c_int(o.size) + val[0] = np.ctypeslib.as_ctypes(o) + + log_print(f"GET array [double] {n}: {o}") + return 0 def param_provider_get_int_array( @@ -254,19 +263,28 @@ def param_provider_get_int_array( n = name.decode('utf-8') c = reader.current() - if n in c: - o = c[n] - if isinstance(o, list): - o = np.ascontiguousarray(o) - if not isinstance(o, np.ndarray) or o.dtype != int or not o.flags.c_contiguous: - return -1 + if n not in c: + log_print(f"Parameter {n} not found.") + return -1 - n_elem[0] = ctypes.c_int(o.size) - val[0] = o.ctypes.data_as(ctypes.POINTER(ctypes.c_int)) - log_print(f"GET array [int] {n}: {o}") - return 0 + o = c[n] - return -1 + # Ensure object is a properly aligned numpy array + if isinstance(o, list): # Convert lists to numpy arrays + o = np.array(o, dtype=np.double) + c[n] = o # Update the reader's storage + + # Validate the array + if not isinstance(o, np.ndarray) or o.dtype != np.int32 or not o.flags.c_contiguous: + log_print(f"Error: Parameter {n} is not a contiguous int array.") + return -1 + + # Provide array data to the caller + n_elem[0] = ctypes.c_int(o.size) + val[0] = np.ctypeslib.as_ctypes(o) + + log_print(f"GET array [int] {n}: {o}") + return 0 # %% Array items