Coverage for bim2sim/plugins/PluginOpenFOAM/bim2sim_openfoam/task/init_openfoam_setup.py: 0%
143 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-01 10:24 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-01 10:24 +0000
1import math
2import pathlib
3import shutil
4from datetime import datetime
6import pandas as pd
8from bim2sim.plugins.PluginEnergyPlus.bim2sim_energyplus.utils import \
9 PostprocessingUtils
10from bim2sim.plugins.PluginOpenFOAM.bim2sim_openfoam.openfoam_elements.openfoam_case import \
11 OpenFOAMCase
12from bim2sim.plugins.PluginOpenFOAM.bim2sim_openfoam.utils.openfoam_utils import \
13 OpenFOAMUtils
14from bim2sim.tasks.base import ITask
15from butterfly import fvSolution, fvSchemes, controlDict, \
16 decomposeParDict, foamfile, turbulenceProperties, g
19class InitializeOpenFOAMSetup(ITask):
20 """This ITask initializes the OpenFOAM Setup.
21 """
23 reads = ()
24 touches = ('openfoam_case',)
26 single_use = False
28 def __init__(self, playground):
29 super().__init__(playground)
31 def run(self):
32 openfoam_case = OpenFOAMCase(playground=self.playground)
33 self.create_directory(openfoam_case)
34 # setup system
35 self.create_fvSolution(openfoam_case)
36 self.create_fvSchemes(openfoam_case)
37 self.create_controlDict(openfoam_case,
38 self.playground.sim_settings.total_iterations)
39 self.create_decomposeParDict(openfoam_case)
40 # setup constant
41 self.create_g(openfoam_case)
42 self.create_radiationProperties(openfoam_case)
43 self.create_thermophysicalProperties(openfoam_case)
44 self.create_turbulenceProperties(openfoam_case)
45 self.create_jobscripts(openfoam_case, self.playground.sim_settings)
46 self.read_ep_results(openfoam_case,
47 date=self.playground.sim_settings.simulation_date,
48 time=self.playground.sim_settings.simulation_time)
49 return openfoam_case,
51 def create_directory(self, openfoam_case):
52 openfoam_case.default_templates_dir = (
53 pathlib.Path(__file__).parent.parent / 'assets' / 'templates')
54 openfoam_case.openfoam_dir = self.paths.export / 'OpenFOAM'
55 openfoam_case.openfoam_dir.mkdir(exist_ok=True)
56 openfoam_case.openfoam_systems_dir = (openfoam_case.openfoam_dir /
57 'system')
58 openfoam_case.openfoam_constant_dir = (openfoam_case.openfoam_dir /
59 'constant')
60 openfoam_case.openfoam_0_dir = openfoam_case.openfoam_dir / '0'
61 openfoam_case.openfoam_systems_dir.mkdir(exist_ok=True)
62 openfoam_case.openfoam_constant_dir.mkdir(exist_ok=True)
63 openfoam_case.openfoam_0_dir.mkdir(exist_ok=True)
64 openfoam_case.openfoam_triSurface_dir = (
65 openfoam_case.openfoam_constant_dir / 'triSurface')
66 openfoam_case.openfoam_triSurface_dir.mkdir(exist_ok=True)
68 @staticmethod
69 def create_fvSolution(openfoam_case):
70 openfoam_case.fvSolution = fvSolution.FvSolution()
71 if openfoam_case.transient_simulation:
72 # todo: for Final values, fix $U, $... that are currently not
73 # recognized (and not imported). Options: (1) add an extended
74 # version of fvSolution that does not contain any $... assignments,
75 # or (2) modify the foamFile parser (?) to handle $... assignments
76 # self.fvSolution = self.fvSolution.from_file(
77 # self.default_templates_dir / 'system' / 'transient'
78 # / 'fvSolution')
79 # implemented hotfix: copy file to temp dir.
80 shutil.copy2(openfoam_case.default_templates_dir / 'system' /
81 'transient' / 'fvSolution',
82 openfoam_case.openfoam_systems_dir)
83 # todo: currently, fvSolution cannot be accessed using butterfly
84 openfoam_case.fvSolution = None
85 else:
86 openfoam_case.fvSolution = openfoam_case.fvSolution.from_file(
87 openfoam_case.default_templates_dir / 'system' / 'steadyState'
88 / 'fvSolution')
89 openfoam_case.fvSolution.save(openfoam_case.openfoam_dir)
91 @staticmethod
92 def create_fvSchemes(openfoam_case):
93 openfoam_case.fvSchemes = fvSchemes.FvSchemes()
94 if openfoam_case.transient_simulation:
95 openfoam_case.fvSchemes = openfoam_case.fvSchemes.from_file(
96 openfoam_case.default_templates_dir /
97 'system' / 'transient' /
98 'fvSchemes')
99 else:
100 openfoam_case.fvSchemes = openfoam_case.fvSchemes.from_file(
101 openfoam_case.default_templates_dir /
102 'system' /
103 'steadyState' /
104 'fvSchemes')
105 openfoam_case.fvSchemes.save(openfoam_case.openfoam_dir)
107 @staticmethod
108 def create_controlDict(openfoam_case, total_iterations):
109 openfoam_case.controlDict = controlDict.ControlDict()
110 if openfoam_case.transient_simulation:
111 openfoam_case.controlDict = openfoam_case.controlDict.from_file(
112 openfoam_case.default_templates_dir / 'system' / 'transient' /
113 'controlDict')
114 else:
115 openfoam_case.controlDict = openfoam_case.controlDict.from_file(
116 openfoam_case.default_templates_dir / 'system' / 'steadyState' /
117 'controlDict')
118 openfoam_case.controlDict.save(openfoam_case.openfoam_dir)
119 openfoam_case.controlDict.endTime = total_iterations
121 @staticmethod
122 def create_decomposeParDict(openfoam_case):
123 openfoam_case.decomposeParDict = decomposeParDict.DecomposeParDict()
124 if openfoam_case.transient_simulation:
125 openfoam_case.decomposeParDict = (
126 openfoam_case.decomposeParDict.from_file(
127 openfoam_case.default_templates_dir / 'system' /
128 'transient' / 'decomposeParDict'))
129 else:
130 openfoam_case.decomposeParDict = (
131 openfoam_case.decomposeParDict.from_file(
132 openfoam_case.default_templates_dir / 'system' /
133 'steadyState' / 'decomposeParDict'))
134 prev_procs = openfoam_case.decomposeParDict.get_value_by_parameter(
135 'numberOfSubdomains')
136 if prev_procs != openfoam_case.n_procs:
137 hc1 = openfoam_case.decomposeParDict.get_value_by_parameter(
138 'hierarchicalCoeffs')
139 distrib = OpenFOAMUtils.split_into_three_factors(
140 openfoam_case.n_procs)
141 openfoam_case.decomposeParDict.set_value_by_parameter(
142 'numberOfSubdomains', str(openfoam_case.n_procs))
143 distrib = '(' + str(distrib[0]) + ' ' + str(distrib[1]) + ' ' + \
144 str(distrib[2]) + ')'
145 hc1['n'] = distrib
146 openfoam_case.decomposeParDict.set_value_by_parameter(
147 'hierarchicalCoeffs', hc1)
148 openfoam_case.decomposeParDict.save(openfoam_case.openfoam_dir)
150 @staticmethod
151 def create_g(openfoam_case):
152 openfoam_case.g = g.G()
153 openfoam_case.g = openfoam_case.g.from_file(
154 openfoam_case.default_templates_dir / 'constant' / 'g')
155 openfoam_case.g.save(openfoam_case.openfoam_dir)
157 @staticmethod
158 def create_radiationProperties(openfoam_case):
159 # todo: create radiationProperties module?
160 if openfoam_case.radiation_model == 'fvDOM':
161 thispath = (openfoam_case.default_templates_dir / 'constant' /
162 'radiation' / 'fvDOM' /
163 'radiationProperties')
164 else: # P1 or preconditioned fvDOM or "noRadiation"
165 thispath = (openfoam_case.default_templates_dir / 'constant' /
166 'radiation' / 'P1' /
167 'radiationProperties')
168 posixpath = thispath.as_posix()
170 openfoam_case.radiationProperties = foamfile.FoamFile.from_file(
171 posixpath)
172 if openfoam_case.radiation_model == 'none':
173 openfoam_case.radiationProperties.set_value_by_parameter(
174 'radiationModel', 'none')
175 openfoam_case.radiationProperties.set_value_by_parameter(
176 'radiation', 'off')
177 openfoam_case.radiationProperties.save(openfoam_case.openfoam_dir)
179 @staticmethod
180 def create_thermophysicalProperties(openfoam_case):
181 # todo: create thermophysicalProperties module?
182 thispath = (openfoam_case.default_templates_dir / 'constant' /
183 'thermophysicalProperties')
184 posixpath = thispath.as_posix()
186 openfoam_case.thermophysicalProperties = foamfile.FoamFile.from_file(
187 posixpath)
188 openfoam_case.thermophysicalProperties.save(openfoam_case.openfoam_dir)
190 @staticmethod
191 def create_turbulenceProperties(openfoam_case):
192 openfoam_case.turbulenceProperties = (
193 turbulenceProperties.TurbulenceProperties())
194 openfoam_case.turbulenceProperties = (
195 openfoam_case.turbulenceProperties.from_file(
196 openfoam_case.default_templates_dir / 'constant' /
197 'turbulenceProperties'))
198 openfoam_case.turbulenceProperties.save(openfoam_case.openfoam_dir)
200 @staticmethod
201 def create_jobscripts(openfoam_case, simsettings):
202 openfoam_case.openfoam_scripts_dir = openfoam_case.openfoam_dir
203 openfoam_case.openfoam_scripts_dir.mkdir(exist_ok=True)
204 script_files = ['fullRun.sh', 'runMeshing.sh', 'runSimulation.sh']
205 comp_acct = "" if not simsettings.cluster_compute_account else (
206 "#SBATCH --account=" + simsettings.cluster_compute_account)
207 replacements = {"JOBNAME": simsettings.cluster_jobname,
208 "STIME": simsettings.cluster_max_runtime_simulation,
209 "MTIME": simsettings.cluster_max_runtime_meshing,
210 "COMP_ACCOUNT": comp_acct,
211 "NNODES": str(math.ceil(
212 openfoam_case.n_procs/simsettings.cluster_cpu_per_node)),
213 "NPROCS": str(openfoam_case.n_procs)}
214 open(openfoam_case.openfoam_dir / 'paraview.foam', 'x')
215 for script_file in script_files:
216 src = openfoam_case.default_templates_dir / 'scripts' / script_file
217 with open(src, 'r') as file:
218 content = file.read()
219 for key, value in replacements.items():
220 content = content.replace(key, value)
221 dst = openfoam_case.openfoam_scripts_dir / script_file
222 with open(dst, 'w') as file:
223 file.write(content)
225 def read_ep_results(self, openfoam_case, year=1900, date='12/21', time=11):
226 try:
227 full_results_df = pd.read_csv(
228 self.paths.export / 'EnergyPlus' / 'SimResults' /
229 self.playground.project.name
230 / 'eplusout.csv')
231 full_results_df['Date/Time'] = full_results_df['Date/Time'].apply(
232 PostprocessingUtils._string_to_datetime)
233 full_results_df = full_results_df.set_index('Date/Time')
234 target_date = datetime.strptime(f"{year}-{date} {time:02}", "%Y-%m/%d %H")
235 if target_date in full_results_df.index:
236 openfoam_case.timestep_df = full_results_df.loc[
237 f"{year}-{date} {time:02}:00:00"]
238 else:
239 self.logger.warning(f"The requested date: {year}-{date} {time:02} "
240 f"is not available in the eplusout.csv file. "
241 f"Calculating the closest available timestep "
242 f"for the selected hour of the day.")
243 target_day = pd.to_datetime(target_date).dayofyear
244 dates = full_results_df.index
245 filtered_dates = dates[dates.hour == time]
246 delta = (filtered_dates.dayofyear - target_day) % 366
247 min_delta = delta.min()
248 new_date = filtered_dates[delta == min_delta]
249 openfoam_case.timestep_df = full_results_df.loc[
250 new_date].squeeze(axis=0)
251 self.logger.warning(f"The new date is set to {new_date}. This is "
252 f"the timestep for the OpenFOAM simulation.")
253 except FileNotFoundError:
254 self.logger.warning("No sim_results found. Boundary conditions "
255 "cannot be generated. ")