-
Notifications
You must be signed in to change notification settings - Fork 1
288 water to water heat pump bypass mode #344
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ae3a6a0
085e6db
30065d2
0d2ff22
23d3025
d6167d0
4300c03
86feca1
1341a82
4c11a17
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -45,8 +45,6 @@ | |
|
|
||
| connected_ports: list[str] | ||
| """List of ids of the connected ports.""" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Je verwijdert solver_asset: Waarom? Alles refereert intern nog steeds naar solver_asset... |
||
| solver_asset: BaseAsset | ||
| """The asset object use for the solver.""" | ||
| asset_type = "asset_abstract" | ||
| """The type of the asset.""" | ||
| number_of_con_points: int = 2 | ||
|
|
@@ -106,9 +104,9 @@ | |
| """ | ||
| for i in range(len(self.connected_ports)): | ||
| output_dict_temp = { | ||
| PROPERTY_MASSFLOW: sign_output(i) * self.solver_asset.get_mass_flow_rate(i), | ||
| PROPERTY_PRESSURE: self.solver_asset.get_pressure(i), | ||
| PROPERTY_TEMPERATURE: self.solver_asset.get_temperature(i), | ||
| PROPERTY_VOLUMEFLOW: sign_output(i) * self.get_volume_flow_rate(i), | ||
| } | ||
| self.outputs[i].append(output_dict_temp) | ||
|
|
@@ -122,8 +120,8 @@ | |
| :param int i: The index of the port. | ||
| :return float: The volume flow rate. | ||
| """ | ||
| rho = fluid_props.get_density(self.solver_asset.get_temperature(i)) | ||
| return self.solver_asset.get_mass_flow_rate(i) / rho | ||
|
Check failure on line 124 in src/omotes_simulator_core/entities/assets/asset_abstract.py
|
||
|
|
||
| @abstractmethod | ||
| def write_to_output(self) -> None: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -147,9 +147,9 @@ def _calculate_massflowrate(self) -> None: | |
| def _set_solver_asset_setpoint(self) -> None: | ||
| """Set the setpoint of solver asset.""" | ||
| if self.mass_flowrate >= 0: | ||
| self.solver_asset.supply_temperature = self.cold_well_temperature # injection | ||
| else: | ||
| self.solver_asset.supply_temperature = self.hot_well_temperature # production | ||
| else: | ||
| self.solver_asset.supply_temperature = self.cold_well_temperature # injection | ||
| self.solver_asset.mass_flow_rate_set_point = self.mass_flowrate # type: ignore | ||
|
|
||
| def set_setpoints(self, setpoints: dict) -> None: | ||
|
|
@@ -158,9 +158,7 @@ def set_setpoints(self, setpoints: dict) -> None: | |
| :param Dict setpoints: The setpoints that should be set for the asset. | ||
| The keys of the dictionary are the names of the setpoints and the values are the values | ||
| """ | ||
| if self.current_time == self.time: | ||
| return | ||
| self.current_time = self.time | ||
|
|
||
| # Default keys required | ||
| necessary_setpoints = { | ||
| PROPERTY_TEMPERATURE_IN, | ||
|
|
@@ -171,23 +169,30 @@ def set_setpoints(self, setpoints: dict) -> None: | |
| setpoints_set = set(setpoints.keys()) | ||
| # Check if all setpoints are in the setpoints | ||
| if necessary_setpoints.issubset(setpoints_set): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Persoonlijk vind ik de -1 * explicieter, waardoor het direct zichtbaar is dat er nog iets met de waarde gebeurt. De - is te missen. |
||
| self.thermal_power_allocation = -1 * setpoints[PROPERTY_HEAT_DEMAND] | ||
| self.thermal_power_allocation = -setpoints[PROPERTY_HEAT_DEMAND] | ||
| if self.first_time_step: | ||
| self.temperature_in = setpoints[PROPERTY_TEMPERATURE_IN] | ||
| self.temperature_out = setpoints[PROPERTY_TEMPERATURE_OUT] | ||
| if self.thermal_power_allocation >= 0: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Kan je een kort comment toevoegen wat je hier aan het doen bent (ivm verwarring over sign/property) |
||
| self.temperature_in = setpoints[PROPERTY_TEMPERATURE_OUT] | ||
| self.temperature_out = setpoints[PROPERTY_TEMPERATURE_IN] | ||
| else: | ||
| self.temperature_in = setpoints[PROPERTY_TEMPERATURE_IN] | ||
| self.temperature_out = setpoints[PROPERTY_TEMPERATURE_OUT] | ||
| self.first_time_step = False | ||
| else: | ||
| # After the first time step: use solver temperature | ||
| if self.thermal_power_allocation >= 0: | ||
| self.temperature_out = self.solver_asset.get_temperature(0) | ||
| self.temperature_in = self.hot_well_temperature | ||
| self.temperature_out = self.solver_asset.get_temperature(1) | ||
| else: | ||
| self.temperature_in = self.solver_asset.get_temperature(0) | ||
| self.temperature_out = self.cold_well_temperature | ||
| self.temperature_in = self.cold_well_temperature | ||
| self.temperature_out = self.solver_asset.get_temperature(1) | ||
|
|
||
| self._calculate_massflowrate() | ||
| self._run_rosim() | ||
| if self.current_time != self.time: | ||
| self._run_rosim() | ||
| self.current_time = self.time | ||
| self._set_solver_asset_setpoint() | ||
|
|
||
| else: | ||
| # Print missing setpoints | ||
| logger.error( | ||
|
|
@@ -279,7 +284,7 @@ def _init_rosim(self) -> None: | |
| } | ||
| # initially charging 12 weeks with 85-35 temperature 1 MW | ||
| logger.info("initializing ates with charging for 12 weeks") | ||
| for i in range(12): | ||
| for i in range(0): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Waarom vervangen? Als dit niet nodig is graag ook comment aanpassen. |
||
| logger.info(f"charging ates week {i + 1}") | ||
| self.set_time_step(3600 * 24 * 7) | ||
| self.set_time(datetime(2023, 1, i + 1, 0, 0, 0)) | ||
|
|
@@ -315,3 +320,21 @@ def _run_rosim(self) -> None: | |
|
|
||
| self.hot_well_temperature = celcius_to_kelvin(ates_temperature[0]) # convert to K | ||
| self.cold_well_temperature = celcius_to_kelvin(ates_temperature[1]) # convert to K | ||
|
|
||
| def get_heat_supplied(self) -> float: | ||
| """Get the actual heat supplied by the asset. | ||
|
|
||
| :return float: The actual heat supplied by the asset [W]. | ||
| """ | ||
| return ( | ||
| self.solver_asset.get_internal_energy(1) - self.solver_asset.get_internal_energy(0) | ||
| ) * self.solver_asset.get_mass_flow_rate(0) | ||
|
|
||
| def is_converged(self) -> bool: | ||
| """Check if the asset has converged with accepted error of 0.1%. | ||
|
|
||
| :return: True if the asset has converged, False otherwise | ||
| """ | ||
| return abs(self.get_heat_supplied() - self.thermal_power_allocation) < ( | ||
| abs(self.thermal_power_allocation) * 0.001 | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,14 +14,13 @@ | |
| # along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
| """Module containing the class for a heat trasnfer asset.""" | ||
|
|
||
| import numpy as np | ||
|
|
||
| from omotes_simulator_core.entities.assets.asset_defaults import ( | ||
| PRIMARY, | ||
| PROPERTY_HEAT_DEMAND, | ||
| PROPERTY_SET_PRESSURE, | ||
| PROPERTY_TEMPERATURE_IN, | ||
| PROPERTY_TEMPERATURE_OUT, | ||
| PROPERTY_BYPASS, | ||
| SECONDARY, | ||
| ) | ||
| from omotes_simulator_core.entities.assets.controller.asset_controller_abstract import ( | ||
|
|
@@ -41,26 +40,39 @@ def __init__(self, name: str, identifier: str, factor: float): | |
| super().__init__(name, identifier) | ||
| self.factor = factor | ||
|
|
||
| def set_asset(self, heat_demand: float) -> dict[str, dict[str, float]]: | ||
| def set_asset(self, heat_demand: float, bypass: bool = False) -> dict[str, dict[str, float]]: | ||
| """Method to set the asset to the given heat demand. | ||
|
|
||
| The supply and return temperatures are also set. | ||
| :param float heat_demand: Heat demand to set. | ||
| :param bypass: When true the heat exchange is bypassed, so the heat demand is not | ||
| reduced by the factor. Default is False. | ||
| """ | ||
| # TODO set correct values also for prim and secondary side. | ||
| return { | ||
| self.id: { | ||
| PRIMARY + PROPERTY_HEAT_DEMAND: heat_demand, | ||
| PRIMARY + PROPERTY_TEMPERATURE_OUT: 273.15 + 30, | ||
| PRIMARY + PROPERTY_TEMPERATURE_IN: 273.15 + 40, | ||
| SECONDARY | ||
| + PROPERTY_HEAT_DEMAND: np.abs(heat_demand) | ||
| * self.factor | ||
| * ( | ||
| np.sign(heat_demand) * -1 | ||
| ), # Invert sign of secondary heat demand, as it is opposite to primary side. | ||
| SECONDARY + PROPERTY_TEMPERATURE_OUT: 273.15 + 80, | ||
| SECONDARY + PROPERTY_TEMPERATURE_IN: 273.15 + 50, | ||
| PROPERTY_SET_PRESSURE: False, | ||
| if bypass: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ik heb hier wel een vraag over. Zetten we nu standaard temperaturen in dit asset met set_asset of is dit alleen een initialisatie stap?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Klopt de volgorder van de temperaturen; ik zou verwachtten dat PRIM_OUT gleijk moet zijn aan SEC_IN. Dit lijkt nu niet het geval? |
||
| return { | ||
| self.id: { | ||
| PRIMARY + PROPERTY_HEAT_DEMAND: heat_demand, | ||
| PRIMARY + PROPERTY_TEMPERATURE_OUT: 273.15 + 80, | ||
| PRIMARY + PROPERTY_TEMPERATURE_IN: 273.15 + 50, | ||
| SECONDARY + PROPERTY_HEAT_DEMAND: heat_demand * -1, | ||
| SECONDARY + PROPERTY_TEMPERATURE_OUT: 273.15 + 80, | ||
| SECONDARY + PROPERTY_TEMPERATURE_IN: 273.15 + 50, | ||
| SECONDARY + PROPERTY_SET_PRESSURE: False, | ||
| PRIMARY + PROPERTY_SET_PRESSURE: False, | ||
| PROPERTY_BYPASS: True, | ||
| } | ||
| } | ||
| else: | ||
| return { | ||
| self.id: { | ||
| PRIMARY + PROPERTY_HEAT_DEMAND: heat_demand / self.factor, | ||
| PRIMARY + PROPERTY_TEMPERATURE_OUT: 273.15 + 30, | ||
| PRIMARY + PROPERTY_TEMPERATURE_IN: 273.15 + 50, | ||
| SECONDARY + PROPERTY_HEAT_DEMAND: -heat_demand, | ||
| SECONDARY + PROPERTY_TEMPERATURE_OUT: 273.15 + 80, | ||
| SECONDARY + PROPERTY_TEMPERATURE_IN: 273.15 + 40, | ||
| SECONDARY + PROPERTY_SET_PRESSURE: False, | ||
| PRIMARY + PROPERTY_SET_PRESSURE: False, | ||
| PROPERTY_BYPASS: False, | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,11 +16,15 @@ | |
|
|
||
| import datetime | ||
|
|
||
| from numpy.ma.core import product | ||
|
|
||
| from omotes_simulator_core.entities.assets.asset_defaults import ( | ||
|
Comment on lines
17
to
21
|
||
| PROPERTY_HEAT_DEMAND, | ||
| PROPERTY_SET_PRESSURE, | ||
| PROPERTY_TEMPERATURE_IN, | ||
| PROPERTY_TEMPERATURE_OUT, | ||
| SECONDARY, | ||
| PRIMARY, | ||
| ) | ||
| from omotes_simulator_core.entities.assets.controller.controller_consumer import ControllerConsumer | ||
| from omotes_simulator_core.entities.assets.controller.controller_heat_transfer import ( | ||
|
|
@@ -49,7 +53,7 @@ class ControllerNetwork: | |
| """List of all producers in the network.""" | ||
| storages: list[ControllerAtesStorage | ControllerIdealHeatStorage] | ||
| """List of all storages in the network.""" | ||
| factor_to_first_network: float | ||
| factor_to_first_network: list[float] | ||
| """Factor to calculate power in the first network in the list of networks.""" | ||
| path: list[str] | ||
| """Path from this network to the first network in the total system.""" | ||
|
|
@@ -69,7 +73,7 @@ def __init__( | |
| self.consumers = consumers_in | ||
| self.producers = producers_in | ||
| self.storages = storages_in | ||
| self.factor_to_first_network = factor_to_first_network | ||
| self.factor_to_first_network = [factor_to_first_network] | ||
| self.path: list[str] = [] | ||
|
|
||
| def exists(self, identifier: str) -> bool: | ||
|
|
@@ -91,39 +95,39 @@ def exists(self, identifier: str) -> bool: | |
|
|
||
| def get_total_heat_demand(self, time: datetime.datetime) -> float: | ||
| """Method which the total heat demand at the given time corrected to the first network.""" | ||
| return ( | ||
| return float( | ||
| sum([consumer.get_heat_demand(time) for consumer in self.consumers]) | ||
| * self.factor_to_first_network | ||
| * product(self.factor_to_first_network) | ||
| ) | ||
|
|
||
| def get_total_discharge_storage(self) -> float: | ||
| """Method to get the total storage discharge of the network corrected to the first network. | ||
|
|
||
| :return float: Total heat discharge of all storages. | ||
| """ | ||
| return ( | ||
| float(sum([storage.effective_max_discharge_power for storage in self.storages])) | ||
| * self.factor_to_first_network | ||
| return float( | ||
| sum([storage.effective_max_discharge_power for storage in self.storages]) | ||
| * product(self.factor_to_first_network) | ||
| ) | ||
|
|
||
| def get_total_charge_storage(self) -> float: | ||
| """Method to get the total storage charge of the network corrected to the first network. | ||
|
|
||
| :return float: Total heat charge of all storages. | ||
| """ | ||
| return ( | ||
| float(sum([storage.effective_max_charge_power for storage in self.storages])) | ||
| * self.factor_to_first_network | ||
| return float( | ||
| sum([storage.effective_max_charge_power for storage in self.storages]) | ||
| * product(self.factor_to_first_network) | ||
| ) | ||
|
|
||
| def get_total_supply(self) -> float: | ||
| """Method to get the total heat supply of the network. | ||
|
|
||
| :return float: Total heat supply of all producers. | ||
| """ | ||
| return ( | ||
| float(sum([producer.power for producer in self.producers])) | ||
| * self.factor_to_first_network | ||
| return float( | ||
| sum([producer.power for producer in self.producers]) | ||
| * product(self.factor_to_first_network) | ||
| ) | ||
|
|
||
| def set_supply_to_max(self, priority: int = 0) -> dict: | ||
|
|
@@ -166,6 +170,8 @@ def set_storage_charge_power(self, factor: float = 1) -> dict: | |
| for storage in self.storages: | ||
| storage_settings[storage.id] = { | ||
| PROPERTY_HEAT_DEMAND: +1 * storage.effective_max_charge_power * factor, | ||
| PROPERTY_TEMPERATURE_OUT: storage.temperature_out, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Waarom heb je de temperatuur nodig voor een storage? Deze wordt toch niet geset maar gebasseerd op de internal state? |
||
| PROPERTY_TEMPERATURE_IN: storage.temperature_in, | ||
| } | ||
| return storage_settings | ||
|
|
||
|
|
@@ -180,6 +186,8 @@ def set_storage_discharge_power(self, factor: float = 1) -> dict: | |
| # Discharging is negative (e.g., heat from component/system to the network) | ||
| storage_settings[storage.id] = { | ||
| PROPERTY_HEAT_DEMAND: -1 * storage.effective_max_discharge_power * factor, | ||
| PROPERTY_TEMPERATURE_OUT: storage.temperature_out, | ||
| PROPERTY_TEMPERATURE_IN: storage.temperature_in, | ||
| } | ||
| return storage_settings | ||
|
|
||
|
|
@@ -226,15 +234,17 @@ def get_total_supply_priority(self, priority: int) -> float: | |
| sum([producer.power for producer in self.producers if producer.priority == priority]) | ||
| ) | ||
|
|
||
| def set_pressure(self) -> str: | ||
| """Returns the id of the asset for which the pressure can be set for this network. | ||
| def set_pressure(self) -> tuple[str, str]: | ||
| """Returns the id of the asset for which the pressure can be set for this network and the key in the set points dict. | ||
|
|
||
| The controller needs to set per hydraulic separated part of the system the pressure. | ||
| The network can thus pass back the id for which asset the pressure needs to be set. | ||
| The controller can then do this. | ||
| """ | ||
| if self.heat_transfer_assets_sec: | ||
| return self.heat_transfer_assets_sec[0].id | ||
| if self.producers: | ||
| return self.producers[0].id | ||
| return (self.producers[0].id, PROPERTY_SET_PRESSURE) | ||
| if self.heat_transfer_assets_sec: | ||
| return (self.heat_transfer_assets_sec[0].id, SECONDARY + PROPERTY_SET_PRESSURE) | ||
| if self.heat_transfer_assets_prim: | ||
| return (self.heat_transfer_assets_prim[0].id, PRIMARY + PROPERTY_SET_PRESSURE) | ||
| raise ValueError("No asset found for which the pressure can be set.") | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -134,6 +134,6 @@ def is_converged(self) -> bool: | |||||
|
|
||||||
| :return: True if the asset has converged, False otherwise | ||||||
| """ | ||||||
| return abs(self.get_heat_supplied() - (-self.thermal_power_allocation)) < ( | ||||||
| (-self.thermal_power_allocation) * 0.001 | ||||||
| return abs(self.get_heat_supplied() - self.thermal_power_allocation) < ( | ||||||
| self.thermal_power_allocation * 0.001 | ||||||
|
||||||
| self.thermal_power_allocation * 0.001 | |
| abs(self.thermal_power_allocation) * 0.001 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Zou hier inderdaad niet nog een extra abs ergens omheen moeten?
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -210,7 +210,7 @@ def write_to_output(self) -> None: | |
| self.solver_asset.get_heat_power_primary() # type: ignore | ||
| ), | ||
| PROPERTY_HEAT_LOSS: ( | ||
| self.solver_asset.get_heat_power_primary() # type: ignore | ||
| -self.solver_asset.get_heat_power_primary() # type: ignore | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Waarom de -? Het is toch het verschil tussen 'sec en prim' |
||
| - self.solver_asset.get_heat_power_secondary() # type: ignore | ||
| ), | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.