made sure always IGNORE=@ in models estimating simulated data sets
[PsN.git] / lib / tool_subs.pm
blob73841e217966fca2474ded8b1c1d0766df2ade3d
1 # {{{ include
3 start include statements
4 use ext::Parallel::ForkManager;
5 use strict;
6 use Cwd;
7 use File::Copy 'cp';
8 use OSspecific;
9 use Storable;
10 use Math::Random;
11 use ui;
12 use Data::Dumper;
13 use Config;
14 our $AUTOLOAD;
15 if ( $PsN::config -> {'_'} -> {'use_database'} ) {
16 # Testing DBD::mysql:
17 require DBI;
19 end include
21 # }}} include statements
23 # {{{ new
25 start new
27 # The I<retries>, I<picky>, I<nm_version>, I<seed> and I<threads>
28 # attributes can be specified as either a scalar or as an
29 # array. The scalar value will be applied to all models whereas
30 # the array holds values per model. If an array is given it must
31 # be of the same length as the number of models.
33 # The I<directory> is the folder where the tools stores
34 # temporary data and runs subtools (or in the modelfit case,
35 # runs NONMEM). If unsure of what this means, leave it undefined
36 # and a default will be used, e.g. modelfit_dir3 or something.
38 # The base_directory refers to the directory where the tool
39 # should place its own directory. Default is current directory.
41 # A more interresting attribute is I<threads> which sets how many
42 # parallel executions of NONMEM that will run. Some tips are:
43 # Setting the number of threads higher than the number of nodes in
44 # your cluster/supercomputer can make your runs slower. The
45 # biggest limiting factor is the amount of memory needed by
46 # NONMEM. With smaller runs, just set the thread number to the
47 # number of nodes available.
49 # The I<directory> is the folder where the tools stores
50 # temporary data and runs subtools (or in the modelfit case,
51 # runs NONMEM). Each NONMEM run will have its own sub directory
52 # NM_run[X] where [X] is an index running from 0 to the number of
53 # runs-1. If unsure of what this means, leave it undefined and a
54 # default will be used, e.g. modelfit_dir3 or something.
56 # Next, the I<compress> and I<remove_temp_files> attributes are good
57 # if you want to save some hard disk space. I<compress> set to 1
58 # will put all NONMEM output in to an tar/gz archive named
59 # I<problem_files.tgz> placed in the I<NM_run[X]> directory
60 # described above. If I<remove_temp_files> is set to 1, the NONMEM
61 # files: 'FCON', 'FDATA', 'FSTREAM', 'PRDERR' will be removed.
63 # I<clean> is a stronger version of I<remove_temp_files>; it will also
64 # remove I<NM_run[X]> and all that is in these.
66 # I<retries> is the number of times L</run> will alter initial
67 # values and (re)execute NONMEM when executions fail. I<retries>
68 # can either be an integer, specifying the number of retries for
69 # all models, or it can be an array with the number of retries
70 # specific for each modelfile as elements. The default value is
71 # B<5>. The algorithm for altering the initial values works
72 # roughly like this: For each each new try, a random new initial
73 # value is drawn from a uniform distribution with limits +-n*10%
74 # of the original intial estimate and where n i equal to the retry
75 # number. I.e. the first retry, the borders of the distribution
76 # are +-10%. The algorithm ensures that the new values are within
77 # specified boundaries.
79 # =begin html
81 # For a full dexcription of the algorithm, see <a
82 # href="model/problem/record/init_option.html#set_random_init">set_random_init</a>
83 # of the <a
84 # href="model/problem/record/init_option.html">init_option
85 # class</a>.
87 # =end html
89 # =begin man
91 # For a full dexcription of the algorithm, see I<set_random_init>
92 # of the I<init_option> class.
94 # =end man
96 # If I<picky> is set to 1, the output from NONMEM will be checked
97 # more thoroughly. If any of the lines below are found in the
98 # minimization message, a rerun is initiated.
100 # ESTIMATE OF THETA IS NEAR THE BOUNDARY AND
101 # PARAMETER ESTIMATE IS NEAR ITS BOUNDARY
102 # R MATRIX ALGORITHMICALLY SINGULAR
103 # S MATRIX ALGORITHMICALLY SINGULAR
105 # I<nm_version> is a string with the version number of NONMEM that
106 # will be used. The installed versions of NONMEM must be specified
107 # in OSspecific.pm, the class responsible for system specific
108 # features settings.
110 # I<logfile> specifies the name of the logfile.
112 # If I<debug> is set to 1(true), (many!) debug messages will be
113 # printed.
115 # I<extra_files> is an array of strings where each string is a
116 # file needed for NONMEM execution. Those file will be moved
117 # to the I<NM_run[X]> directory.
119 # I<seed> is just a way to set a seed number.
121 # If a directory is given as argument to a tool, it will extract
122 # all information about what has already been run in this
123 # directory and continue there. If nothing is left to do, it will
124 # still produce the output as a normal run would. This is useful
125 # both for resuming crashed runs as well as for extracting
126 # information form an old run.
128 $this -> {'seed'} = defined $parm{'seed'} ? $parm{'seed'} : random_uniform_integer(1,0,10000000);
130 #Initiate the random generator if a seed is given (which it is, see above)
131 random_set_seed_from_phrase( $this -> {'seed'} );
133 # The base_directory refers to the directory where the tool should place its own
134 # directory
135 if ( defined $parm{'base_directory'} ) {
136 $this -> {'base_directory'} = $parm{'base_directory'};
137 } else {
138 my ($uniquePath, $file) = OSspecific::absolute_path( '', '' );
139 $this -> {'base_directory'} = $uniquePath;
142 my @tool_name_full = split( '::', ref $this );
143 my $tool_name = $tool_name_full[$#tool_name_full];
145 # The directory is the folder where the tools stores temporary data and
146 # runs subtools (or in the modelfit case, runs NONMEM)
147 if ( defined $parm{'directory'} ) {
148 my $dummy;
149 ( $this -> {'directory'}, $dummy ) = OSspecific::absolute_path( $parm{'directory'}, '');
150 } else {
151 my $file;
152 $this -> {'directory'} =
153 OSspecific::unique_path( $tool_name.'_dir' ,
154 $this -> {'base_directory'} );
158 # Create my temporary directory
159 $this -> _make_dir;
160 if ( $PsN::config -> {'_'} -> {'use_database'} ) {
161 my( $found_log, $found_tool_id ) = $this -> read_log;
163 $this -> register_in_database unless ( $found_tool_id );
165 $this -> log_object unless ( $found_log and $found_tool_id );
168 debug -> die( message => "No model specified!" )
169 unless ( defined $this -> {'models'} and scalar @{$this -> {'models'}} > 0 );
170 foreach my $mod ( @{$this -> {'models'}} ) {
171 debug -> die( message => "Supplied argument model is not defined" )
172 unless defined $mod;
174 # Make sure that the filenames are absolute and collect model_ids
175 my @model_ids;
176 foreach my $model ( @{$this -> {'models'}} ) {
177 my $model_id = $model -> model_id;
178 if( not defined $model_id ) {
179 $model_id = $model -> register_in_database;
181 my $datas = $model -> datas;
182 if( defined $datas ) {
183 foreach my $data ( @{$datas} ) {
184 my $data_id = $data -> data_id;
185 if( not defined $data_id ) {
186 $data -> register_in_database;
191 push( @model_ids, $model -> model_id );
192 my ($directory, $filename) = OSspecific::absolute_path( $model -> directory, $model -> filename );
193 $model -> filename( $filename );
194 $model -> directory( $directory );
195 if ( defined $model -> outputs ) {
196 my @outputs = @{$model -> outputs};
197 foreach my $output ( @outputs ) {
198 my ($directory, $filename) = OSspecific::absolute_path( $outputs[0] -> directory, $outputs[0] -> filename );
199 $output -> filename( $filename );
200 $output -> directory( $directory );
203 if ( defined $model -> datas ) {
204 my @datas = @{$model -> datas};
205 foreach my $data ( @datas ) {
206 my ($directory, $filename) = OSspecific::absolute_path( $datas[0] -> directory, $datas[0] -> filename );
207 $data -> filename( $filename );
208 $data -> directory( $directory );
212 $this -> {'model_ids'} = \@model_ids;
213 if ( not -e $this -> {'directory'}."done.database.tool_models" ) {
214 $this -> register_tm_relation( model_ids => \@model_ids,
215 prepared_models => 0 );
216 open( DB, ">".$this -> {'directory'}."done.database.tool_models" );
217 print DB "";
218 close( DB );
221 end new
223 # }}} new
225 # {{{ log_object
227 start log_object
229 open( OLOG, '>',$self -> {'directory'}.'object.txt' );
230 $Data::Dumper::Maxdepth = 1;
231 print OLOG Dumper $self;
232 $Data::Dumper::Maxdepth = 0;
233 close( OLOG );
235 end log_object
237 # }}} log_object
239 # {{{ read_log
240 start read_log
242 if( -e $self -> {'directory'}.'object.txt' ) {
243 $found_log = 1;
244 open( OLOG, '<'.$self -> {'directory'}.'object.txt' );
245 my @olog = <OLOG>;
246 my $str = "(";
247 for ( my $i = 1; $i < $#olog; $i++ ) {
248 $str = $str.$olog[$i];
250 $str = $str.")";
251 my %tmp = eval( $str );
253 if( exists $tmp{'tool_id'} ) {
254 $self -> {'tool_id'} = $tmp{'tool_id'};
255 $found_tool_id = 1;
257 close( OLOG );
260 end read_log
261 # }}} read_log
263 # {{{ register_in_database
265 start register_in_database
267 if ( $PsN::config -> {'_'} -> {'use_database'} ) {
268 my @tool_name_full = split( '::', ref $self );
269 my $tool_name = $tool_name_full[$#tool_name_full];
270 my ( $date_str, $time_str );
271 if ( $Config{osname} eq 'MSWin32' ) {
272 $date_str = `date /T`;
273 $time_str = ' '.`time /T`;
274 } else {
275 # Assuming UNIX
276 $date_str = `date`;
278 chomp($date_str);
279 chomp($time_str);
280 my $date_time = $date_str.$time_str;
281 # Backslashes messes up the sql syntax
282 my $dir_str = $self->{'directory'};
283 $dir_str =~ s/\\/\//g;
285 my $dbh = DBI -> connect("DBI:mysql:host=".$PsN::config -> {'_'} -> {'database_server'}.
286 ";databse=".$PsN::config -> {'_'} -> {'project'},
287 $PsN::config -> {'_'} -> {'user'},
288 $PsN::config -> {'_'} -> {'password'},
289 {'RaiseError' => 1});
290 my $sth;
291 if ( defined $self -> {'parent_tool_id'} ) {
292 # print "INSERT INTO tool (parent_tool_id,name,date,directory) ".
293 # "VALUES (".$self -> {'parent_tool_id'}.", '".
294 # "$tool_name', '$date_time', '$dir_str' )\n";
295 $sth = $dbh -> prepare("INSERT INTO ".$PsN::config -> {'_'} -> {'project'}.
296 ".tool (parent_tool_id,name,date,directory) ".
297 "VALUES (".$self -> {'parent_tool_id'}.", '".
298 "$tool_name', '$date_time', '$dir_str' )");
299 } else {
300 $sth = $dbh -> prepare("INSERT INTO ".$PsN::config -> {'_'} -> {'project'}.
301 ".tool (name,date,directory) ".
302 "VALUES ('$tool_name', '$date_time', '$dir_str' )");
304 $sth -> execute;
305 $self -> {'tool_id'} = $sth->{'mysql_insertid'};
306 $sth -> finish;
307 $dbh -> disconnect;
310 end register_in_database
312 # }}} register_in_database
314 # {{{ register_tm_relation
316 start register_tm_relation
317 if ( $PsN::config -> {'_'} -> {'use_database'} and
318 defined $self -> {'tool_id'} and $#model_ids >= 0 ) {
319 my $dbh = DBI -> connect("DBI:mysql:host=".$PsN::config -> {'_'} -> {'database_server'}.
320 ";databse=".$PsN::config -> {'_'} -> {'project'},
321 $PsN::config -> {'_'} -> {'user'},
322 $PsN::config -> {'_'} -> {'password'},
323 {'raiseerror' => 1});
324 my $sth;
325 my $values;
326 my $columns = "( tool_id, model_id, prepared_model )";
327 foreach my $model_id ( @model_ids ) {
328 if ( defined $model_id ) {
329 $values = $values."," if ( defined $values );
330 if( $prepared_models ) {
331 $values = $values."(".$self -> {'tool_id'}.", $model_id, 1 )";
332 } else {
333 $values = $values."(".$self -> {'tool_id'}.", $model_id, 0 )";
337 $sth = $dbh -> prepare( "INSERT INTO ".$PsN::config -> {'_'} -> {'project'}.
338 ".tool_model $columns VALUES $values" );
339 $sth -> execute;
340 $sth -> finish if ( defined $sth );
341 $dbh -> disconnect;
343 end register_tm_relation
345 # }}} register_tm_relation
347 # {{{ copy
349 start copy
351 #%{$tool} = %{$self};
352 #$tool -> {'models'} = undef;
353 #$tool -> {'tools'} = undef;
354 #@{$tool -> {'models'}} = ();
355 #@{$tool -> {'tools'}} = ();
356 #foreach my $model ( @{$self -> {'models'}} ) {
357 # push( @{$tool -> {'models'}}, $model -> copy );
359 #foreach my $subtool ( @{$self -> {'tools'}} ) {
360 # push( @{$tool -> {'tools'}}, $subtool -> copy );
362 #bless( $tool, ref( $self ) );
364 # ! NOTE ! This is not a deep copy ! NOTE !
365 # This function has now been replaced with "copying" the "reference object" in new().
367 # my $all_parameters = {};
369 # foreach my $valid_p ( keys %{ $self -> {'__valid_parameters'} } ){
370 # if( defined $self -> {$valid_p} ) {
371 # $all_parameters -> {$valid_p} = $self -> {$valid_p};
372 # }
375 # %{$all_parameters} = (%{$all_parameters}, %parameters);
377 # use Data::Dumper;
378 # $Data::Dumper::Maxdepth = 3;
379 # print Dumper( $all_parameters );
380 # $Data::Dumper::Maxdepth = 0;
382 # my $tool_string = ref $self;
384 # $tool = "$tool_string" -> new( %{$all_parameters} );
386 end copy
388 # }}} copy
390 # {{{ pre_fork_setup
392 start pre_fork_setup
394 # Runs the pre_fork_setup specific for the subtool
395 my $sub_pre_fork_setup = $self -> {'subtools'} -> [0];
396 if ( defined $sub_pre_fork_setup ) {
397 $sub_pre_fork_setup = $sub_pre_fork_setup.'_pre_fork_setup';
398 if ( defined( $self -> can( $sub_pre_fork_setup ) ) ) {
399 $self -> $sub_pre_fork_setup;
403 end pre_fork_setup
405 # }}} pre_fork_setup
407 # {{{ print_results
409 start print_results
412 # Run the print_results specific for the subtool
413 my $sub_print_results = $self -> {'subtools'} -> [0];
414 if ( defined $sub_print_results ) {
415 sub get_dim {
416 my $arr = shift;
417 my $dim = shift;
418 my $size_ref = shift;
419 $dim++;
420 if ( defined $arr and ref($arr) eq 'ARRAY' ) {
421 push( @{$size_ref}, scalar @{$arr} );
422 ( $dim, $size_ref ) = get_dim( $arr->[0], $dim, $size_ref );
424 return ( $dim, $size_ref );
426 sub format_value {
427 my $val = shift;
428 if ( not defined $val or $val eq '' ) {
429 return sprintf("%10s",$PsN::output_style).',';
430 } else {
431 $_ = $val;
432 my $nodot = /.*\..*/ ? 0 : 1;
433 $_ =~ s/\.//g;
434 if ( /.*\D+.*/ or $nodot) {
435 return sprintf("%10s",$val).',';
436 } else {
437 return sprintf("%10.5f",$val).',';
441 debug -> die( message => "No results_file defined" )
442 unless ( defined $self -> {'results_file'} );
443 open ( RES, ">".$self -> {'directory'}.$self -> {'results_file'} );
444 if ( defined $self -> {'results'} ) {
445 my @all_results = @{$self -> {'results'}};
446 for ( my $i = 0; $i <= $#all_results; $i++ ) {
447 if ( defined $all_results[$i]{'own'} ) {
448 my @my_results = @{$all_results[$i]{'own'}};
449 for ( my $j = 0; $j <= $#my_results; $j++ ) {
450 # These size estimates include the problem and sub_problem dimensions:
451 my ( $ldim, $lsize_ref ) = get_dim( $my_results[$j]{'labels'}, -1, [] );
452 my ( $vdim, $vsize_ref ) = get_dim( $my_results[$j]{'values'}, -1, [] );
453 print RES $my_results[$j]{'name'},"\n" if ( $vdim > 1 );
454 # Loop the problems:
455 if ( defined $my_results[$j]{'values'} and
456 scalar @{$my_results[$j]{'values'}} >= 0 ) {
457 my @values = @{$my_results[$j]{'values'}};
458 my @labels;
459 if ( defined $my_results[$j]{'labels'} and
460 scalar @{$my_results[$j]{'labels'}} >= 0 ) {
461 @labels = @{$my_results[$j]{'labels'}};
463 # Print Header Labels
464 if ( $ldim == 0 ) {
465 my $label = \@labels;
466 print RES ','.format_value($label),"\n";
467 } elsif ( $ldim == 2 ) {
468 print RES ',';
469 for ( my $n = 0; $n < scalar @{$labels[1]}; $n++ ) {
470 my $label = $labels[1][$n];
471 print RES format_value($label);
474 print RES "\n";
475 # print RES "\n" if ( scalar @{$labels[1]} );
477 # Print the values:
478 if ( $vdim == 0 ) {
479 print RES ','.format_value(\@values),"\n";
480 } elsif ( $vdim == 1 ) {
481 for ( my $m = 0; $m < scalar @values; $m++ ) {
482 my $label = $labels[$m];
483 print RES ','.format_value($label);
484 my $val = $values[$m];
485 print RES ','.format_value($val),"\n";
487 } elsif ( $vdim == 2 ) {
488 for ( my $m = 0; $m < scalar @values; $m++ ) {
489 my $label;
490 if ( $ldim == 1 ) {
491 $label = $labels[$m];
492 } elsif ( $ldim == 2 ) {
493 $label = $labels[0][$m];
495 print RES format_value($label);
496 if( defined $values[$m] ){
497 for ( my $n = 0; $n < scalar @{$values[$m]}; $n++ ) {
498 print RES format_value($values[$m][$n]);
501 print RES "\n";
509 close( RES );
510 } else {
511 debug -> warn( level => 2,
512 message => "No subtools defined".
513 ", using default printing routine" );
516 end print_results
518 # }}} print_results
520 # {{{ post_fork_analyze
522 start post_fork_analyze
524 # Runs the post_fork_analyze specific for the subtool
525 my $sub_post_fork_analyze = $self -> {'subtools'} -> [0];
526 if ( defined $sub_post_fork_analyze ) {
527 $sub_post_fork_analyze = $sub_post_fork_analyze.'_post_fork_analyze';
528 if ( defined( $self -> can( $sub_post_fork_analyze ) ) ) {
529 $self -> $sub_post_fork_analyze;
532 if ( defined $self -> {'results_file'} ) {
533 #$self -> print_results;
536 end post_fork_analyze
538 # }}} post_fork_analyze
540 # {{{ setup
542 start setup
544 $self -> _prepare_model( model_number => $model_number );
546 # Run the setup specific for the subtool
547 my $sub_setup = $self -> {'subtools'} -> [0];
548 if ( defined $sub_setup ) {
549 $sub_setup = $sub_setup.'_setup';
550 $self -> $sub_setup( model_number => $model_number );
552 end setup
554 # }}} setup
556 # {{{ _make_dir
558 start _make_dir
560 mkdir( $self -> {'directory'} ) unless ( -e $self -> {'directory'} );
562 end _make_dir
564 # }}} _make_dir
566 # {{{ run
568 # {{{ documentation
570 # results structure:
572 # {results}
574 # |->[0] First model
575 # | |
576 # | |->{own} The results from this tool on the first model
577 # | | |
578 # | | |->[0]
579 # | | | |
580 # | | | |->{name} e.g. 'parameter.estimates'
581 # | | | |
582 # | | | |->{labels}
583 # | | | | |
584 # | | | | |->[0]... e.g. ['TH1', 'TH2', 'TH3'] indexed on problem and sub problem
585 # | | | | |->[1]
586 # | | | | |...
587 # | | | | |->[#problems]
588 # | | | |
589 # | | | |->{values}
590 # | | | |
591 # | | | |->[0] e.g. [0.21, 20.3, 3] indexed as above
592 # | | | |->[1]
593 # | | | |...
594 # | | | |->[#problems]
595 # | | |
596 # | | |->[1]
597 # | | | |
598 # | | | |->{name} e.g. 'standard.errors'
599 # | | | |->{labels}
600 # | | | |->{values}
601 # | |->{subtools} The results from the subtools on the first model
602 # | |
603 # | |->[0] First sub tool
604 # | | |
605 # | | |->[0] First model of the prepared models sent to the first sub tool
606 # | | | |
607 # | | | |->{own} The first sub tools results on the first model
608 # | | | | |
609 # | | | | |->[0] First result type
610 # | | | | | |
611 # | | | | | |->{name}
612 # | | | | | |->{labels}
613 # | | | | | |->{values}
614 # | | | | |
615 # | | | | |->[1] Second result type
616 # | | | | | |
617 # | | | | | |->{name}
618 # | | | | | |->{labels}
619 # | | | | | |->{values}
620 # | | | |->{subtools} Another tool level
621 # | | | | ...
622 # | | |->[1] Second model of the prepared models sent to the first sub tool
623 # | | | |
624 # | | | |->{own} The first sub tools results on the second model
625 # | | | | |
626 # | | | | |->[0] First result type
627 # | | | | | |
628 # | | | | | |->{name}
629 # | | | | | |->{labels}
630 # | | | | | |->{values}
631 # | | | | |
632 # | | | | |->[1] Second result type
633 # | | | | | |
634 # | | | | | |->{name}
635 # | | | | | |->{labels}
636 # | | | | | |->{values}
637 # | | | |->{subtools} Another tool level
638 # | | | | ...
639 # | | | |...
640 # | | |->[#prepared models] Last model of the prepared models sent to the first sub tool
641 # | | | |
642 # | | | |->{own} The first sub tools results on the last model
643 # | | | | |
644 # | | | | |->[0] First result type
645 # | | | | | |
646 # | | | | | |->{name}
647 # | | | | | |->{labels}
648 # | | | | | |->{values}
649 # | | | | |
650 # | | | | |->[1] Second result type
651 # | | | | | |
652 # | | | | | |->{name}
653 # | | | | | |->{labels}
654 # | | | | | |->{values}
655 # | | | |->{subtools} Another tool level
656 # | | | | ...
657 # | |->[1] Second sub tool
658 # | |...
659 # | |->[#tools] Last sub tool
660 # |
661 # |->[1] Second model. All above repeated for this model.
662 # |...
663 # |->[#models] Last model. As above.
665 # Prepared_models structure:
667 # {prepared_models}
669 # |->[0] First model
670 # | |
671 # | |->{own} The prepared models of this tool using the first model as base
672 # | | |
673 # | | |->[0] First prep model
674 # | | |->[1] Second prep model
675 # | | |...
676 # | | |->[#prep_models] Last prep model
677 # | |
678 # | |->{subtools} The prepared models of the subtools on the first model. Only one sub tool per prepared model above.
679 # | |
680 # | |->[0] First model of the models (prepared above) sent to the first sub tool
681 # | | |
682 # | | |->{own} The first sub tools prepared models on its first model
683 # | | | |
684 # | | | |->[0] First prep model
685 # | | | |->[1] Second prep model
686 # | | | |...
687 # | | | |->[#prep_models]Last prep model
688 # | | |
689 # | | |->{subtools}
690 # | |
691 # | |->[1] Second model of the models (prepared above) sent to the first sub tool
692 # | | |
693 # | | |->{own} The first sub tools prepared models on its second model
694 # | | | |
695 # | | | |->[0] First prep model
696 # | | | |->[1] Second prep model
697 # | | | |...
698 # | | | |->[#prep_models]Last prep model
699 # | | |
700 # | | |->{subtools}
701 # | |
703 # }}}
705 start run
707 my $return_dir = getcwd();
708 chdir( $self -> {'directory'} );
710 $self -> pre_fork_setup;
712 my @models = @{$self -> {'models'}};
713 # Use the thread number of this tool level:
714 my $threads = ref( $self -> {'threads'} ) eq 'ARRAY' ?
715 $self -> {'threads'} -> [0] : $self -> {'threads'};
717 # No point in using more threads than models
718 $threads = $#models + 1 if ( $threads > $#models + 1);
720 # Currently parallel execution is not supported on windows platforms
721 $threads = 1 if( $Config{osname} eq 'MSWin32' );
723 # Create new forkmanager
724 my $pm = ext::Parallel::ForkManager -> new($threads) if ( $threads > 1 );
725 my $aborting = 0;
726 $pm -> run_on_finish( sub { my ( $pid, $exit_code, $ident ) = @_;
727 if( $exit_code ){
728 debug -> die( message => "Subtool died, exiting." );
730 } ) if ( $threads > 1 );
732 # Store some globals for single-thread mode to make each loop
733 # over the models see the same (fresh) prepared attributes as
734 # in the parallel mode.
735 my @pre_fork_tools;
737 # THREAD if ( $threads == 1 ) {
738 # THREAD if ( defined $self -> {'tools'} ) {
739 # THREAD @pre_fork_tools = @{$self -> {'tools'}};
740 # THREAD }
741 # THREAD }
743 # Loop over the models
744 for ( my $i = 1; $i <= scalar @models; $i++ ) {
745 # Spawn new processes
746 $pm -> start and next if ( $threads > 1 );
748 # model_number is a member that tells the tool which model
749 # it is currently working on.
750 $self -> model_number( $i );
752 # Reset some globals: (only needed for threads==1)
753 # THREAD if ( $threads == 1 && defined $self -> {'tools'}) {
754 # THREAD @{$self -> {'tools'}} = @pre_fork_tools;
755 # THREAD }
757 # Make sure that each process gets a unique random sequence:
758 random_set_seed_from_phrase(random_uniform_integer(1,0,10000*$i));
759 # srand(rand()*10000*$i);
761 # First, run setup
762 $self -> setup( model_number => $i );
764 # Run the subtools
765 my @tool_results = ();
766 my @tool_models = ();
767 if ( defined $self -> {'tools'} ) {
768 foreach my $tool (@{$self -> {'tools'}}){
769 # There is to date (2004-01-27 no tool that creates more than one internal
770 # tool. Hence this is a loop of one cycle. But to be general, again...
771 # Run the tool:
772 my( $returns, $prep_models ) = $tool -> run;
773 # push the sub tool's return values
774 push ( @tool_results, $returns );
775 if ( defined $prep_models ) {
776 push ( @tool_models, $prep_models );
777 } else {
778 'debug' -> warn(level => 1,
779 message => "inside " . ref($self) . " but no prep_models defined from $tool $i");
781 $self -> post_subtool_analyze;
784 } else {
785 debug -> warn( level => 2,
786 message => "No tool object to run from tool object." );
789 $self -> {'results'}[$i-1]{'subtools'}= \@tool_results;
790 $self -> {'prepared_models'}[$i-1]{'subtools'} = \@tool_models;
792 # Analyze the results
793 $self -> analyze( model_number => $i );
795 Storable::store( $self -> {'prepared_models'},
796 $self -> {'directory'}."/m$i/prepared_models.log" );
797 if ( $threads > 1 ) {
798 Storable::store( $self -> {'results'},
799 $self -> {'directory'}."/m$i/results.log" );
800 # Maybe redundant to transfer back both prepared_models as well as tools
802 # Actually, by principle everything interesting for
803 # a parent should be placed in "results" or possibly
804 # "prepared_models".
806 #Storable::store( $self -> {'tools'},
807 # $self -> {'directory'}."/m$i/tools.log" );
809 $pm -> finish if ( $threads > 1 );
811 $pm -> wait_all_children if ( $threads > 1 );
813 for( my $i = 1; $i <= scalar @{$self -> {'models'}}; $i++ ) {
814 my @prepared_models = @{Storable::retrieve( $self -> {'directory'}.
815 "/m$i/prepared_models.log" )};
816 unlink( $self -> {'directory'} . "/m$i/prepared_models.log" );
817 $self->{'prepared_models'}[$i-1] = $prepared_models[$i-1];
820 if ( $threads > 1 ) {
821 for( my $i = 1; $i <= scalar @{$self -> {'models'}}; $i++ ) {
822 my @model_results = @{Storable::retrieve( $self -> {'directory'}.
823 "/m$i/results.log" )};
824 # It is important to keep the number of dimensions: push the first value, not the
825 # whole array!
826 $self->{'results'}[$i-1] = $model_results[$i-1];
828 # Read comment aboud tools.log near storable above.
830 #push( @{$self -> {'tools'}},
831 # Storable::retrieve( $self -> {'directory'}.
832 # "/m$i/tools.log" ) );
837 # Perform analyses that need to be done after all models have
838 # been run and processed. Also write a result file if one is
839 # defined.
840 $self -> post_fork_analyze;
842 chdir($return_dir);
843 if( $self -> {'clean'} >= 3 ){
845 my $top_dir = $self -> {'directory'};
846 foreach my $dir ( <$top_dir/m*> ){
847 if( $dir =~ /m[0123456789]+/ ){
848 unlink( <$dir/*> );
849 rmdir( $dir );
853 if( not $self -> {'top_tool'} ){
854 my $dir = $self -> {'directory'};
855 unlink( <$dir/*> );
856 rmdir( $dir );
859 # @results = @{$self -> {'results'}};
860 # @prepared_models = @{$self -> {'prepared_models'}};
862 end run
864 # }}} run
866 # {{{ _prepare_model
868 start _prepare_model
871 my ($newdir, $newfile) = OSspecific::absolute_path( $self -> {'directory'} . '/m'.$model_number, '' );
872 debug -> warn( level => 2,
873 message => "Making directory\t\t" . $newdir );
874 mkdir( $newdir );
875 if ( defined $self -> models() ) {
876 my @models = @{$self -> models()};
877 if ( defined $models[$model_number - 1] ) {
878 my $model = $models[$model_number - 1];
879 # copy the msfi files
880 my @new_names;
881 if( defined $model -> msfi_names() ){
882 foreach my $msfi_files( @{$model -> msfi_names()} ){
883 foreach my $msfi_file( @{$msfi_files} ){
884 if ( defined $msfi_file ) {
885 my ( $dir, $filename ) = OSspecific::absolute_path($model -> directory,
886 $msfi_file );
887 cp( $dir.$filename, $newdir.$filename );
888 push( @new_names, $filename );
889 } else {
890 push( @new_names, undef );
894 $model -> msfi_names( new_names => \@new_names );
899 end _prepare_model
901 # }}} _prepare_model
903 # {{{ analyze
905 start analyze
907 $self -> {'raw_results'}[$model_number-1] =
908 $self -> {'tools'} -> [0] -> raw_results if( defined $self -> {'tools'} -> [0] );
909 my $sub_analyze = $self -> {'subtools'} -> [0];
910 if ( defined $sub_analyze ) {
911 $sub_analyze = $sub_analyze.'_analyze';
912 if( defined $self -> can( $sub_analyze ) ){
913 $self -> $sub_analyze( model_number => $model_number );
917 end analyze
919 # }}} analyze
921 # {{{ _modelfit_raw_results_callback
923 start _modelfit_raw_results_callback
925 my ($dir,$file) =
926 OSspecific::absolute_path( $self -> {'directory'},
927 $self -> {'raw_results_file'}[$model_number-1] );
928 my ($dir,$nonp_file) =
929 OSspecific::absolute_path( $self -> {'directory'},
930 $self -> {'raw_nonp_file'}[$model_number-1] );
931 $subroutine = sub {
932 my $modelfit = shift;
933 $modelfit -> raw_results_file( $dir.$file );
934 $modelfit -> raw_nonp_file( $dir.$nonp_file );
936 return $subroutine;
938 end _modelfit_raw_results_callback
940 # }}} _modelfit_raw_results_callback
942 # {{{ read_raw_results
943 start read_raw_results
945 undef $self -> {'raw_results_header'};
946 for ( my $i = 1; $i <= scalar @{$self->{'models'}}; $i++ ) { # All models
947 if ( -e $self -> {'directory'}.'raw_results'.$i.'.csv' ) {
948 open( RRES, $self -> {'directory'}.'raw_results'.$i.'.csv' );
949 my @file = <RRES>;
950 close( RRES );
951 map { chomp; my @tmp = split(',',$_); $_ = \@tmp } @file ;
952 $self -> {'raw_results_header'} -> [$i-1] = shift @file;
953 $self -> {'raw_results'} -> [$i-1] = \@file;
955 if ( -e $self -> {'directory'}.'raw_nonp_results'.$i.'.csv' ) {
956 open( RRES, $self -> {'directory'}.'raw_nonp_results'.$i.'.csv' );
957 my @file = <RRES>;
958 close( RRES );
959 map { chomp; my @tmp = split(',',$_); $_ = \@tmp } @file ;
960 $self -> {'raw_nonp_results'} -> [$i-1] = \@file;
964 end read_raw_results
965 # }}} read_raw_results
967 # {{{ create_raw_results_rows
968 start create_raw_results_rows
971 unless( $model -> outputs -> [0] -> parsed ){
972 $model -> outputs -> [0] -> abort_on_fail(0);
973 $model -> outputs -> [0] -> _read_problems;
976 if( $model -> outputs -> [0] -> parsed_successfully ){
977 my @probs = @{$model -> outputs -> [0] -> problem_structure};
978 my $np = scalar @probs; # #probs
979 my $model_row = 0;
980 # ------------ Push model, problem and sub-problem numbers --------------
982 for( my $j = 0; $j < $np; $j++ ) {
983 my $ns = $probs[$j]; # #subprobs
984 for( my $k = 0; $k < $ns; $k++ ) {
985 my $row = $model_row++;
986 push( @{$return_rows[$row]}, ($model_number,($j+1),($k+1)) );
990 # --------------------- Loop all result categories ----------------------
992 foreach my $category ( @{$self -> {'raw_results_header'}},'npomega' ){
993 next if( $category eq 'model' or $category eq 'problem' or $category eq 'subproblem' );
994 my ( $accessor, $res );
996 # {{{ Get the values for the category
998 if ( $category eq 'theta' or $category eq 'omega' or $category eq 'sigma' or
999 $category eq 'setheta' or $category eq 'seomega' or $category eq 'sesigma' or
1000 $category eq 'npomega' or $category eq 'eigen' ) {
1001 $accessor = $category.'s';
1002 $res = $model -> {'outputs'} -> [0] -> $accessor;
1004 if( defined $res and ref $res eq 'ARRAY' and $category ne 'npomega' ){
1005 my $prob_num = 0;
1006 foreach my $prob ( @{$res} ){
1008 if( defined $prob and ref $prob eq 'ARRAY' and
1009 defined $prob -> [0] and ref $prob -> [0] eq 'ARRAY' and
1010 defined $return_rows[$prob_num] ){
1012 # The last check in the IF above could be put there to
1013 # avoid a bug. If "output::problem_structure" is
1014 # correct and output::accessor is correct,
1015 # $return_rows[$prob_num] should allways be
1016 # defined. TODO
1018 my $tmp = scalar @{$return_rows[$prob_num]} . ",". scalar @{$prob -> [0]};
1019 $raw_line_structure -> {$model_number} -> { $category } = $tmp;
1021 $prob_num++;
1025 } elsif ( $category eq 'shrinkage_etas' ) {
1026 # Shrinkage does not work for subproblems right now.
1027 $res = $model -> eta_shrinkage;
1028 } elsif ( $category eq 'shrinkage_wres' ) {
1029 # Shrinkage does not work for subproblems right now.
1030 # get ofv just to get the prob-subp structure
1031 $res = $model -> wres_shrinkage;
1032 } else {
1033 $accessor = $category;
1034 $res = $model -> {'outputs'} -> [0] -> $accessor;
1037 # }}} Get the values for the category
1038 my $return_array_ref;
1039 if( $category eq 'npomega' ){
1040 $return_array_ref = \@nonp_return_rows;
1041 next;
1042 } else {
1043 $return_array_ref = \@return_rows;
1046 my $model_row = 0; # Need to mask previous definition of model_row
1048 if( defined $res ) {
1049 for( my $j = 0; $j < $np; $j++ ) {
1050 my $ns = $probs[$j]; # #subprobs
1051 if( defined $res -> [$j] ) {
1052 for( my $k = 0; $k < $ns; $k++ ) {
1053 my $row = $model_row++;
1054 if( ref $res -> [$j] eq 'ARRAY' ){
1055 if( defined $res -> [$j][$k] ) {
1056 if ( ref $res -> [$j][$k] eq 'ARRAY' ) {
1057 push( @{$return_array_ref -> [$row]}, @{$res -> [$j][$k]} );
1058 push( @{$return_array_ref -> [$row]},
1059 (undef) x ($max_hash -> {$category} - scalar @{$res -> [$j][$k]}) );
1060 } else {
1061 push( @{$return_array_ref -> [$row]}, $res -> [$j][$k] );
1063 } else {
1064 push( @{$return_array_ref -> [$row]},
1065 (undef) x $max_hash -> {$category} );
1067 } else {
1068 push( @{$return_array_ref -> [$row]},
1069 $res -> [$j] );
1072 } else {
1074 # {{{ Push undefs for missing subprobs
1076 for( my $k = 0; $k < $ns; $k++ ) {
1077 my $row = $model_row++;
1078 push( @{$return_array_ref -> [$row]},
1079 (undef) x $max_hash -> {$category} );
1082 # }}} Push undefs for missing subprobs
1087 } else {
1089 # {{{ Push undefs for missing probs/subprobs
1091 for( my $j = 0; $j < $np; $j++ ) {
1092 my $ns = $probs[$j]; # #subprobs
1093 for( my $k = 0; $k < $ns; $k++ ) {
1094 my $row = $model_row++;
1095 push( @{$return_array_ref -> [$row]},
1096 (undef) x $max_hash -> {$category} );
1100 # }}} Push undefs for missing probs/subprobs
1106 } else {
1107 # Output not parsed successfully.
1108 push( @{$return_rows[0]}, ($model_number+1).
1109 ",run failed - Could not parse the output file: ".
1110 $model -> [0] -> {'outputs'} -> [0] -> filename );
1113 end create_raw_results_rows
1114 # }}}
1116 # {{{ post_subtool_analyze
1118 start post_subtool_analyze
1120 my $sub_analyze = $self -> {'subtools'} -> [0];
1121 if ( defined $sub_analyze ) {
1122 $sub_analyze = $sub_analyze.'_post_subtool_analyze';
1123 if( defined $self -> can( $sub_analyze ) ){
1124 $self -> $sub_analyze( model_number => $model_number );
1128 end post_subtool_analyze
1130 # }}} analyze
1132 # {{{ harvest_output
1134 start harvest_output
1137 # harvest_output is a complement to AUTOLOAD below. AUTOLOAD is
1138 # currently used to find the AUTOLOAD:ed accessor in any
1139 # existing subtool, model, data or outputobject. It is
1140 # inefficient in that it will have to be called for once for
1141 # each accessor. harvest_output will take a list of accessors
1142 # that it will search for in each object, saving time and
1143 # foremost; memory. Also it will take arguments such as
1144 # "search_models", "search_subtools" that will make things more
1145 # efficient if you know where to search.
1147 unless( $search_models + $search_output + $search_data <= 1 ){
1148 'debug' -> die( message => "This is a PsN bug: Only one of the 'search_' options can and must be specified.".
1149 "\t search_models: $search_models\n".
1150 "\t search_data: $search_data\n".
1151 "\t search_output: $search_output");
1154 if ( $search_subtools ) {
1155 'debug' -> warn( level => 1,
1156 message => "\n\nSearching subtools, which is a very untested functionality!!\n\n" );
1158 # if ( defined $self -> {'tools'} ) {
1159 # my @tools = @{$self -> {'tools'}};
1160 # foreach my $tool_ref ( @tools ) {
1161 # foreach my $tool ( @{$tool_ref} ) {
1162 # if ( $tool -> can( $accessor ) ) {
1163 # push( @result, $tool -> $accessor( %accessor_parameters ) );
1164 # } else {
1165 # 'debug' -> die(message => "Accessor $accessor is not available in the tool " . ref($tool) );
1169 # } else {
1170 # 'debug' -> warn( level => 1,
1171 # message => "Supposed to be run by the sub tools but no sub tools were defined" );
1174 } else {
1176 sub models_traverse2 {
1177 my %parameters = @_;
1178 my @models = $parameters{'models'} ? @{$parameters{'models'}} : ();
1179 my $search_models = $parameters{'search_models'};
1180 my $search_output = $parameters{'search_output'};
1181 my $search_data = $parameters{'search_data'};
1182 my $accessor_parameters = $parameters{'accessor_parameters'};
1183 my $accessors = $parameters{'accessors'};
1184 my %results;
1186 for( my $i = 0; $i < scalar (@models); $i++ ){
1188 foreach my $model ( @{$models[$i]{'own'}} ) {
1190 foreach my $accessor( @{$accessors} ) {
1192 if( $search_models and $model -> can( $accessor ) ) {
1193 push( @{$results{$accessor}[$i]{'own'}}, $model -> $accessor( %{$accessor_parameters} ) );
1195 } elsif( $search_data and $model -> datas -> [0] -> can( $accessor ) ) {
1196 push( @{$results{$accessor}[$i]{'own'}}, $model -> datas -> [0] -> $accessor( %{$accessor_parameters} ) );
1198 } elsif( $search_output and $model -> outputs -> [0] -> can( $accessor ) ) {
1199 push( @{$results{$accessor}[$i]{'own'}}, $model -> outputs -> [0] -> $accessor( %{$accessor_parameters} ) );
1201 } else {
1202 'debug' -> die( message => "Neither model, data, output have a method for $accessor" );
1205 if ( defined $models[$i]{'subtools'} ) {
1206 push( @{$results{$accessor}[$i]{'subtools'}}, models_traverse2( models => $models[$i]{'subtools'} ) );
1210 if( $search_data ){
1211 $model -> datas -> [0] -> flush();
1213 if( $search_output ){
1214 $model -> outputs -> [0] -> flush();
1219 return \%results;
1223 my @models;
1225 if ( $search_original_models ) {
1226 @models = @{$self -> {'models'}};
1227 } elsif ( defined $self -> {'prepared_models'} ) {
1228 @models = @{$self -> {'prepared_models'}};
1229 } else {
1230 'debug' -> warn( level => 2,
1231 message => "Trying @accessors, but no prepared models available" );
1232 return {};
1235 %result = %{models_traverse2( models => \@models,
1236 search_models => $search_models,
1237 search_output => $search_output,
1238 search_data => $search_data,
1239 accessor_parameters => \%accessor_parameters,
1240 accessors => \@accessors )};
1243 end harvest_output
1245 # }}}
1247 # {{{ AUTOLOAD
1249 start AUTOLOAD
1251 debug -> warn( level => 2,
1252 message => "Caught method $AUTOLOAD" );
1253 debug -> warn( level => 2,
1254 message => "arguments: @_" );;
1255 my %parm = @_;
1256 my $original_models = $parm{'original_models'};
1257 delete( $parm{'original_models'} );
1258 my $class = $parm{'class'};
1259 $AUTOLOAD =~ s/.*://;
1260 return if $AUTOLOAD eq 'DESTROY';
1262 # TODO: Kolla att orginalmodellen körs med submetod i run!!!!! kolla också var resultaten
1263 # läggs!!!
1265 if ( $class =~ /tool::/ ) {
1266 delete( $parm{'mod_array'} );
1267 delete( $parm{'original_models'} );
1268 delete( $parm{'class'} );
1269 @_ = %parm;
1270 if ( defined $self -> {'tools'} ) {
1271 my @tools = @{$self -> {'tools'}};
1272 my $accessor = $AUTOLOAD;
1273 foreach my $tool_ref ( @tools ) {
1274 foreach my $tool ( @{$tool_ref} ) {
1275 if ( $tool -> can( $accessor ) ) {
1276 push( @result, $tool -> $accessor( @_ ) );
1277 } else {
1278 'debug' -> die(message => "Accessor $accessor is not available in the tool " . ref($tool) );
1282 } else {
1283 print "AUTOLOAD in ",ref($self)," caught tool $AUTOLOAD. It was ",
1284 "supposed to be run by the sub tools but no sub tools were defined\n";
1286 } else {
1287 my @models;
1288 my @prep_models;
1289 if ( $original_models ) {
1290 @models = @{$self -> {'models'}};
1291 } elsif ( defined $self -> {'prepared_models'} ) {
1292 'debug' -> warn(level => 1,
1293 message => "Using prepared models" );
1294 @prep_models = @{$self -> {'prepared_models'}};
1295 } else {
1296 print "WARNING: tool -> AUTOLOAD: Trying $AUTOLOAD, but no prepared models available\n";
1299 sub models_traverse {
1300 my %parm = @_;
1301 my $mod_array_ref = $parm{'mod_array'};
1302 my $class = $parm{'class'};
1303 delete( $parm{'mod_array'} );
1304 delete( $parm{'class'} );
1305 @_ = %parm;
1306 my @mod_array;
1307 @mod_array = defined $mod_array_ref ? @{$mod_array_ref} : ();
1308 my @inner_result = ();
1309 # my $i = 0;
1310 for ( my $i = 0; $i <= $#mod_array; $i++ ) {
1311 foreach my $model ( @{$mod_array[$i]{'own'}} ) {
1312 unless ( defined $class ) {
1313 my $mod_can = defined $model -> can( $AUTOLOAD ) ? 1 : 0;
1314 my $out_can = (defined $model -> outputs and
1315 defined $model -> outputs -> [0] and
1316 defined $model -> outputs -> [0] -> can($AUTOLOAD))
1317 ? 1 : 0;
1318 my $dat_can = (defined $model -> datas and
1319 defined $model -> datas -> [0] and
1320 defined $model -> datas -> [0] -> can($AUTOLOAD))
1321 ? 1 : 0;
1322 if ( ($mod_can + $out_can + $dat_can) > 1 ) {
1323 my $classes;
1324 $classes = 'model ' if $mod_can;
1325 $classes = $classes.'output ' if $out_can;
1326 $classes = $classes.'data ' if $dat_can;
1327 'debug' -> die( message => "Accessor $AUTOLOAD available in multiple classes: $classes" );
1329 if ( $mod_can + $out_can + $dat_can == 0 ) {
1330 'debug' -> die( message => "Accessor $AUTOLOAD is not available in any of the model, ".
1331 "output or data classes OR no output or data object available".
1332 " through this model" );
1334 if ( $mod_can ) {
1335 push( @{$inner_result[$i]{'own'}}, $model -> $AUTOLOAD( @_ ) );
1336 } elsif ( $out_can ) {
1337 push( @{$inner_result[$i]{'own'}}, $model -> outputs -> [0] -> $AUTOLOAD( @_ ) );
1338 } elsif ( $dat_can ) {
1339 push( @{$inner_result[$i]{'own'}}, $model -> datas -> [0] -> $AUTOLOAD( @_ ) );
1341 } else {
1342 if ( $class eq 'model' ) {
1343 push( @{$inner_result[$i]{'own'}}, $model -> $AUTOLOAD( @_ ) );
1344 } else {
1345 my $class_accessor = $class.'s';
1346 push( @{$inner_result[$i]{'own'}}, $model -> $class_accessor -> [0] -> $AUTOLOAD( @_ ) );
1350 # $i++;
1351 if ( defined $mod_array[$i]{'subtools'} ) {
1352 push( @{$inner_result[$i]{'subtools'}},
1353 models_traverse( mod_array => $mod_array[$i]{'subtools'},
1354 class => $class,
1355 %parm ) );
1358 return \@inner_result;
1360 if ( $original_models ) {
1361 debug -> warn( level => 2,
1362 message => "Traversing ".scalar $models[0]{'own'}." model(s)" );
1363 @result = @{models_traverse( mod_array => \@models,
1364 %parm )};
1365 } else {
1366 @result = @{models_traverse( mod_array => \@prep_models,
1367 %parm )};
1371 end AUTOLOAD
1373 # }}} AUTOLOAD