ATLAS Offline Software
Loading...
Searching...
No Matches
python.trfMPITools Namespace Reference

Classes

class  MPIType

Functions

 signalError (message)
 getMPIRank ()
 getMPIType ()
 setupMPIConfig (output, dataDict)
 mpiShouldValidate ()
 mpiOutputs ()
 mergeOutputs ()

Variables

 msg = logging.getLogger(__name__)
 mpiConfig = None

Function Documentation

◆ getMPIRank()

python.trfMPITools.getMPIRank ( )
Return MPI rank

Definition at line 41 of file trfMPITools.py.

41def getMPIRank():
42 """Return MPI rank"""
43 if mpiConfig is not None:
44 return int(mpiConfig["rank"])
45 if "RANK" not in os.environ:
46 return -1
47 else:
48 try:
49 return int(os.environ["RANK"])
50 except ValueError:
51 signalError("$RANK environment variable is not an integer")
52 return -2 # Only here to placate PyRight
53
54

◆ getMPIType()

python.trfMPITools.getMPIType ( )
Return MPI type

Definition at line 55 of file trfMPITools.py.

55def getMPIType():
56 """Return MPI type"""
57 if mpiConfig is not None:
58 return mpiConfig["type"]
59 if "RANK" not in os.environ:
60 return MPIType.NOMPI
61 if getMPIRank() == 0:
62 return MPIType.MPIMASTER
63 else:
64 return MPIType.MPIWORKER
65
66

◆ mergeOutputs()

python.trfMPITools.mergeOutputs ( )
Merge outputs into rank 0

Definition at line 123 of file trfMPITools.py.

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 # First wait for all ranks to reach this point so we don't start merging before some outputs are fully closed
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 # Now continue: First the logs
155 if getMPIRank() == 0:
156 import sqlite3 as sq3
157 from glob import glob
158
159 # Merge log databases
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 # Then everything else
172 msg.info("Rank output directories are:\n{}".format(pprint.pformat(rank_dirs)))
173 all_merge_inputs = list(
174 map(
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 # Remove PoolFileCatalog
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 # remove empty files from rank 0
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 # Add to list
203 merge_helper.value.extend(merge_inputs)
204 merge_lists.append((fn, merge_inputs))
205 # Remove non-existent output files from mpiOutputs
206 defn.value = [x[0] for x in merge_lists if len(x[1]) >= 1]
207 # Merge each final output in a different rank
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 # Create a file to indicate we are done
232 open("done_merging", "a").close()
233 if getMPIRank() == 0:
234 # In rank 0, wait until all other ranks have finished merging
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")
STL class.

◆ mpiOutputs()

python.trfMPITools.mpiOutputs ( )

Definition at line 119 of file trfMPITools.py.

119def mpiOutputs():
120 return mpiConfig["outputs"].values()
121
122

◆ mpiShouldValidate()

python.trfMPITools.mpiShouldValidate ( )

Definition at line 111 of file trfMPITools.py.

111def mpiShouldValidate():
112 if getMPIType() == MPIType.NOMPI:
113 return True # validate if we're not in MPI mode
114 if getMPIRank() == 0:
115 return True # validate in rank 0
116 return False # don't validate in other ranks
117
118

◆ setupMPIConfig()

python.trfMPITools.setupMPIConfig ( output,
dataDict )
Check environment is correct if we are in MPI mode, and setup dictionaries

Definition at line 67 of file trfMPITools.py.

67def setupMPIConfig(output, dataDict):
68 """Check environment is correct if we are in MPI mode, and setup dictionaries"""
69 global mpiConfig
70 if "RANK" not in os.environ:
71 signalError(
72 "Running in MPI mode but the $RANK environment variable is not set!"
73 )
74 rank = getMPIRank()
75 if not os.getcwd().endswith("rank-{}".format(getMPIRank())):
76 signalError(
77 "Running in MPI mode with rank {0} but working directory is not called rank-{0}".format(
78 getMPIRank()
79 )
80 )
81 mpiType = getMPIType()
82 mpiConfig = {}
83 mpiConfig["rank"] = rank
84 mpiConfig["type"] = mpiType
85 mpiConfig["outputs"] = {
86 dataType: deepcopy(dataDict[dataType]) for dataType in output
87 }
88 # expand any [ ] lists in output filenames
89 output_proc_regex = re.compile(r"(.+)\‍[(.*)](.*)")
90 for v in mpiConfig["outputs"].values():
91 v.multipleOK = True
92 new_list = []
93 list_to_remove = []
94 for fn in v.value:
95 if ("[" in fn) and ("]" in fn):
96 match = output_proc_regex.match(fn)
97 new_list.extend(
98 [
99 f"{match.group(1)}{it}{match.group(3)}"
100 for it in match.group(2).split(",")
101 ]
102 )
103 list_to_remove.append(match.group(1))
104 else:
105 new_list.append(fn)
106 list_to_remove.append(fn)
107 v.value = new_list
108 v.list_to_remove = list(set(list_to_remove))
109
110
STL class.
std::vector< std::string > split(const std::string &s, const std::string &t=":")
Definition hcg.cxx:179

◆ signalError()

python.trfMPITools.signalError ( message)

Definition at line 34 of file trfMPITools.py.

34def signalError(message):
35 msg.error(message)
36 raise trfExceptions.TransformSetupException(
37 trfExit.nameToCode("TRF_SETUP"), message
38 )
39
40

Variable Documentation

◆ mpiConfig

python.trfMPITools.mpiConfig = None

Definition at line 23 of file trfMPITools.py.

◆ msg

python.trfMPITools.msg = logging.getLogger(__name__)

Definition at line 21 of file trfMPITools.py.