123def mergeOutputs():
124 """Merge outputs into rank 0"""
125 if mpiConfig is None:
126 msg.warning("trfMPITools.mergeOutputs called when we are not in MPI mode")
127 return
128 rank_dir_regex = re.compile("rank-([0-9]+)$")
129 rank_dirs = {
130 int(m.group(1)): m.string
131 for m in (rank_dir_regex.search(d.path) for d in os.scandir("..") if d.is_dir())
132 if m and int(m.group(1)) > 0
133 }
134 num_ranks = len(rank_dirs) + 1
135
136 open("athena_done", "a").close()
137 files_to_check = [
138 (rank, f"../rank-{rank}/athena_done") for rank in range(0, num_ranks)
139 ]
140 count = 0
141 files_to_check = list(
142 it.filterfalse(lambda f: os.path.exists(f[1]), files_to_check)
143 )
144 while files_to_check:
145 if count % 10 == 0 and getMPIRank() == 0:
146 msg.info(
147 f"{count // 10 + 1}: Waiting for all ranks to finish athena: {list(map(lambda x: x[0], files_to_check))}"
148 )
149 count += 1
150 sleep(6)
151 files_to_check = list(
152 it.filterfalse(lambda f: os.path.exists(f[1]), files_to_check)
153 )
154
155 if getMPIRank() == 0:
156 import sqlite3 as sq3
157 from glob import glob
158
159
160 conn = sq3.connect("mpilog.db")
161 cur = conn.cursor()
162 tables = ["ranks", "files", "event_log"]
163 for db in glob("../rank-[1-9]*/mpilog.db"):
164 cur.execute("ATTACH DATABASE ? as db", (db,))
165 for table in tables:
166 upsert = "INSERT OR IGNORE" if table == "files" else "INSERT"
167 cur.execute(f"{upsert} INTO {table} SELECT * from db.{table}")
168 conn.commit()
169 cur.execute("DETACH DATABASE db")
170 conn.close()
171
172 msg.info("Rank output directories are:\n{}".format(pprint.pformat(rank_dirs)))
173 all_merge_inputs = list(
175 lambda f: f.path,
176 filter(
177 lambda f: f.is_file(),
178 it.chain.from_iterable(
map(os.scandir, rank_dirs.values())),
179 ),
180 )
181 )
182
183 try:
184 os.remove("PoolFileCatalog.xml")
185 except FileNotFoundError:
186 pass
187 for dtype, defn in mpiConfig["outputs"].items():
188 if getMPIRank() == 0:
189 msg.info(f"Output type is {dtype}")
190 merge_helper = deepcopy(defn)
191 merge_helper.multipleOK = True
192 if getMPIRank() == 0:
193 for fn in defn.list_to_remove:
194
195 try:
196 os.remove(fn)
197 except FileNotFoundError:
198 pass
199 merge_lists = []
200 for fn in defn.value:
201 merge_inputs = sorted(filter(lambda s: s.endswith(fn), all_merge_inputs))
202
203 merge_helper.value.extend(merge_inputs)
204 merge_lists.append((fn, merge_inputs))
205
206 defn.value = [x[0] for x in merge_lists if len(x[1]) >= 1]
207
208 if getMPIRank() >= len(merge_lists):
209 msg.info(f"In rank {getMPIRank()}, not merging")
210 continue
211 for idx in range(getMPIRank(), len(merge_lists), num_ranks):
212 my_merge = merge_lists[idx]
213 if len(my_merge[1]) < 1:
214 msg.info(
215 f"In rank {getMPIRank()}, no inputs for ../rank-0/{my_merge[0]}"
216 )
217 continue
218 msg.info(
219 f"In rank {getMPIRank()}, merging into ../rank-0/{my_merge[0]}. Inputs are \n{pprint.pformat(my_merge[1])}"
220 )
221 try:
222 merge_helper.selfMerge(f"../rank-0/{my_merge[0]}", my_merge[1])
223 except Exception as e:
224 msg.error(
225 f"Merge failure in rank {getMPIRank()} merging into {my_merge[0]}: {e}"
226 )
227 with open("../rank-0/merge_failure", "a") as f:
228 f.write(
229 f"Merge failure in rank {getMPIRank()} merging into {my_merge[0]}: {e}\n"
230 )
231
232 open("done_merging", "a").close()
233 if getMPIRank() == 0:
234
235 files_to_check = [
236 (rank, f"../rank-{rank}/done_merging") for rank in range(0, num_ranks)
237 ]
238 count = 0
239 files_to_check = list(
240 it.filterfalse(lambda f: os.path.exists(f[1]), files_to_check)
241 )
242 while files_to_check:
243 if count % 10 == 0:
244 msg.info(
245 f"Waiting for all ranks to finish merging: {list(map(lambda x: x[0], files_to_check))}"
246 )
247 count += 1
248 sleep(6)
249 files_to_check = list(
250 it.filterfalse(lambda f: os.path.exists(f[1]), files_to_check)
251 )
252 if not os.path.exists("merge_failure"):
253 msg.info("All ranks done merging")
254 else:
255 msg.error("ERRORS WHILE MERGING")
256 raise RuntimeError("Output merging error")