Coverage for teaser/data/output/aixlib_output.py: 93%

100 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2025-04-29 16:01 +0000

1"""This module contains function for AixLib model generation""" 

2 

3import os 

4import warnings 

5import shutil 

6from mako.template import Template 

7from mako.lookup import TemplateLookup 

8import teaser.logic.utilities as utilities 

9import teaser.data.output.modelica_output as modelica_output 

10 

11 

12def export_multizone( 

13 buildings, 

14 prj, 

15 path=None, 

16 use_postprocessing_calc=False, 

17 export_vars=None, 

18 custom_multizone_template_path=None): 

19 """Exports models for AixLib library 

20 

21 Exports a building for 

22 AixLib.ThermalZones.ReducedOrder.Multizone.MultizoneEquipped models 

23 using the ThermalZoneEquipped and supporting models, like tables and weather 

24 model. Depending on chosen calculation method the parameter set to 1,2, 

25 3 or 4 element model. By default it uses the correction for solar 

26 glazing (corG) and decoupled heat conduction through windows ( 

27 merge_windows=False). In contrast to versions < 0.5 TEASER now does not 

28 support any other model options, as we observed no need, since single 

29 ThermalZones are identical with IBPSA models. If you miss one of the 

30 old options please contact us. 

31 

32 This function uses Mako Templates specified in 

33 data.output.modelicatemplates.AixLib 

34 

35 Parameters 

36 ---------- 

37 

38 buildings : list of instances of Building 

39 list of TEASER instances of a Building that is exported to a AixLib 

40 MultizoneEquipped models. If you want to export a single building, 

41 please pass it over as a list containing only that building. 

42 prj : instance of Project 

43 Instance of TEASER Project object to access Project related 

44 information, e.g. name or version of used libraries 

45 path : string 

46 if the Files should not be stored in default output path of TEASER, 

47 an alternative path can be specified as a full path 

48 use_postprocessing_calc : bool 

49 If activated the exported model will use the multizonePostProcessing 

50 to calculate common outputs for simulation time like total heating 

51 demands. Only supported for Aixlib. Default is False. 

52 export_vars : str 

53 Holds the string about which variables to export following the 

54 __Dymola_selection syntax. 

55 custom_multizone_template_path : str 

56 if a custom template for writing the multizone model should be used, 

57 its path can be specified as a full path 

58 

59 Attributes 

60 ---------- 

61 

62 lookup : TemplateLookup object 

63 Instance of mako.TemplateLookup to store general functions for templates 

64 zone_template_1 : Template object 

65 Template for ThermalZoneRecord using 1 element model 

66 zone_template_2 : Template object 

67 Template for ThermalZoneRecord using 2 element model 

68 zone_template_3 : Template object 

69 Template for ThermalZoneRecord using 3 element model 

70 zone_template_4 : Template object 

71 Template for ThermalZoneRecord using 4 element model 

72 zone_template_5 : Template object 

73 Template for ThermalZoneRecord using 5 element model 

74 model_template : Template object 

75 Template for MultiZone model 

76 """ 

77 

78 if path is None: 

79 path = utilities.get_full_path("") 

80 

81 lookup = TemplateLookup(directories=[utilities.get_full_path( 

82 os.path.join('data', 'output', 'modelicatemplate'))]) 

83 zone_template_1 = Template( 

84 filename=utilities.get_full_path( 

85 "data/output/modelicatemplate/AixLib" 

86 "/AixLib_ThermalZoneRecord_OneElement"), 

87 lookup=lookup) 

88 zone_template_2 = Template( 

89 filename=utilities.get_full_path( 

90 "data/output/modelicatemplate/AixLib" 

91 "/AixLib_ThermalZoneRecord_TwoElement"), 

92 lookup=lookup) 

93 zone_template_3 = Template( 

94 filename=utilities.get_full_path( 

95 "data/output/modelicatemplate/AixLib" 

96 "/AixLib_ThermalZoneRecord_ThreeElement"), 

97 lookup=lookup) 

98 zone_template_4 = Template( 

99 filename=utilities.get_full_path( 

100 "data/output/modelicatemplate/AixLib" 

101 "/AixLib_ThermalZoneRecord_FourElement"), 

102 lookup=lookup) 

103 zone_template_5 = Template( 

104 filename=utilities.get_full_path( 

105 "data/output/modelicatemplate/AixLib" 

106 "/AixLib_ThermalZoneRecord_FiveElement"), 

107 lookup=lookup) 

108 if custom_multizone_template_path: 

109 model_template = Template( 

110 filename=custom_multizone_template_path, 

111 lookup=lookup) 

112 else: 

113 model_template = Template( 

114 filename=utilities.get_full_path( 

115 "data/output/modelicatemplate/AixLib/AixLib_Multizone"), 

116 lookup=lookup) 

117 test_script_template = Template( 

118 filename=utilities.get_full_path( 

119 "data/output/modelicatemplate/modelica_test_script"), 

120 lookup=lookup) 

121 

122 dir_resources = utilities.create_path(os.path.join(path, "Resources")) 

123 dir_scripts = utilities.create_path(os.path.join(dir_resources, "Scripts")) 

124 dir_dymola = utilities.create_path(os.path.join(dir_scripts, "Dymola")) 

125 

126 uses = [ 

127 'Modelica(version="' + prj.modelica_info.version + '")', 

128 'AixLib(version="' + prj.buildings[-1].library_attr.version + '")'] 

129 modelica_output.create_package( 

130 path=path, 

131 name=prj.name, 

132 uses=uses) 

133 modelica_output.create_package_order( 

134 path=path, 

135 package_list=buildings, 

136 extra=None) 

137 if not prj.weather_file_path.startswith("modelica:"): 

138 modelica_output.copy_weather_data(prj.weather_file_path, path) 

139 if prj.t_soil_mode == 3: 

140 if not prj.t_soil_file_path.startswith("modelica:"): 

141 modelica_output.copy_weather_data(prj.t_soil_file_path, path) 

142 

143 for i, bldg in enumerate(buildings): 

144 

145 ass_error = "You chose IBPSA calculation, " \ 

146 "but want to export AixLib models, " \ 

147 "this is not possible" 

148 

149 assert bldg.used_library_calc == 'AixLib', ass_error 

150 

151 bldg_path = os.path.join(path, bldg.name) 

152 utilities.create_path(bldg_path) 

153 utilities.create_path(os.path.join(bldg_path, bldg.name + "_DataBase")) 

154 bldg.library_attr.modelica_set_temp(path=bldg_path) 

155 bldg.library_attr.modelica_set_temp_cool(path=bldg_path) 

156 bldg.library_attr.modelica_AHU_boundary( 

157 path=bldg_path) 

158 bldg.library_attr.modelica_gains_boundary( 

159 path=bldg_path) 

160 

161 modelica_output.create_package(path=bldg_path, name=bldg.name, within=bldg.parent.name) 

162 modelica_output.create_package_order( 

163 path=bldg_path, 

164 package_list=[bldg], 

165 extra=[bldg.name + "_DataBase"]) 

166 

167 if bldg.building_id is None: 

168 bldg.building_id = i 

169 else: 

170 try: 

171 bldg.building_id = int(bldg.building_id) 

172 except UserWarning: 

173 warnings.warn("Cannot convert building_id to integer, " 

174 "is set to ", i, "which is the enumeration " 

175 "number of the building in " 

176 "the project list.") 

177 bldg.building_id = i 

178 with open(os.path.join(bldg_path, bldg.name + ".mo"), 'w') as out_file: 

179 

180 out_file.write(model_template.render_unicode( 

181 bldg=bldg, 

182 weather=bldg.parent.weather_file_path, 

183 modelica_info=bldg.parent.modelica_info, 

184 use_postprocessing_calc=use_postprocessing_calc, 

185 export_vars=export_vars)) 

186 out_file.close() 

187 

188 _help_test_script(bldg, dir_dymola, test_script_template) 

189 

190 zone_path = os.path.join(bldg_path, bldg.name + "_DataBase") 

191 

192 for zone in bldg.thermal_zones: 

193 

194 with open(os.path.join(zone_path, bldg.name + '_' + zone.name + '.mo'), 

195 'w') as out_file: 

196 if type(zone.model_attr).__name__ == "OneElement": 

197 out_file.write(zone_template_1.render_unicode(zone=zone)) 

198 elif type(zone.model_attr).__name__ == "TwoElement": 

199 out_file.write(zone_template_2.render_unicode(zone=zone)) 

200 elif type(zone.model_attr).__name__ == "ThreeElement": 

201 out_file.write(zone_template_3.render_unicode(zone=zone)) 

202 elif type(zone.model_attr).__name__ == "FourElement": 

203 out_file.write(zone_template_4.render_unicode(zone=zone)) 

204 elif type(zone.model_attr).__name__ == "FiveElement": 

205 out_file.write(zone_template_5.render_unicode(zone=zone)) 

206 

207 out_file.close() 

208 

209 modelica_output.create_package( 

210 path=zone_path, 

211 name=bldg.name + '_DataBase', 

212 within=prj.name + '.' + bldg.name) 

213 modelica_output.create_package_order( 

214 path=zone_path, 

215 package_list=bldg.thermal_zones, 

216 addition=bldg.name + "_", 

217 extra=None) 

218 

219 _copy_script_unit_tests(os.path.join(dir_scripts, "runUnitTests.py")) 

220 _copy_reference_results(dir_resources, prj) 

221 

222 print("Exports can be found here:") 

223 print(path) 

224 

225 

226def _copy_reference_results(dir_resources, prj): 

227 """Copy reference results to modelica output. 

228 

229 Parameters 

230 ---------- 

231 dir_resources : str 

232 Resources directory of the modelica output 

233 prj : teaser.project.Project 

234 Project to be exported 

235 """ 

236 

237 if prj.dir_reference_results is not None: 

238 dir_ref_out = os.path.join(dir_resources, "ReferenceResults") 

239 if not os.path.exists(dir_ref_out): 

240 os.mkdir(dir_ref_out) 

241 dir_ref_out_dymola = os.path.join(dir_ref_out, "Dymola") 

242 if not os.path.exists(dir_ref_out_dymola): 

243 os.mkdir(dir_ref_out_dymola) 

244 for filename in os.listdir(prj.dir_reference_results): 

245 if filename.endswith(".txt"): 

246 shutil.copy2( 

247 os.path.join(prj.dir_reference_results, filename), 

248 os.path.join(dir_ref_out_dymola, filename) 

249 ) 

250 

251 

252def _help_test_script(bldg, dir_dymola, test_script_template): 

253 """Create a test script for regression testing with BuildingsPy 

254 

255 Parameters 

256 ---------- 

257 bldg : teaser.logic.buildingobjects.building.Building 

258 Building for which test script is created 

259 dir_dymola : str 

260 Output directory for Dymola scripts 

261 test_script_template : mako.template.Template 

262 Template for the test script 

263 

264 Returns 

265 ------- 

266 dir_scripts : str 

267 Path to the scripts directory 

268 """ 

269 

270 dir_building = os.path.join(dir_dymola, bldg.name) 

271 if not os.path.exists(dir_building): 

272 os.mkdir(dir_building) 

273 with open(os.path.join(dir_building, bldg.name + ".mos"), 'w') as out_file: 

274 

275 names_variables = [] 

276 for i, zone in enumerate(bldg.thermal_zones): 

277 names_variables.append(f"multizone.PHeater[{i+1}]") 

278 names_variables.append(f"multizone.PCooler[{i+1}]") 

279 names_variables.append(f"multizone.TAir[{i+1}]") 

280 out_file.write(test_script_template.render_unicode( 

281 project=bldg.parent, 

282 bldg=bldg, 

283 stop_time=3600 * 24 * 365, 

284 names_variables=names_variables, 

285 )) 

286 out_file.close() 

287 

288 

289def _copy_script_unit_tests(destination_path): 

290 """Copies the script to run the unit tests. 

291 

292 Parameters 

293 ---------- 

294 destination_path : str 

295 path of where the weather file should be placed 

296 """ 

297 

298 source_path = utilities.get_full_path("data/output/runUnitTests.py") 

299 shutil.copy2(source_path, destination_path)