44import shutil
55import subprocess
66import time
7+ from io import StringIO
78from threading import Thread
89
910from packaging .version import Version
1213from tqdm .contrib .logging import logging_redirect_tqdm
1314
1415from archetypal .eplus_interface .exceptions import EnergyPlusProcessError
15- from archetypal .eplus_interface .version import EnergyPlusVersion
1616from archetypal .utils import log
1717
1818
@@ -41,59 +41,66 @@ def __init__(self, idf, tmp):
4141 @property
4242 def cmd (self ):
4343 """Get the command."""
44- cmd_path = Path ( shutil . which ( "Slab" , path = self . run_dir ))
45- return [cmd_path ]
44+ # if platform is windows
45+ return [self . slabexe ]
4646
4747 def run (self ):
48- """Wrapper around the EnergyPlus command line interface."""
48+ """Wrapper around the Slab command line interface."""
4949 self .cancelled = False
50- # get version from IDF object or by parsing the IDF file for it
5150
5251 # Move files into place
5352 self .epw = self .idf .epw .copy (self .run_dir / "in.epw" ).expand ()
5453 self .idfname = Path (self .idf .savecopy (self .run_dir / "in.idf" )).expand ()
5554 self .idd = self .idf .iddname .copy (self .run_dir ).expand ()
5655
57- # Get executable using shutil.which (determines the extension based on
58- # the platform, eg: .exe. And copy the executable to tmp
56+ # Get executable using shutil.which
5957 slab_exe = shutil .which ("Slab" , path = self .eplus_home )
6058 if slab_exe is None :
6159 log (
62- f"The Slab program could not be found at " f" '{ self .eplus_home } '" ,
60+ f"The Slab program could not be found at '{ self .eplus_home } '" ,
6361 lg .WARNING ,
6462 )
6563 return
66- self .slabexe = Path (slab_exe ).copy (self .run_dir )
64+ else :
65+ slab_exe = (self .eplus_home / slab_exe ).expand ()
66+ self .slabexe = slab_exe
6767 self .slabidd = (self .eplus_home / "SlabGHT.idd" ).copy (self .run_dir )
68+ self .outfile = self .idf .name
6869
69- # The GHTin.idf file is copied from the self.include list (added by
70- # ExpandObjects. If self.include is empty, no need to run Slab.
70+ # The GHTin.idf file is copied from the self.include list
7171 self .include = [Path (file ).copy (self .run_dir ) for file in self .idf .include ]
7272 if not self .include :
7373 self .cleanup_callback ()
7474 return
7575
7676 # Run Slab Program
77- with logging_redirect_tqdm (loggers = [lg .getLogger (self . idf . name )]):
77+ with logging_redirect_tqdm (loggers = [lg .getLogger ("archetypal" )]):
7878 with tqdm (
7979 unit_scale = True ,
8080 miniters = 1 ,
81- desc = f"RunSlab #{ self .idf .position } -{ self .idf .name } " ,
81+ desc = f"{ self . slabexe } #{ self .idf .position } -{ self .idf .name } " ,
8282 position = self .idf .position ,
8383 ) as progress :
8484 self .p = subprocess .Popen (
8585 self .cmd ,
8686 stdout = subprocess .PIPE ,
8787 stderr = subprocess .PIPE ,
88- shell = True , # can use shell
89- cwd = self .run_dir . abspath () ,
88+ shell = False ,
89+ cwd = self .run_dir ,
9090 )
9191 start_time = time .time ()
9292 self .msg_callback ("Begin Slab Temperature Calculation processing . . ." )
93- for line in self .p .stdout :
94- self .msg_callback (line .decode ("utf-8" ).strip ("\n " ))
93+
94+ # Read stdout line by line
95+ for line in iter (self .p .stdout .readline , b"" ):
96+ decoded_line = line .decode ("utf-8" ).strip ()
97+ self .msg_callback (decoded_line )
9598 progress .update ()
9699
100+ # Process stderr after stdout is fully read
101+ stderr = self .p .stderr .read ()
102+ stderr_lines = stderr .decode ("utf-8" ).splitlines ()
103+
97104 # We explicitly close stdout
98105 self .p .stdout .close ()
99106
@@ -102,20 +109,20 @@ def run(self):
102109
103110 # Communicate callbacks
104111 if self .cancelled :
105- self .msg_callback ("RunSlab cancelled" )
106- # self.cancelled_callback(self.std_out, self.std_err)
112+ self .msg_callback ("Slab cancelled" )
107113 else :
108114 if self .p .returncode == 0 :
109115 self .msg_callback (
110- "RunSlab completed in {:,.2f} seconds" .format (
116+ "Slab completed in {:,.2f} seconds" .format (
111117 time .time () - start_time
112118 )
113119 )
114120 self .success_callback ()
115- for line in self . p . stderr :
116- self .msg_callback (line . decode ( "utf-8" ) )
121+ for line in stderr_lines :
122+ self .msg_callback (line )
117123 else :
118- self .msg_callback ("RunSlab failed" )
124+ self .msg_callback ("Slab failed" , level = lg .ERROR )
125+ self .msg_callback ("\n " .join (stderr_lines ), level = lg .ERROR )
119126 self .failure_callback ()
120127
121128 def msg_callback (self , * args , ** kwargs ):
@@ -124,16 +131,27 @@ def msg_callback(self, *args, **kwargs):
124131
125132 def success_callback (self ):
126133 """Parse surface temperature and append to IDF file."""
127- temp_schedule = self .run_dir / "SLABSurfaceTemps.txt"
128- if temp_schedule .exists ():
129- with open (self .idf .idfname , "a" ) as outfile :
130- with open (temp_schedule ) as infile :
131- next (infile ) # Skipping first line
132- next (infile ) # Skipping second line
133- for line in infile :
134- outfile .write (line )
135- # invalidate attributes dependant on idfname, since it has changed
136- self .idf ._reset_dependant_vars ("idfname" )
134+ for temp_schedule in self .run_dir .glob ("SLABSurfaceTemps*" ):
135+ if temp_schedule .exists ():
136+ slab_models = self .idf .__class__ (
137+ StringIO (open (temp_schedule , "r" ).read ()),
138+ file_version = self .idf .file_version ,
139+ as_version = self .idf .as_version ,
140+ prep_outputs = False ,
141+ )
142+ # Loop on all objects and using self.newidfobject
143+ added_objects = []
144+ for sequence in slab_models .idfobjects .values ():
145+ if sequence :
146+ for obj in sequence :
147+ data = obj .to_dict ()
148+ key = data .pop ("key" )
149+ added_objects .append (
150+ self .idf .newidfobject (key = key .upper (), ** data )
151+ )
152+ del slab_models # remove loaded_string model
153+ else :
154+ self .msg_callback ("No SLABSurfaceTemps.txt file found." , level = lg .ERROR )
137155 self .cleanup_callback ()
138156
139157 def cleanup_callback (self ):
0 commit comments