diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 3a81e60e4..0c17f1c87 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -29,11 +29,11 @@
-
+
-
-
-
+
+
+
@@ -59,22 +59,9 @@
-
-
-
-
-
@@ -110,7 +97,7 @@
-
+
{
@@ -126,84 +113,102 @@
- {
+ "keyToString": {
+ "ASKED_ADD_EXTERNAL_FILES": "true",
+ "Git.Branch.Popup.ShowAllRemotes": "true",
+ "MATLAB_INTERPRETER": "/usr/local/MATLAB/R2018a/bin/matlab",
+ "Python tests.Nosetests for test_load_save_load.test_load_save_load.executor": "Debug",
+ "Python tests.Nosetests for test_power_flow.test_zip.executor": "Debug",
+ "Python tests.Nosetests for test_ptdf.test_compensated_ptdf.executor": "Run",
+ "Python tests.Nosetests for test_topology_processor.test_topology_rts.executor": "Debug",
+ "Python tests.Nosetests for tests.test_ac_opf.executor": "Run",
+ "Python tests.Nosetests for tests.test_ac_opf.test_pegase89.executor": "Debug",
+ "Python tests.Nosetests for tests.test_hydro.executor": "Run",
+ "Python tests.Nosetests for tests.test_latin_hypercube.test_lhs.executor": "Run",
+ "Python tests.Nosetests for tests.test_load_save_load.executor": "Run",
+ "Python tests.Nosetests for tests.test_opf_time_series.test_opf_ts.executor": "Run",
+ "Python tests.Nosetests for tests.test_topology_processor.executor": "Run",
+ "Python tests.Nosetests for tests.test_topology_processor.test_topology_rts.executor": "Run",
+ "Python tests.Nosetests in admittance_matrix_test.py.executor": "Run",
+ "Python tests.Nosetests in deep_copy_test.py.executor": "Run",
+ "Python tests.Nosetests in test_ptdf.py.executor": "Run",
+ "Python tests.Nosetests in test_topology_processor.py.executor": "Run",
+ "Python tests.Nosetests in tests.executor": "Run",
+ "Python tests.pytest in test_hydro.py.executor": "Run",
+ "Python.AnalysisDialogue.executor": "Run",
+ "Python.ExecuteGridCal.executor": "Run",
+ "Python.ac_opf_derivatives_bound_slacks.executor": "Debug",
+ "Python.acopf_run.executor": "Run",
+ "Python.case10.executor": "Run",
+ "Python.cgmes_rdfs_graph.executor": "Run",
+ "Python.make_wheels.executor": "Run",
+ "Python.new_circuit_objects.executor": "Run",
+ "Python.pymoo_example.executor": "Debug",
+ "Python.update_gui_file (1).executor": "Run",
+ "Python.update_gui_file.executor": "Run",
+ "RunOnceActivity.OpenProjectViewOnStart": "true",
+ "RunOnceActivity.ShowReadmeOnStart": "true",
+ "WebServerToolWindowFactoryState": "false",
+ "git-widget-placeholder": "205__ACOPF",
+ "last_opened_file_path": "/home/santi/Descargas/tramontana",
+ "node.js.detected.package.eslint": "true",
+ "node.js.selected.package.eslint": "(autodetect)",
+ "node.js.selected.package.tslint": "(autodetect)",
+ "nodejs_package_manager_path": "npm",
+ "run.code.analysis.last.selected.profile": "aDefault",
+ "settings.editor.selected.configurable": "reference.idesettings.debugger.python",
+ "two.files.diff.last.used.file": "C:/WorkProjects/PycharmProjects/GridCal/src/trunk/cgmes_py_generator/cgmes_v2_4_15/cgmes_enums.py",
+ "vue.rearranger.settings.migration": "true"
}
-}]]>
+}
+
+
+
-
-
-
-
-
+
+
+
+
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -225,6 +230,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -276,6 +304,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -380,50 +422,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -470,43 +468,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -556,7 +517,7 @@
-
+
@@ -569,11 +530,11 @@
-
+
-
+
@@ -586,8 +547,25 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -663,22 +641,29 @@
-
-
-
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -1354,29 +1339,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
1656059954202
@@ -1721,7 +1683,7 @@
1698766404661
-
+
@@ -1754,7 +1716,7 @@
-
+
@@ -1767,32 +1729,32 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2088,7 +2050,7 @@
- file://$PROJECT_DIR$/src/GridCal/Gui/Diagrams/MapWidget/Layers/map_layer.py
+ file://$PROJECT_DIR$/src/GridCal/Gui/MapWidget/Layers/map_layer.py
86
@@ -2179,7 +2141,7 @@
file://$PROJECT_DIR$/examples/node_groups_run.py
- 10
+ 14
@@ -2187,33 +2149,6 @@
8
-
- file://$PROJECT_DIR$/src/GridCalEngine/Devices/Parents/editable_device.py
- 594
-
-
-
- file://$PROJECT_DIR$/src/GridCalEngine/Devices/Parents/injection_parent.py
- 145
-
-
-
-
- file://$PROJECT_DIR$/src/GridCalEngine/Devices/profile.py
- 358
-
-
-
-
- file://$PROJECT_DIR$/src/GridCalEngine/Devices/sparse_array.py
- 200
-
-
-
- file://$PROJECT_DIR$/src/GridCalEngine/Devices/Parents/editable_device.py
- 604
-
-
file://$PROJECT_DIR$/src/GridCalEngine/IO/gridcal/pack_unpack.py
998
@@ -2221,48 +2156,28 @@
file://$PROJECT_DIR$/src/GridCalEngine/IO/gridcal/pack_unpack.py
- 872
+ 938
file://$PROJECT_DIR$/src/GridCalEngine/IO/gridcal/pack_unpack.py
- 999
-
-
-
- file://$PROJECT_DIR$/src/gridcal_packaging.py
- 300
-
-
-
- file://$PROJECT_DIR$/src/gridcal_packaging.py
- 313
-
-
-
- file://$PROJECT_DIR$/src/gridcal_packaging.py
- 114
-
-
-
- file://$PROJECT_DIR$/src/GridCalEngine/IO/gridcal/pack_unpack.py
- 1084
-
+ 716
+
- file://$PROJECT_DIR$/src/GridCalEngine/IO/gridcal/pack_unpack.py
- 825
-
+ file://$PROJECT_DIR$/src/GridCalEngine/Simulations/OPF/opf_driver.py
+ 221
+
- file://$PROJECT_DIR$/src/GridCal/Gui/Diagrams/DiagramEditorWidget/diagram_editor_widget.py
- 1266
-
+ file://$PROJECT_DIR$/src/GridCalEngine/Simulations/OPF/linear_opf_ts.py
+ 1320
+
- file://$PROJECT_DIR$/src/GridCal/Gui/Diagrams/DiagramEditorWidget/terminal_item.py
- 374
-
+ file://$PROJECT_DIR$/src/GridCalEngine/Simulations/OPF/linear_opf_ts.py
+ 1543
+
@@ -2274,13 +2189,12 @@
-
+
-
-
+
@@ -2297,12 +2211,11 @@
+
-
-
-
+
@@ -2322,7 +2235,7 @@
-
+
@@ -2336,8 +2249,8 @@
-
-
+
+
@@ -2356,7 +2269,7 @@
-
+
@@ -2378,7 +2291,6 @@
-
@@ -2418,7 +2330,7 @@
-
+
@@ -2426,17 +2338,16 @@
-
+
-
+
-
@@ -2461,7 +2372,6 @@
-
@@ -2482,7 +2392,7 @@
-
+
@@ -2499,7 +2409,7 @@
-
+
@@ -2520,11 +2430,10 @@
-
+
-
@@ -2536,7 +2445,6 @@
-
@@ -2545,7 +2453,6 @@
-
@@ -2555,7 +2462,7 @@
-
+
@@ -2569,20 +2476,19 @@
-
+
-
+
-
@@ -2595,7 +2501,6 @@
-
@@ -2605,7 +2510,7 @@
-
+
@@ -2614,7 +2519,7 @@
-
+
diff --git a/src/GridCalEngine/Compilers/circuit_to_data.py b/src/GridCalEngine/Compilers/circuit_to_data.py
index 552f46fbc..fea4b3e91 100644
--- a/src/GridCalEngine/Compilers/circuit_to_data.py
+++ b/src/GridCalEngine/Compilers/circuit_to_data.py
@@ -1228,9 +1228,13 @@ def get_fluid_node_data(circuit: MultiCircuit,
if time_series:
data.inflow[k] = elm.inflow_prof[t_idx]
data.spillage_cost[k] = elm.spillage_cost_prof[t_idx]
+ data.max_soc[k] = elm.max_soc_prof[t_idx]
+ data.min_soc[k] = elm.min_soc_prof[t_idx]
else:
data.inflow[k] = elm.inflow
data.spillage_cost[k] = elm.spillage_cost
+ data.max_soc[k] = elm.max_soc
+ data.min_soc[k] = elm.min_soc
return data, plant_dict
diff --git a/src/GridCalEngine/DataStructures/fluid_node_data.py b/src/GridCalEngine/DataStructures/fluid_node_data.py
index 4552c98f1..5fbc7a557 100644
--- a/src/GridCalEngine/DataStructures/fluid_node_data.py
+++ b/src/GridCalEngine/DataStructures/fluid_node_data.py
@@ -35,8 +35,9 @@ def __init__(self, nelm: int):
self.min_level = np.zeros(nelm, dtype=float)
self.max_level = np.zeros(nelm, dtype=float)
+ self.min_soc = np.zeros(nelm, dtype=float)
+ self.max_soc = np.zeros(nelm, dtype=float)
self.initial_level = np.zeros(nelm, dtype=float)
- # self.bus_index = np.empty() # TODO: check if relevant
self.inflow = np.zeros(nelm, dtype=float)
self.spillage_cost = np.zeros(nelm, dtype=float)
@@ -61,6 +62,8 @@ def copy(self) -> "FluidNodeData":
data.min_level = self.min_level.copy()
data.max_level = self.max_level.copy()
+ data.min_soc = self.min_soc.copy()
+ data.max_soc = self.max_soc.copy()
data.initial_level = self.initial_level.copy()
data.inflow = self.inflow.copy()
diff --git a/src/GridCalEngine/Devices/Fluid/fluid_node.py b/src/GridCalEngine/Devices/Fluid/fluid_node.py
index 0b13da995..ab9225875 100644
--- a/src/GridCalEngine/Devices/Fluid/fluid_node.py
+++ b/src/GridCalEngine/Devices/Fluid/fluid_node.py
@@ -32,6 +32,8 @@ def __init__(self,
code: str = '',
min_level: float = 0.0,
max_level: float = 0.0,
+ min_soc: float = 0.0,
+ max_soc: float = 1.0,
current_level: float = 0.0,
spillage_cost: float = 1000.0,
inflow: float = 0.0,
@@ -58,6 +60,8 @@ def __init__(self,
self.min_level = min_level # hm3
self.max_level = max_level # hm3
+ self.max_soc = max_soc # p.u.
+ self.min_soc = min_soc # p.u.
self.initial_level = current_level # hm3
self.spillage_cost = spillage_cost # m3/s
self.inflow = inflow # m3/s
@@ -67,12 +71,23 @@ def __init__(self,
self._inflow_prof = Profile(default_value=inflow) # m3/s
self._spillage_cost_prof = Profile(default_value=spillage_cost) # e/(m3/s)
+ self._max_soc_prof = Profile(default_value=max_soc) # p.u.
+ self._min_soc_prof = Profile(default_value=min_soc) # p.u.
+
self.register(key='min_level', units='hm3', tpe=float,
definition="Minimum amount of fluid at the node/reservoir")
self.register(key='max_level', units='hm3', tpe=float,
definition="Maximum amount of fluid at the node/reservoir")
+ self.register(key='min_soc', units='p.u.', tpe=float,
+ definition="Minimum SOC of fluid at the node/reservoir",
+ profile_name='min_soc_prof')
+
+ self.register(key='max_soc', units='p.u.', tpe=float,
+ definition="Maximum SOC of fluid at the node/reservoir",
+ profile_name='max_soc_prof')
+
self.register(key='initial_level', units='hm3', tpe=float,
definition="Initial level of the node/reservoir")
@@ -124,6 +139,40 @@ def inflow_prof(self, val: Union[Profile, np.ndarray]):
else:
raise Exception(str(type(val)) + 'not supported to be set into a inflow_prof')
+ @property
+ def max_soc_prof(self) -> Profile:
+ """
+ Max soc profile
+ :return: Profile
+ """
+ return self._max_soc_prof
+
+ @max_soc_prof.setter
+ def max_soc_prof(self, val: Union[Profile, np.ndarray]):
+ if isinstance(val, Profile):
+ self._max_soc_prof = val
+ elif isinstance(val, np.ndarray):
+ self._max_soc_prof.set(arr=val)
+ else:
+ raise Exception(str(type(val)) + 'not supported to be set into a max soc prof')
+
+ @property
+ def min_soc_prof(self) -> Profile:
+ """
+ Min soc profile
+ :return: Profile
+ """
+ return self._min_soc_prof
+
+ @min_soc_prof.setter
+ def min_soc_prof(self, val: Union[Profile, np.ndarray]):
+ if isinstance(val, Profile):
+ self._min_soc_prof = val
+ elif isinstance(val, np.ndarray):
+ self._min_soc_prof.set(arr=val)
+ else:
+ raise Exception(str(type(val)) + 'not supported to be set into a min soc prof')
+
def copy(self):
"""
Make a deep copy of this object
@@ -135,6 +184,8 @@ def copy(self):
fluid_node.min_level = self.min_level # hm3
fluid_node.max_level = self.max_level # hm3
+ fluid_node.min_soc = self.min_soc # p.u.
+ fluid_node.max_soc = self.max_soc # p.u.
fluid_node.initial_level = self.initial_level # hm3
fluid_node.spillage_cost = self.spillage_cost # m3/s
fluid_node.inflow = self.inflow # m3/s
@@ -143,6 +194,8 @@ def copy(self):
fluid_node.inflow_prof = self.inflow_prof # m3/s
fluid_node.spillage_cost_prof = self.spillage_cost_prof # e/(m3/s)
+ fluid_node.max_soc_prof = self.max_soc_prof # m3
+ fluid_node.min_soc_prof = self.min_soc_prof # m3
return fluid_node
diff --git a/src/GridCalEngine/Simulations/OPF/linear_opf_ts.py b/src/GridCalEngine/Simulations/OPF/linear_opf_ts.py
index cad2faa73..ae6b5d262 100644
--- a/src/GridCalEngine/Simulations/OPF/linear_opf_ts.py
+++ b/src/GridCalEngine/Simulations/OPF/linear_opf_ts.py
@@ -1318,10 +1318,19 @@ def add_hydro_formulation(t: Union[int, None],
f_obj += node_data.spillage_cost[m] * node_vars.spillage[t, m]
# f_obj += node_vars.spillage[t, m]
- node_vars.current_level[t, m] = prob.add_var(lb=node_data.min_level[m],
- ub=node_data.max_level[m],
+ min_abs_level = node_data.max_level[m] * node_data.min_soc[m]
+
+ node_vars.current_level[t, m] = prob.add_var(lb=min_abs_level,
+ ub=node_data.max_level[m] * node_data.max_soc[m],
name=join("level_", [t, m], "_"))
+ if min_abs_level < node_data.min_level[m]:
+ logger.add_error(msg='Node SOC is below the allowed minimum level',
+ value=min_abs_level,
+ expected_value=node_data.min_level[m],
+ device_class="FluidNode",
+ device_property=f"Min SOC at {t}")
+
for m in range(path_data.nelm):
path_vars.flow[t, m] = prob.add_var(lb=path_data.min_flow[m],
ub=path_data.max_flow[m],