epyt-flow 0.5.0__py3-none-any.whl → 0.7.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,6 +12,7 @@ from ..sensor_config import SensorConfig, is_flowunit_simetric, massunit_to_str,
12
12
  AREA_UNIT_CM2, AREA_UNIT_FT2, AREA_UNIT_M2, \
13
13
  SENSOR_TYPE_LINK_FLOW, SENSOR_TYPE_LINK_QUALITY, SENSOR_TYPE_NODE_DEMAND, \
14
14
  SENSOR_TYPE_NODE_PRESSURE, SENSOR_TYPE_NODE_QUALITY, SENSOR_TYPE_PUMP_STATE, \
15
+ SENSOR_TYPE_PUMP_EFFICIENCY, SENSOR_TYPE_PUMP_ENERGYCONSUMPTION, \
15
16
  SENSOR_TYPE_TANK_VOLUME, SENSOR_TYPE_VALVE_STATE, SENSOR_TYPE_NODE_BULK_SPECIES, \
16
17
  SENSOR_TYPE_LINK_BULK_SPECIES, SENSOR_TYPE_SURFACE_SPECIES
17
18
  from ..events import SensorFault, SensorReadingAttack, SensorReadingEvent
@@ -92,11 +93,11 @@ class ScadaData(Serializable):
92
93
  third dimension denotes species concentrations at nodes.
93
94
 
94
95
  The default is None.
95
- pump_energy_usage_data : `numpy.ndarray`, optional
96
+ pumps_energy_usage_data_raw : `numpy.ndarray`, optional
96
97
  Energy usage data of each pump.
97
98
 
98
99
  The default is None.
99
- pump_efficiency_data : `numpy.ndarray`, optional
100
+ pumps_efficiency_data_raw : `numpy.ndarray`, optional
100
101
  Pump efficiency data of each pump.
101
102
 
102
103
  The default is None.
@@ -130,8 +131,10 @@ class ScadaData(Serializable):
130
131
  surface_species_concentration_raw: np.ndarray = None,
131
132
  bulk_species_node_concentration_raw: np.ndarray = None,
132
133
  bulk_species_link_concentration_raw: np.ndarray = None,
133
- pump_energy_usage_data: np.ndarray = None,
134
- pump_efficiency_data: np.ndarray = None,
134
+ pump_energy_usage_data = None,
135
+ pump_efficiency_data = None,
136
+ pumps_energy_usage_data_raw: np.ndarray = None,
137
+ pumps_efficiency_data_raw: np.ndarray = None,
135
138
  sensor_faults: list[SensorFault] = [],
136
139
  sensor_reading_attacks: list[SensorReadingAttack] = [],
137
140
  sensor_reading_events: list[SensorReadingEvent] = [],
@@ -195,14 +198,14 @@ class ScadaData(Serializable):
195
198
  raise TypeError("'bulk_species_link_concentration_raw' must be an instance of " +
196
199
  "'numpy.ndarray' but not of " +
197
200
  f"'{type(bulk_species_link_concentration_raw)}'")
198
- if pump_energy_usage_data is not None:
199
- if not isinstance(pump_energy_usage_data, np.ndarray):
200
- raise TypeError("'pump_energy_usage_data' must be an instance of 'numpy.ndarray' " +
201
- f"but not of '{type(pump_energy_usage_data)}'")
202
- if pump_efficiency_data is not None:
203
- if not isinstance(pump_efficiency_data, np.ndarray):
204
- raise TypeError("'pump_efficiency_data' must be an instance of 'numpy.ndarray' " +
205
- f"but not of '{type(pump_efficiency_data)}'")
201
+ if pumps_energy_usage_data_raw is not None:
202
+ if not isinstance(pumps_energy_usage_data_raw, np.ndarray):
203
+ raise TypeError("'pumps_energy_usage_data_raw' must be an instance of 'numpy.ndarray' " +
204
+ f"but not of '{type(pumps_energy_usage_data_raw)}'")
205
+ if pumps_efficiency_data_raw is not None:
206
+ if not isinstance(pumps_efficiency_data_raw, np.ndarray):
207
+ raise TypeError("'pumps_efficiency_data_raw' must be an instance of 'numpy.ndarray' " +
208
+ f"but not of '{type(pumps_efficiency_data_raw)}'")
206
209
  if len(sensor_faults) != 0:
207
210
  if any(not isinstance(f, SensorFault) for f in sensor_faults):
208
211
  raise TypeError("'sensor_faults' must be a list of " +
@@ -223,6 +226,11 @@ class ScadaData(Serializable):
223
226
  raise TypeError("'frozen_sensor_config' must be an instance of 'bool' " +
224
227
  f"but not of '{type(frozen_sensor_config)}'")
225
228
 
229
+ if pump_efficiency_data is not None or pump_energy_usage_data is not None:
230
+ warnings.warn("Loading a file that was created with an outdated version of EPyT-Flow" +
231
+ " -- support of such old files will be removed in the next release!",
232
+ DeprecationWarning)
233
+
226
234
  def __raise_shape_mismatch(var_name: str) -> None:
227
235
  raise ValueError(f"Shape mismatch in '{var_name}' -- " +
228
236
  "i.e number of time steps in 'sensor_readings_time' " +
@@ -271,12 +279,12 @@ class ScadaData(Serializable):
271
279
  if surface_species_concentration_raw is not None:
272
280
  if surface_species_concentration_raw.shape[0] != n_time_steps:
273
281
  __raise_shape_mismatch("surface_species_concentration_raw")
274
- if pump_energy_usage_data is not None:
275
- if pump_energy_usage_data.shape[0] != n_time_steps:
276
- __raise_shape_mismatch("pump_energy_usage_data")
277
- if pump_efficiency_data is not None:
278
- if pump_efficiency_data.shape[0] != n_time_steps:
279
- __raise_shape_mismatch("pump_efficiency_data")
282
+ if pumps_energy_usage_data_raw is not None:
283
+ if pumps_energy_usage_data_raw.shape[0] != n_time_steps:
284
+ __raise_shape_mismatch("pumps_energy_usage_data_raw")
285
+ if pumps_efficiency_data_raw is not None:
286
+ if pumps_efficiency_data_raw.shape[0] != n_time_steps:
287
+ __raise_shape_mismatch("pumps_efficiency_data_raw")
280
288
 
281
289
  self.__sensor_config = sensor_config
282
290
  self.__sensor_noise = sensor_noise
@@ -286,8 +294,6 @@ class ScadaData(Serializable):
286
294
  self.__sensor_readings = None
287
295
  self.__frozen_sensor_config = frozen_sensor_config
288
296
  self.__sensor_readings_time = sensor_readings_time
289
- self.__pump_energy_usage_data = pump_energy_usage_data
290
- self.__pump_efficiency_data = pump_efficiency_data
291
297
 
292
298
  if self.__frozen_sensor_config is False:
293
299
  self.__pressure_data_raw = pressure_data_raw
@@ -301,6 +307,8 @@ class ScadaData(Serializable):
301
307
  self.__surface_species_concentration_raw = surface_species_concentration_raw
302
308
  self.__bulk_species_node_concentration_raw = bulk_species_node_concentration_raw
303
309
  self.__bulk_species_link_concentration_raw = bulk_species_link_concentration_raw
310
+ self.__pumps_energy_usage_data_raw = pumps_energy_usage_data_raw
311
+ self.__pumps_efficiency_data_raw = pumps_efficiency_data_raw
304
312
  else:
305
313
  sensor_config = self.__sensor_config
306
314
 
@@ -338,6 +346,14 @@ class ScadaData(Serializable):
338
346
  self.__pumps_state_data_raw = __reduce_data(data=pumps_state_data_raw,
339
347
  item_to_idx=pump_to_idx,
340
348
  sensors=sensor_config.pump_state_sensors)
349
+ self.__pumps_energy_usage_data_raw = \
350
+ __reduce_data(data=pumps_energy_usage_data_raw,
351
+ item_to_idx=pump_to_idx,
352
+ sensors=sensor_config.pump_enegeryconsumption_sensors)
353
+ self.__pumps_efficiency_data_raw = \
354
+ __reduce_data(data=pumps_efficiency_data_raw,
355
+ item_to_idx=pump_to_idx,
356
+ sensors=sensor_config.pump_efficiency_sensors)
341
357
  self.__valves_state_data_raw = __reduce_data(data=valves_state_data_raw,
342
358
  item_to_idx=valve_to_idx,
343
359
  sensors=sensor_config.valve_state_sensors)
@@ -881,6 +897,10 @@ class ScadaData(Serializable):
881
897
  quality_link_sensors=self.__sensor_config.quality_link_sensors,
882
898
  valve_state_sensors=self.__sensor_config.valve_state_sensors,
883
899
  pump_state_sensors=self.__sensor_config.pump_state_sensors,
900
+ pump_efficiency_sensors=
901
+ self.__sensor_config.pump_efficiency_sensors,
902
+ pump_energyconsumption_sensors=
903
+ self.__sensor_config.pump_energyconsumption_sensors,
884
904
  tank_volume_sensors=self.__sensor_config.tank_volume_sensors,
885
905
  bulk_species_node_sensors=
886
906
  self.__sensor_config.bulk_species_node_sensors,
@@ -906,8 +926,8 @@ class ScadaData(Serializable):
906
926
  pumps_state_data_raw=self.pumps_state_data_raw,
907
927
  valves_state_data_raw=self.valves_state_data_raw,
908
928
  tanks_volume_data_raw=tanks_volume_data,
909
- pump_energy_usage_data=self.pump_energy_usage_data,
910
- pump_efficiency_data=self.pump_efficiency_data,
929
+ pumps_energy_usage_data_raw=self.pumps_energyconsumption_data_raw,
930
+ pumps_efficiency_data_raw=self.pumps_efficiency_data_raw,
911
931
  bulk_species_node_concentration_raw=bulk_species_node_concentrations,
912
932
  bulk_species_link_concentration_raw=bulk_species_link_concentrations,
913
933
  surface_species_concentration_raw=surface_species_concentrations)
@@ -1154,44 +1174,28 @@ class ScadaData(Serializable):
1154
1174
  return deepcopy(self.__bulk_species_link_concentration_raw)
1155
1175
 
1156
1176
  @property
1157
- def pump_energy_usage_data(self) -> np.ndarray:
1177
+ def pumps_energyconsumption_data_raw(self) -> np.ndarray:
1158
1178
  """
1159
- Gets the energy usage of each pump.
1160
-
1161
- .. note::
1162
- This attribute is NOT included in
1163
- :func:`~epyt_flow.simulation.scada.scada_data.ScadaData.get_data` --
1164
- calling this function is the only way of accessing the energy usage of each pump.
1165
-
1166
- The odering in the returned NumPy array corresponds to the ordering
1167
- of the pumps in EPANET.
1179
+ Gets the raw energy consumption of each pump.
1168
1180
 
1169
1181
  Returns
1170
1182
  -------
1171
1183
  `numpy.ndarray`
1172
- Energy usage of each pump.
1184
+ Energy consumption of each pump.
1173
1185
  """
1174
- return deepcopy(self.__pump_energy_usage_data)
1186
+ return deepcopy(self.__pumps_energy_usage_data_raw)
1175
1187
 
1176
1188
  @property
1177
- def pump_efficiency_data(self) -> np.ndarray:
1189
+ def pumps_efficiency_data_raw(self) -> np.ndarray:
1178
1190
  """
1179
- Gets the pumps' efficiency.
1180
-
1181
- .. note::
1182
- This attribute is NOT included in
1183
- :func:`~epyt_flow.simulation.scada.scada_data.ScadaData.get_data` --
1184
- calling this function is the only way of accessing the pumps' efficiency.
1185
-
1186
- The odering in the returned NumPy array corresponds to the ordering
1187
- of the pumps in EPANET.
1191
+ Gets the raw efficiency of each pump.
1188
1192
 
1189
1193
  Returns
1190
1194
  -------
1191
1195
  `numpy.ndarray`
1192
1196
  Pumps' efficiency.
1193
1197
  """
1194
- return deepcopy(self.__pump_efficiency_data)
1198
+ return deepcopy(self.__pumps_efficiency_data_raw)
1195
1199
 
1196
1200
  def __init(self):
1197
1201
  self.__apply_sensor_noise = lambda x: x
@@ -1222,6 +1226,12 @@ class ScadaData(Serializable):
1222
1226
  elif sensor_event.sensor_type == SENSOR_TYPE_PUMP_STATE:
1223
1227
  idx = self.__sensor_config.get_index_of_reading(
1224
1228
  pump_state_sensor=sensor_event.sensor_id)
1229
+ elif sensor_event.sensor_type == SENSOR_TYPE_PUMP_EFFICIENCY:
1230
+ idx = self.__sensor_config.get_index_of_reading(
1231
+ pump_efficiency_sensor=sensor_event.sensor_id)
1232
+ elif sensor_event.sensor_type == SENSOR_TYPE_PUMP_ENERGYCONSUMPTION:
1233
+ idx = self.__sensor_config.get_index_of_reading(
1234
+ pump_energyconsumption_sensor=sensor_event.sensor_id)
1225
1235
  elif sensor_event.sensor_type == SENSOR_TYPE_TANK_VOLUME:
1226
1236
  idx = self.__sensor_config.get_index_of_reading(
1227
1237
  tank_volume_sensor=sensor_event.sensor_id)
@@ -1256,8 +1266,8 @@ class ScadaData(Serializable):
1256
1266
  "surface_species_concentration_raw": self.__surface_species_concentration_raw,
1257
1267
  "bulk_species_node_concentration_raw": self.__bulk_species_node_concentration_raw,
1258
1268
  "bulk_species_link_concentration_raw": self.__bulk_species_link_concentration_raw,
1259
- "pump_energy_usage_data": self.__pump_energy_usage_data,
1260
- "pump_efficiency_data": self.__pump_efficiency_data}
1269
+ "pumps_energy_usage_data_raw": self.__pumps_energy_usage_data_raw,
1270
+ "pumps_efficiency_data_raw": self.__pumps_efficiency_data_raw}
1261
1271
 
1262
1272
  return super().get_attributes() | attr
1263
1273
 
@@ -1286,8 +1296,9 @@ class ScadaData(Serializable):
1286
1296
  other.bulk_species_node_concentration_raw) \
1287
1297
  and np.all(self.__bulk_species_link_concentration_raw ==
1288
1298
  other.bulk_species_link_concentration_raw) \
1289
- and np.all(self.__pump_energy_usage_data == other.pump_energy_usage_data) \
1290
- and np.all(self.__pump_efficiency_data == other.pump_efficiency_data)
1299
+ and np.all(self.__pumps_energy_usage_data_raw ==
1300
+ other.pumps_energyconsumption_data_raw) \
1301
+ and np.all(self.__pumps_efficiency_data_raw == other.pumps_efficiency_data_raw)
1291
1302
  except Exception as ex:
1292
1303
  warnings.warn(ex.__str__())
1293
1304
  return False
@@ -1308,8 +1319,8 @@ class ScadaData(Serializable):
1308
1319
  f"surface_species_concentration_raw: {self.__surface_species_concentration_raw} " + \
1309
1320
  f"bulk_species_node_concentration_raw: {self.__bulk_species_node_concentration_raw}" +\
1310
1321
  f" bulk_species_link_concentration_raw: {self.__bulk_species_link_concentration_raw}" +\
1311
- f" pump_efficiency_data: {self.__pump_efficiency_data} " + \
1312
- f"pump_energy_usage_data: {self.__pump_energy_usage_data}"
1322
+ f" pumps_efficiency_data_raw: {self.__pumps_efficiency_data_raw} " + \
1323
+ f"pumps_energy_usage_data_raw: {self.__pumps_energy_usage_data_raw}"
1313
1324
 
1314
1325
  def change_sensor_config(self, sensor_config: SensorConfig) -> None:
1315
1326
  """
@@ -1491,11 +1502,13 @@ class ScadaData(Serializable):
1491
1502
  self.__sensor_config.surface_species_sensors = \
1492
1503
  other.sensor_config.surface_species_sensors
1493
1504
 
1494
- if self.__pump_energy_usage_data is None and other.pump_energy_usage_data is not None:
1495
- self.__pump_energy_usage_data = other.pump_energy_usage_data
1505
+ if self.__pumps_energy_usage_data_raw is None and \
1506
+ other.pumps_energy_usage_data_raw is not None:
1507
+ self.__pumps_energy_usage_data_raw = other.pumps_energy_usage_data_raw
1496
1508
 
1497
- if self.__pump_efficiency_data is None and other.pump_efficiency_data is not None:
1498
- self.__pump_efficiency_data = other.pump_efficiency_data
1509
+ if self.__pumps_efficiency_data_raw is None and \
1510
+ other.pumps_efficiency_data_raw is not None:
1511
+ self.__pumps_efficiency_data_raw = other.pumps_efficiency_data_raw
1499
1512
 
1500
1513
  self.__init()
1501
1514
 
@@ -1581,14 +1594,14 @@ class ScadaData(Serializable):
1581
1594
  other.bulk_species_link_concentration_raw),
1582
1595
  axis=0)
1583
1596
 
1584
- if self.__pump_energy_usage_data is not None:
1585
- self.__pump_energy_usage_data = np.concatenate(
1586
- (self.__pump_energy_usage_data, other.pump_energy_usage_data),
1597
+ if self.__pumps_energy_usage_data_raw is not None:
1598
+ self.__pumps_energy_usage_data_raw = np.concatenate(
1599
+ (self.__pumps_energy_usage_data_raw, other.pumps_energy_usage_data_raw),
1587
1600
  axis=0)
1588
1601
 
1589
- if self.__pump_efficiency_data is not None:
1590
- self.__pump_efficiency_data = np.concatenate(
1591
- (self.__pump_efficiency_data, other.pump_efficiency_data),
1602
+ if self.__pumps_efficiency_data_raw is not None:
1603
+ self.__pumps_efficiency_data_raw = np.concatenate(
1604
+ (self.__pumps_efficiency_data_raw, other.pumps_efficiency_data_raw),
1592
1605
  axis=0)
1593
1606
 
1594
1607
  def get_data(self) -> np.ndarray:
@@ -1609,6 +1622,8 @@ class ScadaData(Serializable):
1609
1622
  "nodes_quality": self.__node_quality_data_raw,
1610
1623
  "links_quality": self.__link_quality_data_raw,
1611
1624
  "pumps_state": self.__pumps_state_data_raw,
1625
+ "pumps_efficiency": self.__pumps_efficiency_data_raw,
1626
+ "pumps_energyconsumption": self.__pumps_energy_usage_data_raw,
1612
1627
  "valves_state": self.__valves_state_data_raw,
1613
1628
  "tanks_volume": self.__tanks_volume_data_raw,
1614
1629
  "bulk_species_node_concentrations": self.__bulk_species_node_concentration_raw,
@@ -1632,6 +1647,10 @@ class ScadaData(Serializable):
1632
1647
  data.append(self.__valves_state_data_raw)
1633
1648
  if self.__pumps_state_data_raw is not None:
1634
1649
  data.append(self.__pumps_state_data_raw)
1650
+ if self.__pumps_efficiency_data_raw is not None:
1651
+ data.append(self.__pumps_efficiency_data_raw)
1652
+ if self.__pumps_energy_usage_data_raw is not None:
1653
+ data.append(self.__pumps_energy_usage_data_raw)
1635
1654
  if self.__tanks_volume_data_raw is not None:
1636
1655
  data.append(self.__tanks_volume_data_raw)
1637
1656
  if self.__surface_species_concentration_raw is not None:
@@ -1896,6 +1915,85 @@ class ScadaData(Serializable):
1896
1915
  for s_id in sensor_locations]
1897
1916
  return self.__sensor_readings[:, idx]
1898
1917
 
1918
+ def get_data_pumps_efficiency(self, sensor_locations: list[str] = None) -> np.ndarray:
1919
+ """
1920
+ Gets the final pump efficiency sensor readings -- note that those might be subject to
1921
+ given sensor faults and sensor noise/uncertainty.
1922
+
1923
+ Parameters
1924
+ ----------
1925
+ sensor_locations : `list[str]`, optional
1926
+ Existing pump efficiency sensor locations for which the sensor readings are requested.
1927
+ If None, the readings from all pump efficiency sensors are returned.
1928
+
1929
+ The default is None.
1930
+
1931
+ Returns
1932
+ -------
1933
+ `numpy.ndarray`
1934
+ Pump efficiency sensor readings.
1935
+ """
1936
+ if self.__sensor_config.pump_efficiency_sensors == []:
1937
+ raise ValueError("No pump efficiency sensors set")
1938
+ if sensor_locations is not None:
1939
+ if not isinstance(sensor_locations, list):
1940
+ raise TypeError("'sensor_locations' must be an instance of 'list[str]' " +
1941
+ f"but not of '{type(sensor_locations)}'")
1942
+ if any(s_id not in self.__sensor_config.pump_efficiency_sensors
1943
+ for s_id in sensor_locations):
1944
+ raise ValueError("Invalid sensor ID in 'sensor_locations' -- note that all " +
1945
+ "sensors in 'sensor_locations' must be set in the current " +
1946
+ "pump efficiency sensor configuration")
1947
+ else:
1948
+ sensor_locations = self.__sensor_config.pump_efficiency_sensors
1949
+
1950
+ if self.__sensor_readings is None:
1951
+ self.get_data()
1952
+
1953
+ idx = [self.__sensor_config.get_index_of_reading(pump_efficiency_sensor=s_id)
1954
+ for s_id in sensor_locations]
1955
+ return self.__sensor_readings[:, idx]
1956
+
1957
+ def get_data_pumps_energyconsumption(self, sensor_locations: list[str] = None) -> np.ndarray:
1958
+ """
1959
+ Gets the final pump energy consumption sensor readings -- note that those might be subject
1960
+ to given sensor faults and sensor noise/uncertainty.
1961
+
1962
+ Parameters
1963
+ ----------
1964
+ sensor_locations : `list[str]`, optional
1965
+ Existing pump energy consumption sensor locations for which
1966
+ the sensor readings are requested.
1967
+ If None, the readings from all pump energy consumption sensors are returned.
1968
+
1969
+ The default is None.
1970
+
1971
+ Returns
1972
+ -------
1973
+ `numpy.ndarray`
1974
+ Pump energy consumption sensor readings.
1975
+ """
1976
+ if self.__sensor_config.pump_energyconsumption_sensors == []:
1977
+ raise ValueError("No pump energy consumption sensors set")
1978
+ if sensor_locations is not None:
1979
+ if not isinstance(sensor_locations, list):
1980
+ raise TypeError("'sensor_locations' must be an instance of 'list[str]' " +
1981
+ f"but not of '{type(sensor_locations)}'")
1982
+ if any(s_id not in self.__sensor_config.pump_energyconsumption_sensors
1983
+ for s_id in sensor_locations):
1984
+ raise ValueError("Invalid sensor ID in 'sensor_locations' -- note that all " +
1985
+ "sensors in 'sensor_locations' must be set in the current " +
1986
+ "pump efficiency sensor configuration")
1987
+ else:
1988
+ sensor_locations = self.__sensor_config.pump_energyconsumption_sensors
1989
+
1990
+ if self.__sensor_readings is None:
1991
+ self.get_data()
1992
+
1993
+ idx = [self.__sensor_config.get_index_of_reading(pump_energyconsumption_sensor=s_id)
1994
+ for s_id in sensor_locations]
1995
+ return self.__sensor_readings[:, idx]
1996
+
1899
1997
  def get_data_valves_state(self, sensor_locations: list[str] = None) -> np.ndarray:
1900
1998
  """
1901
1999
  Gets the final valve state sensor readings -- note that those might be subject to
@@ -2052,7 +2150,7 @@ class ScadaData(Serializable):
2052
2150
  raise TypeError("'bulk_species_sensor_locations' must be an instance of 'dict'" +
2053
2151
  f" but not of '{type(bulk_species_sensor_locations)}'")
2054
2152
  for species_id in bulk_species_sensor_locations:
2055
- if species_id not in self.__sensor_config.bulk_species_sensors:
2153
+ if species_id not in self.__sensor_config.bulk_species_node_sensors:
2056
2154
  raise ValueError(f"Species '{species_id}' is not included in the " +
2057
2155
  "sensor configuration")
2058
2156
 
@@ -3,6 +3,7 @@ Module provides a class for specifying scenario configurations.
3
3
  """
4
4
  from typing import Any
5
5
  from copy import deepcopy
6
+ import os
6
7
  import json
7
8
  import numpy as np
8
9
 
@@ -219,7 +220,7 @@ class ScenarioConfig(Serializable):
219
220
  `str`
220
221
  Path to the .inp file.
221
222
  """
222
- return self.__f_inp_in
223
+ return os.path.join(self._parent_path, self.__f_inp_in)
223
224
 
224
225
  @property
225
226
  def f_msx_in(self) -> str:
@@ -231,7 +232,10 @@ class ScenarioConfig(Serializable):
231
232
  `str`
232
233
  Path to the .msx file.
233
234
  """
234
- return self.__f_msx_in
235
+ if self.__f_msx_in is None:
236
+ return None
237
+ else:
238
+ return os.path.join(self._parent_path, self.__f_msx_in)
235
239
 
236
240
  @property
237
241
  def general_params(self) -> dict: