1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package ffx.algorithms.commands;
39
40 import edu.rit.pj.Comm;
41 import edu.rit.pj.IntegerSchedule;
42 import edu.rit.pj.WorkerIntegerForLoop;
43 import edu.rit.pj.WorkerRegion;
44 import edu.rit.pj.WorkerTeam;
45 import ffx.algorithms.cli.AlgorithmsCommand;
46 import ffx.numerics.Potential;
47 import ffx.potential.Utilities;
48 import ffx.utilities.FFXCommand;
49 import ffx.utilities.FFXBinding;
50 import ffx.utilities.FileUtils;
51 import picocli.CommandLine.Command;
52 import picocli.CommandLine.Option;
53 import picocli.CommandLine.Unmatched;
54
55 import java.io.File;
56 import java.util.ArrayList;
57 import java.util.Collections;
58 import java.util.List;
59
60 import static java.lang.String.format;
61 import static org.apache.commons.io.FilenameUtils.normalize;
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 @Command(description = " Run an FFX command on a series of files.", name = "ForEachFile")
82 public class ForEachFile extends AlgorithmsCommand {
83
84
85
86
87 @Option(names = {"--recurse"}, defaultValue = "0", paramLabel = "0",
88 description = "Maximum recursion level (0 only includes the current directory).")
89 private int recurse;
90
91
92
93
94 @Option(names = {"--regex", "--fileSelectionRegex"}, paramLabel = ".*", defaultValue = ".*",
95 description = "Locate files that match a regular expression ('.*' matches all files).")
96 private String regex;
97
98
99
100
101
102 @Option(names = {"--regex2", "--fileSelectionRegex2"}, paramLabel = "", defaultValue = "",
103 description = "Locate files that match a 2nd regular expression ('.*' matches all files).")
104 private String regex2;
105
106
107
108
109 @Option(names = {"--schedule"}, defaultValue = "dynamic", paramLabel = "dynamic",
110 description = "Load balancing will use a [Dynamic, Fixed, or Guided] schedule.")
111 private String schedule;
112
113
114
115
116 @Option(names = {"-v", "--verbose"}, defaultValue = "false", paramLabel = "false",
117 description = "Print additional logging for errors.")
118 private boolean verbose;
119
120
121
122
123 @Unmatched
124 private List<String> unmatched = null;
125
126
127
128
129 private Class<? extends FFXCommand> script;
130
131
132
133
134 private List<File> files;
135
136
137
138
139 private boolean twoFilesPerCommand = false;
140
141
142
143
144 private List<File> files2;
145
146
147
148
149 private IntegerSchedule integerSchedule;
150
151
152
153
154 public ForEachFile() {
155 super();
156 }
157
158
159
160
161
162
163 public ForEachFile(FFXBinding binding) {
164 super(binding);
165 }
166
167
168
169
170
171
172 public ForEachFile(String[] args) {
173 super(args);
174 }
175
176
177
178
179 @Override
180 public ForEachFile run() {
181
182 if (!init()) {
183 return this;
184 }
185
186
187 System.setProperty("pj.use.mpi", "false");
188
189 script = getCommand(unmatched.get(0));
190 if (script != null) {
191 logger.info(format(" The %s will be run on each file.", script));
192 } else {
193 logger.info(format(" %s was not recognized.", unmatched.get(0)));
194 return this;
195 }
196
197 Comm world = Comm.world();
198 int numProc = world.size();
199 int rank = world.rank();
200
201 if (numProc > 1) {
202 logger.info(format(" Number of processes: %d", numProc));
203 logger.info(format(" Rank of this process: %d", rank));
204 }
205
206
207 unmatched.remove(0);
208
209
210 File cwd = new File(".");
211 files = FileUtils.traverseFiles(cwd, recurse, regex);
212
213 if (!regex2.isEmpty()) {
214 twoFilesPerCommand = true;
215 files2 = FileUtils.traverseFiles(cwd, recurse, regex2);
216 }
217
218
219 Collections.sort(files);
220 if (twoFilesPerCommand) {
221 if (files.size() != files2.size()) {
222 logger.info(" The number of files matched by the two regular expressions do not agree.");
223 logger.info(" The number of files matched by the first regular expression: " + files.size());
224 for (File file : files) {
225 logger.info(" File: " + file.getAbsolutePath());
226 }
227 logger.info(" The number of files matched by the second regular expression: " + files2.size());
228 for (File file : files2) {
229 logger.info(" File: " + file.getAbsolutePath());
230 }
231 return this;
232 }
233 Collections.sort(files2);
234 }
235
236
237 try {
238 integerSchedule = IntegerSchedule.parse(schedule.toLowerCase());
239 logger.info(" Parallel Schedule: " + schedule);
240 } catch (Exception e) {
241 integerSchedule = IntegerSchedule.dynamic();
242 logger.info(" Parallel Schedule: Dynamic");
243 }
244
245
246 WorkerTeam workerTeam = new WorkerTeam(world);
247 try {
248 workerTeam.execute(new ForEachFileRegion());
249 } catch (Exception e) {
250 logger.severe("Error executing ForEachFileRegion: " + e.getMessage());
251 }
252
253
254 System.clearProperty("pj.use.mpi");
255
256 return this;
257 }
258
259
260
261
262 private class ForEachFileRegion extends WorkerRegion {
263
264 @Override
265 public void run() throws Exception {
266 int numFiles = files.size();
267 execute(0, numFiles - 1, new ForEachFileLoop());
268 }
269
270 }
271
272
273
274
275 private class ForEachFileLoop extends WorkerIntegerForLoop {
276
277 @Override
278 public IntegerSchedule schedule() {
279 return integerSchedule;
280 }
281
282 @Override
283 public void run(int lb, int ub) throws Exception {
284 for (int i = lb; i <= ub; i++) {
285 File file = files.get(i);
286 if (!file.exists()) {
287 logger.info(format(" Ignoring file that does not exist: %s", file.getAbsolutePath()));
288 continue;
289 }
290 File dualTopologyFile = null;
291 if (twoFilesPerCommand) {
292 dualTopologyFile = files2.get(i);
293 if (!dualTopologyFile.exists()) {
294 logger.info(format(" Ignoring dual topology file that does not exist: %s", dualTopologyFile.getAbsolutePath()));
295 continue;
296 }
297 }
298
299 String path = normalize(file.getAbsolutePath());
300 logger.info(format(" Current File: %s", path));
301
302 String dualTopologyPath = null;
303 if (twoFilesPerCommand) {
304 dualTopologyPath = normalize(dualTopologyFile.getAbsolutePath());
305 logger.info(format(" Current Dual Topology File: %s", dualTopologyPath));
306 }
307
308 List<String> commandArgs = new ArrayList<>();
309
310 boolean foundFile = false;
311 boolean found2File = false;
312 for (String arg : unmatched) {
313 if (arg.equalsIgnoreCase("FILE")) {
314
315 commandArgs.add(path);
316 foundFile = true;
317 } else if (twoFilesPerCommand && arg.equalsIgnoreCase("FILE2")) {
318 commandArgs.add(dualTopologyPath);
319 found2File = true;
320 } else {
321 commandArgs.add(arg);
322 }
323 }
324
325 if (!foundFile) {
326
327 commandArgs.add(path);
328 }
329 if (twoFilesPerCommand && !found2File) {
330
331 commandArgs.add(dualTopologyPath);
332 }
333
334
335 FFXBinding binding = new FFXBinding();
336 binding.setVariable("args", commandArgs);
337
338
339 FFXCommand command = script.getDeclaredConstructor().newInstance();
340 command.setBinding(binding);
341
342 try {
343 command.run();
344 } catch (Exception e) {
345 logger.info(format(" Exception for file: %s", path));
346 if (twoFilesPerCommand) {
347 logger.info(format(" Dual topology file: %s", dualTopologyPath));
348 }
349 if (verbose) {
350 logger.info(e.toString());
351 logger.info(Utilities.stackTraceToString(e));
352 }
353 }
354 }
355 }
356 }
357
358 @Override
359 public List<Potential> getPotentials() {
360 return Collections.emptyList();
361 }
362
363 }