|
| 1 | +from libecalc.common.errors.exceptions import IllegalStateException |
| 2 | +from libecalc.common.logger import logger |
| 3 | +from libecalc.domain.process.compressor.core.train.utils.common import FLOATING_POINT_PRECISION |
1 | 4 | from libecalc.domain.process.value_objects.fluid_stream import FluidStream |
2 | 5 |
|
3 | 6 |
|
4 | 7 | class Splitter: |
5 | 8 | def __init__(self, number_of_outputs: int): |
6 | 9 | self.number_of_outputs = number_of_outputs |
7 | 10 |
|
8 | | - def split_stream(self, stream: FluidStream, split_fractions: list[float]) -> list[FluidStream]: |
| 11 | + def split_stream( |
| 12 | + self, stream: FluidStream, split_fractions: list[float] | None = None, rates: list[float] | None = None |
| 13 | + ) -> list[FluidStream]: |
9 | 14 | """ |
10 | 15 | Splits the fluid stream into streams based on the split fractions. |
11 | 16 |
|
12 | 17 | Args: |
13 | 18 | stream (FluidStream): The fluid stream to be split. |
14 | | - split_fractions (Sequence[float]): The fractions of the stream to go to the different output streams (0.0 to 1.0). |
| 19 | + split_fractions (list[float] | None): The fractions of the stream to go to the different output streams (0.0 to 1.0). |
| 20 | + The split fractions should sum to 1.0. |
| 21 | + rates (list[float] | None): The flow rates for each output stream. |
15 | 22 |
|
16 | 23 | Returns: |
17 | 24 | list[FluidStream]: A list containing the resulting FluidStreams. |
18 | 25 | """ |
| 26 | + if (split_fractions is None) == (rates is None): |
| 27 | + raise IllegalStateException("Either split_fractions or rates must be provided.") |
| 28 | + if rates is not None: |
| 29 | + split_fractions = self.convert_rates_to_split_fractions(rates) |
| 30 | + |
19 | 31 | # make sure number of split fractions matches number of outputs |
| 32 | + assert split_fractions is not None |
20 | 33 | if len(split_fractions) != self.number_of_outputs: |
21 | 34 | raise ValueError("Number of split fractions must match number of outputs.") |
22 | 35 |
|
23 | | - # normalize split fractions |
24 | | - total = sum(split_fractions) |
25 | | - normalized_fractions = [f / total for f in split_fractions] |
| 36 | + # make sure split fractions sum to 1.0 |
| 37 | + total_fraction = sum(split_fractions) |
| 38 | + if total_fraction != 1.0: |
| 39 | + raise IllegalStateException("Split fractions must sum to 1.0.") |
26 | 40 |
|
27 | 41 | return [ |
28 | 42 | FluidStream( |
29 | 43 | thermo_system=stream.thermo_system, |
30 | 44 | mass_rate_kg_per_h=stream.mass_rate_kg_per_h * split_fraction, |
31 | 45 | ) |
32 | | - for split_fraction in normalized_fractions |
| 46 | + for split_fraction in split_fractions |
33 | 47 | ] |
| 48 | + |
| 49 | + @staticmethod |
| 50 | + def convert_rates_to_split_fractions(rates: list[float]) -> list[float]: |
| 51 | + """ |
| 52 | + Converts a list of flow rates to split fractions. Some of the flow rates may be very small negative |
| 53 | + numbers due to floating point precision issues (conversion between standard rate and mass rate and back), |
| 54 | + these are corrected to zero if they are less than . |
| 55 | +
|
| 56 | + Args: |
| 57 | + rates (list[float]): The flow rates for each output stream. |
| 58 | +
|
| 59 | + Returns: |
| 60 | + list[float]: The corresponding split fractions. |
| 61 | + """ |
| 62 | + if any(rate < -FLOATING_POINT_PRECISION for rate in rates): |
| 63 | + raise IllegalStateException( |
| 64 | + f"Negative rate found when splitting stream in compressor stage splitter. " |
| 65 | + f"Rates: {rates}. Correcting negative rate to zero." |
| 66 | + ) |
| 67 | + elif any(-FLOATING_POINT_PRECISION < rate < 0 for rate in rates): |
| 68 | + logger.warning( |
| 69 | + f"Negative rate found when splitting stream in compressor stage splitter. " |
| 70 | + f"Rates before correction: {rates}. Correcting negative rate to zero." |
| 71 | + ) |
| 72 | + rates = [max(rate, 0.0) for rate in rates] |
| 73 | + |
| 74 | + return [rate / sum(rates) for rate in rates] |
0 commit comments