Coverage for ebcpy/modelica/manipulate_ds.py: 97%

61 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-09-19 12:21 +0000

1"""Functions to manipulate (or extract information of) the 

2dsfinal.txt and dsin.txt files created by Modelica.""" 

3 

4from io import StringIO 

5import re 

6 

7import pandas as pd 

8 

9 

10def convert_ds_file_to_dataframe(filename): 

11 """ 

12 Function to convert a given dsfinal or dsfin file to a DataFrame. 

13 The index is the name of the variable. Further, 

14 the following columns are used analog to the dsfinal: 

15 column 1: Type of initial value: 

16 = -2: special case: for continuing simulation (column 2 = value) 

17 = -1: fixed value (column 2 = fixed value) 

18 = 0: free value, i.e., no restriction (column 2 = initial value) 

19 > 0: desired value (column 1 = weight for optimization, 

20 column 2 = desired value) 

21 use weight=1, since automatic scaling usually 

22 leads to equally weighted terms 

23 column 2: fixed, free or desired value according to column 1. 

24 column 3: Minimum value (ignored, if Minimum >= Maximum). 

25 column 4: Maximum value (ignored, if Minimum >= Maximum). 

26 Minimum and maximum restrict the search range in initial 

27 value calculation. They might also be used for scaling. 

28 column 5: Category of variable: 

29 = 1: parameter. 

30 = 2: state. 

31 = 3: state derivative. 

32 = 4: output. 

33 = 5: input. 

34 = 6: auxiliary variable. 

35 column 6: Data type of variable and flags according to dsBaseType 

36 

37 :param str,os.path.normpath filename: 

38 Filepath to the dsfinal or dsinto be loaded. 

39 :return: pd.DataFrame 

40 Converted DataFrame 

41 """ 

42 # Open file and get relevant content by splitting the lines. 

43 with open(filename, "r") as file: 

44 content = file.read().split("\n") 

45 

46 # Gets the X out of 'char initialName(X,Y)' 

47 pattern_size_initial_name = r'^char initialName\((\d+),(\d+)\)$' 

48 for number_line_initial_name, line in enumerate(content): 

49 match_size_initial_name = re.match(pattern_size_initial_name, line) 

50 if match_size_initial_name: 

51 size_initial_names = int(match_size_initial_name.string.split("(")[-1].split(",")[0]) 

52 break 

53 else: 

54 raise ValueError("Could not find initial names in file") 

55 

56 # Number of line below line "double initialValue(X,Y)" 

57 number_line_initial_value = number_line_initial_name + size_initial_names + 3 

58 if "double initialValue" in content[number_line_initial_value]: 

59 number_line_initial_value += 1 

60 

61 # Check if two or on-line dsfinal / dsin 

62 if "#" in content[number_line_initial_value]: 

63 step_size = 1 

64 else: 

65 step_size = 2 

66 

67 # trim content: 

68 ini_val_list = content[number_line_initial_value: (number_line_initial_value + 

69 step_size * size_initial_names)] 

70 # Alter list to create one-lined list. 

71 if step_size == 2: 

72 ini_val_list_one_line = [] 

73 for i in range(0, len(ini_val_list), 2): 

74 # Concat two line into one line 

75 ini_val_list_one_line.append(ini_val_list[i] + ini_val_list[i + 1]) 

76 else: 

77 ini_val_list_one_line = ini_val_list 

78 

79 # Convert to DataFrame. Use a csv-method as it is much faster. 

80 out = StringIO() 

81 # csv_writer = writer(out) 

82 out.write("1;2;3;4;5;6;initialName\n") 

83 # df = pd.DataFrame({1:[], 2:[], 3:[], 4:[], 5:[], 6:[], "initialName":[]}) 

84 for line in ini_val_list_one_line: 

85 # Get only the string of the initialName 

86 ini_name = line.split("#")[-1].replace(" ", "").replace("\n", "") 

87 vals = line.split("#")[0].split() 

88 vals.append(ini_name) 

89 out.write(";".join(vals) + "\n") 

90 out.seek(0) 

91 df = pd.read_csv(out, header=0, sep=";", dtype=object) 

92 df = df.set_index("initialName") 

93 return df 

94 

95 

96def eliminate_parameters_from_ds_file(filename, savepath, exclude_paras, del_aux_paras=True): 

97 """ 

98 Create a new dsfinal file out of the given dsfinal.txt 

99 All parameters except those listed in exclude_paras 

100 will be eliminated from the dsfinal file. 

101 Used for continuing of simulation in calibration problems. 

102 

103 :param str,os.path.normpath filename: 

104 Filepath to the dsfinal or dsin file 

105 :param str,os.path.normpath savepath: 

106 .txt-file for storing output of this function 

107 :param list exclude_paras: 

108 List of parameters to exclude. 

109 :param bool del_aux_paras: 

110 Whether to delete auxiliary parameters or not. 

111 Default value is True. 

112 """ 

113 

114 # Check types 

115 if not savepath.endswith(".txt"): 

116 raise TypeError('File %s is not of type .txt' % savepath) 

117 if not isinstance(exclude_paras, list): 

118 raise TypeError(f"Given exclude_paras is of type {type(exclude_paras).__name__} " 

119 f"but should be of type list") 

120 

121 df = convert_ds_file_to_dataframe(filename) 

122 

123 # Manipulate DataFrame 

124 if del_aux_paras: 

125 # Delete all rows with a parameter or an auxiliary value 

126 df = df[(df["5"] != "1") & (df["5"] != "6") | [idx in exclude_paras for idx in df.index]] 

127 else: 

128 df = df[(df["5"] != "1") | [idx in exclude_paras for idx in df.index]] 

129 

130 # Generate string out of trimmed DataFrame 

131 longest_name = len(max(df.index, key=len)) 

132 char_initial_name = "char initialName(%s,%s)" % (len(df.index), longest_name) 

133 char_initial_name += "\n" + "\n".join(df.index) 

134 double_initial_value = "double initialValue(%s,6)" % len(df.index) # Always 6 

135 for index, row in df.iterrows(): 

136 double_initial_value += "\n" + " ".join(row) + " # " + index 

137 

138 # Create resulting dsFinal string 

139 string_new_ds_final = char_initial_name + "\n\n" + double_initial_value 

140 

141 # Reuses the experiment, tuning parameters etc. settings 

142 number_line_initial_name = 104 # Line where the string char initialName(,) is always stored 

143 with open(filename, "r") as file: 

144 content = file.read().split("\n") 

145 

146 new_content = "\n".join(content[:number_line_initial_name]) 

147 new_content += "\n" + string_new_ds_final 

148 # Save new content to given savepath 

149 with open(savepath, "a+") as file: 

150 file.seek(0) 

151 file.truncate() # Delete all content of the given file 

152 file.write(new_content)