44import os
55import tempfile
66import shutil
7+ import time
78
8- from contextlib import contextmanager
9+ from contextlib import contextmanager , nullcontext
910from copy import deepcopy
1011from pathlib import Path
1112
1516from eppy .runner .run_functions import run
1617import eppy .json_functions as json_functions
1718
19+ import sqlite3
20+ import pandas as pd
21+
1822import energytool .base .idf_utils
1923from energytool .base .parse_results import read_eplus_res
2024from energytool .outputs import get_results
@@ -39,6 +43,7 @@ class SimuOpt(enum.Enum):
3943 OUTPUTS = "outputs"
4044 EPW_FILE = "epw_file"
4145 VERBOSE = "verbose"
46+ OUTPUT_FREQUENCY = "OUTPUT_FREQUENCY"
4247
4348
4449@contextmanager
@@ -53,7 +58,65 @@ def temporary_directory():
5358 yield temp_dir
5459
5560 finally :
56- shutil .rmtree (temp_dir )
61+ for _ in range (20 ):
62+ try :
63+ shutil .rmtree (temp_dir )
64+ break
65+ except PermissionError :
66+ time .sleep (0.2 )
67+
68+
69+ def ensure_sql_output (idf ):
70+ objs = idf .idfobjects ["OUTPUT:SQLITE" ]
71+ if not objs :
72+ idf .newidfobject ("OUTPUT:SQLITE" , Option_Type = "SimpleAndTabular" )
73+
74+
75+ def read_sql_timeseries (sql_path , ref_year = None , unify_frequency = True ):
76+
77+ query = """
78+ SELECT
79+ t.Month,
80+ t.Day,
81+ t.Hour,
82+ t.Minute,
83+ rdd.KeyValue || ':' || rdd.Name || ' [' || rdd.Units || '](' || rdd.ReportingFrequency || ')' AS variable,
84+ rd.Value
85+ FROM ReportData rd
86+ JOIN ReportDataDictionary rdd
87+ ON rd.ReportDataDictionaryIndex = rdd.ReportDataDictionaryIndex
88+ JOIN Time t
89+ ON rd.TimeIndex = t.TimeIndex
90+ """
91+
92+ with sqlite3 .connect (sql_path ) as conn :
93+ df = pd .read_sql_query (query , conn )
94+
95+ if ref_year is None :
96+ ref_year = 2000
97+
98+ dt = pd .to_datetime (
99+ dict (
100+ year = ref_year ,
101+ month = df .Month ,
102+ day = df .Day ,
103+ hour = df .Hour ,
104+ minute = df .Minute ,
105+ )
106+ )
107+
108+ df ["datetime" ] = dt
109+
110+ df = df .pivot (index = "datetime" , columns = "variable" , values = "Value" )
111+ df = df .sort_index ()
112+
113+ if unify_frequency :
114+ step = df .index .to_series ().diff ().dropna ().mode ()[0 ]
115+ full_index = pd .date_range (df .index .min (), df .index .max (), freq = step )
116+ df = df .reindex (full_index )
117+ df = df .ffill ()
118+
119+ return df
57120
58121
59122class Building (Model ):
@@ -200,6 +263,7 @@ def simulate(
200263 self ,
201264 property_dict = None ,
202265 simulation_options = None ,
266+ working_directory = None ,
203267 idf_save_path = None ,
204268 ** simulation_kwargs ,
205269 ) -> pd .DataFrame :
@@ -333,6 +397,12 @@ def simulate(
333397 ),
334398 )
335399
400+ output_frequency = simulation_options .get (
401+ SimuOpt .OUTPUT_FREQUENCY .value ,
402+ "Timestep" ,
403+ )
404+ working_idf .output_frequency = output_frequency
405+
336406 # PRE-PROCESS
337407 system_list = [sys for sublist in working_syst .values () for sys in sublist ]
338408 for system in system_list :
@@ -342,29 +412,37 @@ def simulate(
342412 if SimuOpt .VERBOSE .value not in simulation_options .keys ():
343413 simulation_options [SimuOpt .VERBOSE .value ] = "v"
344414
415+ ensure_sql_output (working_idf )
416+
417+ import gc
418+
419+ gc .collect ()
420+
345421 # SIMULATE
346- with temporary_directory () as temp_dir :
422+ if working_directory is None :
423+ context = temporary_directory ()
424+ else :
425+ working_directory = Path (working_directory )
426+ working_directory .mkdir (parents = True , exist_ok = True )
427+ context = nullcontext (working_directory )
428+
429+ with context as temp_dir :
430+
347431 working_idf .saveas ((Path (temp_dir ) / "in.idf" ).as_posix (), encoding = "utf-8" )
348432 idd_ref = working_idf .idd_version
349433 run (
350434 idf = working_idf ,
351435 weather = epw_path ,
352- output_directory = temp_dir . replace ( " \\ " , "/" ),
436+ output_directory = Path ( temp_dir ). as_posix ( ),
353437 annual = False ,
354438 design_day = False ,
355- idd = None ,
356- epmacro = False ,
357- expandobjects = False ,
358- readvars = True ,
359- output_prefix = None ,
360- output_suffix = None ,
361- version = False ,
439+ readvars = False ,
362440 verbose = simulation_options [SimuOpt .VERBOSE .value ],
363441 ep_version = f"{ idd_ref [0 ]} -{ idd_ref [1 ]} -{ idd_ref [2 ]} " ,
364442 )
365443
366- eplus_res = read_eplus_res (
367- Path (temp_dir ) / "eplusout.csv " , ref_year = ref_year
444+ eplus_res = read_sql_timeseries (
445+ Path (temp_dir ) / "eplusout.sql " , ref_year = ref_year
368446 )
369447
370448 # Save IDF file after pre-process
0 commit comments