diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index a696d60..03d9d8a 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -13,7 +13,7 @@ on: jobs: test-job: runs-on: ubuntu-latest - + defaults: run: shell: bash -l {0} @@ -23,7 +23,7 @@ jobs: env: CONDA_FILE: environment.yml - + steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} @@ -51,4 +51,4 @@ jobs: - name: Test run: | - pytest + pytest \ No newline at end of file diff --git a/CADETPythonSimulator/componentsystem.py b/CADETPythonSimulator/componentsystem.py new file mode 100644 index 0000000..d790180 --- /dev/null +++ b/CADETPythonSimulator/componentsystem.py @@ -0,0 +1,265 @@ +from CADETProcess.processModel import ComponentSystem, Component, Species +from CADETProcess.dataStructure import UnsignedFloat, String, Integer +from CADETProcess.dataStructure import Structure + +from CADETPythonSimulator.exception import CADETPythonSimError +from functools import wraps + +class CPSSpecies(Structure): + """Species class. + + Represent a species in a chemical system. + Same as in cadet Process but with added + density and Volume + + Attributes + ---------- + name : str + The name of the species. + charge : int, optional + The charge of the species. Default is 0. + molecular_weight : float + The molecular weight of the species. + density : float + Density of the species. + molecular_volume : float + The molecular volume of the species + + """ + name = String() + charge = Integer(default=0) + molecular_weight = UnsignedFloat() + density = UnsignedFloat() + molecular_volume = UnsignedFloat() + +class CPSComponent(Component): + """Information about single component. + Inherits from CadetProcess Component + Same function but with fixed molecular weight and added densities and volume + + A component can contain subspecies (e.g. differently charged variants). + + Attributes + ---------- + name : String + Name of the component. + species : list + List of Subspecies. + n_species : int + Number of Subspecies. + label : list + Name of component (including species). + charge : list + Charge of component (including species). + molecular_weight : list + Molecular weight of component (including species). + density : list + density of component (including species). + molecular_volume : list + Molecular volume of component (including species). + + See Also + -------- + Species + ComponentSystem + + """ + def __init__(self, + name=None, + species=None, + charge=None, + molecular_weight=None, + density=None, + molecular_volume=None): + + self.name = name + self._species = [] + + if species is None: + self.add_species(name, charge, molecular_weight, density, molecular_volume) + elif isinstance(species, str): + self.add_species(species, + charge, molecular_weight, density, molecular_volume) + elif isinstance(species, list): + if charge is None: + charge = len(species) * [None] + if molecular_weight is None: + molecular_weight = len(species) * [None] + if density is None: + density = len(species) * [None] + if molecular_volume is None: + molecular_volume = len(species) * [None] + for i, spec in enumerate(species): + self.add_species(spec, + charge[i], molecular_weight[i], density[i], molecular_volume[i]) + else: + raise CADETPythonSimError("Could not determine number of species") + + def add_species(self, species, *args, **kwargs): + if not isinstance(species, CPSSpecies): + species = CPSSpecies(species, *args, **kwargs) + self._species.append(species) + + @property + def molecular_volume(self): + """list of float or None: The molecular volume of the subspecies.""" + return [spec.molecular_volume for spec in self.species] + + @property + def density(self): + """list of float or None: The density of the subspecies.""" + return [spec.density for spec in self.species] + + @property + def molecular_weight(self): + """list of float or None: The molecular weights of the subspecies.""" + return [spec.molecular_weight for spec in self.species] + +class CPSComponentSystem(ComponentSystem): + """Information about components in system. Inherits from Component System. Adds + molecular Volume to the Component System. + + A component can contain subspecies (e.g. differently charged variants). + + Attributes + ---------- + name : String + Name of the component system. + components : list + List of individual components. + n_species : int + Number of Subspecies. + n_comp : int + Number of all component species. + n_components : int + Number of components. + indices : dict + Component indices. + names : list + Names of all components. + species : list + Names of all component species. + charge : list + Charges of all components species. + molecular_weight : list + Molecular weights of all component species. + molecular_volume : list + Molecular volume of all component species. + + See Also + -------- + Species + Component + + """ + + def __init__( + self, + components=None, + name=None, + charges=None, + molecular_weights=None, + densities=None, + molecular_volume=None + ): + """Initialize the ComponentSystem object. + + Parameters + ---------- + components : int, list, None + The number of components or the list of components to be added. + If None, no components are added. + name : str, None + The name of the ComponentSystem. + charges : list, None + The charges of each component. + molecular_weights : list, None + The molecular weights of each component. + densities : list, None + Densities of each component + molecular_volume : list, None + The molecular volume of each component. + + Raises + ------ + CADETProcessError + If the `components` argument is neither an int nor a list. + + """ + + self.name = name + + self._components = [] + + if components is None: + return + + if isinstance(components, int): + n_comp = components + components = [str(i) for i in range(n_comp)] + elif isinstance(components, list): + n_comp = len(components) + else: + raise CADETPythonSimError("Could not determine number of components") + + if charges is None: + charges = n_comp * [None] + if molecular_weights is None: + molecular_weights = n_comp * [None] + if densities is None: + densities = n_comp * [None] + if molecular_volume is None: + molecular_volume = n_comp * [None] + + for i, comp in enumerate(components): + self.add_component( + comp, + charge=charges[i], + molecular_weight=molecular_weights[i], + density=densities[i], + molecular_volume=molecular_volume[i] + ) + + @wraps(CPSComponent.__init__) + def add_component(self, component, *args, **kwargs): + """ + Add a component to the system. + + Parameters + ---------- + component : {str, Component} + The class of the component to be added. + *args : list + The positional arguments to be passed to the component class's constructor. + **kwargs : dict + The keyword arguments to be passed to the component class's constructor. + + """ + if not isinstance(component, CPSComponent): + component = CPSComponent(component, *args, **kwargs) + + if component.name in self.names: + raise CADETPythonSimError( + f"Component '{component.name}' " + "already exists in ComponentSystem." + ) + + self._components.append(component) + + @property + def molecular_volumes(self): + """list: List of species molecular volumes.""" + molecular_volumes = [] + for comp in self.components: + molecular_volumes += comp.molecular_volume + + return molecular_volumes + + @property + def densities(self): + """list: List of species densities.""" + densities = [] + for comp in self.components: + densities += comp.density + + return densities diff --git a/CADETPythonSimulator/exception.py b/CADETPythonSimulator/exception.py index 26c9181..b5e246b 100644 --- a/CADETPythonSimulator/exception.py +++ b/CADETPythonSimulator/exception.py @@ -5,4 +5,4 @@ class CADETPythonSimError(Exception): class NotInitializedError(CADETPythonSimError): """Exception raised when a unit operation is not yet initialized.""" - pass + pass \ No newline at end of file diff --git a/CADETPythonSimulator/residual.py b/CADETPythonSimulator/residual.py index cd0276c..f182d04 100644 --- a/CADETPythonSimulator/residual.py +++ b/CADETPythonSimulator/residual.py @@ -70,13 +70,81 @@ def calculate_residual_concentration_cstr( return c_dot * V + V_dot * c - Q_in * c_in + Q_out * c -def calculate_residuals_visc_cstr(): - """Calculate the residual of the Viscosity equation of the CSTR.""" +def calculate_residual_visc_cstr(): + """ + Calculates the residual of the Viscosity equation of the CSTR + """ warnings.warn("Viscosity of CSTR not yet implemented") return 0 -def calculate_residual_def(): - """Calculate the residual equations fo a dead end filtration equation.""" - raise NotImplementedError +def calculate_residual_cake_vol_def( + V_dot_f : float, + rejection : np.ndarray, + molar_volume : np.ndarray, + c_in : np.ndarray, + V_dot_C : float + ) -> float: + """ + Residual equation for the Volume + + Parameters + ---------- + V_dot_f : float + Flowrate of incoming feed + rejection : float + Rejection of the filter + gamma : float + Portion of suspended material + V_dot_C : float + Change of Cake Volume + """ + + return -V_dot_C + np.sum(rejection * molar_volume * c_in * V_dot_f) + + +def calculate_residual_press_easy_def( + V_dot_P : float, + V_C : float, + deltap : float, + A :float, + mu : float, + Rm : float, + alpha : float + ) -> float: + """ + Calculates the residual equations fo a dead end filtration equation for the pressure + in the easy model. + + Parameters + ---------- + V_dot_P : np.ndarray + FLow of the Permeate through the membrane and Cake + V_C : float + Volume of the Cake + deltap : float + Pressure drop in this unit + A : float + Filtration area + mu : float + dynamic Viscosity + Rm : float + resistance of the medium + alpha : float + Specific cake resistance + """ + + hyd_resistance = (Rm + alpha*V_C/A) * mu + + return -V_dot_P + deltap * A *hyd_resistance + + + +def calculate_residual_visc_def(): + """ + Calculates the residual of the Viscosity equation of the CSTR + """ + warnings.warn("Viscosity of def not yet implemented") + + return 0 diff --git a/CADETPythonSimulator/system_solver.py b/CADETPythonSimulator/system_solver.py index fbd04ca..ce9d6f3 100644 --- a/CADETPythonSimulator/system_solver.py +++ b/CADETPythonSimulator/system_solver.py @@ -423,6 +423,7 @@ def couple_unit_operations( origin_port = origin_info['port'] y_origin_unit = y_initial[self.unit_slices[origin_unit]] + s_unit = origin_unit.get_outlet_state(y_origin_unit, origin_port) s_new += s_unit * Q_destination # Accumulate weighted states diff --git a/CADETPythonSimulator/unit_operation.py b/CADETPythonSimulator/unit_operation.py index be23726..1a2a1bd 100644 --- a/CADETPythonSimulator/unit_operation.py +++ b/CADETPythonSimulator/unit_operation.py @@ -11,10 +11,16 @@ ) from CADETProcess.dynamicEvents import Section +from CADETPythonSimulator.componentsystem import CPSComponentSystem from CADETPythonSimulator.exception import NotInitializedError, CADETPythonSimError from CADETPythonSimulator.state import State, state_factory from CADETPythonSimulator.residual import ( - calculate_residual_volume_cstr, calculate_residual_concentration_cstr, calculate_residuals_visc_cstr + calculate_residual_volume_cstr, + calculate_residual_concentration_cstr, + calculate_residual_visc_cstr, + calculate_residual_press_easy_def, + calculate_residual_cake_vol_def, + calculate_residual_visc_def ) from CADETPythonSimulator.rejection import RejectionBase from CADETPythonSimulator.cake_compressibility import CakeCompressibilityBase @@ -35,7 +41,7 @@ class UnitOperationBase(Structure): """ name = String() - component_system = Typed(ty=ComponentSystem) + component_system = Typed(ty=CPSComponentSystem) _state_structures = [] _parameters = [] @@ -109,7 +115,7 @@ def y(self, y: np.ndarray) -> NoReturn: @property def state_derivatives(self) -> dict[str, State]: - """dict: State derivative array blocks of the unit operation, indexed by name.""" + """dict: State derivative array block of the unit operation, indexed by name.""" if self._state_derivatives is None: raise NotInitializedError("Unit operation state is not yet initialized.") @@ -567,14 +573,16 @@ def compute_residual( Q_in = self.Q_in[0] Q_out = self.Q_out[0] - # for i in range(self.n_comp): - # self.residuals['bulk']['c'][i] = c_dot[i] * V + V_dot * c[i] - Q_in * c_in[i] + Q_out * c[i] - # Alternative: Can we vectorize this? - self.residuals['bulk']['c'] = calculate_residual_concentration_cstr(c, c_dot, V, V_dot, Q_in, Q_out, c_in) - self.residuals['bulk']['Volume'] = calculate_residual_volume_cstr(V, V_dot, Q_in, Q_out) + self.residuals['bulk']['c'] = calculate_residual_concentration_cstr( + c, c_dot, V, V_dot, Q_in, Q_out, c_in + ) + + self.residuals['bulk']['Volume'] = calculate_residual_volume_cstr( + V, V_dot, Q_in, Q_out + ) - self.residuals['inlet']['viscosity'] = calculate_residuals_visc_cstr() + self.residuals['inlet']['viscosity'] = calculate_residual_visc_cstr() class DeadEndFiltration(UnitOperationBase): """ @@ -592,9 +600,14 @@ class DeadEndFiltration(UnitOperationBase): Model for cake compressibility. """ - retentate = { + cake = { 'dimensions': (), - 'entries': {'c': 'n_comp', 'viscosity': 1, 'Rc': 1, 'mc': 'n_comp'}, + 'entries': {'c': 'n_comp', + 'viscosity': 1, + 'pressure': 1, + 'cakevolume': 1, + 'permeate': 1 + }, 'n_inlet_ports': 1, } permeate = { @@ -602,63 +615,107 @@ class DeadEndFiltration(UnitOperationBase): 'entries': {'c': 'n_comp', 'viscosity': 1, 'Volume': 1}, 'n_outlet_ports': 1, } - _state_structures = ['retentate', 'permeate'] - - rejection_model = Typed(ty=RejectionBase) - cake_compressibility_model = Typed(ty=CakeCompressibilityBase) + _state_structures = ['cake', 'permeate'] membrane_area = UnsignedFloat() membrane_resistance = UnsignedFloat() + specific_cake_resistance = UnsignedFloat() +# molar_volume = SizedUnsignedNdArray(size = 'n_comp') => component system erben von cadet process + rejection = Typed(ty=RejectionBase) _parameters = [ 'membrane_area', 'membrane_resistance', + 'specific_cake_resistance', + 'rejection' ] - def delta_p(self): - raise NotImplementedError() + def compute_residual( + self, + t: float, + ) -> NoReturn: - def specific_cake_resistance(self, delta_p: float) -> float: - """ - Compute specific resistance as a function of delta_p. + Q_in = self.Q_in[0] + Q_out = self.Q_out[0] - Parameters - ---------- - delta_p : float - Pressure difference. + c_in = self.states['cake']['c'] + c_in_dot = self.state_derivatives['cake']['c'] - Returns - ------- - float - Specific cake resistance. + V_C = self.states['cake']['cakevolume'] + V_dot_C = self.state_derivatives['cake']['cakevolume'] - """ - raise self.cake_compressibility_model.specific_cake_resistance(delta_p) + V_p = self.states['cake']['permeate'] + Q_p = self.state_derivatives['cake']['cakevolume'] - def compute_residual( - self, - t: float, - y: np.ndarray, - y_dot: np.ndarray, - residual: np.ndarray - ) -> NoReturn: - # 0, 1, 2 - # y = Vp, Rc, mc - # TODO: Needs to be extended to include c_in / c_out - # y = [*c_i_in], viscosity_in, Vp, Rc, mc, [*c_i_out], viscosity_out + viscosity_in = self.states['cake']['viscosity'] + + c = self.states['permeate']['c'] + c_dot = self.state_derivatives['permeate']['c'] - c_in = y[0: self.n_comp] - viscosity_in = y[self.n_comp] + V = self.states['permeate']['Volume'] + V_dot = self.state_derivatives['permeate']['Volume'] - densities = self.component_system.densities + deltap = self.states['cake']['pressure'] - residual[self.n_dof_coupling + 0] = ((self.membrane_area*self.delta_p(t)/viscosity_in)/(self.membrane_resistance+y[1])) - y_dot[0] - residual[self.n_dof_coupling + 1] = (1/self.membrane_area) * (y_dot[2] * self.specific_cake_resistance(self.p(t))) - y_dot[1] + #parameters + molecular_weights = self.component_system.molecular_weights + molar_volume = self.component_system.molecular_volumes + membrane_area = self.parameters['membrane_area'] + membrane_resistance = self.parameters['membrane_resistance'] + specific_cake_resistance = self.parameters['specific_cake_resistance'] - residual[self.n_dof_coupling + 2] = ((self.c(t) * y_dot[0]) / (1-self.c(t)/self.density)) - y_dot[2] + rejection = np.array( + [self.rejection.get_rejection(mw) for mw in molecular_weights]) - self.residuals['retentate'] - self.residuals['permeate'] + # Handle inlet DOFs, which are simply copied to the residual + self.residuals['cake']['c'] = c_in + self.residuals['cake']['cakevolume'] = calculate_residual_cake_vol_def( + Q_in, + rejection, + molar_volume, + c_in, + V_dot_C + ) + + self.residuals['cake']['pressure'] = calculate_residual_press_easy_def( + Q_p, + V_C, + deltap, + membrane_area, + viscosity_in, + membrane_resistance, + specific_cake_resistance + ) + + self.residuals['cake']['permeate'] = calculate_residual_volume_cstr( + V_C, + V_dot_C, + Q_in, + Q_p + ) + + self.residuals['cake']['viscosity'] = calculate_residual_visc_def() + + new_c_in = (1-rejection)*c_in + + self.residuals['permeate']['c'] = calculate_residual_concentration_cstr( + c, + c_dot, + V, + V_dot, + Q_p, + Q_out, + new_c_in + ) + + self.residuals['permeate']['Volume'] = calculate_residual_volume_cstr( + V, + V_dot, + Q_p, + Q_out + ) + + self.residuals['permeate']['viscosity'] = calculate_residual_visc_cstr() diff --git a/pyproject.toml b/pyproject.toml index 523485d..8aa8299 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,4 +68,8 @@ version = { attr = "CADETPythonSimulator.__version__" } [tool.ruff] # Same as Black. line-length = 88 -indent-width = 4 \ No newline at end of file +indent-width = 4 + +[tool.ruff.lint] +select = ["E", "F", "W"] +ignore = ["F401"] \ No newline at end of file diff --git a/tests/test_residual.py b/tests/test_residual.py index b267e7c..9bcadd9 100644 --- a/tests/test_residual.py +++ b/tests/test_residual.py @@ -2,77 +2,81 @@ import pytest from CADETPythonSimulator.residual import ( - calculate_residual_volume_cstr, calculate_residual_concentration_cstr + calculate_residual_volume_cstr, + calculate_residual_concentration_cstr, + calculate_residual_cake_vol_def, + calculate_residual_press_easy_def ) from CADETPythonSimulator.exception import CADETPythonSimError + # random number test -TestCaseConc_level1 = { - "values": { - "c": np.array([1, 2, 3]), - "c_dot": np.array([4, 5, 6]), - "V": 1, - "V_dot": 2, - "Q_in": 3, - "Q_out": 4, - "c_in": np.array([7, 8, 9]) +TestCaseCSTRConc_level1 = { + "values" : { + "c" : np.array([1, 2, 3]), + "c_dot" : np.array([4, 5, 6]), + "V" : 1, + "V_dot" : 2, + "Q_in" : 3, + "Q_out" : 4, + "c_in" : np.array([7, 8, 9]) }, "expected": np.array([-11, -7, -3]) } # flow in and out are equal, concentrations to -TestCaseConc_equal = { - "values": { - "c": np.array([0.1,]), - "c_dot": np.array([0,]), - "V": 1, - "V_dot": 0, - "Q_in": 1, - "Q_out": 1, - "c_in": np.array([0.1,]) +TestCaseCSTRConc_equal = { + "values" : { + "c" : np.array([0.1,]), + "c_dot" : np.array([0,]), + "V" : 1, + "V_dot" : 0, + "Q_in" : 1, + "Q_out" : 1, + "c_in" : np.array([0.1,]) }, "expected": np.array([0,]) } -# flow in and out are equal, but concentrations going into the unit are not -TestCaseConc_diffcin = { - "values": { - "c": np.array([0.1,]), - "c_dot": np.array([0,]), - "V": 1.0, - "V_dot": 0.0, - "Q_in": 1.0, - "Q_out": 1.0, - "c_in": np.array([0.2,]) +# flow in and out are equal, but concentrations going into the unit is not +TestCaseCSTRConc_diffcin = { + "values" : { + "c" : np.array([0.1,]), + "c_dot" : np.array([0,]), + "V" : 1.0, + "V_dot" : 0.0, + "Q_in" : 1.0, + "Q_out" : 1.0, + "c_in" : np.array([0.2,]) }, "expected": np.array([-0.1,]) } -# flow in and out are not equal, concentrantions going in are -TestCaseConc_diffvol = { - "values": { - "c": np.array([0.1,]), - "c_dot": np.array([0,]), - "V": 1.0, - "V_dot": 1.0, - "Q_in": 2.0, - "Q_out": 1.0, - "c_in": np.array([0.1,]) +#flow in and out are not equal, concentrantions going in are +TestCaseCSTRConc_diffvol = { + "values" : { + "c" : np.array([0.1,]), + "c_dot" : np.array([0,]), + "V" : 1.0, + "V_dot" : 1.0, + "Q_in" : 2.0, + "Q_out" : 1.0, + "c_in" : np.array([0.1,]) }, "expected": np.array([0,]) } -# flow in and out are not, equal, concentrations aren't equal too -TestCaseConc_diffvolc = { - "values": { - "c": np.array([0.1,]), - "c_dot": np.array([0.2,]), - "V": 1.0, - "V_dot": 1.0, - "Q_in": 2.0, - "Q_out": 1.0, - "c_in": np.array([0.2,]) +#flow in and out are not, equal, concentrations aren't equal too +TestCaseCSTRConc_diffvolc = { + "values" : { + "c" : np.array([0.1,]), + "c_dot" : np.array([0.2,]), + "V" : 1.0, + "V_dot" : 1.0, + "Q_in" : 2.0, + "Q_out" : 1.0, + "c_in" : np.array([0.2,]) }, "expected": np.array([0,]) } @@ -81,11 +85,11 @@ @pytest.mark.parametrize( "parameters", [ - TestCaseConc_level1, - TestCaseConc_equal, - TestCaseConc_diffcin, - TestCaseConc_diffvol, - TestCaseConc_diffvolc + TestCaseCSTRConc_level1, + TestCaseCSTRConc_equal, + TestCaseCSTRConc_diffcin, + TestCaseCSTRConc_diffvol, + TestCaseCSTRConc_diffvolc ] ) class TestResidualConcCSTR(): @@ -93,7 +97,10 @@ def test_calculation_concentration_cstr(self, parameters): param_vec_conc = parameters["values"].values() - residual = calculate_residual_concentration_cstr(*param_vec_conc) + np.testing.assert_array_almost_equal( + calculate_residual_concentration_cstr(*param_vec_conc), + parameters["expected"] + ) np.testing.assert_array_almost_equal(residual, parameters["expected"]) @@ -170,9 +177,124 @@ def test_calculation_cstr(self, parameters): param_vec_volume = parameters["values"].values() - residual = calculate_residual_volume_cstr(*param_vec_volume) + np.testing.assert_equal( + calculate_residual_volume_cstr(*param_vec_volume), + parameters["expected"] + ) + +# Testcase 1: Membrane rejects all +TestCaseDEFCake_rejects_all = { + "values" : { + "V_dot_f" : 1.0, + "rejection" : np.array([1, 1]), + "molar_volume" : np.array([1, 1]), + "c_in" : np.array([0.5, 0.5]), + "V_dot_C" : 1.0 + }, + "expected" : 0 +} - np.testing.assert_equal(residual, parameters["expected"]) + +# Testcase 2: Membrane rejects nothing +TestCaseDEFCake_rejects_not = { + "values" : { + "V_dot_f" : 1.0, + "rejection" : np.array([0, 0]), + "molar_volume" : np.array([1, 1]), + "c_in" : np.array([0.5, 0.5]), + "V_dot_C" : 0.0 + }, + "expected" : 0 +} + +# Testcase 3: Membrane rejects only Component 2 +TestCaseDEFCake_rejects_2 = { + "values" : { + "V_dot_f" : 1.0, + "rejection" : np.array([0, 1]), + "molar_volume" : np.array([1, 1]), + "c_in" : np.array([0.5, 0.5]), + "V_dot_C" : 0.5 + }, + "expected" : 0 +} + +# Testcase 4: Component 2 is larger then 1 +TestCaseDEFCake_C2_le_C1 = { + "values" : { + "V_dot_f" : 1.0, + "rejection" : np.array([1, 1]), + "molar_volume" : np.array([0.5, 1]), + "c_in" : np.array([0.5, 0.5]), + "V_dot_C" : 0.75 + }, + "expected" : 0 +} + +@pytest.mark.parametrize( + "parameters", + [ + TestCaseDEFCake_rejects_all, + TestCaseDEFCake_rejects_not, + TestCaseDEFCake_rejects_2, + TestCaseDEFCake_C2_le_C1 + ] +) + +class TestResidualCakeVolDEF(): + def test_calculation_def(self, parameters): + + param_vec_cake_vol = parameters["values"].values() + + np.testing.assert_equal(calculate_residual_cake_vol_def(*param_vec_cake_vol), + parameters["expected"] + ) + +# Case 1 : Equally large hyraulic resistance +TestCaseDEFPressureDrop = { + "values" : { + "V_dot_P" : 1, + "V_C" : 1, + "deltap" : 0.5, + "A" : 1, + "mu" : 1, + "Rm" : 1, + "alpha" : 1, + }, + "expected" : 0 +} + +# Case 2 : No cake yet +TestCaseDEFPressureDrop_no_cake = { + "values" : { + "V_dot_P" : 0.5, + "V_C" : 0, + "deltap" : 0.5, + "A" : 1, + "mu" : 1, + "Rm" : 1, + "alpha" : 1, + }, + "expected" : 0 +} + +@pytest.mark.parametrize( + "parameters", + [ + TestCaseDEFPressureDrop, + TestCaseDEFPressureDrop_no_cake + ] +) + + +class TestResidualPressureDropDEF(): + def test_calculation_def(self, parameters): + + param_vec_pressure = parameters["values"].values() + + np.testing.assert_equal(calculate_residual_press_easy_def(*param_vec_pressure), + parameters["expected"] + ) TestCaseConcError = { @@ -210,3 +332,4 @@ def test_calculation_concentration_cstr_error(self, parameters): with pytest.raises(CADETPythonSimError): calculate_residual_concentration_cstr(*param_vec_volume) + diff --git a/tests/test_unit_operation.py b/tests/test_unit_operation.py index b03d9e0..eaeec36 100644 --- a/tests/test_unit_operation.py +++ b/tests/test_unit_operation.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from CADETProcess.processModel import ComponentSystem +from CADETPythonSimulator.componentsystem import CPSComponentSystem from CADETPythonSimulator.unit_operation import ( UnitOperationBase, @@ -15,7 +15,7 @@ # %% Unit Operation Fixtures -class TwoComponentFixture(ComponentSystem): +class TwoComponentFixture(CPSComponentSystem): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -378,9 +378,18 @@ def test_initialize(self, unit_operation: UnitOperationBase, expected: dict): 'Q_out' : [4] }, [ - ("calculate_residual_concentration_cstr", lambda c, c_dot, V, V_dot, Q_in, Q_out, c_in: c_dot * V + V_dot * c - Q_in * c_in + Q_out * c), - ("calculate_residuals_visc_cstr", lambda *args : 0), - ("calculate_residual_volume_cstr", lambda V, V_dot, Q_in, Q_out: V_dot - Q_in + Q_out) + ("calculate_residual_concentration_cstr", + lambda c, c_dot, V, V_dot, Q_in, Q_out, c_in: + c_dot * V + V_dot * c - Q_in * c_in + Q_out * c + ), + ("calculate_residual_visc_cstr", + lambda *args : + 0 + ), + ("calculate_residual_volume_cstr", + lambda V, V_dot, Q_in, Q_out: + V_dot - Q_in + Q_out + ) ], { 'inlet' : { @@ -444,9 +453,12 @@ def test_unit_residual( for unit_module, module_dict in expected.items(): for property, value in module_dict.items(): - np.testing.assert_equal(value, unit_operation.residuals[unit_module][property]) + np.testing.assert_equal( + value, + unit_operation.residuals[unit_module][property] + ) # %% Run tests if __name__ == "__main__": - pytest.main(["test_unit_operation.py"]) \ No newline at end of file + pytest.main(["test_unit_operation.py"])