1 | #!/usr/bin/env perl
|
---|
2 | #eval 'exec perl $0 $*'
|
---|
3 | # if 0;
|
---|
4 |
|
---|
5 | $prefix = '@prefix@';
|
---|
6 | $exec_prefix = "@exec_prefix@";
|
---|
7 | $bindir = "@bindir@";
|
---|
8 | $scdatadir = "@scdatadir@";
|
---|
9 |
|
---|
10 | $pwd = `pwd`;
|
---|
11 | chomp $pwd;
|
---|
12 | $pwd =~ s/\/$//;
|
---|
13 |
|
---|
14 | use POSIX;
|
---|
15 |
|
---|
16 | # The path to the ccaffiene executable
|
---|
17 | $ccafe = "@CCAFE_BIN@/ccafe-batch";
|
---|
18 |
|
---|
19 | # The threadgrp specialization
|
---|
20 | $threadgrp = "none";
|
---|
21 |
|
---|
22 | # The mpi launch command
|
---|
23 | $launch = "@CCALAUNCH@";
|
---|
24 |
|
---|
25 | # The number of tasks
|
---|
26 | $ntask = 1;
|
---|
27 |
|
---|
28 | # A filename with a list of nodes.
|
---|
29 | $nodefile = "";
|
---|
30 |
|
---|
31 | # A command line argument with a list of nodes.
|
---|
32 | $nodes = "";
|
---|
33 |
|
---|
34 | # A format string to convert a node number to a node name
|
---|
35 | $nodename = "%d";
|
---|
36 |
|
---|
37 | # The number of nodes each job will use.
|
---|
38 | $nnodeperjob = "nnode";
|
---|
39 |
|
---|
40 | # The number of threads each process will use.
|
---|
41 | $nthreadperproc = 1;
|
---|
42 |
|
---|
43 | # The number of processes to run on each node.
|
---|
44 | $nprocpernode = 1;
|
---|
45 |
|
---|
46 | # The directory where the input file is to be found.
|
---|
47 | $inputprefix = "";
|
---|
48 |
|
---|
49 | # The directory where the output file is to be placed.
|
---|
50 | $outputprefix = "";
|
---|
51 |
|
---|
52 | # If true, then print out help info and exit
|
---|
53 | $help = 0;
|
---|
54 |
|
---|
55 | # If true, then don't actually run anything
|
---|
56 | $debug = 0;
|
---|
57 |
|
---|
58 | # If true, then print out extra info.
|
---|
59 | $verbose = 0;
|
---|
60 |
|
---|
61 | # If true, then overwrite output files that seem up-to-date
|
---|
62 | $rerun = 0;
|
---|
63 |
|
---|
64 | # If true, then do not overwrite any output file.
|
---|
65 | $onlynew = 0;
|
---|
66 |
|
---|
67 | ######################################################################
|
---|
68 |
|
---|
69 | use Getopt::Long;
|
---|
70 |
|
---|
71 | if (!GetOptions("launch=s" => \$launch,
|
---|
72 | "threadgrp=s" => \$threadgrp,
|
---|
73 | "nnodeperjob=i" => \$nnodeperjob,
|
---|
74 | "nthreadperproc=i" => \$nthreadperproc,
|
---|
75 | "nprocpernode=i" => \$nprocpernode,
|
---|
76 | "outputprefix=s" => \$outputprefix,
|
---|
77 | "inputprefix=s" => \$inputprefix,
|
---|
78 | "nodefile=s" => \$nodefile,
|
---|
79 | "nodes=s" => \$nodes,
|
---|
80 | "nodename=s" => \$nodename,
|
---|
81 | "help!" => \$help,
|
---|
82 | "rerun!" => \$rerun,
|
---|
83 | "onlynew!" => \$onlynew,
|
---|
84 | "debug!" => \$debug,
|
---|
85 | "verbose!" => \$verbose,
|
---|
86 | )) {
|
---|
87 | $help=1;
|
---|
88 | }
|
---|
89 |
|
---|
90 | if ("$launch" eq "@" . "CCALAUNCH@") {
|
---|
91 | $launch = "mpirun [-hf %NODEFILE%] -n %NPROC% %CCARUNPROC% %CCAFE% --ccafe-rc %INPUT% --ccafe-remap-stdio --ccafe-outputdir %OUTPUT%";
|
---|
92 | }
|
---|
93 |
|
---|
94 | $outputprefix =~ s/\/$//;
|
---|
95 | $inputprefix =~ s/\/$//;
|
---|
96 |
|
---|
97 | ######################################################################
|
---|
98 |
|
---|
99 | if ($help) {
|
---|
100 | print "Usage: $ARGV[0] [options] [rcfile1.in] [rcfile2.in] ...\n";
|
---|
101 | print "Options:\n";
|
---|
102 | print " --nnodeperjob n run with n nodes per job (value: $nnodeperjob)\n";
|
---|
103 | print " --nprocpernode n run with n procs per node (value: $nprocpernode)\n";
|
---|
104 | print " --nthreadperproc n use n threads per process (value: $nthreadperproc)\n";
|
---|
105 | print " --threadgrp grp use the given threading layer (value: $threadgrp)\n";
|
---|
106 | print " none: uses MPQC's default\n";
|
---|
107 | print " proc: does a single threaded run\n";
|
---|
108 | print " posix: use POSIX threads\n";
|
---|
109 | printf " --launch cmd use the given cmd to launch jobs--see below (value: %s)\n",
|
---|
110 | (($launch eq "")?"<not set>":$launch);
|
---|
111 | printf " --nodefile file a file listing nodes to use (value: %s)\n",
|
---|
112 | (($nodefile eq "")?"<not set>":$nodefile);
|
---|
113 | printf " --nodes nodes a command line list of machines to use (value: %s)\n",
|
---|
114 | (($nodes eq "")?"<not set>":$nodes);
|
---|
115 | printf " groups can be given as 8-10,12,15-17 for example\n";
|
---|
116 | printf " --nodename fmt converts node num to name (value: %s)\n", $nodename;
|
---|
117 | print " --rerun overwrite output file, even if up-to-date\n";
|
---|
118 | print " --onlynew do not overwrite output file, even if not up-to-date\n";
|
---|
119 | print " --inputprefix dir path to prefix input files with\n";
|
---|
120 | print " --outputprefix dir path to prefix output files with\n";
|
---|
121 | print " --debug don't actually run mpqc\n";
|
---|
122 | print " --help print this help\n";
|
---|
123 | print "\n";
|
---|
124 | print "The launch command can contain special strings that will be substituted.\n";
|
---|
125 | print "These are:\n";
|
---|
126 | print " %NPROC% The number of processes to start.\n";
|
---|
127 | print " %NODEFILE% The name of a file containing the node names.\n";
|
---|
128 | print " %NODELIST% A comma separated list of node names.\n";
|
---|
129 | print " For these last two, if they are contained within square brackets\n";
|
---|
130 | print " and a substitution is not available, then everything within the\n";
|
---|
131 | print " the brackets is removed.\n";
|
---|
132 | print "Examples of the launch argument:\n";
|
---|
133 | print " mpirun [-hf %NODEFILE%] -n %NPROC% %CCAFE% --ccafe-rc %INPUT% --ccafe-remap-stdio --ccafe-outp
|
---|
134 | utdir %OUTPUT%\n";
|
---|
135 | print " mpirun [-H %NODELIST%] -n %NPROC% %CCAFE% --ccafe-rc %INPUT% --ccafe-remap-stdio --ccafe-outp
|
---|
136 | utdir %OUTPUT%\n";
|
---|
137 | exit 0;
|
---|
138 | }
|
---|
139 |
|
---|
140 | ######################################################################
|
---|
141 |
|
---|
142 | @nodelist = ();
|
---|
143 | if ($nodes ne "") {
|
---|
144 | $nodes =~ s/-/../g;
|
---|
145 | foreach my $i (eval $nodes) {
|
---|
146 | $nodelist[$#nodelist + 1] = sprintf "$nodename", $i;
|
---|
147 | }
|
---|
148 | }
|
---|
149 | elsif ("$nodefile" eq "" && exists($ENV{"PBS_NODEFILE"})) {
|
---|
150 | $nodefile=$ENV{"PBS_NODEFILE"};
|
---|
151 | }
|
---|
152 | if ("$nodefile" ne "" && -f "$nodefile") {
|
---|
153 | my %nodesfound = {};
|
---|
154 | open(NODEFILE,"<$nodefile");
|
---|
155 | while(<NODEFILE>) {
|
---|
156 | if (/(\S+)/) {
|
---|
157 | my $nodename = $1;
|
---|
158 | if (!exists($nodesfound{$nodename})) {
|
---|
159 | $nodelist[$#nodelist + 1] = $nodename;
|
---|
160 | $nodesfound{$nodename} = 1;
|
---|
161 | }
|
---|
162 | }
|
---|
163 | }
|
---|
164 | close(NODEFILE);
|
---|
165 | }
|
---|
166 |
|
---|
167 | if ($#nodelist == -1) {
|
---|
168 | $nnode = 1;
|
---|
169 | }
|
---|
170 | else {
|
---|
171 | $nnode = $#nodelist + 1;
|
---|
172 | }
|
---|
173 |
|
---|
174 | if ($nnodeperjob eq "nnode") {
|
---|
175 | $nnodeperjob = $nnode;
|
---|
176 | }
|
---|
177 |
|
---|
178 | $nprocperjob = $nnodeperjob * $nprocpernode;
|
---|
179 |
|
---|
180 | $nnodeperjob = POSIX::ceil($nprocperjob / $nprocpernode);
|
---|
181 |
|
---|
182 | @jobnodes = ();
|
---|
183 | %jobnnodes = {};
|
---|
184 | %nodelist = {};
|
---|
185 | %nodefile = {};
|
---|
186 | $maxjobs = 0;
|
---|
187 | while (($maxjobs + 1) * $nnodeperjob <= $nnode) {
|
---|
188 | $jobnnodes[$maxjobs++] = $nnodeperjob;
|
---|
189 | }
|
---|
190 |
|
---|
191 | if ($maxjobs == 0) {
|
---|
192 | die "requested $nnodeperjob nodes but have $nnode nodes";
|
---|
193 | }
|
---|
194 |
|
---|
195 | $nodesbegin = 0;
|
---|
196 | foreach my $i (0..$maxjobs-1) {
|
---|
197 | my $nodesend = $nodesbegin + $jobnnodes[$i];
|
---|
198 | my @slice = (@nodelist)[$nodesbegin..($nodesend-1)];
|
---|
199 | $jobnodes{$i} = \@slice;
|
---|
200 | $nodesbegin = $nodesend;
|
---|
201 | foreach my $j (@slice) {
|
---|
202 | if ($nodelist{$i} eq "") { $nodelist{$i} = $j; }
|
---|
203 | else { $nodelist{$i} = sprintf "%s,%s", $nodelist{$i}, $j; }
|
---|
204 | }
|
---|
205 | $nodefile{$i} = ".tmp.nodefile.$$.$i";
|
---|
206 | open(NODEFILE,">" . $nodefile{$i});
|
---|
207 | foreach my $j (@slice) {
|
---|
208 | printf NODEFILE "%s\n", $j;
|
---|
209 | }
|
---|
210 | close(NODEFILE);
|
---|
211 | }
|
---|
212 |
|
---|
213 | ######################################################################
|
---|
214 |
|
---|
215 | if ($threadgrp eq "none" && $nthreadperproc > 1) {
|
---|
216 | $threadgrp = "posix";
|
---|
217 | }
|
---|
218 |
|
---|
219 | if ($threadgrp eq "proc") {
|
---|
220 | $ENV{"THREADGRP"} = "<ProcThreadGrp>:()";
|
---|
221 | }
|
---|
222 | elsif ($threadgrp eq "posix") {
|
---|
223 | $ENV{"THREADGRP"} = "<PthreadThreadGrp>:(num_threads=$nthreadperproc)";
|
---|
224 | }
|
---|
225 |
|
---|
226 | ######################################################################
|
---|
227 |
|
---|
228 | $usingthreads = 0;
|
---|
229 | if ($maxjobs > 1) {
|
---|
230 | require threads;
|
---|
231 | require threads::shared;
|
---|
232 | $usingthreads = 1;
|
---|
233 | }
|
---|
234 |
|
---|
235 | ######################################################################
|
---|
236 |
|
---|
237 | # autoflush output
|
---|
238 | $| = 1;
|
---|
239 |
|
---|
240 | @allfiles = reverse(get_file_list());
|
---|
241 |
|
---|
242 | my @seqfiles : shared = ();
|
---|
243 | my @files : shared = ();
|
---|
244 | foreach my $file (@allfiles) {
|
---|
245 | $files[$#files+1] = $file;
|
---|
246 | }
|
---|
247 |
|
---|
248 | printf "Running a maximum of %d jobs at a time.\n", $maxjobs;
|
---|
249 | printf "Running %d processes per job.\n", $nprocperjob;
|
---|
250 | printf "Running %d threads per process.\n", $nthreadperproc;
|
---|
251 | foreach my $i (0..$maxjobs-1) {
|
---|
252 | print "Nodes in slot $i:";
|
---|
253 | foreach my $j (@{$jobnodes{$i}}) {
|
---|
254 | printf " \"%s\"", $j;
|
---|
255 | }
|
---|
256 | print "\n";
|
---|
257 | }
|
---|
258 | printenvvar("MESSAGEGRP");
|
---|
259 | printenvvar("THREADGRP");
|
---|
260 | printenvvar("MEMORYGRP");
|
---|
261 | printenvvar("SCLIBDIR");
|
---|
262 | printenvvar("INTEGRAL");
|
---|
263 |
|
---|
264 | $thecount = 0;
|
---|
265 | $n = 0;
|
---|
266 |
|
---|
267 | if ($usingthreads) {
|
---|
268 | @threads = ();
|
---|
269 |
|
---|
270 | foreach my $jobnum (0..$maxjobs-1) {
|
---|
271 | my $thr = threads->new(\&jobrunner, $jobnum);
|
---|
272 | $threads[$#threads+1] = $thr;
|
---|
273 | }
|
---|
274 |
|
---|
275 | foreach my $thr (@threads) {
|
---|
276 | $thr->join();
|
---|
277 | }
|
---|
278 |
|
---|
279 | @threads = ();
|
---|
280 | }
|
---|
281 | else {
|
---|
282 | foreach my $jobnum (0..$maxjobs-1) {
|
---|
283 | jobrunner($jobnum);
|
---|
284 | }
|
---|
285 | }
|
---|
286 |
|
---|
287 | foreach $i (values(%nodefile)) {
|
---|
288 | unlink "$i";
|
---|
289 | }
|
---|
290 |
|
---|
291 | sub get_next_file {
|
---|
292 | my $jobnum = shift;
|
---|
293 | lock(@seqfiles) if ($usingthreads);
|
---|
294 | if ($#seqfiles >= 0 && $jobnum == 0) {
|
---|
295 | return pop(@seqfiles);
|
---|
296 | }
|
---|
297 | lock(@files) if ($usingthreads);
|
---|
298 | if ($#files >= 0) {
|
---|
299 | return pop(@files);
|
---|
300 | }
|
---|
301 | return "";
|
---|
302 | }
|
---|
303 |
|
---|
304 | sub jobrunner {
|
---|
305 | my $jobslot = shift;
|
---|
306 |
|
---|
307 | my $file;
|
---|
308 |
|
---|
309 | while ( ($file = get_next_file($jobslot)) ne "") {
|
---|
310 | $file =~ s/\.in//;
|
---|
311 | $tmp_out = "$pwd/$outputprefix/$file.tmp";
|
---|
312 | $ENV{"CCACHEM_RESULTS_DIR"} = "$tmp_out";
|
---|
313 | my $in = "$inputprefix/$file.in";
|
---|
314 | my $cmd = "$launch";
|
---|
315 | $cmd =~ s/%CCAFE%/$ccafe/;
|
---|
316 | $cmd =~ s/%NPROC%/$nprocperjob/;
|
---|
317 | $cmd =~ s/%INPUT%/$in/;
|
---|
318 |
|
---|
319 | my $ccarunproc = "$scdatadir/ccarunproc $ccafe";
|
---|
320 | if (exists($ENV{THREADGRP})) {
|
---|
321 | $ccarunproc = "$ccarunproc " . &isoencode("$ENV{THREADGRP}");
|
---|
322 | }
|
---|
323 | else {
|
---|
324 | $ccarunproc = "$ccarunproc none";
|
---|
325 | }
|
---|
326 | # no mem/message groups for now
|
---|
327 | $ccarunproc = "$ccarunproc none none";
|
---|
328 | #results dir
|
---|
329 | $ccarunproc = "$ccarunproc " . &isoencode("$ENV{CCACHEM_RESULTS_DIR}");
|
---|
330 |
|
---|
331 | $cmd =~ s|%CCARUNPROC%|$ccarunproc|;
|
---|
332 |
|
---|
333 | $cmd = substitute_optional_parameter($cmd, "%OUTPUT%", "$tmp_out");
|
---|
334 | $cmd = substitute_optional_parameter($cmd, "%NODELIST%", $nodelist{$jobslot});
|
---|
335 | $cmd = substitute_optional_parameter($cmd, "%NODEFILE%", $nodefile{$jobslot});
|
---|
336 |
|
---|
337 | mkdir $tmp_out;
|
---|
338 | printf "starting in slot %d: %s\n", $jobslot, "$cmd";
|
---|
339 | $cmd = "true" if ($debug);
|
---|
340 | $pid = fork();
|
---|
341 | if ($pid == 0) {
|
---|
342 | exec("$cmd");
|
---|
343 | die "exec returned";
|
---|
344 | }
|
---|
345 | waitpid($pid,'');
|
---|
346 |
|
---|
347 | rename "$tmp_out/pOut0", "$pwd/$outputprefix/$file.out";
|
---|
348 | unlink "$tmp_out/pErr0";
|
---|
349 | rename "$tmp_out/results.txt", "$pwd/$outputprefix/$file.results";
|
---|
350 | rmdir "$tmp_out";
|
---|
351 | }
|
---|
352 | }
|
---|
353 |
|
---|
354 | sub get_file_list {
|
---|
355 | my @dirfiles;
|
---|
356 | my @argfiles;
|
---|
357 |
|
---|
358 | if ($readdir ne "") {
|
---|
359 | opendir(DIR,"$readdir");
|
---|
360 | @tdirfiles = sort(readdir(DIR));
|
---|
361 | foreach my $j (@tdirfiles) {
|
---|
362 | if ($j =~ /\.in$/) {
|
---|
363 | $dirfiles[$#dirfiles+1] = $j;
|
---|
364 | }
|
---|
365 | }
|
---|
366 | closedir(DIR);
|
---|
367 | }
|
---|
368 |
|
---|
369 | @argfiles = sort(@ARGV);
|
---|
370 |
|
---|
371 | my @allfiles = (@dirfiles, @argfiles);
|
---|
372 |
|
---|
373 | my @files;
|
---|
374 |
|
---|
375 | foreach my $infile (@allfiles) {
|
---|
376 | my $out = outfile("$infile");
|
---|
377 | $out = "$outputprefix$out";
|
---|
378 | $in = "$inputprefix$infile";
|
---|
379 | if (!$rerun
|
---|
380 | && (-f "$out")
|
---|
381 | && ($onlynew
|
---|
382 | || (-M "$out" < -M "$in" && (! -f "$ccafe" || -M "$out" < -M "$mpqc")))) {
|
---|
383 | if ($verbose) {
|
---|
384 | print "$in: skipping: $out up-to-date\n";
|
---|
385 | }
|
---|
386 | }
|
---|
387 | else {
|
---|
388 | if ($verbose) {
|
---|
389 | print "$in: will be run\n";
|
---|
390 | }
|
---|
391 | $files[$#files+1] = "$infile";
|
---|
392 | }
|
---|
393 | }
|
---|
394 |
|
---|
395 | return @files;
|
---|
396 | }
|
---|
397 |
|
---|
398 | sub outfile {
|
---|
399 | my $in = shift;
|
---|
400 |
|
---|
401 | my $outbase = "$in";
|
---|
402 | $outbase =~ s/\.[^.]*$//;
|
---|
403 | $outbase = sprintf "%s.out", "$outbase";
|
---|
404 | my $out;
|
---|
405 |
|
---|
406 | if ($uniqout) {
|
---|
407 | $out = "$outbase";
|
---|
408 | my $outversion = 1;
|
---|
409 | while (-f "$out") {
|
---|
410 | $outversion++;
|
---|
411 | $out = sprintf "%s.%02d", "$outbase", $outversion;
|
---|
412 | }
|
---|
413 | }
|
---|
414 |
|
---|
415 | return "$out";
|
---|
416 | }
|
---|
417 |
|
---|
418 | sub printenvvar {
|
---|
419 | my $envvar = shift;
|
---|
420 | if (exists($ENV{$envvar})) {
|
---|
421 | printf "Using %s = \"%s\"\n", $envvar, $ENV{$envvar};
|
---|
422 | }
|
---|
423 | }
|
---|
424 |
|
---|
425 | sub substitute_optional_parameter {
|
---|
426 | my $str = shift;
|
---|
427 | my $name = shift;
|
---|
428 | my $value = shift;
|
---|
429 | if ($value ne "") {
|
---|
430 | $str =~ s/\[([^[]*$name[^[]*)\]/$1/;
|
---|
431 | $str =~ s/$name/$value/;
|
---|
432 | }
|
---|
433 | else {
|
---|
434 | $str =~ s/\[([^[]*$name[^[]*)\]//;
|
---|
435 | }
|
---|
436 | return $str;
|
---|
437 | }
|
---|
438 |
|
---|
439 | sub isoencode {
|
---|
440 | my $str = shift;
|
---|
441 | $str =~ s/ /%20/g;
|
---|
442 | $str =~ s/\</%3c/g;
|
---|
443 | $str =~ s/\>/%3e/g;
|
---|
444 | $str =~ s/\[/%5b/g;
|
---|
445 | $str =~ s/\]/%5d/g;
|
---|
446 | $str =~ s/\$/%24/g;
|
---|
447 | $str =~ s/:/%38/g;
|
---|
448 | $str =~ s/\(/%28/g;
|
---|
449 | $str =~ s/\)/%29/g;
|
---|
450 | return $str;
|
---|
451 | }
|
---|