3 start include statements
4 use ext
::Parallel
::ForkManager
;
15 if ( $PsN::config
-> {'_'} -> {'use_database'} ) {
21 # }}} include statements
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.
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>
84 # href="model/problem/record/init_option.html">init_option
91 # For a full dexcription of the algorithm, see I<set_random_init>
92 # of the I<init_option> class.
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
110 # I<logfile> specifies the name of the logfile.
112 # If I<debug> is set to 1(true), (many!) debug messages will be
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
135 if ( defined $parm{'base_directory'} ) {
136 $this -> {'base_directory'} = $parm{'base_directory'};
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'} ) {
149 ( $this -> {'directory'}, $dummy ) = OSspecific
::absolute_path
( $parm{'directory'}, '');
152 $this -> {'directory'} =
153 OSspecific
::unique_path
( $tool_name.'_dir' ,
154 $this -> {'base_directory'} );
158 # Create my temporary directory
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" )
174 # Make sure that the filenames are absolute and collect 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" );
229 open( OLOG
, '>',$self -> {'directory'}.'object.txt' );
230 $Data::Dumper
::Maxdepth
= 1;
231 print OLOG Dumper
$self;
232 $Data::Dumper
::Maxdepth
= 0;
242 if( -e
$self -> {'directory'}.'object.txt' ) {
244 open( OLOG
, '<'.$self -> {'directory'}.'object.txt' );
247 for ( my $i = 1; $i < $#olog; $i++ ) {
248 $str = $str.$olog[$i];
251 my %tmp = eval( $str );
253 if( exists $tmp{'tool_id'} ) {
254 $self -> {'tool_id'} = $tmp{'tool_id'};
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`;
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});
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' )");
300 $sth = $dbh -> prepare
("INSERT INTO ".$PsN::config
-> {'_'} -> {'project'}.
301 ".tool (name,date,directory) ".
302 "VALUES ('$tool_name', '$date_time', '$dir_str' )");
305 $self -> {'tool_id'} = $sth->{'mysql_insertid'};
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});
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 )";
333 $values = $values."(".$self -> {'tool_id'}.", $model_id, 0 )";
337 $sth = $dbh -> prepare
( "INSERT INTO ".$PsN::config
-> {'_'} -> {'project'}.
338 ".tool_model $columns VALUES $values" );
340 $sth -> finish
if ( defined $sth );
343 end register_tm_relation
345 # }}} register_tm_relation
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};
375 # %{$all_parameters} = (%{$all_parameters}, %parameters);
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} );
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;
412 # Print results created by 'prepare_results' methods specific to the
413 # tools. prepare_results and print_results are usually called from
414 # the tool scripts (e.g. bin/bootstrap)
416 my $sub_print_results = $self -> {'subtools'} -> [0];
418 if ( defined $sub_print_results ) {
420 # Only if we have a subtool, which we allways do, 'modelfit' is as
421 # usual the inner tool in the basic case.
424 ### get_dim subroutine recurses through arrays of arrays and
425 ### returns the number of levels (assumes the same number of
426 ### levels in alls subarrays).
428 ### 1st argument is the reference to the toplevel array.
429 ### 2nd argument is a starting level.
430 ### 3rd argument is an array giving the size of the arrays at each
431 ### level (assuming same size arrays at each level)
436 my $size_ref = shift;
438 if ( defined $arr and ref($arr) eq 'ARRAY' ) {
439 push( @
{$size_ref}, scalar @
{$arr} );
440 ( $dim, $size_ref ) = get_dim
( $arr->[0], $dim, $size_ref );
442 return ( $dim, $size_ref );
445 ### format_value returns a string for a given number. If the value
446 ### is not defined it is returned as NaN or NA, depending on the
447 ### output_style configured. Numbers without decimals get 10
448 ### digits, Numbers with decimals get 10 digis and 5 decimal
453 if ( not defined $val or $val eq '' ) {
454 return sprintf("%10s",$PsN::out_miss_data
).',';
457 my $nodot = /.*\..*/ ?
0 : 1;
459 if ( /.*\D+.*/ or $nodot) {
460 return sprintf("%10s",$val).',';
462 return sprintf("%10.5f",$val).',';
468 ### format_label does the same thing as format value, but does not
469 ### print out "NA" or "NaN" in case of missing data.
473 if ( not defined $val or $val eq '' ) {
477 my $nodot = /.*\..*/ ?
0 : 1;
479 if ( /.*\D+.*/ or $nodot) {
480 return sprintf("%10s",$val).',';
482 return sprintf("%10.5f",$val).',';
487 ### The main part of the method will loop through the 'own'
488 ### results, each element of the 'own' array is a hash with three
491 ### 'name' of the result, will be used as header (only if
492 ### values are defined).
494 ### 'values' either a single value, a list of values or a table of
497 ### 'lables' either a single value(?), a list of values used as
498 ### header for the 'values' list or table. It can be a table, Then
499 ### the first row will be printed before each row in the values
500 ### table, and the second row will be the header.
502 debug
-> die( message
=> "No results_file defined" )
503 unless ( defined $self -> {'results_file'} );
505 open ( RES
, ">".$self -> {'directory'}.$self -> {'results_file'} );
507 if ( defined $self -> {'results'} ) {
508 my @all_results = @
{$self -> {'results'}};
510 for ( my $i = 0; $i <= $#all_results; $i++ ) {
511 if ( defined $all_results[$i]{'own'} ) {
512 my @my_results = @
{$all_results[$i]{'own'}};
514 for ( my $j = 0; $j <= $#my_results; $j++ ) {
515 # These size estimates include the problem and sub_problem dimensions:
516 my ( $ldim, $lsize_ref ) = get_dim
( $my_results[$j]{'labels'}, -1, [] );
517 my ( $vdim, $vsize_ref ) = get_dim
( $my_results[$j]{'values'}, -1, [] );
518 print RES
$my_results[$j]{'name'},"\n" if ( $vdim > 1 );
520 if ( defined $my_results[$j]{'values'} and
521 scalar @
{$my_results[$j]{'values'}} >= 0 ) {
522 my @values = @
{$my_results[$j]{'values'}};
524 if ( defined $my_results[$j]{'labels'} and
525 scalar @
{$my_results[$j]{'labels'}} >= 0 ) {
526 @labels = @
{$my_results[$j]{'labels'}};
529 # Print Header Labels
531 my $label = \
@labels;
532 print RES
','.format_label
($label),"\n";
533 } elsif ( $ldim == 2 ) {
535 for ( my $n = 0; $n < scalar @
{$labels[1]}; $n++ ) {
536 my $label = $labels[1][$n];
537 print RES format_label
($label);
542 # Print the values (with labels on each row if ldim == 2:
544 print RES
','.format_value
(\
@values),"\n";
545 } elsif ( $vdim == 1 ) {
546 for ( my $m = 0; $m < scalar @values; $m++ ) {
547 my $label = $labels[$m];
548 print RES
','.format_label
($label);
549 my $val = $values[$m];
550 print RES
','.format_value
($val),"\n";
552 } elsif ( $vdim == 2 ) {
553 for ( my $m = 0; $m < scalar @values; $m++ ) {
556 $label = $labels[$m];
557 } elsif ( $ldim == 2 ) {
558 $label = $labels[0][$m];
560 print RES format_label
($label);
561 if( defined $values[$m] ){
562 for ( my $n = 0; $n < scalar @
{$values[$m]}; $n++ ) {
563 print RES format_value
($values[$m][$n]);
576 debug
-> warn( level
=> 2,
577 message
=> "No subtools defined".
578 ", using default printing routine" );
585 # {{{ post_fork_analyze
587 start post_fork_analyze
589 # Runs the post_fork_analyze specific for the subtool
590 my $sub_post_fork_analyze = $self -> {'subtools'} -> [0];
591 if ( defined $sub_post_fork_analyze ) {
592 $sub_post_fork_analyze = $sub_post_fork_analyze.'_post_fork_analyze';
593 if ( defined( $self -> can
( $sub_post_fork_analyze ) ) ) {
594 $self -> $sub_post_fork_analyze;
597 if ( defined $self -> {'results_file'} ) {
598 #$self -> print_results;
601 end post_fork_analyze
603 # }}} post_fork_analyze
609 $self -> _prepare_model
( model_number
=> $model_number );
611 # Run the setup specific for the subtool
612 my $sub_setup = $self -> {'subtools'} -> [0];
613 if ( defined $sub_setup ) {
614 $sub_setup = $sub_setup.'_setup';
615 $self -> $sub_setup( model_number
=> $model_number );
625 mkdir( $self -> {'directory'} ) unless ( -e
$self -> {'directory'} );
641 # | |->{own} The results from this tool on the first model
645 # | | | |->{name} e.g. 'parameter.estimates'
649 # | | | | |->[0]... e.g. ['TH1', 'TH2', 'TH3'] indexed on problem and sub problem
652 # | | | | |->[#problems]
656 # | | | |->[0] e.g. [0.21, 20.3, 3] indexed as above
659 # | | | |->[#problems]
663 # | | | |->{name} e.g. 'standard.errors'
666 # | |->{subtools} The results from the subtools on the first model
668 # | |->[0] First sub tool
670 # | | |->[0] First model of the prepared models sent to the first sub tool
672 # | | | |->{own} The first sub tools results on the first model
674 # | | | | |->[0] First result type
676 # | | | | | |->{name}
677 # | | | | | |->{labels}
678 # | | | | | |->{values}
680 # | | | | |->[1] Second result type
682 # | | | | | |->{name}
683 # | | | | | |->{labels}
684 # | | | | | |->{values}
685 # | | | |->{subtools} Another tool level
687 # | | |->[1] Second model of the prepared models sent to the first sub tool
689 # | | | |->{own} The first sub tools results on the second model
691 # | | | | |->[0] First result type
693 # | | | | | |->{name}
694 # | | | | | |->{labels}
695 # | | | | | |->{values}
697 # | | | | |->[1] Second result type
699 # | | | | | |->{name}
700 # | | | | | |->{labels}
701 # | | | | | |->{values}
702 # | | | |->{subtools} Another tool level
705 # | | |->[#prepared models] Last model of the prepared models sent to the first sub tool
707 # | | | |->{own} The first sub tools results on the last model
709 # | | | | |->[0] First result type
711 # | | | | | |->{name}
712 # | | | | | |->{labels}
713 # | | | | | |->{values}
715 # | | | | |->[1] Second result type
717 # | | | | | |->{name}
718 # | | | | | |->{labels}
719 # | | | | | |->{values}
720 # | | | |->{subtools} Another tool level
722 # | |->[1] Second sub tool
724 # | |->[#tools] Last sub tool
726 # |->[1] Second model. All above repeated for this model.
728 # |->[#models] Last model. As above.
730 # Prepared_models structure:
736 # | |->{own} The prepared models of this tool using the first model as base
738 # | | |->[0] First prep model
739 # | | |->[1] Second prep model
741 # | | |->[#prep_models] Last prep model
743 # | |->{subtools} The prepared models of the subtools on the first model. Only one sub tool per prepared model above.
745 # | |->[0] First model of the models (prepared above) sent to the first sub tool
747 # | | |->{own} The first sub tools prepared models on its first model
749 # | | | |->[0] First prep model
750 # | | | |->[1] Second prep model
752 # | | | |->[#prep_models]Last prep model
756 # | |->[1] Second model of the models (prepared above) sent to the first sub tool
758 # | | |->{own} The first sub tools prepared models on its second model
760 # | | | |->[0] First prep model
761 # | | | |->[1] Second prep model
763 # | | | |->[#prep_models]Last prep model
772 my $return_dir = getcwd
();
773 chdir( $self -> {'directory'} );
775 $self -> pre_fork_setup
;
777 my @models = @
{$self -> {'models'}};
778 # Use the thread number of this tool level:
779 my $threads = ref( $self -> {'threads'} ) eq 'ARRAY' ?
780 $self -> {'threads'} -> [0] : $self -> {'threads'};
782 # No point in using more threads than models
783 $threads = $#models + 1 if ( $threads > $#models + 1);
785 # Currently parallel execution is not supported on windows platforms
786 $threads = 1 if( $Config{osname
} eq 'MSWin32' );
788 # Create new forkmanager
789 my $pm = ext
::Parallel
::ForkManager
-> new
($threads) if ( $threads > 1 );
791 $pm -> run_on_finish
( sub { my ( $pid, $exit_code, $ident ) = @_;
793 debug
-> die( message
=> "Subtool died, exiting." );
795 } ) if ( $threads > 1 );
797 # Store some globals for single-thread mode to make each loop
798 # over the models see the same (fresh) prepared attributes as
799 # in the parallel mode.
802 # THREAD if ( $threads == 1 ) {
803 # THREAD if ( defined $self -> {'tools'} ) {
804 # THREAD @pre_fork_tools = @{$self -> {'tools'}};
808 # Loop over the models
809 for ( my $i = 1; $i <= scalar @models; $i++ ) {
810 # Spawn new processes
811 $pm -> start
and next if ( $threads > 1 );
813 # model_number is a member that tells the tool which model
814 # it is currently working on.
815 $self -> model_number
( $i );
817 # Reset some globals: (only needed for threads==1)
818 # THREAD if ( $threads == 1 && defined $self -> {'tools'}) {
819 # THREAD @{$self -> {'tools'}} = @pre_fork_tools;
822 # Make sure that each process gets a unique random sequence:
823 random_set_seed_from_phrase
(random_uniform_integer
(1,0,10000*$i));
824 # srand(rand()*10000*$i);
827 $self -> setup
( model_number
=> $i );
830 my @tool_results = ();
831 my @tool_models = ();
832 if ( defined $self -> {'tools'} ) {
833 foreach my $tool (@
{$self -> {'tools'}}){
834 # There is to date (2004-01-27 no tool that creates more than one internal
835 # tool. Hence this is a loop of one cycle. But to be general, again...
837 my( $returns, $prep_models ) = $tool -> run
;
838 # push the sub tool's return values
839 push ( @tool_results, $returns );
840 if ( defined $prep_models ) {
841 push ( @tool_models, $prep_models );
843 'debug' -> warn(level
=> 1,
844 message
=> "inside " . ref($self) . " but no prep_models defined from $tool $i");
846 $self -> post_subtool_analyze
;
850 debug
-> warn( level
=> 2,
851 message
=> "No tool object to run from tool object." );
854 $self -> {'results'}[$i-1]{'subtools'}= \
@tool_results;
855 $self -> {'prepared_models'}[$i-1]{'subtools'} = \
@tool_models;
857 # Analyze the results
858 $self -> analyze
( model_number
=> $i );
860 Storable
::store
( $self -> {'prepared_models'},
861 $self -> {'directory'}."/m$i/prepared_models.log" );
862 if ( $threads > 1 ) {
863 Storable
::store
( $self -> {'results'},
864 $self -> {'directory'}."/m$i/results.log" );
865 # Maybe redundant to transfer back both prepared_models as well as tools
867 # Actually, by principle everything interesting for
868 # a parent should be placed in "results" or possibly
871 #Storable::store( $self -> {'tools'},
872 # $self -> {'directory'}."/m$i/tools.log" );
874 $pm -> finish
if ( $threads > 1 );
876 $pm -> wait_all_children
if ( $threads > 1 );
878 for( my $i = 1; $i <= scalar @
{$self -> {'models'}}; $i++ ) {
879 my @prepared_models = @
{Storable
::retrieve
( $self -> {'directory'}.
880 "/m$i/prepared_models.log" )};
881 unlink( $self -> {'directory'} . "/m$i/prepared_models.log" );
882 $self->{'prepared_models'}[$i-1] = $prepared_models[$i-1];
885 if ( $threads > 1 ) {
886 for( my $i = 1; $i <= scalar @
{$self -> {'models'}}; $i++ ) {
887 my @model_results = @
{Storable
::retrieve
( $self -> {'directory'}.
888 "/m$i/results.log" )};
889 # It is important to keep the number of dimensions: push the first value, not the
891 $self->{'results'}[$i-1] = $model_results[$i-1];
893 # Read comment aboud tools.log near storable above.
895 #push( @{$self -> {'tools'}},
896 # Storable::retrieve( $self -> {'directory'}.
897 # "/m$i/tools.log" ) );
902 # Perform analyses that need to be done after all models have
903 # been run and processed. Also write a result file if one is
905 $self -> post_fork_analyze
;
908 if( $self -> {'clean'} >= 3 ){
910 my $top_dir = $self -> {'directory'};
911 foreach my $dir ( <$top_dir/m
*> ){
912 if( $dir =~ /m[0123456789]+/ ){
918 if( not $self -> {'top_tool'} ){
919 my $dir = $self -> {'directory'};
924 # @results = @{$self -> {'results'}};
925 # @prepared_models = @{$self -> {'prepared_models'}};
936 my ($newdir, $newfile) = OSspecific
::absolute_path
( $self -> {'directory'} . '/m'.$model_number, '' );
937 debug
-> warn( level
=> 2,
938 message
=> "Making directory\t\t" . $newdir );
940 if ( defined $self -> models
() ) {
941 my @models = @
{$self -> models
()};
942 if ( defined $models[$model_number - 1] ) {
943 my $model = $models[$model_number - 1];
944 # copy the msfi files
946 if( defined $model -> msfi_names
() ){
947 foreach my $msfi_files( @
{$model -> msfi_names
()} ){
948 foreach my $msfi_file( @
{$msfi_files} ){
949 if ( defined $msfi_file ) {
950 my ( $dir, $filename ) = OSspecific
::absolute_path
($model -> directory
,
952 cp
( $dir.$filename, $newdir.$filename );
953 push( @new_names, $filename );
955 push( @new_names, undef );
959 $model -> msfi_names
( new_names
=> \
@new_names );
972 $self -> {'raw_results'}[$model_number-1] =
973 $self -> {'tools'} -> [0] -> raw_results
if( defined $self -> {'tools'} -> [0] );
974 my $sub_analyze = $self -> {'subtools'} -> [0];
975 if ( defined $sub_analyze ) {
976 $sub_analyze = $sub_analyze.'_analyze';
977 if( defined $self -> can
( $sub_analyze ) ){
978 $self -> $sub_analyze( model_number
=> $model_number );
986 # {{{ _modelfit_raw_results_callback
988 start _modelfit_raw_results_callback
991 OSspecific
::absolute_path
( $self -> {'directory'},
992 $self -> {'raw_results_file'}[$model_number-1] );
993 my ($dir,$nonp_file) =
994 OSspecific
::absolute_path
( $self -> {'directory'},
995 $self -> {'raw_nonp_file'}[$model_number-1] );
997 my $modelfit = shift;
998 $modelfit -> raw_results_file
( $dir.$file );
999 $modelfit -> raw_nonp_file
( $dir.$nonp_file );
1003 end _modelfit_raw_results_callback
1005 # }}} _modelfit_raw_results_callback
1007 # {{{ read_raw_results
1008 start read_raw_results
1010 undef $self -> {'raw_results_header'};
1011 for ( my $i = 1; $i <= scalar @
{$self->{'models'}}; $i++ ) { # All models
1012 if ( -e
$self -> {'directory'}.'raw_results'.$i.'.csv' ) {
1013 open( RRES
, $self -> {'directory'}.'raw_results'.$i.'.csv' );
1016 map { chomp; my @tmp = split(',',$_); $_ = \
@tmp } @file ;
1017 $self -> {'raw_results_header'} -> [$i-1] = shift @file;
1018 $self -> {'raw_results'} -> [$i-1] = \
@file;
1020 if ( -e
$self -> {'directory'}.'raw_nonp_results'.$i.'.csv' ) {
1021 open( RRES
, $self -> {'directory'}.'raw_nonp_results'.$i.'.csv' );
1024 map { chomp; my @tmp = split(',',$_); $_ = \
@tmp } @file ;
1025 $self -> {'raw_nonp_results'} -> [$i-1] = \
@file;
1029 end read_raw_results
1030 # }}} read_raw_results
1032 # {{{ create_raw_results_rows
1033 start create_raw_results_rows
1036 unless( $model -> outputs
-> [0] -> parsed
){
1037 $model -> outputs
-> [0] -> abort_on_fail
(0);
1038 $model -> outputs
-> [0] -> _read_problems
;
1041 if( $model -> outputs
-> [0] -> parsed_successfully
){
1042 my @probs = @
{$model -> outputs
-> [0] -> problem_structure
};
1043 my $np = scalar @probs; # #probs
1045 # ------------ Push model, problem and sub-problem numbers --------------
1047 for( my $j = 0; $j < $np; $j++ ) {
1048 my $ns = $probs[$j]; # #subprobs
1049 for( my $k = 0; $k < $ns; $k++ ) {
1050 my $row = $model_row++;
1051 push( @
{$return_rows[$row]}, ($model_number,($j+1),($k+1)) );
1055 # --------------------- Loop all result categories ----------------------
1057 foreach my $category ( @
{$self -> {'raw_results_header'}},'npomega' ){
1058 next if( $category eq 'model' or $category eq 'problem' or $category eq 'subproblem' );
1059 my ( $accessor, $res );
1061 # {{{ Get the values for the category
1063 if ( $category eq 'theta' or $category eq 'omega' or $category eq 'sigma' or
1064 $category eq 'setheta' or $category eq 'seomega' or $category eq 'sesigma' or
1065 $category eq 'npomega' or $category eq 'eigen' ) {
1066 $accessor = $category.'s';
1067 $res = $model -> {'outputs'} -> [0] -> $accessor;
1068 } elsif ( $category eq 'shrinkage_etas' ) {
1069 # Shrinkage does not work for subproblems right now.
1070 $res = $model -> eta_shrinkage
;
1071 } elsif ( $category eq 'shrinkage_wres' ) {
1072 # Shrinkage does not work for subproblems right now.
1073 # get ofv just to get the prob-subp structure
1074 $res = $model -> wres_shrinkage
;
1076 $accessor = $category;
1077 $res = $model -> {'outputs'} -> [0] -> $accessor;
1080 # {{{ Create entry in raw_line_structure
1081 if( defined $res and ref $res eq 'ARRAY' and $category ne 'npomega' ){
1083 foreach my $prob ( @
{$res} ){
1084 if( defined $prob and ref $prob eq 'ARRAY' ){
1085 if( defined $prob -> [0] and ref $prob -> [0] eq 'ARRAY' and
1086 defined $return_rows[$prob_num] ){
1088 # The last check in the IF above could be put there to
1089 # avoid a bug. If "output::problem_structure" is
1090 # correct and output::accessor is correct,
1091 # $return_rows[$prob_num] should allways be
1094 my $tmp = scalar @
{$return_rows[$prob_num]} . ",". scalar @
{$prob -> [0]};
1095 $raw_line_structure -> {$model_number} -> { $category } = $tmp;
1097 } elsif( defined $prob -> [0] and defined $return_rows[$prob_num]) {
1098 my $tmp = scalar @
{$return_rows[$prob_num]} . ",1";
1099 $raw_line_structure -> {$model_number} -> { $category } = $tmp;
1108 # }}} Get the values for the category
1109 my $return_array_ref;
1110 if( $category eq 'npomega' ){
1111 $return_array_ref = \
@nonp_return_rows;
1114 $return_array_ref = \
@return_rows;
1117 my $model_row = 0; # Need to mask previous definition of model_row
1119 if( defined $res ) {
1120 for( my $j = 0; $j < $np; $j++ ) {
1121 my $ns = $probs[$j]; # #subprobs
1122 if( defined $res -> [$j] ) {
1123 for( my $k = 0; $k < $ns; $k++ ) {
1124 my $row = $model_row++;
1125 if( ref $res -> [$j] eq 'ARRAY' ){
1126 if( defined $res -> [$j][$k] ) {
1127 if ( ref $res -> [$j][$k] eq 'ARRAY' ) {
1128 push( @
{$return_array_ref -> [$row]}, @
{$res -> [$j][$k]} );
1129 push( @
{$return_array_ref -> [$row]},
1130 (undef) x
($max_hash -> {$category} - scalar @
{$res -> [$j][$k]}) );
1132 push( @
{$return_array_ref -> [$row]}, $res -> [$j][$k] );
1135 push( @
{$return_array_ref -> [$row]},
1136 (undef) x
$max_hash -> {$category} );
1139 push( @
{$return_array_ref -> [$row]},
1145 # {{{ Push undefs for missing subprobs
1147 for( my $k = 0; $k < $ns; $k++ ) {
1148 my $row = $model_row++;
1149 push( @
{$return_array_ref -> [$row]},
1150 (undef) x
$max_hash -> {$category} );
1153 # }}} Push undefs for missing subprobs
1160 # {{{ Push undefs for missing probs/subprobs
1162 for( my $j = 0; $j < $np; $j++ ) {
1163 my $ns = $probs[$j]; # #subprobs
1164 for( my $k = 0; $k < $ns; $k++ ) {
1165 my $row = $model_row++;
1166 push( @
{$return_array_ref -> [$row]},
1167 (undef) x
$max_hash -> {$category} );
1171 # }}} Push undefs for missing probs/subprobs
1177 $raw_line_structure -> {$model_number} -> {'line_numbers'} = scalar @return_rows;
1180 # Output not parsed successfully.
1181 $return_rows[0] = [ $model_number.
1182 ",run failed - Could not parse the output file: ".
1183 $model -> {'outputs'} -> [0] -> filename
];
1186 end create_raw_results_rows
1189 # {{{ post_subtool_analyze
1191 start post_subtool_analyze
1193 my $sub_analyze = $self -> {'subtools'} -> [0];
1194 if ( defined $sub_analyze ) {
1195 $sub_analyze = $sub_analyze.'_post_subtool_analyze';
1196 if( defined $self -> can
( $sub_analyze ) ){
1197 $self -> $sub_analyze( model_number
=> $model_number );
1201 end post_subtool_analyze
1205 # {{{ harvest_output
1207 start harvest_output
1210 # harvest_output is a complement to AUTOLOAD below. AUTOLOAD is
1211 # currently used to find the AUTOLOAD:ed accessor in any
1212 # existing subtool, model, data or outputobject. It is
1213 # inefficient in that it will have to be called for once for
1214 # each accessor. harvest_output will take a list of accessors
1215 # that it will search for in each object, saving time and
1216 # foremost; memory. Also it will take arguments such as
1217 # "search_models", "search_subtools" that will make things more
1218 # efficient if you know where to search.
1220 unless( $search_models + $search_output + $search_data <= 1 ){
1221 'debug' -> die( message
=> "This is a PsN bug: Only one of the 'search_' options can and must be specified.".
1222 "\t search_models: $search_models\n".
1223 "\t search_data: $search_data\n".
1224 "\t search_output: $search_output");
1227 if ( $search_subtools ) {
1228 'debug' -> warn( level
=> 1,
1229 message
=> "\n\nSearching subtools, which is a very untested functionality!!\n\n" );
1231 # if ( defined $self -> {'tools'} ) {
1232 # my @tools = @{$self -> {'tools'}};
1233 # foreach my $tool_ref ( @tools ) {
1234 # foreach my $tool ( @{$tool_ref} ) {
1235 # if ( $tool -> can( $accessor ) ) {
1236 # push( @result, $tool -> $accessor( %accessor_parameters ) );
1238 # 'debug' -> die(message => "Accessor $accessor is not available in the tool " . ref($tool) );
1243 # 'debug' -> warn( level => 1,
1244 # message => "Supposed to be run by the sub tools but no sub tools were defined" );
1249 sub models_traverse2
{
1250 my %parameters = @_;
1251 my @models = $parameters{'models'} ? @
{$parameters{'models'}} : ();
1252 my $search_models = $parameters{'search_models'};
1253 my $search_output = $parameters{'search_output'};
1254 my $search_data = $parameters{'search_data'};
1255 my $accessor_parameters = $parameters{'accessor_parameters'};
1256 my $accessors = $parameters{'accessors'};
1259 for( my $i = 0; $i < scalar (@models); $i++ ){
1261 foreach my $model ( @
{$models[$i]{'own'}} ) {
1263 foreach my $accessor( @
{$accessors} ) {
1265 if( $search_models and $model -> can
( $accessor ) ) {
1266 push( @
{$results{$accessor}[$i]{'own'}}, $model -> $accessor( %{$accessor_parameters} ) );
1268 } elsif( $search_data and $model -> datas
-> [0] -> can
( $accessor ) ) {
1269 push( @
{$results{$accessor}[$i]{'own'}}, $model -> datas
-> [0] -> $accessor( %{$accessor_parameters} ) );
1271 } elsif( $search_output and $model -> outputs
-> [0] -> can
( $accessor ) ) {
1272 push( @
{$results{$accessor}[$i]{'own'}}, $model -> outputs
-> [0] -> $accessor( %{$accessor_parameters} ) );
1275 'debug' -> die( message
=> "Neither model, data, output have a method for $accessor" );
1278 if ( defined $models[$i]{'subtools'} ) {
1279 push( @
{$results{$accessor}[$i]{'subtools'}}, models_traverse2
( models
=> $models[$i]{'subtools'} ) );
1284 $model -> datas
-> [0] -> flush
();
1286 if( $search_output ){
1287 $model -> outputs
-> [0] -> flush
();
1298 if ( $search_original_models ) {
1299 @models = @
{$self -> {'models'}};
1300 } elsif ( defined $self -> {'prepared_models'} ) {
1301 @models = @
{$self -> {'prepared_models'}};
1303 'debug' -> warn( level
=> 2,
1304 message
=> "Trying @accessors, but no prepared models available" );
1308 %result = %{models_traverse2
( models
=> \
@models,
1309 search_models
=> $search_models,
1310 search_output
=> $search_output,
1311 search_data
=> $search_data,
1312 accessor_parameters
=> \
%accessor_parameters,
1313 accessors
=> \
@accessors )};
1324 debug
-> warn( level
=> 2,
1325 message
=> "Caught method $AUTOLOAD" );
1326 debug
-> warn( level
=> 2,
1327 message
=> "arguments: @_" );;
1329 my $original_models = $parm{'original_models'};
1330 delete( $parm{'original_models'} );
1331 my $class = $parm{'class'};
1332 $AUTOLOAD =~ s/.*://;
1333 return if $AUTOLOAD eq 'DESTROY';
1335 # TODO: Kolla att orginalmodellen körs med submetod i run!!!!! kolla också var resultaten
1338 if ( $class =~ /tool::/ ) {
1339 delete( $parm{'mod_array'} );
1340 delete( $parm{'original_models'} );
1341 delete( $parm{'class'} );
1343 if ( defined $self -> {'tools'} ) {
1344 my @tools = @
{$self -> {'tools'}};
1345 my $accessor = $AUTOLOAD;
1346 foreach my $tool_ref ( @tools ) {
1347 foreach my $tool ( @
{$tool_ref} ) {
1348 if ( $tool -> can
( $accessor ) ) {
1349 push( @result, $tool -> $accessor( @_ ) );
1351 'debug' -> die(message
=> "Accessor $accessor is not available in the tool " . ref($tool) );
1356 print "AUTOLOAD in ",ref($self)," caught tool $AUTOLOAD. It was ",
1357 "supposed to be run by the sub tools but no sub tools were defined\n";
1362 if ( $original_models ) {
1363 @models = @
{$self -> {'models'}};
1364 } elsif ( defined $self -> {'prepared_models'} ) {
1365 'debug' -> warn(level
=> 1,
1366 message
=> "Using prepared models" );
1367 @prep_models = @
{$self -> {'prepared_models'}};
1369 print "WARNING: tool -> AUTOLOAD: Trying $AUTOLOAD, but no prepared models available\n";
1372 sub models_traverse
{
1374 my $mod_array_ref = $parm{'mod_array'};
1375 my $class = $parm{'class'};
1376 delete( $parm{'mod_array'} );
1377 delete( $parm{'class'} );
1380 @mod_array = defined $mod_array_ref ? @
{$mod_array_ref} : ();
1381 my @inner_result = ();
1383 for ( my $i = 0; $i <= $#mod_array; $i++ ) {
1384 foreach my $model ( @
{$mod_array[$i]{'own'}} ) {
1385 unless ( defined $class ) {
1386 my $mod_can = defined $model -> can
( $AUTOLOAD ) ?
1 : 0;
1387 my $out_can = (defined $model -> outputs
and
1388 defined $model -> outputs
-> [0] and
1389 defined $model -> outputs
-> [0] -> can
($AUTOLOAD))
1391 my $dat_can = (defined $model -> datas
and
1392 defined $model -> datas
-> [0] and
1393 defined $model -> datas
-> [0] -> can
($AUTOLOAD))
1395 if ( ($mod_can + $out_can + $dat_can) > 1 ) {
1397 $classes = 'model ' if $mod_can;
1398 $classes = $classes.'output ' if $out_can;
1399 $classes = $classes.'data ' if $dat_can;
1400 'debug' -> die( message
=> "Accessor $AUTOLOAD available in multiple classes: $classes" );
1402 if ( $mod_can + $out_can + $dat_can == 0 ) {
1403 'debug' -> die( message
=> "Accessor $AUTOLOAD is not available in any of the model, ".
1404 "output or data classes OR no output or data object available".
1405 " through this model" );
1408 push( @
{$inner_result[$i]{'own'}}, $model -> $AUTOLOAD( @_ ) );
1409 } elsif ( $out_can ) {
1410 push( @
{$inner_result[$i]{'own'}}, $model -> outputs
-> [0] -> $AUTOLOAD( @_ ) );
1411 } elsif ( $dat_can ) {
1412 push( @
{$inner_result[$i]{'own'}}, $model -> datas
-> [0] -> $AUTOLOAD( @_ ) );
1415 if ( $class eq 'model' ) {
1416 push( @
{$inner_result[$i]{'own'}}, $model -> $AUTOLOAD( @_ ) );
1418 my $class_accessor = $class.'s';
1419 push( @
{$inner_result[$i]{'own'}}, $model -> $class_accessor -> [0] -> $AUTOLOAD( @_ ) );
1424 if ( defined $mod_array[$i]{'subtools'} ) {
1425 push( @
{$inner_result[$i]{'subtools'}},
1426 models_traverse
( mod_array
=> $mod_array[$i]{'subtools'},
1431 return \
@inner_result;
1433 if ( $original_models ) {
1434 debug
-> warn( level
=> 2,
1435 message
=> "Traversing ".scalar $models[0]{'own'}." model(s)" );
1436 @result = @
{models_traverse
( mod_array
=> \
@models,
1439 @result = @
{models_traverse
( mod_array
=> \
@prep_models,