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
« 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."""
4from io import StringIO
5import re
7import pandas as pd
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
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")
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")
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
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
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
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
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.
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 """
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")
121 df = convert_ds_file_to_dataframe(filename)
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]]
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
138 # Create resulting dsFinal string
139 string_new_ds_final = char_initial_name + "\n\n" + double_initial_value
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")
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)