wolfhece 2.0.44__py3-none-any.whl → 2.0.45__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.
wolfhece/Model1D.py CHANGED
@@ -8,6 +8,7 @@ import math
8
8
  import matplotlib.pyplot as plt
9
9
  import multiprocessing
10
10
  import numpy as np
11
+
11
12
  import os, warnings
12
13
  import pandas as pd
13
14
  import shutil
@@ -862,6 +863,343 @@ class Creator_1D:
862
863
  znes.myzones.pop(id)
863
864
  return znes
864
865
 
866
+ def place_minimum_altitude(self, zones: Zones, filename: str, save_as: str = '') -> Zones:
867
+ """Return a Zones object in which all altitudes below a defined treshold
868
+ for specific vectors (sections specified in a csv file) are raised
869
+ to the new altitudes.
870
+
871
+ :param zones: Zones object containing the vectors
872
+ :type zones: Zones
873
+ :param filename: .csv file containing the sections and their minimum altitudes,
874
+ first column is the section name and second column is the minimum altitude.
875
+ :type filename: str
876
+ :param save_as: path to the the file where the modified Zones will be saved
877
+ if no path is given the results is not saved but only returned , defaults to ''
878
+ :type save_as: str, optional
879
+ :return: New Zones object with the modified altitudes
880
+ :rtype: Zones
881
+ """
882
+ # Read the csv file as a pandas dataframe
883
+ # df_sections = self.read_csv_as_dataframe(filename, column_names=['section', 'altitude'])
884
+ df_sections = pd.read_csv(filename, names=['section', 'altitude'])
885
+ df_sections['section'] = df_sections['section'].astype(str)
886
+ # Replace the minimum altitude by comparing the altitude of each vertex to the minimum altitude
887
+ for zne in zones.myzones:
888
+ for vec in tqdm(zne.myvectors, desc='Replacing minimum altitude', colour= Colors.TQDM.value, unit='vector'):
889
+ if vec.myname in df_sections['section'].values:
890
+ index = df_sections.index[df_sections['section'] == vec.myname][0]
891
+ test = df_sections['altitude'][index]
892
+ for vert in vec.myvertices:
893
+ if vert.z < test:
894
+ vert.z = test
895
+ if self.wx_exists:
896
+ logging.info(f"Minimum altitude for {vec.myname} is now {test}")
897
+ else:
898
+ print(f"Minimum altitude for {vec.myname} is now {test}")
899
+ # Save the new Zones
900
+ zones.find_minmax(update=True)
901
+ if save_as:
902
+ zones.saveas(save_as)
903
+ return zones
904
+
905
+ def change_minimum_altitude(self, zones: Zones,filename: str, save_as: str = '') -> Zones:
906
+ """Return a Zones object in which all altitudes below a defined treshold
907
+ for specific vectors (sections specified in a csv file) are raised
908
+ to the new altitudes.
909
+
910
+ :param zones: Zones object containing the vectors
911
+ :type zones: Zones
912
+ :param filename: .csv file containing the sections and their minimum altitudes,
913
+ first column is the section name and second column is the minimum altitude.
914
+ :type filename: str
915
+ :param save_as: path to the the file where the modified Zones will be saved
916
+ if no path is given the results is not saved but only returned , defaults to ''
917
+ :type save_as: str, optional
918
+ :return: New Zones object with the modified altitudes
919
+ :rtype: Zones
920
+ """
921
+ # Read the csv file as a pandas dataframe
922
+ # df_sections = self.read_csv_as_dataframe(filename, column_names=['section', 'altitude'])
923
+ df_sections = pd.read_csv(filename, names=['section', 'altitude', 'new_altitude'])
924
+ df_sections['section'] = df_sections['section'].astype(str)
925
+ # Replace the minimum altitude by comparing the altitude of each vertex to the minimum altitude
926
+ for zne in zones.myzones:
927
+ for vec in tqdm(zne.myvectors, desc='Replacing minimum altitude', colour= Colors.TQDM.value, unit='vector'):
928
+ if vec.myname in df_sections['section'].values:
929
+ index = df_sections.index[df_sections['section'] == vec.myname][0]
930
+ test = df_sections['altitude'][index]
931
+ new_value = df_sections['new_altitude'][index]
932
+ for vert in vec.myvertices:
933
+ if vert.z < test:
934
+ vert.z =new_value
935
+ if self.wx_exists:
936
+ logging.info(f"Minimum altitude for {vec.myname} has been changed to {new_value}.")
937
+ else:
938
+ print(f"Minimum altitude for {vec.myname} has been changed to {new_value}.")
939
+ # Save the new Zones
940
+ zones.find_minmax(update=True)
941
+ if save_as:
942
+ zones.saveas(save_as)
943
+ return zones
944
+
945
+ def __change_minimum_altitude(self, zones: Zones,filename: str, save_as: str = '') -> Zones:
946
+ """Return a Zones object in which all altitudes below a defined treshold
947
+ for specific vectors (sections specified in a csv file) are raised
948
+ to the new altitudes.
949
+
950
+ :param zones: Zones object containing the vectors
951
+ :type zones: Zones
952
+ :param filename: .csv file containing the sections and their minimum altitudes,
953
+ first column is the section name and second column is the minimum altitude.
954
+ :type filename: str
955
+ :param save_as: path to the the file where the modified Zones will be saved
956
+ if no path is given the results is not saved but only returned , defaults to ''
957
+ :type save_as: str, optional
958
+ :return: New Zones object with the modified altitudes
959
+ :rtype: Zones
960
+ """
961
+ # Read the csv file as a pandas dataframe
962
+ # df_sections = self.read_csv_as_dataframe(filename, column_names=['section', 'altitude'])
963
+ df_sections = pd.read_csv(filename, names=['section', 'altitude', 'new_altitude'])
964
+ # Replace the minimum altitude by comparing the altitude of each vertex to the minimum altitude
965
+ for zne in zones.myzones:
966
+ for vec in tqdm(zne.myvectors, desc='Replacing minimum altitude', colour= Colors.TQDM.value, unit='vector'):
967
+ if vec.myname in df_sections['section'].values or int(vec.myname) in df_sections['section'].values:
968
+
969
+ try:
970
+ index = df_sections.index[df_sections['section'] == vec.myname][0]
971
+ except IndexError:
972
+ index = df_sections.index[df_sections['section'] == int(vec.myname)][0]
973
+
974
+ test = df_sections['altitude'][index]
975
+ new_value = df_sections['new_altitude'][index]
976
+ for vert in vec.myvertices:
977
+ if vert.z < test:
978
+ vert.z =new_value
979
+ if self.wx_exists:
980
+ logging.info(f"Minimum altitude for {vec.myname} has been changed to {new_value}.")
981
+ else:
982
+ print(f"Minimum altitude for {vec.myname} has been changed to {new_value}.")
983
+ # Save the new Zones
984
+ zones.find_minmax(update=True)
985
+ if save_as:
986
+ zones.saveas(save_as)
987
+ return zones
988
+
989
+ def reverse_sense_zones(self, zones: Zones, save_as: str = '') -> Zones:
990
+ """Reverse the sense of the vectors in a Zones object.
991
+
992
+ :param zones: Zones object containing the vectors
993
+ :type zones: Zones
994
+ :param save_as: path to the the file where the modified Zones will be saved
995
+ if no path is given the results is not saved but only returned , defaults to ''
996
+ :type save_as: str, optional
997
+ :return: New Zones object with the reversed vectors
998
+ :rtype: Zones
999
+ """
1000
+ znes = zones.deepcopy_zones()
1001
+ for zne in znes.myzones:
1002
+ for vec in zne.myvectors:
1003
+ vec.myvertices.reverse()
1004
+ znes.find_minmax(update=True)
1005
+ if save_as:
1006
+ znes.saveas(save_as)
1007
+ return znes
1008
+
1009
+ def v_shape_cross_section(self,
1010
+ cross_sections: crosssections,
1011
+ profile_name: str,
1012
+ increment:float = None,
1013
+ zmax:float = None,
1014
+ save_as:str ='') -> crosssections:
1015
+ """Create a V shape profile from a given profile in a cross section object
1016
+ and return the new cross section object.
1017
+ """
1018
+
1019
+ prof = cross_sections.get_profile(profile_name)
1020
+ self.v_shape(prof, increment, zmax)
1021
+ cross_sections.find_minmax(update=True)
1022
+
1023
+ if save_as:
1024
+ cross_sections.saveas(save_as)
1025
+ return cross_sections
1026
+
1027
+ def v_shape(self,
1028
+ prof: profile,
1029
+ increment = None,
1030
+ zmax = None) -> profile:
1031
+ sz = prof.get_sz()
1032
+
1033
+ # If the max height is not provided.
1034
+ if zmax is None:
1035
+ zmax = max(sz[1])
1036
+ zmin = min(sz[1])
1037
+ # Distance between point
1038
+ height = zmax - zmin
1039
+
1040
+ # To check if the number of vertices is odd or even
1041
+ modulus = len(sz[0]) % 2
1042
+
1043
+ # Odd case
1044
+ if modulus != 0:
1045
+ start = round(len(sz[0])/2)
1046
+ coords_mid = prof.myvertices[start -1]
1047
+ coords_mid.z = zmin
1048
+ # Even case
1049
+ elif modulus == 0:
1050
+ start = int(len(sz[0])/2)
1051
+ coords_mid = prof.myvertices[start - 1]
1052
+ coords_mid.z = zmin
1053
+ coords_mid_1 = prof.myvertices[start]
1054
+ coords_mid_1.z = zmin
1055
+ # in case the increment is not given,the height is divided equally
1056
+ if increment is None:
1057
+ number_of_increment = start
1058
+ increment = height/number_of_increment
1059
+
1060
+ for i in range(1, start):
1061
+ z = zmin + (increment*i)
1062
+ vert_1 = prof.myvertices[(start - 1) - i]
1063
+ if modulus == 0:
1064
+ vert_2 = prof.myvertices[(start ) + i]
1065
+ elif modulus != 0:
1066
+ vert_2 = prof.myvertices [(start - 1) + i]
1067
+
1068
+ vert_1.z = z
1069
+ vert_2.z = z
1070
+
1071
+
1072
+ prof.find_minmax()
1073
+
1074
+ def transform_to_rectangular_shape(self,
1075
+ prof: profile,
1076
+ nb_vertices: int,
1077
+ zmin: float = None,
1078
+ zmax: float = None):
1079
+ sz = prof.get_sz()
1080
+ if zmin is None:
1081
+ zmin = min(sz[1])
1082
+ if zmax is None:
1083
+ zmax = max(sz[1])
1084
+ modulus = len(sz[0]) % 2
1085
+ if modulus != 0:
1086
+ start = round(len(sz[0])/2)
1087
+ coords_mid = prof.myvertices[start -1]
1088
+ coords_mid.z = zmin
1089
+
1090
+ elif modulus == 0:
1091
+ start = int(len(sz[0])/2)
1092
+ coords_mid = prof.myvertices[start - 1]
1093
+ coords_mid.z = zmin
1094
+ coords_mid_1 = prof.myvertices[start]
1095
+ coords_mid_1.z = zmin
1096
+
1097
+ for i in range(1,start):
1098
+ vert_1 = prof.myvertices[(start - 1) - i]
1099
+ if modulus == 0:
1100
+ vert_2 = prof.myvertices[(start ) + i]
1101
+ elif modulus != 0:
1102
+ vert_2 = prof.myvertices [(start - 1) + i]
1103
+ if i < nb_vertices:
1104
+ vert_1.z = zmin
1105
+ vert_2.z = zmin
1106
+ else:
1107
+ vert_1.z = zmax
1108
+ vert_2.z = zmax
1109
+
1110
+ prof.find_minmax()
1111
+
1112
+ def rectangular_shape_cross_section(self,
1113
+ cross_sections: crosssections,
1114
+ profile_name: str,
1115
+ nb_vertices: int,
1116
+ zmin: float = None,
1117
+ zmax: float = None,
1118
+ save_as: str = '') -> crosssections:
1119
+ """Create a rectangular shape profile from a given profile in a cross section object
1120
+ and return the new cross section object.
1121
+ """
1122
+ prof = cross_sections.get_profile(profile_name)
1123
+ self.transform_to_rectangular_shape(prof, nb_vertices, zmin, zmax)
1124
+ cross_sections.find_minmax(update=True)
1125
+ if save_as:
1126
+ cross_sections.saveas(save_as)
1127
+ return cross_sections
1128
+
1129
+ def v_shape_vector(self,
1130
+ prof: vector,
1131
+ increment = None,
1132
+ zmax = None) -> vector:
1133
+ sz = prof.get_sz()
1134
+ if zmax is None:
1135
+ zmax = max(sz[1])
1136
+ zmin = min(sz[1])
1137
+ # Distance between point
1138
+ height = zmax - zmin
1139
+ # To check if the number of vertices is odd or even
1140
+ modulus = len(sz[0]) % 2
1141
+
1142
+ # Odd case
1143
+ if modulus != 0:
1144
+ start = round(len(sz[0])/2)
1145
+ sz[1][start] = zmin
1146
+ # Even case
1147
+ elif modulus == 0:
1148
+ start = int(len(sz[0])/2)
1149
+ sz[1][start] = zmin
1150
+ sz[1][start+1] = zmin
1151
+
1152
+ # in case the increment is not given,the height is divided equally
1153
+ if increment is None:
1154
+ number_of_increment = len(range(start))
1155
+ increment = height/number_of_increment
1156
+
1157
+ def _v_shape(self,
1158
+ prof: profile,
1159
+ increment = None,
1160
+ zmax = None) -> profile:
1161
+ sz = prof.get_sz()
1162
+ # If the max height is not provided.
1163
+ if zmax is None:
1164
+ zmax = max(sz[1])
1165
+ zmin = min(sz[1])
1166
+ # Distance between point
1167
+ height = zmax - zmin
1168
+ # Reshaping the profile
1169
+ origin = prof.get_xy_from_s(sz[0][0])
1170
+ end = prof.get_xy_from_s(sz[0][-1])
1171
+ sz[1] = 0
1172
+ trace = [[origin.x, origin.y], [end.x, end.y]]
1173
+ prof.set_sz(np.array([sz[0], sz[1]]), trace)
1174
+ # To check if the number of vertices is odd or even
1175
+ modulus = len(sz[0]) % 2
1176
+ # Odd case
1177
+ if modulus != 0:
1178
+ start = round(len(sz[0])/2)
1179
+ sz[1][start] = zmin
1180
+ # Even case
1181
+ else:
1182
+ start = int(len(sz[0])/2)
1183
+ sz[1][start] = zmin
1184
+ sz[1][start+1] = zmin
1185
+
1186
+ # in case the increment is not given,the height is divided equally
1187
+ if increment is None:
1188
+ number_of_increment = len(range(start))
1189
+ increment = height/number_of_increment
1190
+
1191
+ for i in range(start):
1192
+ sz[1][i] = zmax - increment*i
1193
+ sz[1][-i] = sz[1][i]
1194
+ origin = prof.get_xy_from_s(sz[0][0])
1195
+ end = prof.get_xy_from_s(sz[0][-1])
1196
+ # origin = prof.myvertices[0]
1197
+ # end = prof.myvertices[-1]
1198
+ trace = [[origin.x, origin.y], [end.x, end.y]]
1199
+ prof.set_sz(np.array([sz[0], sz[1]]), trace)
1200
+
1201
+
1202
+
865
1203
  # --- Vectors - plotting methods ---
866
1204
  #____________________________________
867
1205
 
@@ -944,11 +1282,23 @@ class Creator_1D:
944
1282
  fig_width = 30,
945
1283
  fig_height = 10,
946
1284
  color = 'red',
947
- linewidth = 2) -> None:
1285
+ linewidth = 2,
1286
+ x_unit = '$m$',
1287
+ y_unit = '$m$') -> None:
948
1288
  """Plot the distance between 2 parallels (for instance river width)
949
1289
  as a function of their mid-vector length.
950
1290
  - Id : zone index
951
- - The discretization step is chosen by the user
1291
+ - The discretization step is chosen by the user.
1292
+
1293
+ .. notes:: The procedure is the following:
1294
+ From the 3 vectors of the zone, the center vector is discretized
1295
+ based on the discretization step provided by the user (1 length unit by default).
1296
+ The number of points is calculated as the length of the center vector divided by the discretization step.
1297
+ the number of points selected is the smallest integer greater than the division result.
1298
+ The newpoints are then projected on the left and right vectors.
1299
+ The distance between the left and right vectors is then calculated,
1300
+ and plotted as a function of the distance along the center vector, after a postprocessing check using
1301
+ subtrings and vectors.
952
1302
 
953
1303
  :param zones: `Zones` object containing the vectors,
954
1304
  :type zones: Zones
@@ -961,7 +1311,7 @@ class Creator_1D:
961
1311
  :param ticks_spacing: Discretization of the x axis, defaults to 1000
962
1312
  :type ticks_spacing: int, optional
963
1313
 
964
- .. notes:: FIXME Should be an option in GUI.
1314
+ .. notes:: FIXME Should be an option in GUI and check whether the substring step is necessary.
965
1315
  .. todo:: 1) FIXME Add detailed information (legends, title, an so on) to the plot.
966
1316
  .. todo:: 2) FIXME Think about a test for this method. could it be used in GUI?
967
1317
  """
@@ -977,21 +1327,51 @@ class Creator_1D:
977
1327
  lsc = vec2.asshapely_ls()
978
1328
  lsr = vec3.asshapely_ls()
979
1329
  lss= [lsl,lsc,lsr] # FIXME
1330
+
1331
+ # Visual idea of the river as linestrings
1332
+ # lsl _________________________
1333
+
1334
+ # lsc -------------------------
1335
+
1336
+ # lsr _________________________
1337
+
980
1338
  #Number of points
981
1339
  nb = int(np.ceil(lsc.length/discretization))
982
1340
  #Adimensional distances along center vector
983
1341
  sloc = np.linspace(0.,1.,nb,endpoint=True)
1342
+
1343
+ # Visual idea of the adimendsional riverbed rediscretized with equidistant points
1344
+ # 0|-----|-----|-----|-----|1
1345
+
984
1346
  #Points along center vector
985
1347
  ptsc = [lsc.interpolate(curs,True) for curs in sloc]
1348
+
1349
+ # Visual idea of the riverbed rediscretized with equidistant points
1350
+ # lsc0|-----|-----|-----|-----|lscf
1351
+
986
1352
  #Real distances along left, right and center vector
987
1353
  sl = [lsl.project(curs) for curs in ptsc]
988
1354
  sr = [lsr.project(curs) for curs in ptsc]
989
1355
  sc = [lsc.project(curs) for curs in ptsc]
1356
+
1357
+ # Visual idea of the river rediscretized with equidistant points (projection of those points on banks)
1358
+
1359
+ # lsl0|_____|_____|_____|_____|lslf
1360
+
1361
+ # lsc0|-----|-----|-----|-----|lscf
1362
+
1363
+ # lsr0|_____|_____|_____|_____|lsrf
1364
+
990
1365
  # Zones of polygones
991
1366
  zonepoly1 = zone(name='polygons_1')
992
1367
  zonepoly2 = zone(name='polygons_2')
993
1368
  left = []
994
1369
  right = []
1370
+ # For consistency reasons, the distance between 2 successive points is converted
1371
+ # to substring and then each point of the substring is stored as a vertex.
1372
+ # The operation is done at both banks at the same time,
1373
+ # therefore, each vertex has it corresponding point on the other bank.
1374
+ # The point are then stored in 2 vectors from which the distance is computed since the number of vertex is the same.
995
1375
 
996
1376
  for i in range(len(sl)-1):
997
1377
  #mean distance along center will be stored as Z value of each vertex
@@ -1049,8 +1429,8 @@ class Creator_1D:
1049
1429
 
1050
1430
  ax.set_ylim(min(width)-0.5, max(width)+0.5)
1051
1431
  ax.set_xlim(0,max(sc))
1052
- ax.set_ylabel('Width ($m$)', fontsize='large' )
1053
- ax.set_xlabel('Length ($m$)', fontsize='large')
1432
+ ax.set_ylabel(f'Width [{y_unit}]', fontsize='large' )
1433
+ ax.set_xlabel(f'Length [{x_unit}]', fontsize='large')
1054
1434
  ax.xaxis.set_major_locator(MultipleLocator(ticks_spacing))
1055
1435
  ax.xaxis.set_major_formatter('{x:.0f}')
1056
1436
  ax.xaxis.set_minor_locator(MultipleLocator(ticks_spacing/5))
@@ -1346,7 +1726,7 @@ class Creator_1D:
1346
1726
  else:
1347
1727
  raise Exception('An error occured while extracting the WolfArrays from the VRT file.')
1348
1728
 
1349
-
1729
+
1350
1730
 
1351
1731
  # # --- Vector + Arrays Get values from array (Vector + Arrays) ---
1352
1732
  #__________________________________________________________________
@@ -1968,6 +2348,7 @@ class Creator_1D:
1968
2348
  """
1969
2349
  Return the HSPW relations (Initial Conditions) of all profiles
1970
2350
  as a numpy arrray for a given water depth or a given altitude.
2351
+ np.array
1971
2352
 
1972
2353
  :param crosses: List of cross sections
1973
2354
  :type crosses: list[list[profile]]
@@ -2577,16 +2958,20 @@ class Creator_1D:
2577
2958
  :rtype: None
2578
2959
  """
2579
2960
  # Files
2961
+ # Implemented to write only the qini file
2580
2962
  file_aini = self.initialize_file(save_as, fileExtensions.AINI.value)
2581
2963
  file_zini = self.initialize_file(save_as, fileExtensions.ZINI.value)
2582
2964
  file_hini = self.initialize_file(save_as, fileExtensions.HINI.value)
2583
2965
  file_qini = self.initialize_file(save_as, fileExtensions.QINI.value)
2966
+
2584
2967
  if '.aini' in file_choice:
2585
2968
  self.write_file_from_np_array(file_aini, ic, [0, 1, 2, 3], nb_updates=nb_updates, desc= 'Writing aini:')
2586
2969
  if '.zini' in file_choice:
2587
2970
  self.write_file_from_np_array(file_zini, ic, [0, 1, 2, -1], nb_updates=nb_updates, desc ='Writing zini:')
2588
2971
  if '.hini' in file_choice:
2589
2972
  self.write_file_from_np_array(file_hini, ic, [0, 1, 2, -2], nb_updates=nb_updates, desc = 'Writing hini:')
2973
+
2974
+ # qini
2590
2975
  self.write_file_from_np_array(file_qini, ic, [0, 1, 2], nb_updates=nb_updates, desc= 'Writing qini:', force_last=True, q=q)
2591
2976
 
2592
2977
  def write_lengthsvecz_file(self,
@@ -2777,8 +3162,8 @@ class Creator_1D:
2777
3162
  :rtype: list[list[list,list]]
2778
3163
  """
2779
3164
  data = pd.read_csv(file,header=0, delimiter='\t',names=['time','discharge'])
2780
- hydroraph = [[list(data['time']), list(data['discharge'])]]
2781
- return hydroraph
3165
+ hydrograph = [[list(data['time']), list(data['discharge'])]]
3166
+ return hydrograph
2782
3167
 
2783
3168
  def _write_infiltrations(self,
2784
3169
  selected_profiles:list[str],
@@ -3216,6 +3601,7 @@ class Creator_1D:
3216
3601
  """
3217
3602
  # FIXME add an option to select the middle of the crossection instead of just the bottom.
3218
3603
  # FIXME delete discretization from all examples and from this method. It's not used
3604
+
3219
3605
  # Creation of files
3220
3606
  vecz_file = self.initialize_file(save_as,'.vecz')
3221
3607
  vec_file = self.initialize_file(save_as,'.vec')
@@ -3331,8 +3717,10 @@ class Creator_1D:
3331
3717
 
3332
3718
  assert prof.banksbed_postype == postype.BY_VERTEX
3333
3719
  # FIXME insert a test checking the intersections between profiles and parallels.
3720
+ assert isinstance(left_inter, Point), f'The section: {prof.myname} - left intersection is not a Point but a {type(left_inter)}'
3334
3721
  prof.bankleft = wolfvertex(left_inter.x, left_inter.y, left_inter.z)
3335
3722
  prof.bed = wolfvertex(bed_inter.x, bed_inter.y, bed_inter.z)
3723
+ assert isinstance(right_inter, Point), f'The section: {prof.myname} - left intersection is not a Point but a {type(right_inter)}'
3336
3724
  prof.bankright = wolfvertex(right_inter.x, right_inter.y, right_inter.z)
3337
3725
  prof.find_minmax()
3338
3726
 
@@ -3987,8 +4375,8 @@ class Creator_1D:
3987
4375
  """
3988
4376
  splitted_path = os.path.split(batch_file)
3989
4377
  directory = splitted_path[0]
3990
- command = f"start cmd.exe /k {batch_file}"
3991
- shell_window = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
4378
+ command = f'start cmd.exe /k "{batch_file}"'
4379
+ subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
3992
4380
 
3993
4381
  if self.wx_exists:
3994
4382
  logging.info(f'{directory} Check the new shell window.\nThe simulation is running...')
@@ -4096,7 +4484,7 @@ class Creator_1D:
4096
4484
  break
4097
4485
  print(l)
4098
4486
 
4099
- def _run_bat_files(self, bat_file, force_cwd = False):
4487
+ def _run_bat_files(self, bat_file, force_cwd = False, initial_condition = False):
4100
4488
  """
4101
4489
  Run the .bat file in a windows shell
4102
4490
  to start the computations (simulation).
@@ -4105,12 +4493,18 @@ class Creator_1D:
4105
4493
  :type batch_file: str
4106
4494
  :param force_cwd: Force current working directory, defaults to False
4107
4495
  :type force_cwd: bool, optional
4108
- FIXME Deprecated.
4109
4496
  """
4110
4497
  splitted_path = os.path.split(bat_file)
4111
4498
  directory = splitted_path[0]
4112
4499
  simulation_name = splitted_path[1]
4113
- logging.warn(f'{simulation_name} running... \nThe process may take time.')
4500
+ if initial_condition:
4501
+ if self.wx_exists:
4502
+ logging.info(f'{simulation_name} running... \nComputing initial conditions.')
4503
+ else:
4504
+ logging.warn(f'{simulation_name} running... \nComputing initial conditions.')
4505
+ else:
4506
+ logging.warn(f'{simulation_name} running... \nThe process may take time.')
4507
+
4114
4508
  if force_cwd:
4115
4509
  directory = os.path.split(bat_file)[0]
4116
4510
  run = subprocess.run(bat_file, shell=True, capture_output= True, text=True,cwd=directory )
@@ -4864,10 +5258,9 @@ class Creator_1D:
4864
5258
  bank_file = banks
4865
5259
  bank_file.saveas(directory + '\\banks.vecz')
4866
5260
 
4867
- # Polygons
5261
+
4868
5262
  # FIXME is it necessery to have parallels (next round), is it only for cell size or a better used could be found?
4869
- if isinstance(parallels, Zones):
4870
- polygons = self.create_polygons(parallels, discretisation, polygon_number, directory)
5263
+
4871
5264
 
4872
5265
  # Write the tabulated relations of each cross section (profile)
4873
5266
  write_relations = self.write_relations_profiles(sorted_crosssections, directory)
@@ -4884,7 +5277,10 @@ class Creator_1D:
4884
5277
  # FIXME to be verified issue with the extent of profiles
4885
5278
  # (the number of new polygons is different from the number of digitized profiles)
4886
5279
  # May be a test should be implemented on each profiles (probable solution).
4887
- roughnesses = self.roughness_from_polygons(roughness, polygons, mode=roughness_selection)
5280
+ if isinstance(parallels, Zones):
5281
+ # Polygons
5282
+ polygons = self.create_polygons(parallels, discretisation, polygon_number, directory)
5283
+ roughnesses = self.roughness_from_polygons(roughness, polygons, mode=roughness_selection)
4888
5284
 
4889
5285
  roughness_file = self.write_roughnesses(roughnesses, directory)
4890
5286
 
@@ -4913,6 +5309,11 @@ class Creator_1D:
4913
5309
  # Write the .vec file (discretization file of the simulation topography - midriver)
4914
5310
  vec_file2 = self.write_vector_files(sorted_crosssections, save_as=directory, which_type='vec')
4915
5311
 
5312
+ # Write the generic file
5313
+ genfile = self.write_generic_file(directory)
5314
+
5315
+ #-----------------------------------------------------------
5316
+
4916
5317
  # Write the infiltrations
4917
5318
  if isinstance(hydrographs, dict):
4918
5319
  infil = self.write_infiltrations_from_dict(sorted_crosssections,
@@ -4928,6 +5329,35 @@ class Creator_1D:
4928
5329
  writing_type_infiltration,
4929
5330
  epsilon_infiltration)
4930
5331
 
5332
+
5333
+
5334
+ # Update the distribution of initial discharges from the infiltrations
5335
+ self.update_qini_file_from_infiltration_dict(hydrographs, directory)
5336
+
5337
+ # 22. Compute a steady state solution
5338
+ #---------------------------------------------------------------
5339
+ if steady == 'precomputation':
5340
+ # The first batch file is used to compute the steady state solution
5341
+ bat_file = self.write_batch_file(directory_of_executable = exe_file,
5342
+ directory_simulation= directory,
5343
+ simulation_name= simulation_name)
5344
+ self.start_from_steady_state(directory)
5345
+ batch_file = self.write_batch_file(directory_of_executable = exe_file,
5346
+ directory_simulation = directory,
5347
+ simulation_name = simulation_name,
5348
+ wetdry = wetdry,
5349
+ steady ='no precomputation',
5350
+ executable_type = executable_type)
5351
+ else:
5352
+ batch_file = self.write_batch_file(directory_of_executable = exe_file,
5353
+ directory_simulation = directory,
5354
+ simulation_name = simulation_name,
5355
+ wetdry = wetdry,
5356
+ steady = steady,
5357
+ executable_type = executable_type)
5358
+
5359
+ #------------------------------
5360
+
4931
5361
  # write the simulation parameters
4932
5362
  if write_in == 'times':
4933
5363
  write_type = 2
@@ -4935,25 +5365,69 @@ class Creator_1D:
4935
5365
  write_type = 1
4936
5366
  if self.wx_exists == None:
4937
5367
  param = self.write_parameters(directory, write_freq= output_frequency, write_type= write_type, max_time=time)
4938
- # # if steady == 'precomputation' or steady == 'steady':
4939
- # # self.correct_parameters(directory, from_steady=True)
4940
- # # else:
4941
- # # self.correct_parameters(directory)
4942
- # self.correct_parameters(directory) FIXME for other cases when the GUI is not used
4943
5368
 
4944
- genfile = self.write_generic_file(directory)
4945
- batch_file = self.write_batch_file(directory_of_executable = exe_file,
4946
- directory_simulation = directory,
4947
- simulation_name = simulation_name,
4948
- wetdry = wetdry,
4949
- steady = steady,
4950
- executable_type = executable_type)
5369
+
4951
5370
  if run_simulation == 'yes':
4952
5371
  # logging.warn(f'{simulation_name} running... \nThe process may take time.')
4953
5372
  self.run_bat_files(batch_file)
4954
5373
  # logging.warn(f'{simulation_name} completed.')
4955
5374
  return (directory,simulation_name, sorted_crosssections, ic_relations, nb_updates)
4956
5375
 
5376
+ def find_file_from_extension(self, directory:str, file_extension:str) -> str:
5377
+ """Return a file in a directory based on its extension.
5378
+
5379
+ :param directory: file path to the folder
5380
+ :type directory: str
5381
+ :param file_extension: file extension
5382
+ :type file_extension: str
5383
+ :return: file path
5384
+ """
5385
+ for file in os.listdir(directory):
5386
+ if file.endswith(file_extension):
5387
+ return os.path.join(directory, file)
5388
+
5389
+ raise Exception(f'No file with the extension {file_extension} was found in the directory {directory}.')
5390
+
5391
+ def start_from_steady_state(self, directory:str, plot = True):
5392
+ """Compute the steady state solution and update the initial conditions based on the results.
5393
+
5394
+ :param directory: directory (simulation folder)
5395
+ :type directory: str
5396
+ :param plot: wheter the process should open a matplotlib figure
5397
+ containing the steady state solution or not, defaults to True
5398
+ :type plot: bool, optional
5399
+ """
5400
+ self.write_parameters(directory, write_type= 2, max_time=0)
5401
+ bat_file = self.find_file_from_extension(directory, '.bat')
5402
+ self._run_bat_files(bat_file, initial_condition= True)
5403
+ # self.run_bat_files(bat_file)
5404
+ self.update_initial_conditions_from_results(directory, time_step=1, plot=plot)
5405
+
5406
+ def update_initial_conditions_from_results(self,
5407
+ directory:str,
5408
+ time_step:int =1,
5409
+ plot:bool = True):
5410
+ results = Wolfresults_1D(directory)
5411
+
5412
+ if plot:
5413
+ results.plot_variables(figures=['water level','discharge', 'froude','water depth','velocity','wetted section' ]
5414
+ ,time_step=time_step, grid_x_m= 1000)
5415
+ self.log_message('Proposed initial conditions plotted.')
5416
+
5417
+ results.update_ic_from_time_steps(time_step)
5418
+ self.log_message('New initial conditions.')
5419
+
5420
+ def log_message(self, message:str):
5421
+ """Message for the user.
5422
+
5423
+ :param message: message
5424
+ :type message: str
5425
+ """
5426
+ if self.wx_exists:
5427
+ logging.info(message)
5428
+ else:
5429
+ print(message)
5430
+
4957
5431
  def create_multiple_simulations_from_csv(self,
4958
5432
  csv_filename:str,
4959
5433
  folder_path:str,
@@ -5125,7 +5599,7 @@ class Creator_1D:
5125
5599
  infiltrations.append(infiltration)
5126
5600
  return infiltrations
5127
5601
 
5128
- def _create_simulations_from_csv(self,
5602
+ def create_simulations_from_csv(self,
5129
5603
  csv_filename:str,
5130
5604
  folder_path:str,
5131
5605
  cross_sections: crosssections,
@@ -5133,8 +5607,8 @@ class Creator_1D:
5133
5607
  banks: Zones,
5134
5608
  boundary_conditions: dict,
5135
5609
  roughness:Union[float,WolfArray],
5136
- hydrographs: list[list[list]],
5137
5610
  exe_file:str,
5611
+ hydrographs: list[list[list]] = None,
5138
5612
  topography: WolfArray = None,
5139
5613
  initial_discharge:float = None,
5140
5614
  simulation_name:str = 'simul',
@@ -5157,18 +5631,78 @@ class Creator_1D:
5157
5631
  writing_type_infiltration: Literal['continuous', 'stepwise'] = 'continuous',
5158
5632
  epsilon_infiltration:float = 0.01,
5159
5633
  force_steady = True) -> None:
5160
- """Deprecated"""
5161
-
5162
- infiltrations = self.read_csv_as_infiltrations(csv_filename)
5163
- names = self.read_csv_as_dataframe(csv_filename)
5634
+ """
5635
+ Create simulations from a csv file.
5164
5636
 
5165
- # # df = self.read_csv_as_dataframe(csv_filename,column_names=['discharge'])
5166
- # # discharge = df['discharge'][0]
5637
+ :param csv_filename: Csv filename
5638
+ :type csv_filename: str
5639
+ :param folder_path: Folder path
5640
+ :type folder_path: str
5641
+ :param cross_sections: Cross sections
5642
+ :type cross_sections: crosssections
5643
+ :param parallels: Parallels
5644
+ :type parallels: Zones
5645
+ :param banks: Banks
5646
+ :type banks: Zones
5647
+ :param boundary_conditions: Boundary conditions
5648
+ :type boundary_conditions: dict
5649
+ :param roughness: Roughness
5650
+ :type roughness: Union[float,WolfArray]
5651
+ :param exe_file: Exe file
5652
+ :type exe_file: str
5653
+ :param hydrographs: Hydrographs, defaults to None
5654
+ :type hydrographs: list[list[list]], optional
5655
+ :param topography: Topography, defaults to None
5656
+ :type topography: WolfArray, optional
5657
+ :param initial_discharge: Initial discharge, defaults to None
5658
+ :type initial_discharge: float, optional
5659
+ :param simulation_name: Simulation name, defaults to 'simul'
5660
+ :type simulation_name: str, optional
5661
+ :param discretisation: Discretisation, defaults to 10.
5662
+ :type discretisation: float, optional
5663
+ :param extrapolation_of_extremities: Extrapolation of extremities, defaults to 100.
5664
+ :type extrapolation_of_extremities: float, optional
5665
+ :param initial_depth: Initial depth, defaults to 1.
5666
+ :type initial_depth: float, optional
5667
+ :param write_in: Write in, defaults to 'times'
5668
+ :type write_in: Literal['iterations', 'times'], optional
5669
+ :param output_frequency: Output frequency, defaults to 900
5670
+ :type output_frequency: int, optional
5671
+ :param polygon_number: Polygon number, defaults to 1
5672
+ :type polygon_number: int, optional
5673
+ :param code_verbosity_profile: Code verbosity profile, defaults to [1]
5674
+ :type code_verbosity_profile: list, optional
5675
+ :param simulation_time: Simulation time, defaults to None
5676
+ :type simulation_time: int, optional
5677
+ :param roughness_option: Roughness option, defaults to 'under_profile'
5678
+ :type roughness_option: Literal['under_profile', 'under_polygons'], optional
5679
+ :param roughness_selection: Roughness selection, defaults to 'mean'
5680
+ :type roughness_selection: Literal['min','mean','median','max'], optional
5681
+ :param file_type_initial_cond: File type initial cond, defaults to '.aini'
5682
+ :type file_type_initial_cond: Literal['.aini','.hini','.zini'], optional
5683
+ :param infiltration_profiles: Infiltration profiles, defaults to ['1']
5684
+ :type infiltration_profiles: list, optional
5685
+ :param wetdry: Wetdry, defaults to 'evolutive'
5686
+ :type wetdry: Literal['fixed', 'evolutive'], optional
5687
+ :param steady: Steady, defaults to 'precomputation'
5688
+ :type steady: Literal['no precomputation', 'precomputation', 'steady'], optional
5689
+ :param executable_type: Executable type, defaults to 'wolfcli'
5690
+ :type executable_type: Literal['wolfcli', 'wolfclid'], optional
5691
+ :param run_simulation: Run simulation, defaults to 'no'
5692
+ :type run_simulation: Literal['yes', 'no'], optional
5693
+ :param writing_type_infiltration: Writing type infiltration, defaults to 'continuous'
5694
+ :type writing_type_infiltration: Literal['continuous', 'stepwise'], optional
5695
+ :param epsilon_infiltration: Epsilon infiltration, defaults to 0.01
5696
+ :type epsilon_infiltration: float, optional
5697
+ :param force_steady: Force steady, defaults to True
5698
+ :type force_steady: bool, optional
5699
+ """
5700
+ df = self.read_csv_as_dataframe(csv_filename,column_names=['discharge'])
5701
+ discharge = df['discharge'][0]
5167
5702
  # hydrograph = Hydrograph({0:discharge})
5168
5703
  if force_steady:
5169
- hydrographs = infiltrations[0]
5170
5704
  # hydrographs = {}
5171
- # hydrographs =[[[0],[discharge]]]
5705
+ hydrographs =[[[0],[discharge]]]
5172
5706
  original_simulation =self.write_simulation_files_from_crosssections(folder_path,
5173
5707
  cross_sections,
5174
5708
  parallels,
@@ -5178,7 +5712,7 @@ class Creator_1D:
5178
5712
  hydrographs,
5179
5713
  exe_file,
5180
5714
  topography,
5181
- initial_discharge,
5715
+ discharge,
5182
5716
  simulation_name,
5183
5717
  discretisation,
5184
5718
  extrapolation_of_extremities,
@@ -5200,15 +5734,19 @@ class Creator_1D:
5200
5734
  epsilon_infiltration = epsilon_infiltration)
5201
5735
  procedures =[]
5202
5736
 
5203
- for new_infiltration in infiltrations:
5204
- hydrographs = new_infiltration
5205
- values = list(new_infiltration.values())
5206
- extension =f'_Q{int(values[0])}' # FIXME
5207
- new_sim = self.copy_simulation_files(original_simulation[0],original_simulation[0] + extension)
5737
+ new_sims =[]
5738
+ for new_discharge in list(df['discharge']):
5739
+ hydrographs =[[[0],[new_discharge]]]
5740
+
5741
+ extension =f'_Q{int(new_discharge)}'
5742
+ new_sim_name = original_simulation[0] + extension
5743
+ new_sim = self.copy_simulation_files(original_simulation[0],new_sim_name, ignore_filetype='*.bat')
5744
+ new_sims.append(new_sim)
5745
+
5208
5746
 
5209
5747
  self.write_ini_files(original_simulation[3],
5210
5748
  new_sim,
5211
- new_infiltration,
5749
+ new_discharge,
5212
5750
  original_simulation[4],
5213
5751
  file_choice=file_type_initial_cond)
5214
5752
 
@@ -5224,7 +5762,6 @@ class Creator_1D:
5224
5762
  new_name=simulation_name + extension)
5225
5763
  procedures.append(new_batchfile)
5226
5764
 
5227
-
5228
5765
  # # procedure = multiprocessing.Process(target= run_batch_file_multiprocess, args = (new_batchfile))
5229
5766
  # # procedure.start()
5230
5767
  # procedures.append(new_batchfile)
@@ -5232,48 +5769,762 @@ class Creator_1D:
5232
5769
  # self.run_batch_file(new_batchfile)
5233
5770
  # # procedure.start()
5234
5771
 
5772
+ batch_file_group = self.write_batch_simulations(directory_of_executable = exe_file,
5773
+ simulations_path=new_sims,
5774
+ wetdry = wetdry,
5775
+ steady = steady,
5776
+ executable_type = executable_type)
5777
+
5235
5778
  if run_simulation == 'yes':
5236
- runs=[self.run_batch_file(i) for i in tqdm(procedures,'Running simulations', colour= Colors.TQDM.value)]
5237
- # # pool = multiprocessing.Pool(processes=len(procedures))
5779
+ # runs=[self.run_batch_file(i) for i in tqdm(procedures,'Running simulations', colour= Colors.TQDM.value)]
5780
+ # run = self.run_batch_file(batch_file_group)
5781
+ run = self.run_bat_files(batch_file_group)
5782
+ # # pool = multiprocessing.Pool(processes=len(procedures))
5783
+ # runs=[multiprocessing.Process(run_batch_file_multiprocess, args=i) for i in tqdm(procedures)]
5784
+
5785
+ def copy_simulation_files(self,
5786
+ simulation:str,
5787
+ save_as:str,
5788
+ ignore_filetype: Literal['.*qini','*.aini', '*.zini','*.hini','*.cl', '*.rough', 'infil*','*.inf'
5789
+ '*.infil','*.param','*.top','*.ptv','*.gtv','.log', '.count'
5790
+ '*.HEAD','*.RA', '*.RB','*.RQ','*.RS','*.vecz','*.vec','*.txt', '*.bat'] = '') -> str:
5791
+ """
5792
+ Copy simulation files.
5793
+
5794
+ :param simulation: Simulation
5795
+ :type simulation: str
5796
+ :param save_as: Save as
5797
+ :type save_as: str
5798
+ :param ignore_filetype: Ignore filetype, defaults to ''
5799
+ :type ignore_filetype: str, optional
5800
+ :return: Copied directory
5801
+ :rtype: str
5802
+ """
5803
+ if ignore_filetype != '':
5804
+ try:
5805
+ copied_directory = shutil.copytree(src = simulation, dst= save_as)
5806
+ except FileExistsError:
5807
+ # os.rmdir(save_as)
5808
+ # copied_directory = shutil.copytree(src = simulation, dst= save_as)
5809
+ raise Exception(f"The file named ({save_as}) exists already.\n Rename the new file or delete the existing file.")
5810
+ else:
5811
+ try:
5812
+ copied_directory = shutil.copytree(src = simulation, dst= save_as, ignore=shutil.ignore_patterns(ignore_filetype))
5813
+ except FileExistsError:
5814
+ # os.rmdir(save_as)
5815
+ # copied_directory = shutil.copytree(src = simulation, dst= save_as, ignore=shutil.ignore_patterns(ignore_filetype))
5816
+ raise Exception(f"The file named ({save_as}) exists already.\n Rename the new file or delete the existing file.")
5817
+ return copied_directory
5818
+
5819
+ def copy_multiple_simulations_from_csv(self, csv_filename:str, simulation:str) -> None:
5820
+ """
5821
+ Copy multiple simulations from a csv file.
5822
+
5823
+ :param csv_filename: Csv filename
5824
+ :type csv_filename: str
5825
+ :param simulation: Simulation
5826
+ :type simulation: str
5827
+ """
5828
+
5829
+ df = self.read_csv_as_dataframe(csv_filename,column_names=['discharge'])
5830
+ lgth = df.shape[0]
5831
+ for discharge in tqdm(df['discharge']):
5832
+ self.copy_simulation_files(simulation, simulation + f'_Q{discharge}')
5833
+
5834
+ def distribute_values_as_sum(self, array: np.ndarray) -> np.ndarray:
5835
+ """Return a 1D array were the 0 are filled with the previous value in the table, and
5836
+ the existing values are replaced by their summed with the previous value in the table.
5837
+
5838
+ :param array: Array(1D) containing the vaalues of the initial discharge at specific profiles
5839
+ :type array: np.ndarray
5840
+ :return: Updated array
5841
+ :rtype: np.ndarray
5842
+ """
5843
+ for i in range(len(array) - 1):
5844
+ j = i + 1
5845
+ if array[j] == 0:
5846
+ array[j] = array[i]
5847
+ elif array[i] != 0:
5848
+ array[j] += array[i]
5849
+ return array
5850
+
5851
+ def find_qini_values_from_infiltration_dict(self, infiltration:dict) -> dict:
5852
+ """
5853
+ Return a dictionnary containing the infiltration profiles and their initial discharge (first value).
5854
+
5855
+ These dictionnary can be used
5856
+
5857
+ :param infiltration: Infiltration
5858
+ :type infiltration: dict
5859
+ :return: Initial infiltrations
5860
+ :rtype: dict
5861
+ """
5862
+
5863
+ keys = []
5864
+ values =[]
5865
+ initial_infiltrations = {}
5866
+ for key in infiltration:
5867
+ if isinstance(infiltration[key], Hydrograph):
5868
+ qini = infiltration[key].values[0]
5869
+ elif isinstance(infiltration[key], (list,tuple)):
5870
+ qini = infiltration[key][0]
5871
+ elif isinstance(infiltration[key], (float,int)):
5872
+ qini = infiltration[key]
5873
+ else:
5874
+ raise Exception('The initial discharge is not well defined.')
5875
+ initial_infiltrations[int(key)-1] = qini
5876
+
5877
+ return initial_infiltrations
5878
+
5879
+ def compute_qini_values_from_infiltrations_dict(self,
5880
+ infiltrations: dict,
5881
+ nb_cells: int) -> np.ndarray:
5882
+ """Compute the initial discharge from a dictionary of infiltrations.
5883
+ The method returns an array of initial discharges. The array values are
5884
+ distributed from the distribution (linear sum) of the first infiltration values.
5885
+
5886
+ :param infiltrations: Infiltrations (dictionnary of hydrographs)
5887
+ :type infiltrations: dict
5888
+ :param nb_cells: Number of cells
5889
+ :type nb_cells: int
5890
+ :return: Initial discharges
5891
+ :rtype: np.ndarray
5892
+ """
5893
+ qini_values = np.zeros(nb_cells)
5894
+ initial_infiltrations = self.find_qini_values_from_infiltration_dict(infiltrations)
5895
+ for key in initial_infiltrations:
5896
+ qini_values[key] = initial_infiltrations[key]
5897
+
5898
+ qini_values = self.distribute_values_as_sum(qini_values)
5899
+ # print(qini_values)
5900
+ return qini_values
5901
+
5902
+ def update_qini_file_from_infiltration_dict(self,
5903
+ infiltrations: dict,
5904
+ directory:str):
5905
+ """Upadte the qini file from a dictionary of infiltrations.
5906
+
5907
+ :param infiltrations: Infiltrations
5908
+ :type infiltrations: dict
5909
+ :param directory: Directory (folder)
5910
+ :type directory: str
5911
+ """
5912
+ # Find the qini file from the simulation directory
5913
+ qini_file = self.find_file_from_extension(directory, '.qini')
5914
+ # Read the qini file as a pandas dataframe
5915
+ qini_dataframe = self.read_ini_file_as_dataframe(qini_file)
5916
+ # Find the number of cells in the simulation
5917
+ number_of_cells = len(qini_dataframe['value'])
5918
+ # Compute the qini values from the infiltrations dictionary
5919
+ qini = self.compute_qini_values_from_infiltrations_dict(infiltrations, number_of_cells)
5920
+ # Update the qini file with the computed values
5921
+ self.update_ini_file(qini_file, qini)
5922
+
5923
+ def read_ini_file_as_dataframe(self, ini_file:str):
5924
+ """Read an ini file and return a pandas dataframe.
5925
+
5926
+ :param ini_file: File of initial conditions
5927
+ :type ini_file: str
5928
+ :return: Pandas dataframe
5929
+ :rtype: pd.DataFrame
5930
+ """
5931
+ dataframe = pd.read_csv(ini_file,
5932
+ sep= Constants.SEPARATOR.value,
5933
+ skiprows=1,
5934
+ names=['skeleton', 'zone', 'segment','value']
5935
+ )
5936
+ return dataframe
5937
+
5938
+ def update_ini_file(self, ini_file:str, new_values: np.ndarray)-> None:
5939
+ """Update the initial condition file with new values.
5940
+ The method reads the initial condition file as a pandas dataframe,
5941
+ then replace old the values by the new values.
5942
+
5943
+ :param ini_file: File of initial condition
5944
+ :type ini_file: str
5945
+ :param new_values: New values
5946
+ :type new_values: np.ndarray
5947
+ """
5948
+ df = self.read_ini_file_as_dataframe(ini_file)
5949
+ lgth_values = len(df['value'])
5950
+ lgth_new_values = len(new_values)
5951
+ assert lgth_values == lgth_new_values,\
5952
+ f"The length of the new values - {lgth_new_values} is not consistent with the initial condition file - {lgth_values}."
5953
+
5954
+ df['value'] = new_values
5955
+ sep = Constants.SEPARATOR.value
5956
+ with open(ini_file, 'w') as f:
5957
+ f.write(f"{lgth_new_values}\n")
5958
+ for i in range(lgth_values):
5959
+ f.write(f"{df['skeleton'][i]}{sep}{df['zone'][i]}{sep}{df['segment'][i]}{sep}{str(df['value'][i])}\n")
5960
+
5961
+ # Implement a clever way of writing or updating this file from ic or existing files.
5962
+ # check wolfresults_1D update ini file or find another way to update the file.
5963
+
5964
+
5965
+ # --- Outdated methods ---
5966
+ #_________________________
5967
+
5968
+ def __write_batch_file(self,
5969
+ directory_of_executable:str,
5970
+ directory_simulation:str,
5971
+ simulation_name:str,
5972
+ wetdry:Literal['fixed', 'evolutive']='evolutive',
5973
+ steady:Literal['no precomputation', 'precomputation', 'steady'] = 'precomputation',
5974
+ executable_type: Literal['wolfcli', 'wolfclid'] = 'wolfcli',
5975
+ different_names=False,
5976
+ new_name:str =''
5977
+ ) -> str:
5978
+ if different_names:
5979
+ batch_file =self.initialize_file(directory_of_executable, f'{new_name}.bat','') # To avoid simul name FIXME make it clean
5980
+ else:
5981
+ batch_file =self.initialize_file(directory_of_executable, '.bat')
5982
+
5983
+
5984
+
5985
+ if wetdry == 'fixed':
5986
+ wtd = 0
5987
+ elif wetdry == 'evolutive':
5988
+ wtd= 1
5989
+
5990
+ if steady== 'precomputation':
5991
+ std= 1
5992
+ elif steady == 'no precomputation':
5993
+ std = 0
5994
+ elif steady == 'steady':
5995
+ std=2
5996
+
5997
+ find_full_path ='%~dp0'
5998
+ with open(batch_file,'w') as bat:
5999
+ bat.write(f'cd "{directory_of_executable}"\n')
6000
+ # bat.write(f'{executable_type} run_wolf1d dirin="%~dp0{simulation_name}" in="{simulation_name}" wetdry={wtd} steady={std}')
6001
+ if different_names:
6002
+ bat.write(f'{executable_type} run_wolf1d dirin="{find_full_path}{new_name}" in="{simulation_name}" wetdry={wtd} steady={std}')
6003
+ # if directory_simulation[0] =='.':
6004
+ # bat.write(f'{executable_type} run_wolf1d dirin="{find_full_path}{directory_simulation}" in="{simulation_name}" wetdry={wtd} steady={std}')
6005
+ # else:
6006
+ # bat.write(f'{executable_type} run_wolf1d dirin="{find_full_path}{directory_simulation}" in="{simulation_name}" wetdry={wtd} steady={std}')
6007
+
6008
+ else:
6009
+ bat.write(f'{executable_type} run_wolf1d dirin="{find_full_path}{simulation_name}" in="{simulation_name}" wetdry={wtd} steady={std}')
6010
+
6011
+ return batch_file
6012
+
6013
+ def match_ends_2vectors_outdated(self,
6014
+ zones1: Zones,
6015
+ zones2: Zones,
6016
+ id1:int = 0,
6017
+ id2:int = 0) -> Zones:
6018
+ """
6019
+ Aligns the vertices of 2 successive zone
6020
+ containing each 3 vectors (1 vector and its 2 parallels),
6021
+ so that, the end of each vector matches the begining of its corresponding in the other zone.
6022
+ - id1: zone id in zones1.myzones,
6023
+ - id2: zone id in zones2.myzones.
6024
+ """
6025
+ znes1 = zones1
6026
+ znes2 = zones2
6027
+ vector1_1 = znes1.myzones[id1].myvectors[0]
6028
+ vector1_2 = znes1.myzones[id1].myvectors[1]
6029
+ vector1_3 = znes1.myzones[id1].myvectors[2]
6030
+ vector2_1 = znes2.myzones[id2].myvectors[0]
6031
+ vector2_2 = znes2.myzones[id2].myvectors[1]
6032
+ vector2_3 = znes2.myzones[id2].myvectors[2]
6033
+ i = vector1_1.myvertices
6034
+ j = vector2_1.myvertices
6035
+
6036
+ distance1 = math.sqrt(((i[-1].x - j[0].x)**2) + ((i[-1].y - j[0].y)**2)) #last point - first point
6037
+ distance2 = math.sqrt(((i[-1].x - j[-1].x)**2) + ((i[-1].y - j[-1].y)**2)) #last point - last point
6038
+ distance1_r = math.sqrt(((i[0].x - j[0].x)**2) + ((i[0].y - j[0].y)**2)) # first point - first point
6039
+ distance2_r = math.sqrt(((i[0].x - j[-1].x)**2) + ((i[0].y - j[-1].y)**2)) #first point - last point
6040
+
6041
+ all = [distance1, distance2, distance1_r, distance2_r]
6042
+
6043
+ if min(all) == distance2:
6044
+ vector2_1.myvertices.reverse()
6045
+ vector2_2.myvertices.reverse()
6046
+ vector2_3.myvertices.reverse()
6047
+
6048
+ elif min(all) == distance1_r:
6049
+ vector1_1.myvertices.reverse()
6050
+ vector1_2.myvertices.reverse()
6051
+ vector1_3.myvertices.reverse()
6052
+
6053
+ elif min(all) ==distance2_r:
6054
+ vector1_1.myvertices.reverse()
6055
+ vector1_2.myvertices.reverse()
6056
+ vector1_3.myvertices.reverse()
6057
+ vector2_1.myvertices.reverse()
6058
+ vector2_2.myvertices.reverse()
6059
+ vector2_3.myvertices.reverse()
6060
+
6061
+ return znes1, znes2
6062
+
6063
+ def __save_as_1D_crossections(self,
6064
+ zones: Zones,
6065
+ format ='vecz',
6066
+ save_as: str='') -> crosssections:
6067
+ znes = zones
6068
+ path = save_as + 'profiles' + id +'.vecz'
6069
+ index = 1
6070
+ for vec in znes.myzones[0].myvectors:
6071
+ vec.myname = '%s'%(index)
6072
+ index+=1
6073
+
6074
+ znes.find_minmax()
6075
+ znes.saveas(path)
6076
+ cross= crosssections(mydata= path,format='vecz')
6077
+ cross.format = format
6078
+ if save_as:
6079
+ cross.saveas(save_as)
6080
+ return cross
6081
+
6082
+ def __update_qini_file_from_infiltration_dict(self,
6083
+ infiltrations: dict,
6084
+ directory:str):
6085
+ """Deprecating due to the last for loop (it's obviously a code duplication)."""
6086
+
6087
+ qini_file = self.find_file_from_extension(directory, '.qini')
6088
+ qini_dataframe = self.read_ini_file_as_dataframe(qini_file)
6089
+ number_of_cells = len(qini_dataframe['value'])
6090
+ qini = self.compute_qini_values_from_infiltrations_dict(infiltrations, number_of_cells)
6091
+ for file in os.listdir(directory):
6092
+ if file.endswith('.qini'):
6093
+ qini_file = os.path.join(directory, file)
6094
+ self.update_ini_file(qini_file, qini)
6095
+
6096
+ def _create_simulations_from_csv(self,
6097
+ csv_filename:str,
6098
+ folder_path:str,
6099
+ cross_sections: crosssections,
6100
+ parallels: Zones,
6101
+ banks: Zones,
6102
+ boundary_conditions: dict,
6103
+ roughness:Union[float,WolfArray],
6104
+ hydrographs: list[list[list]],
6105
+ exe_file:str,
6106
+ topography: WolfArray = None,
6107
+ initial_discharge:float = None,
6108
+ simulation_name:str = 'simul',
6109
+ discretisation:float = 10.,
6110
+ extrapolation_of_extremities:float = 100.,
6111
+ initial_depth:float = 1.,
6112
+ write_in: Literal['iterations', 'times'] = 'times',
6113
+ output_frequency:int = 900,
6114
+ polygon_number:int = 1,
6115
+ code_verbosity_profile: list = [1],
6116
+ simulation_time:int = None,
6117
+ roughness_option: Literal['under_profile', 'under_polygons']= 'under_profile',
6118
+ roughness_selection:Literal['min','mean','median','max'] = 'mean',
6119
+ file_type_initial_cond: Literal['.aini','.hini','.zini'] = '.aini',
6120
+ infiltration_profiles:list =['1'],
6121
+ wetdry:Literal['fixed', 'evolutive']='evolutive',
6122
+ steady:Literal['no precomputation', 'precomputation', 'steady'] = 'precomputation',
6123
+ executable_type: Literal['wolfcli', 'wolfclid'] = 'wolfcli',
6124
+ run_simulation: Literal['yes', 'no'] = 'no',
6125
+ writing_type_infiltration: Literal['continuous', 'stepwise'] = 'continuous',
6126
+ epsilon_infiltration:float = 0.01,
6127
+ force_steady = True) -> None:
6128
+ """Deprecated"""
6129
+
6130
+ infiltrations = self.read_csv_as_infiltrations(csv_filename)
6131
+ names = self.read_csv_as_dataframe(csv_filename)
6132
+
6133
+ # # df = self.read_csv_as_dataframe(csv_filename,column_names=['discharge'])
6134
+ # # discharge = df['discharge'][0]
6135
+ # hydrograph = Hydrograph({0:discharge})
6136
+ if force_steady:
6137
+ hydrographs = infiltrations[0]
6138
+ # hydrographs = {}
6139
+ # hydrographs =[[[0],[discharge]]]
6140
+ original_simulation =self.write_simulation_files_from_crosssections(folder_path,
6141
+ cross_sections,
6142
+ parallels,
6143
+ banks,
6144
+ boundary_conditions,
6145
+ roughness,
6146
+ hydrographs,
6147
+ exe_file,
6148
+ topography,
6149
+ initial_discharge,
6150
+ simulation_name,
6151
+ discretisation,
6152
+ extrapolation_of_extremities,
6153
+ initial_depth,
6154
+ write_in,
6155
+ output_frequency,
6156
+ polygon_number,
6157
+ code_verbosity_profile,
6158
+ simulation_time,
6159
+ roughness_option,
6160
+ roughness_selection,
6161
+ file_type_initial_cond,
6162
+ infiltration_profiles,
6163
+ wetdry,
6164
+ steady,
6165
+ executable_type,
6166
+ run_simulation ='no',
6167
+ writing_type_infiltration = writing_type_infiltration,
6168
+ epsilon_infiltration = epsilon_infiltration)
6169
+ procedures =[]
6170
+
6171
+ for new_infiltration in infiltrations:
6172
+ hydrographs = new_infiltration
6173
+ values = list(new_infiltration.values())
6174
+ extension =f'_Q{int(values[0])}' # FIXME
6175
+ new_sim = self.copy_simulation_files(original_simulation[0],original_simulation[0] + extension)
6176
+
6177
+ self.write_ini_files(original_simulation[3],
6178
+ new_sim,
6179
+ new_infiltration,
6180
+ original_simulation[4],
6181
+ file_choice=file_type_initial_cond)
6182
+
6183
+ self.write_infiltrations(infiltration_profiles,original_simulation[2],hydrographs,new_sim)
6184
+
6185
+ new_batchfile = self.write_batch_file(directory_of_executable = exe_file,
6186
+ directory_simulation = new_sim,
6187
+ simulation_name = simulation_name,
6188
+ wetdry = wetdry,
6189
+ steady = steady,
6190
+ executable_type = executable_type,
6191
+ different_names= True,
6192
+ new_name=simulation_name + extension)
6193
+ procedures.append(new_batchfile)
6194
+
6195
+
6196
+ # # procedure = multiprocessing.Process(target= run_batch_file_multiprocess, args = (new_batchfile))
6197
+ # # procedure.start()
6198
+ # procedures.append(new_batchfile)
6199
+ # if run_simulation == 'yes':
6200
+ # self.run_batch_file(new_batchfile)
6201
+ # # procedure.start()
6202
+
6203
+ if run_simulation == 'yes':
6204
+ runs=[self.run_batch_file(i) for i in tqdm(procedures,'Running simulations', colour= Colors.TQDM.value)]
6205
+ # # pool = multiprocessing.Pool(processes=len(procedures))
5238
6206
  # runs=[multiprocessing.Process(run_batch_file_multiprocess, args=i) for i in tqdm(procedures)]
5239
6207
 
5240
- def create_simulations_from_csv(self,
5241
- csv_filename:str,
5242
- folder_path:str,
5243
- cross_sections: crosssections,
5244
- parallels: Zones,
5245
- banks: Zones,
5246
- boundary_conditions: dict,
5247
- roughness:Union[float,WolfArray],
5248
- exe_file:str,
5249
- hydrographs: list[list[list]] = None,
5250
- topography: WolfArray = None,
5251
- initial_discharge:float = None,
5252
- simulation_name:str = 'simul',
5253
- discretisation:float = 10.,
5254
- extrapolation_of_extremities:float = 100.,
5255
- initial_depth:float = 1.,
5256
- write_in: Literal['iterations', 'times'] = 'times',
5257
- output_frequency:int = 900,
5258
- polygon_number:int = 1,
5259
- code_verbosity_profile: list = [1],
5260
- simulation_time:int = None,
5261
- roughness_option: Literal['under_profile', 'under_polygons']= 'under_profile',
5262
- roughness_selection:Literal['min','mean','median','max'] = 'mean',
5263
- file_type_initial_cond: Literal['.aini','.hini','.zini'] = '.aini',
5264
- infiltration_profiles:list =['1'],
5265
- wetdry:Literal['fixed', 'evolutive']='evolutive',
5266
- steady:Literal['no precomputation', 'precomputation', 'steady'] = 'precomputation',
5267
- executable_type: Literal['wolfcli', 'wolfclid'] = 'wolfcli',
5268
- run_simulation: Literal['yes', 'no'] = 'no',
5269
- writing_type_infiltration: Literal['continuous', 'stepwise'] = 'continuous',
5270
- epsilon_infiltration:float = 0.01,
5271
- force_steady = True) -> None:
6208
+ def _start_from_steady_state(self,
6209
+ profile:str,
6210
+ infiltrations:dict,
6211
+ list_of_sorted_cross: list,
6212
+ directory,
6213
+ plot = True,
6214
+ ):
6215
+ qini = infiltrations[profile]
6216
+ if isinstance(qini, Hydrograph):
6217
+ qini = qini.values[0]
6218
+ elif isinstance(qini, (list,tuple)):
6219
+ # FIXME to be verified
6220
+ qini = qini[0]
6221
+ elif qini == float:
6222
+ qini = qini
6223
+ else:
6224
+ raise ValueError('The initial discharge is not well defined.')
6225
+
6226
+ hydrograph = Hydrograph({0:qini})
6227
+
6228
+ new_infiltrations = {profile:hydrograph}
6229
+ self.write_infiltrations_from_dict(list_of_sorted_cross, new_infiltrations, directory)
6230
+ self.write_parameters(directory, write_type= 2, max_time=0)
6231
+ bat_file = self.find_file_from_extension(directory, '.bat')
6232
+ self._run_bat_files(bat_file, initial_condition= True)
6233
+ # self.run_bat_files(bat_file)
6234
+ self.update_initial_conditions_from_results(directory, time_step=1, plot=plot)
6235
+
6236
+ def __write_simulation_files_from_crosssections(self,
6237
+ folder_path:str,
6238
+ cross_sections: crosssections,
6239
+ parallels: Zones,
6240
+ banks: Zones,
6241
+ boundary_conditions: dict,
6242
+ roughness:Union[float,WolfArray],
6243
+ hydrographs: list[list[list]],
6244
+ exe_file:str,
6245
+ topography: WolfArray = None,
6246
+ initial_discharge:float = None,
6247
+ simulation_name:str = 'simul',
6248
+ discretisation:float = 10.,
6249
+ extrapolation_of_extremities:float = 100.,
6250
+ initial_depth:float = 1.,
6251
+ write_in: Literal['iterations', 'times'] = 'times',
6252
+ output_frequency:int = 900,
6253
+ polygon_number:int = 1,
6254
+ code_verbosity_profile: list = [1],
6255
+ simulation_time:int = None,
6256
+ roughness_option: Literal['under_profile', 'under_polygons']= 'under_profile',
6257
+ roughness_selection:Literal['min','mean','median','max'] = 'mean',
6258
+ file_type_initial_cond: Literal['.aini','.hini','.zini'] = '.aini',
6259
+ infiltration_profiles:list =['1'],
6260
+ wetdry:Literal['fixed', 'evolutive']='evolutive',
6261
+ steady:Literal['no precomputation', 'precomputation', 'steady'] = 'precomputation',
6262
+ executable_type: Literal['wolfcli', 'wolfclid'] = 'wolfcli',
6263
+ run_simulation: Literal['yes', 'no'] = 'no',
6264
+ writing_type_infiltration: Literal['continuous', 'stepwise'] = 'continuous',
6265
+ epsilon_infiltration:float = 0.01,
6266
+ new_directory = '',
6267
+ force_steady = True
6268
+ ) -> tuple:
6269
+ """
6270
+ Write the simulation files (the 1D model) from the cross sections and the other parameters with one line of code.
6271
+
6272
+ :param folder_path: Folder path
6273
+ :type folder_path: str
6274
+ :param cross_sections: Cross sections
6275
+ :type cross_sections: crosssections
6276
+ :param parallels: Parallels
6277
+ :type parallels: Zones
6278
+ :param banks: Banks
6279
+ :type banks: Zones
6280
+ :param boundary_conditions: Boundary conditions
6281
+ :type boundary_conditions: dict
6282
+ :param roughness: Roughness
6283
+ :type roughness: Union[float,WolfArray]
6284
+ :param hydrographs: Hydrographs
6285
+ :type hydrographs: list[list[list]]
6286
+ :param exe_file: Exe file
6287
+ :type exe_file: str
6288
+ :param topography: Topography, defaults to None
6289
+ :type topography: WolfArray, optional
6290
+ :param initial_discharge: Initial discharge, defaults to None
6291
+ :type initial_discharge: float, optional
6292
+ :param simulation_name: Simulation name, defaults to 'simul'
6293
+ :type simulation_name: str, optional
6294
+ :param discretisation: Discretisation, defaults to 10.
6295
+ :type discretisation: float, optional
6296
+ :param extrapolation_of_extremities: Extrapolation of extremities, defaults to 100.
6297
+ :type extrapolation_of_extremities: float, optional
6298
+ :param initial_depth: Initial depth, defaults to 1.
6299
+ :type initial_depth: float, optional
6300
+ :param write_in: Write in, defaults to 'times'
6301
+ :type write_in: Literal['iterations', 'times'], optional
6302
+ :param output_frequency: Output frequency, defaults to 900
6303
+ :type output_frequency: int, optional
6304
+ :param polygon_number: Polygon number, defaults to 1
6305
+ :type polygon_number: int, optional
6306
+ :param code_verbosity_profile: Code verbosity profile, defaults to [1]
6307
+ :type code_verbosity_profile: list, optional
6308
+ :param simulation_time: Simulation time, defaults to None
6309
+ :type simulation_time: int, optional
6310
+ :param roughness_option: Roughness option, defaults to 'under_profile'
6311
+ :type roughness_option: Literal['under_profile', 'under_polygons'], optional
6312
+ :param roughness_selection: Roughness selection, defaults to 'mean'
6313
+ :type roughness_selection: Literal['min','mean','median','max'], optional
6314
+ :param file_type_initial_cond: File type initial cond, defaults to '.aini'
6315
+ :type file_type_initial_cond: Literal['.aini','.hini','.zini'], optional
6316
+ :param infiltration_profiles: Infiltration profiles, defaults to ['1']
6317
+ :type infiltration_profiles: list, optional
6318
+ :param wetdry: Wetdry, defaults to 'evolutive'
6319
+ :type wetdry: Literal['fixed', 'evolutive'], optional
6320
+ :param steady: Steady, defaults to 'precomputation'
6321
+ :type steady: Literal['no precomputation', 'precomputation', 'steady'], optional
6322
+ :param executable_type: Executable type, defaults to 'wolfcli'
6323
+ :type executable_type: Literal['wolfcli', 'wolfclid'], optional
6324
+ :param run_simulation: Run simulation, defaults to 'no'
6325
+ :type run_simulation: Literal['yes', 'no'], optional
6326
+ :param writing_type_infiltration: Writing type infiltration, defaults to 'continuous'
6327
+ :type writing_type_infiltration: Literal['continuous', 'stepwise'], optional
6328
+ :param epsilon_infiltration: Epsilon infiltration, defaults to 0.01
6329
+ :type epsilon_infiltration: float, optional
6330
+ :param new_directory: New directory, defaults to ''
6331
+ :type new_directory: str, optional
6332
+ :param force_steady: Force steady, defaults to True
6333
+ :type force_steady: bool, optional
6334
+ :return: tuple
6335
+ :rtype: tuple
6336
+ """
6337
+
6338
+
6339
+ # Simulation directory (contains all simulation files)
6340
+ if new_directory != '':
6341
+ directory = new_directory
6342
+ else:
6343
+ directory = self.create_simulation_directory(folder_path,simulation_name)
6344
+
6345
+ # The extremities of each profile is extrapolated to avoid negative water depths in case of high discharges
6346
+ extrapolated_cross_sections = self.extrapolate_extremities(cross_sections,
6347
+ extrapolation_of_extremities,
6348
+ directory)
6349
+ # The crossection are sorted based on the river bed (middle parallel)
6350
+ sorted_crosssections = self.sort_crossections_list([extrapolated_cross_sections], banks)
6351
+ #
6352
+ if simulation_time:
6353
+ time = simulation_time
6354
+ else:
6355
+ # FIXME Multiple hydrographs
6356
+ if isinstance(hydrographs,dict):
6357
+ times = []
6358
+ for hydro, prof in hydrographs.items():
6359
+ if isinstance(prof, Hydrograph):
6360
+ times.append(max(prof.index))
6361
+ elif isinstance(prof, list):
6362
+ latest_time = prof[0][-1]
6363
+ times.append(latest_time)
6364
+
6365
+ time = max(times)
6366
+
6367
+ else:
6368
+ if isinstance(hydrographs[0], Hydrograph):
6369
+ time = max(hydrographs[0].index)
6370
+ elif isinstance(hydrographs[0], list):
6371
+ time = hydrographs[0][0][-1]
6372
+
6373
+ if initial_discharge:
6374
+ q_ini = initial_discharge
6375
+ else:
6376
+ if isinstance(hydrographs,dict):
6377
+ discharges = []
6378
+ for hydro, prof in hydrographs.items():
6379
+ if isinstance(prof, Hydrograph):
6380
+ first_discharge = prof.values[0]
6381
+ discharges.append(first_discharge)
6382
+ elif isinstance(prof, list):
6383
+ first_discharge = prof[0][0]
6384
+ discharges.append(first_discharge)
6385
+ q_ini = min(discharges)
6386
+ else:
6387
+ if isinstance(hydrographs[0], Hydrograph):
6388
+ q_ini = hydrographs[0].values[0]
6389
+ elif isinstance(hydrographs[0], list):
6390
+ q_ini = hydrographs[0][1][0]
6391
+
6392
+ # Bank file
6393
+ if banks.is2D:
6394
+ bank_file = self.get_values_from_array(banks, topography, directory + '\\banks.vecz')
6395
+ else:
6396
+ bank_file = banks
6397
+ bank_file.saveas(directory + '\\banks.vecz')
6398
+
6399
+ # Polygons
6400
+ # FIXME is it necessery to have parallels (next round), is it only for cell size or a better used could be found?
6401
+ if isinstance(parallels, Zones):
6402
+ polygons = self.create_polygons(parallels, discretisation, polygon_number, directory)
6403
+
6404
+ # Write the tabulated relations of each cross section (profile)
6405
+ write_relations = self.write_relations_profiles(sorted_crosssections, directory)
6406
+
6407
+ # Write roughnesses
6408
+ if isinstance(roughness, float) or isinstance(roughness, int):
6409
+ roughnesses = self.roughness_from_value(sorted_crosssections, value = roughness)
6410
+
6411
+ elif isinstance(roughness, WolfArray):
6412
+ if roughness_option == 'under_profile':
6413
+ roughnesses = self.roughness_from_profiles(roughness, sorted_crosssections, mode=roughness_selection)
6414
+
6415
+ elif roughness_option == 'under_polygons':
6416
+ # FIXME to be verified issue with the extent of profiles
6417
+ # (the number of new polygons is different from the number of digitized profiles)
6418
+ # May be a test should be implemented on each profiles (probable solution).
6419
+ roughnesses = self.roughness_from_polygons(roughness, polygons, mode=roughness_selection)
6420
+
6421
+ roughness_file = self.write_roughnesses(roughnesses, directory)
6422
+
6423
+ # Compute & write the intial conditions
6424
+ ic_relations, nb_updates = self.ic_relations_hspw(sorted_crosssections, initial_depth)
6425
+
6426
+ # The simulation is initialized based on the wetted area (more practical)
6427
+ ini_files = self.write_ini_files(ic_relations,
6428
+ directory,
6429
+ q = q_ini,
6430
+ nb_updates= nb_updates,
6431
+ file_choice=[file_type_initial_cond])
6432
+
6433
+ # Write the boundary conditions
6434
+ cl_file = self.write_cl_file(sorted_crosssections, boundary_conditions, directory)
6435
+
6436
+ # Write .top file (topography)
6437
+ top_file = self.write_top_file(sorted_crosssections,directory)
6438
+
6439
+ # Write the .vec file (discretization file of the simulation topography - lowest riverbed point)
6440
+ vec_file1 = self.write_vector_files(sorted_crosssections, save_as=directory,which_type='vecz')
6441
+
6442
+ # Sets the predefined river banks and bed on each cross sections
6443
+ set_bank = self.set_banksbed_vectors(bank_file)
6444
+
6445
+ # Write the .vec file (discretization file of the simulation topography - midriver)
6446
+ vec_file2 = self.write_vector_files(sorted_crosssections, save_as=directory, which_type='vec')
6447
+
6448
+ # Write the infiltrations
6449
+ if isinstance(hydrographs, dict):
6450
+ infil = self.write_infiltrations_from_dict(sorted_crosssections,
6451
+ hydrographs,
6452
+ directory,
6453
+ writing_type_infiltration,
6454
+ epsilon_infiltration)
6455
+ elif isinstance(hydrographs,list):
6456
+ infil = self.write_infiltrations(infiltration_profiles,
6457
+ sorted_crosssections,
6458
+ hydrographs,
6459
+ directory,
6460
+ writing_type_infiltration,
6461
+ epsilon_infiltration)
6462
+
6463
+ # write the simulation parameters
6464
+ if write_in == 'times':
6465
+ write_type = 2
6466
+ elif write_in =='iterations':
6467
+ write_type = 1
6468
+ if self.wx_exists == None:
6469
+ param = self.write_parameters(directory, write_freq= output_frequency, write_type= write_type, max_time=time)
6470
+ # # if steady == 'precomputation' or steady == 'steady':
6471
+ # # self.correct_parameters(directory, from_steady=True)
6472
+ # # else:
6473
+ # # self.correct_parameters(directory)
6474
+ # self.correct_parameters(directory) FIXME for other cases when the GUI is not used
6475
+
6476
+ genfile = self.write_generic_file(directory)
6477
+ batch_file = self.write_batch_file(directory_of_executable = exe_file,
6478
+ directory_simulation = directory,
6479
+ simulation_name = simulation_name,
6480
+ wetdry = wetdry,
6481
+ steady = steady,
6482
+ executable_type = executable_type)
6483
+ if run_simulation == 'yes':
6484
+ # logging.warn(f'{simulation_name} running... \nThe process may take time.')
6485
+ self.run_bat_files(batch_file)
6486
+ # logging.warn(f'{simulation_name} completed.')
6487
+ return (directory,simulation_name, sorted_crosssections, ic_relations, nb_updates)
6488
+
6489
+ def ____write_simulation_files_from_crosssections(self,
6490
+ folder_path:str,
6491
+ cross_sections: crosssections,
6492
+ parallels: Zones,
6493
+ banks: Zones,
6494
+ boundary_conditions: dict,
6495
+ roughness:Union[float,WolfArray],
6496
+ hydrographs: list[list[list]],
6497
+ exe_file:str,
6498
+ topography: WolfArray = None,
6499
+ initial_discharge:float = None,
6500
+ simulation_name:str = 'simul',
6501
+ discretisation:float = 10.,
6502
+ extrapolation_of_extremities:float = 100.,
6503
+ initial_depth:float = 1.,
6504
+ write_in: Literal['iterations', 'times'] = 'times',
6505
+ output_frequency:int = 900,
6506
+ polygon_number:int = 1,
6507
+ code_verbosity_profile: list = [1],
6508
+ simulation_time:int = None,
6509
+ roughness_option: Literal['under_profile', 'under_polygons']= 'under_profile',
6510
+ roughness_selection:Literal['min','mean','median','max'] = 'mean',
6511
+ file_type_initial_cond: Literal['.aini','.hini','.zini'] = '.aini',
6512
+ infiltration_profiles:list =['1'],
6513
+ wetdry:Literal['fixed', 'evolutive']='evolutive',
6514
+ steady:Literal['no precomputation', 'precomputation', 'steady'] = 'precomputation',
6515
+ executable_type: Literal['wolfcli', 'wolfclid'] = 'wolfcli',
6516
+ run_simulation: Literal['yes', 'no'] = 'no',
6517
+ writing_type_infiltration: Literal['continuous', 'stepwise'] = 'continuous',
6518
+ epsilon_infiltration:float = 0.01,
6519
+ new_directory = '',
6520
+ steady_state_profile = '',
6521
+ force_steady = True,
6522
+ start_from_steady_state = False,
6523
+ plot_steady_state = True
6524
+ ) -> tuple:
5272
6525
  """
5273
- Create simulations from a csv file.
6526
+ Write the simulation files (the 1D model) from the cross sections and the other parameters with one line of code.
5274
6527
 
5275
- :param csv_filename: Csv filename
5276
- :type csv_filename: str
5277
6528
  :param folder_path: Folder path
5278
6529
  :type folder_path: str
5279
6530
  :param cross_sections: Cross sections
@@ -5286,10 +6537,10 @@ class Creator_1D:
5286
6537
  :type boundary_conditions: dict
5287
6538
  :param roughness: Roughness
5288
6539
  :type roughness: Union[float,WolfArray]
6540
+ :param hydrographs: Hydrographs
6541
+ :type hydrographs: list[list[list]]
5289
6542
  :param exe_file: Exe file
5290
6543
  :type exe_file: str
5291
- :param hydrographs: Hydrographs, defaults to None
5292
- :type hydrographs: list[list[list]], optional
5293
6544
  :param topography: Topography, defaults to None
5294
6545
  :type topography: WolfArray, optional
5295
6546
  :param initial_discharge: Initial discharge, defaults to None
@@ -5332,260 +6583,181 @@ class Creator_1D:
5332
6583
  :type writing_type_infiltration: Literal['continuous', 'stepwise'], optional
5333
6584
  :param epsilon_infiltration: Epsilon infiltration, defaults to 0.01
5334
6585
  :type epsilon_infiltration: float, optional
6586
+ :param new_directory: New directory, defaults to ''
6587
+ :type new_directory: str, optional
5335
6588
  :param force_steady: Force steady, defaults to True
5336
6589
  :type force_steady: bool, optional
5337
- """
5338
- df = self.read_csv_as_dataframe(csv_filename,column_names=['discharge'])
5339
- discharge = df['discharge'][0]
5340
- # hydrograph = Hydrograph({0:discharge})
5341
- if force_steady:
5342
- # hydrographs = {}
5343
- hydrographs =[[[0],[discharge]]]
5344
- original_simulation =self.write_simulation_files_from_crosssections(folder_path,
5345
- cross_sections,
5346
- parallels,
5347
- banks,
5348
- boundary_conditions,
5349
- roughness,
5350
- hydrographs,
5351
- exe_file,
5352
- topography,
5353
- discharge,
5354
- simulation_name,
5355
- discretisation,
5356
- extrapolation_of_extremities,
5357
- initial_depth,
5358
- write_in,
5359
- output_frequency,
5360
- polygon_number,
5361
- code_verbosity_profile,
5362
- simulation_time,
5363
- roughness_option,
5364
- roughness_selection,
5365
- file_type_initial_cond,
5366
- infiltration_profiles,
5367
- wetdry,
5368
- steady,
5369
- executable_type,
5370
- run_simulation ='no',
5371
- writing_type_infiltration = writing_type_infiltration,
5372
- epsilon_infiltration = epsilon_infiltration)
5373
- procedures =[]
5374
-
5375
- new_sims =[]
5376
- for new_discharge in list(df['discharge']):
5377
- hydrographs =[[[0],[new_discharge]]]
5378
-
5379
- extension =f'_Q{int(new_discharge)}'
5380
- new_sim_name = original_simulation[0] + extension
5381
- new_sim = self.copy_simulation_files(original_simulation[0],new_sim_name, ignore_filetype='*.bat')
5382
- new_sims.append(new_sim)
5383
-
5384
-
5385
- self.write_ini_files(original_simulation[3],
5386
- new_sim,
5387
- new_discharge,
5388
- original_simulation[4],
5389
- file_choice=file_type_initial_cond)
5390
-
5391
- self.write_infiltrations(infiltration_profiles,original_simulation[2],hydrographs,new_sim)
5392
-
5393
- new_batchfile = self.write_batch_file(directory_of_executable = exe_file,
5394
- directory_simulation = new_sim,
5395
- simulation_name = simulation_name,
5396
- wetdry = wetdry,
5397
- steady = steady,
5398
- executable_type = executable_type,
5399
- different_names= True,
5400
- new_name=simulation_name + extension)
5401
- procedures.append(new_batchfile)
5402
-
5403
- # # procedure = multiprocessing.Process(target= run_batch_file_multiprocess, args = (new_batchfile))
5404
- # # procedure.start()
5405
- # procedures.append(new_batchfile)
5406
- # if run_simulation == 'yes':
5407
- # self.run_batch_file(new_batchfile)
5408
- # # procedure.start()
5409
-
5410
- batch_file_group = self.write_batch_simulations(directory_of_executable = exe_file,
5411
- simulations_path=new_sims,
5412
- wetdry = wetdry,
5413
- steady = steady,
5414
- executable_type = executable_type)
5415
-
5416
- if run_simulation == 'yes':
5417
- # runs=[self.run_batch_file(i) for i in tqdm(procedures,'Running simulations', colour= Colors.TQDM.value)]
5418
- # run = self.run_batch_file(batch_file_group)
5419
- run = self.run_bat_files(batch_file_group)
5420
- # # pool = multiprocessing.Pool(processes=len(procedures))
5421
- # runs=[multiprocessing.Process(run_batch_file_multiprocess, args=i) for i in tqdm(procedures)]
5422
-
5423
- def copy_simulation_files(self,
5424
- simulation:str,
5425
- save_as:str,
5426
- ignore_filetype: Literal['.*qini','*.aini', '*.zini','*.hini','*.cl', '*.rough', 'infil*','*.inf'
5427
- '*.infil','*.param','*.top','*.ptv','*.gtv','.log', '.count'
5428
- '*.HEAD','*.RA', '*.RB','*.RQ','*.RS','*.vecz','*.vec','*.txt', '*.bat'] = '') -> str:
5429
- """
5430
- Copy simulation files.
5431
-
5432
- :param simulation: Simulation
5433
- :type simulation: str
5434
- :param save_as: Save as
5435
- :type save_as: str
5436
- :param ignore_filetype: Ignore filetype, defaults to ''
5437
- :type ignore_filetype: str, optional
5438
- :return: Copied directory
5439
- :rtype: str
5440
- """
5441
- if ignore_filetype != '':
5442
- try:
5443
- copied_directory = shutil.copytree(src = simulation, dst= save_as)
5444
- except FileExistsError:
5445
- # os.rmdir(save_as)
5446
- # copied_directory = shutil.copytree(src = simulation, dst= save_as)
5447
- raise Exception(f"The file named ({save_as}) exists already.\n Rename the new file or delete the existing file.")
6590
+ :return: tuple
6591
+ :rtype: tuple
6592
+ """
6593
+
6594
+
6595
+ # Simulation directory (contains all simulation files)
6596
+ if new_directory != '':
6597
+ directory = new_directory
5448
6598
  else:
5449
- try:
5450
- copied_directory = shutil.copytree(src = simulation, dst= save_as, ignore=shutil.ignore_patterns(ignore_filetype))
5451
- except FileExistsError:
5452
- # os.rmdir(save_as)
5453
- # copied_directory = shutil.copytree(src = simulation, dst= save_as, ignore=shutil.ignore_patterns(ignore_filetype))
5454
- raise Exception(f"The file named ({save_as}) exists already.\n Rename the new file or delete the existing file.")
5455
- return copied_directory
6599
+ directory = self.create_simulation_directory(folder_path,simulation_name)
5456
6600
 
5457
- def copy_multiple_simulations_from_csv(self, csv_filename:str, simulation:str) -> None:
5458
- """
5459
- Copy multiple simulations from a csv file.
6601
+ # The extremities of each profile is extrapolated to avoid negative water depths in case of high discharges
6602
+ extrapolated_cross_sections = self.extrapolate_extremities(cross_sections,
6603
+ extrapolation_of_extremities,
6604
+ directory)
6605
+ # The crossection are sorted based on the river bed (middle parallel)
6606
+ sorted_crosssections = self.sort_crossections_list([extrapolated_cross_sections], banks)
6607
+ #
6608
+ if simulation_time:
6609
+ time = simulation_time
6610
+ else:
6611
+ # FIXME Multiple hydrographs
6612
+ if isinstance(hydrographs,dict):
6613
+ times = []
6614
+ for hydro, prof in hydrographs.items():
6615
+ if isinstance(prof, Hydrograph):
6616
+ times.append(max(prof.index))
6617
+ elif isinstance(prof, list):
6618
+ latest_time = prof[0][-1]
6619
+ times.append(latest_time)
5460
6620
 
5461
- :param csv_filename: Csv filename
5462
- :type csv_filename: str
5463
- :param simulation: Simulation
5464
- :type simulation: str
5465
- """
6621
+ time = max(times)
5466
6622
 
5467
- df = self.read_csv_as_dataframe(csv_filename,column_names=['discharge'])
5468
- lgth = df.shape[0]
5469
- for discharge in tqdm(df['discharge']):
5470
- self.copy_simulation_files(simulation, simulation + f'_Q{discharge}')
6623
+ else:
6624
+ if isinstance(hydrographs[0], Hydrograph):
6625
+ time = max(hydrographs[0].index)
6626
+ elif isinstance(hydrographs[0], list):
6627
+ time = hydrographs[0][0][-1]
5471
6628
 
5472
- # --- Outdated methods ---
5473
- #_________________________
6629
+ if initial_discharge:
6630
+ q_ini = initial_discharge
6631
+ else:
6632
+ if isinstance(hydrographs,dict):
6633
+ discharges = []
6634
+ for hydro, prof in hydrographs.items():
6635
+ if isinstance(prof, Hydrograph):
6636
+ first_discharge = prof.values[0]
6637
+ discharges.append(first_discharge)
6638
+ elif isinstance(prof, list):
6639
+ first_discharge = prof[0][0]
6640
+ discharges.append(first_discharge)
6641
+ q_ini = min(discharges)
6642
+ else:
6643
+ if isinstance(hydrographs[0], Hydrograph):
6644
+ q_ini = hydrographs[0].values[0]
6645
+ elif isinstance(hydrographs[0], list):
6646
+ q_ini = hydrographs[0][1][0]
5474
6647
 
5475
- def __write_batch_file(self,
5476
- directory_of_executable:str,
5477
- directory_simulation:str,
5478
- simulation_name:str,
5479
- wetdry:Literal['fixed', 'evolutive']='evolutive',
5480
- steady:Literal['no precomputation', 'precomputation', 'steady'] = 'precomputation',
5481
- executable_type: Literal['wolfcli', 'wolfclid'] = 'wolfcli',
5482
- different_names=False,
5483
- new_name:str =''
5484
- ) -> str:
5485
- if different_names:
5486
- batch_file =self.initialize_file(directory_of_executable, f'{new_name}.bat','') # To avoid simul name FIXME make it clean
6648
+ # Bank file
6649
+ if banks.is2D:
6650
+ bank_file = self.get_values_from_array(banks, topography, directory + '\\banks.vecz')
5487
6651
  else:
5488
- batch_file =self.initialize_file(directory_of_executable, '.bat')
6652
+ bank_file = banks
6653
+ bank_file.saveas(directory + '\\banks.vecz')
5489
6654
 
6655
+ # Polygons
6656
+ # FIXME is it necessery to have parallels (next round), is it only for cell size or a better used could be found?
6657
+ if isinstance(parallels, Zones):
6658
+ polygons = self.create_polygons(parallels, discretisation, polygon_number, directory)
5490
6659
 
6660
+ # Write the tabulated relations of each cross section (profile)
6661
+ write_relations = self.write_relations_profiles(sorted_crosssections, directory)
5491
6662
 
5492
- if wetdry == 'fixed':
5493
- wtd = 0
5494
- elif wetdry == 'evolutive':
5495
- wtd= 1
6663
+ # Write roughnesses
6664
+ if isinstance(roughness, float) or isinstance(roughness, int):
6665
+ roughnesses = self.roughness_from_value(sorted_crosssections, value = roughness)
5496
6666
 
5497
- if steady== 'precomputation':
5498
- std= 1
5499
- elif steady == 'no precomputation':
5500
- std = 0
5501
- elif steady == 'steady':
5502
- std=2
6667
+ elif isinstance(roughness, WolfArray):
6668
+ if roughness_option == 'under_profile':
6669
+ roughnesses = self.roughness_from_profiles(roughness, sorted_crosssections, mode=roughness_selection)
5503
6670
 
5504
- find_full_path ='%~dp0'
5505
- with open(batch_file,'w') as bat:
5506
- bat.write(f'cd "{directory_of_executable}"\n')
5507
- # bat.write(f'{executable_type} run_wolf1d dirin="%~dp0{simulation_name}" in="{simulation_name}" wetdry={wtd} steady={std}')
5508
- if different_names:
5509
- bat.write(f'{executable_type} run_wolf1d dirin="{find_full_path}{new_name}" in="{simulation_name}" wetdry={wtd} steady={std}')
5510
- # if directory_simulation[0] =='.':
5511
- # bat.write(f'{executable_type} run_wolf1d dirin="{find_full_path}{directory_simulation}" in="{simulation_name}" wetdry={wtd} steady={std}')
5512
- # else:
5513
- # bat.write(f'{executable_type} run_wolf1d dirin="{find_full_path}{directory_simulation}" in="{simulation_name}" wetdry={wtd} steady={std}')
6671
+ elif roughness_option == 'under_polygons':
6672
+ # FIXME to be verified issue with the extent of profiles
6673
+ # (the number of new polygons is different from the number of digitized profiles)
6674
+ # May be a test should be implemented on each profiles (probable solution).
6675
+ roughnesses = self.roughness_from_polygons(roughness, polygons, mode=roughness_selection)
5514
6676
 
5515
- else:
5516
- bat.write(f'{executable_type} run_wolf1d dirin="{find_full_path}{simulation_name}" in="{simulation_name}" wetdry={wtd} steady={std}')
6677
+ roughness_file = self.write_roughnesses(roughnesses, directory)
5517
6678
 
5518
- return batch_file
6679
+ # Compute & write the intial conditions
6680
+ ic_relations, nb_updates = self.ic_relations_hspw(sorted_crosssections, initial_depth)
5519
6681
 
5520
- def match_ends_2vectors_outdated(self,
5521
- zones1: Zones,
5522
- zones2: Zones,
5523
- id1:int = 0,
5524
- id2:int = 0) -> Zones:
5525
- """
5526
- Aligns the vertices of 2 successive zone
5527
- containing each 3 vectors (1 vector and its 2 parallels),
5528
- so that, the end of each vector matches the begining of its corresponding in the other zone.
5529
- - id1: zone id in zones1.myzones,
5530
- - id2: zone id in zones2.myzones.
5531
- """
5532
- znes1 = zones1
5533
- znes2 = zones2
5534
- vector1_1 = znes1.myzones[id1].myvectors[0]
5535
- vector1_2 = znes1.myzones[id1].myvectors[1]
5536
- vector1_3 = znes1.myzones[id1].myvectors[2]
5537
- vector2_1 = znes2.myzones[id2].myvectors[0]
5538
- vector2_2 = znes2.myzones[id2].myvectors[1]
5539
- vector2_3 = znes2.myzones[id2].myvectors[2]
5540
- i = vector1_1.myvertices
5541
- j = vector2_1.myvertices
6682
+ # The simulation is initialized based on the wetted area (more practical)
6683
+ ini_files = self.write_ini_files(ic_relations,
6684
+ directory,
6685
+ q = q_ini,
6686
+ nb_updates= nb_updates,
6687
+ file_choice=[file_type_initial_cond])
5542
6688
 
5543
- distance1 = math.sqrt(((i[-1].x - j[0].x)**2) + ((i[-1].y - j[0].y)**2)) #last point - first point
5544
- distance2 = math.sqrt(((i[-1].x - j[-1].x)**2) + ((i[-1].y - j[-1].y)**2)) #last point - last point
5545
- distance1_r = math.sqrt(((i[0].x - j[0].x)**2) + ((i[0].y - j[0].y)**2)) # first point - first point
5546
- distance2_r = math.sqrt(((i[0].x - j[-1].x)**2) + ((i[0].y - j[-1].y)**2)) #first point - last point
6689
+ # Write the boundary conditions
6690
+ cl_file = self.write_cl_file(sorted_crosssections, boundary_conditions, directory)
5547
6691
 
5548
- all = [distance1, distance2, distance1_r, distance2_r]
6692
+ # Write .top file (topography)
6693
+ top_file = self.write_top_file(sorted_crosssections,directory)
5549
6694
 
5550
- if min(all) == distance2:
5551
- vector2_1.myvertices.reverse()
5552
- vector2_2.myvertices.reverse()
5553
- vector2_3.myvertices.reverse()
6695
+ # Write the .vec file (discretization file of the simulation topography - lowest riverbed point)
6696
+ vec_file1 = self.write_vector_files(sorted_crosssections, save_as=directory,which_type='vecz')
5554
6697
 
5555
- elif min(all) == distance1_r:
5556
- vector1_1.myvertices.reverse()
5557
- vector1_2.myvertices.reverse()
5558
- vector1_3.myvertices.reverse()
6698
+ # Sets the predefined river banks and bed on each cross sections
6699
+ set_bank = self.set_banksbed_vectors(bank_file)
5559
6700
 
5560
- elif min(all) ==distance2_r:
5561
- vector1_1.myvertices.reverse()
5562
- vector1_2.myvertices.reverse()
5563
- vector1_3.myvertices.reverse()
5564
- vector2_1.myvertices.reverse()
5565
- vector2_2.myvertices.reverse()
5566
- vector2_3.myvertices.reverse()
6701
+ # Write the .vec file (discretization file of the simulation topography - midriver)
6702
+ vec_file2 = self.write_vector_files(sorted_crosssections, save_as=directory, which_type='vec')
6703
+ # write the generic file
6704
+ genfile = self.write_generic_file(directory)
6705
+ # Write the batch file
6706
+ batch_file = self.write_batch_file(directory_of_executable = exe_file,
6707
+ directory_simulation = directory,
6708
+ simulation_name = simulation_name,
6709
+ wetdry = wetdry,
6710
+ steady = steady,
6711
+ executable_type = executable_type)
5567
6712
 
5568
- return znes1, znes2
6713
+ # Update initial conditions
6714
+ if start_from_steady_state:
6715
+ self.start_from_steady_state(directory, plot = plot_steady_state)
6716
+
6717
+ # # if profile == '':
6718
+ # # profile = '1'
6719
+ # # self.start_from_steady_state(profile=steady_state_profile,
6720
+ # # infiltrations= hydrographs,
6721
+ # # list_of_sorted_cross= sorted_crosssections,
6722
+ # # directory= directory,
6723
+ # # plot= plot_steady_state)
6724
+
6725
+ # Write the batch file
6726
+ batch_file = self.write_batch_file(directory_of_executable = exe_file,
6727
+ directory_simulation = directory,
6728
+ simulation_name = simulation_name,
6729
+ wetdry = wetdry,
6730
+ steady = 'no precomputation',
6731
+ executable_type = executable_type)
5569
6732
 
5570
- def __save_as_1D_crossections(self,
5571
- zones: Zones,
5572
- format ='vecz',
5573
- save_as: str='') -> crosssections:
5574
- znes = zones
5575
- path = save_as + 'profiles' + id +'.vecz'
5576
- index = 1
5577
- for vec in znes.myzones[0].myvectors:
5578
- vec.myname = '%s'%(index)
5579
- index+=1
6733
+ # Write the infiltrations
6734
+ if isinstance(hydrographs, dict):
6735
+ infil = self.write_infiltrations_from_dict(sorted_crosssections,
6736
+ hydrographs,
6737
+ directory,
6738
+ writing_type_infiltration,
6739
+ epsilon_infiltration)
6740
+ elif isinstance(hydrographs,list):
6741
+ infil = self.write_infiltrations(infiltration_profiles,
6742
+ sorted_crosssections,
6743
+ hydrographs,
6744
+ directory,
6745
+ writing_type_infiltration,
6746
+ epsilon_infiltration)
5580
6747
 
5581
- znes.find_minmax()
5582
- znes.saveas(path)
5583
- cross= crosssections(mydata= path,format='vecz')
5584
- cross.format = format
5585
- if save_as:
5586
- cross.saveas(save_as)
5587
- return cross
6748
+ # write the simulation parameters
6749
+ if write_in == 'times':
6750
+ write_type = 2
6751
+ elif write_in =='iterations':
6752
+ write_type = 1
6753
+ if self.wx_exists == None:
6754
+ param = self.write_parameters(directory, write_freq= output_frequency, write_type= write_type, max_time=time)
5588
6755
 
6756
+ if run_simulation == 'yes':
6757
+ # logging.warn(f'{simulation_name} running... \nThe process may take time.')
6758
+ self.run_bat_files(batch_file)
6759
+ # logging.warn(f'{simulation_name} completed.')
6760
+ return (directory,simulation_name, sorted_crosssections, ic_relations, nb_updates)
5589
6761
 
5590
6762
  # --- Wolf 1D results ---
5591
6763
  #________________________
@@ -5703,11 +6875,14 @@ class Wolfresults_1D:
5703
6875
  self.wetted_sections = np.roll(wetted_sections, 1, axis=0)
5704
6876
 
5705
6877
  self.velocities = self.discharges/self.wetted_sections
5706
- # # FIXME implement a clever way of computing the froude number (TOP width from wetted sections)
6878
+ # To avoid nan values, copy false means the original array is modified and returned.
6879
+ self.velocities = np.nan_to_num(self.velocities, copy=False)
5707
6880
  if self.breath_file != None:
5708
6881
  self.widths = np.loadtxt(self.breath_file)
5709
6882
  if self.widths.shape == self.wetted_sections.shape:
5710
6883
  self.froudes = self.discharges / (self.wetted_sections*np.sqrt(Constants.GRAVITATION.value*(self.wetted_sections/self.widths)))
6884
+ # To avoid nan values, copy false means the original array is modified and returned.
6885
+ self.froudes = np.nan_to_num(self.froudes, copy=False, nan=0.0, posinf=0.0,neginf=0.0)
5711
6886
  # In case the model has written new results
5712
6887
  else:
5713
6888
  if self.breath_directory != None:
@@ -5743,6 +6918,63 @@ class Wolfresults_1D:
5743
6918
 
5744
6919
  # print('done')
5745
6920
 
6921
+ def update_ic_from_time_steps(self, time_step:int = -1) -> np.ndarray:
6922
+ """Return the simulated conditions (a,q,h, r) at a given time step as a `np.ndarray`.
6923
+ - a: Wetted section (index 0),
6924
+ - s: Wetted perimeter (index 1),
6925
+ - w: Top width (index 2),
6926
+ - r: Hydraulic radius (index 3).
6927
+
6928
+ :param time_step: the desired time steps, defaults to -1
6929
+ :type time_step: int, optional
6930
+ :return: the conditions at the specified time step
6931
+ :rtype: np.ndarray
6932
+ """
6933
+ real_time_step = self.convert_time_step(time_step)
6934
+ for file in os.listdir(self.directory):
6935
+ if file.endswith(".aini"):
6936
+ aini_file = os.path.join(self.directory,file)
6937
+ wetted_sections = self.wetted_sections[:,real_time_step]
6938
+ self.update_ini_file(aini_file, wetted_sections)
6939
+ elif file.endswith(".hini"):
6940
+ hini_file = os.path.join(self.directory,file)
6941
+ depths = self.depths[:,real_time_step]
6942
+ self.update_ini_file(hini_file, depths)
6943
+ elif file.endswith(".zini"):
6944
+ zini_file = os.path.join(self.directory,file)
6945
+ water_level = self.water_levels[:,real_time_step]
6946
+ self.update_ini_file(zini_file, water_level)
6947
+ elif file.endswith(".qini"):
6948
+ qini_file = os.path.join(self.directory,file)
6949
+ discharges = self.discharges[:,real_time_step]
6950
+ self.update_ini_file(qini_file, discharges)
6951
+
6952
+
6953
+ # wetted_sections = self.wetted_sections[:,real_time_step]
6954
+ # discharges = self.discharges[:,real_time_step]
6955
+ # depths = self.depths[:,real_time_step]
6956
+ # wetted_perimeters = self.wetted_sections[:,real_time_step] # FIXME not yet available
6957
+ # top_widths = self.widths[:,real_time_step] # FIXME not yet available
6958
+
6959
+ def update_ini_file(self, ini_file:str, new_values: np.ndarray)-> None:
6960
+ """Update the initial condition file with new values."""
6961
+ df = pd.read_csv(ini_file,
6962
+ sep= Constants.SEPARATOR.value,
6963
+ skiprows=1,
6964
+ names=['skeleton', 'zone', 'segment','value']
6965
+ )
6966
+ lgth_values = len(df['value'])
6967
+ lgth_new_values = len(new_values)
6968
+ assert lgth_values == lgth_new_values,\
6969
+ f"The length of the new values - {lgth_new_values} is not consistent with the initial condition file - {lgth_values}."
6970
+
6971
+ df['value'] = new_values
6972
+ sep = Constants.SEPARATOR.value
6973
+ with open(ini_file, 'w') as f:
6974
+ f.write(f"{lgth_new_values}\n")
6975
+ for i in range(lgth_values):
6976
+ f.write(f"{df['skeleton'][i]}{sep}{df['zone'][i]}{sep}{df['segment'][i]}{sep}{str(df['value'][i])}\n")
6977
+
5746
6978
  def plot_water_level(self,
5747
6979
  figax:tuple = None,
5748
6980
  time_step:int = 1,
@@ -7381,8 +8613,8 @@ class Wolfresults_1D:
7381
8613
  banksbed,
7382
8614
  landmark,
7383
8615
  save_as,
7384
- grid_x_m = grid_x_m,
7385
- grid_y_m = grid_y_m,
8616
+ grid_x = grid_x_m,
8617
+ grid_y = grid_y_m,
7386
8618
  convert_step=False,
7387
8619
  steps_limit=steps_limit)
7388
8620
  # axes_for_updates.append((wetted_section,ax4,fig))
@@ -7428,9 +8660,16 @@ class Wolfresults_1D:
7428
8660
 
7429
8661
  fig.suptitle(f'Results - 1D model\n$Time: (step: {real_time_step+1} - $simulated: {self.simulated_times[real_time_step]:#.1f}s$ - real: {self.real_times[real_time_step]:#.1e} s)$',
7430
8662
  fontsize= 'x-large', fontweight= 'bold')
8663
+
8664
+ plt.tight_layout()
8665
+ if save_as != '':
8666
+ plt.savefig(save_as)
7431
8667
  if show:
7432
- plt.tight_layout()
8668
+ # plt.tight_layout()
7433
8669
  plt.show()
8670
+
8671
+
8672
+
7434
8673
  return axes_for_updates
7435
8674
 
7436
8675
  def animate_1D_plots(self,
@@ -7440,7 +8679,9 @@ class Wolfresults_1D:
7440
8679
  landmark: Union[str,Zones]= '',
7441
8680
  figsize:tuple = (20,10),
7442
8681
  alpha:float = 0.3,
7443
- save_as:str=''):
8682
+ save_as:str='',
8683
+ grid_x = 1000,
8684
+ grid_y = 10):
7444
8685
  """
7445
8686
  Animate the selected variables in figures, save them as a file and
7446
8687
  return the information associated with their axes.
@@ -7471,6 +8712,8 @@ class Wolfresults_1D:
7471
8712
  landmark= landmark,
7472
8713
  figsize=figsize,
7473
8714
  alpha=alpha,
8715
+ grid_x_m = grid_x,
8716
+ grid_y_m = grid_y,
7474
8717
  show=False)
7475
8718
  for charactetristic in animation_axes:
7476
8719
  if len(charactetristic) == 3: