diff --git a/src/main/python/utils/UnitOperations.py b/src/main/python/utils/UnitOperations.py index 353da9a..671b78b 100644 --- a/src/main/python/utils/UnitOperations.py +++ b/src/main/python/utils/UnitOperations.py @@ -104,13 +104,9 @@ def OM_Flowsheet_Initialize(self): self.OM_data_init = '' if(self.thermo_pack_req): - if len(self.extra)>1: - for i in range(len(self.extra)): - latest = '' - for j in range(self.extra[i]): - if self.extra[i][j]!='.': - latest += self.extra[i][j] - self.for_naming[i] = latest + if len(self.extra)>1: + # Modelica model names cannot contain '.', e.g. 'DistillationColumn.Cond' + self.for_naming = [e.replace('.', '') for e in self.extra] if(self.thermo_pack_req): if len(self.extra)==1: @@ -133,10 +129,9 @@ def OM_Flowsheet_Initialize(self): self.OM_data_init += ('extends Simulator.UnitOperations.'+self.extra[i]+';\n') for j in range(len(self.extra)-1): self.OM_data_init += (self.for_naming[j] + str(self.counter) +' ' + self.for_naming[j] + '#' + self.multidict[j] + ';\n') + self.OM_data_init += ('end '+self.for_naming[i]+str(self.counter)+';\n') - self.OM_data_init += ('end '+self.for_naming[i]+str(self.counter)+';\n') - - self.OM_data_init += self.for_naming[i] + str(self.counter) + ' ' + self.for_naming + '(Nc = ' + str(len(self.compounds)) + self.OM_data_init += self.for_naming[-1] + str(self.counter) + ' ' + self.name + '(Nc = ' + str(len(self.compounds)) C = ', '.join(_normalize_compound_name(c) for c in self.compounds) self.OM_data_init += ',C = {' + C + '}' @@ -426,9 +421,12 @@ def OM_Flowsheet_Equation(self): self.OM_data_eqn = self.OM_data_eqn + (self.output_stms[1].name + '.F_pc[1,:' + str(self.compounds.index(self.variables['C_Spec']['comp']) + 1) + '] = ' + str( self.variables['C_Spec']['value']) + ';\n') - else: + elif self.mode: self.OM_data_eqn = self.OM_data_eqn + ( - self.name + '.Condenser.' + self.mode + '=' + str(self.modeVal) + ';\n') + self.name + '.condenser.' + self.mode + '=' + str(self.mode_val) + ';\n') + else: + raise ValueError( + "Unsupported condenser specification: " + str(self.variables['C_Spec']['type'])) if self.variables['R_Spec']['type'] == "Product Molar Flow (mol/s)": self.OM_data_eqn = self.OM_data_eqn + (self.output_stms[2].name + '.' + 'F_p[1] = ' + str( diff --git a/tests/test_unit_operations.py b/tests/test_unit_operations.py index 64ff62a..190ab47 100644 --- a/tests/test_unit_operations.py +++ b/tests/test_unit_operations.py @@ -1,15 +1,17 @@ import sys import os import unittest +from types import SimpleNamespace # Ensure Python can find modules under src/main/python sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'src', 'main', 'python'))) from utils.UnitOperations import ( - Mixer, Splitter, Heater, Cooler, Flash, CentrifugalPump, Valve, + UnitOperation, Mixer, Splitter, Heater, Cooler, Flash, CentrifugalPump, Valve, AdiabaticCompressor, AdiabaticExpander, CompoundSeparator, ShortcutColumn, DistillationColumn, ConvertionReactor ) +from python.utils.ComponentSelector import compound_selected class TestUnitOperations(unittest.TestCase): @@ -172,5 +174,56 @@ def test_conversion_reactor_initialization(self): self.assertEqual(reactor.no_of_outputs, 1) +class TestUnitOperationCodeGeneration(unittest.TestCase): + """Regression tests for Modelica code paths that used to crash.""" + + def setUp(self): + self._saved_compounds = list(compound_selected) + compound_selected[:] = ["Water(chemsep)", "Ethanol(chemsep)"] + + def tearDown(self): + compound_selected[:] = self._saved_compounds + + def test_initialize_with_multiple_thermo_models(self): + unit = UnitOperation() + unit.name = 'U1' + unit.thermo_pack_req = True + unit.extra = ['DistillationColumn.Cond', 'DistillationColumn.DistCol'] + unit.multidict = ['x'] + unit.parameters = ['Nt'] + unit.variables = {'Nt': {'value': 12}} + c = UnitOperation.counter + + self.assertEqual( + unit.OM_Flowsheet_Initialize(), + f'model DistillationColumnCond{c}\n' + 'extends Simulator.UnitOperations.DistillationColumn.Cond;\n' + 'extends Simulator.Files.ThermodynamicPackages.RaoultsLaw;\n' + f'end DistillationColumnCond{c};\n' + f'model DistillationColumnDistCol{c}\n' + 'extends Simulator.UnitOperations.DistillationColumn.DistCol;\n' + f'DistillationColumnCond{c} DistillationColumnCond#x;\n' + f'end DistillationColumnDistCol{c};\n' + f'DistillationColumnDistCol{c} U1(Nc = 2,C = {{Water, Ethanol}}, Nt = 12);\n', + ) + + def _column_with_condenser_spec(self, spec): + column = DistillationColumn() + column.variables['C_Spec']['type'] = spec + column.input_stms = {1: SimpleNamespace(name='F')} + column.output_stms = {1: SimpleNamespace(name='D'), 2: SimpleNamespace(name='B')} + return column + + def test_condenser_fallback_uses_selected_mode(self): + column = self._column_with_condenser_spec('Custom') + column.mode, column.mode_val = 'T', 350 + self.assertIn(f'{column.name}.condenser.T=350;\n', column.OM_Flowsheet_Equation()) + + def test_unknown_condenser_spec_without_mode_raises(self): + column = self._column_with_condenser_spec('Custom') + with self.assertRaisesRegex(ValueError, 'Unsupported condenser specification: Custom'): + column.OM_Flowsheet_Equation() + + if __name__ == '__main__': unittest.main()