Added notes_on_cvs
[PsN.git] / lib / model_subs.pm
blob81b09f1b12c1d3eb696bae11b77ee7fce437b7b5
1 # TODO: All: 2004-09-06 Fix absolute paths for data and output files. (under both
2 # windows and unix)
4 # {{{ Include
6 start include statements
7 use Digest::MD5 'md5_hex';
8 use Cwd;
9 use File::Copy 'cp';
10 use Config;
11 use OSspecific;
12 use Storable;
13 use Data::Dumper;
14 use POSIX qw(ceil floor);
15 use model::shrinkage_module;
16 end include statements
18 # }}} include statements
20 # {{{ description, synopsis and see_also
22 # No method, just documentation
23 start description
25 =head1 Description
27 PsN::model is a Perl module for parsing and manipulating NONMEM model
28 files.
30 The model class is built around the NONMEM model file. This is an
31 ordinary ASCII text file that, except for the data, holds all
32 information needed for fitting a non-linear mixed effect model using
33 NONMEM. Typically, a model file contains specifications for a
34 pharmacokinetic and/or a pharmacodynamic model, initial estimates of
35 model parameters, boundaries for model parameters as well as details
36 about the data location and format.
38 =cut
40 end description
42 start synopsis
44 =head1 Synopsis
46 C<< use model; >>
48 C<< my $model_object = model -> new ( filename => 'pheno.mod' ); >>
50 =begin html
52 <pre>
54 =end html
56 $model_object -> initial_values ( parameter_type => 'theta',
57 parameter_numbers => [[1,3]],
58 new_values => [[1.2,34]] );
60 =begin html
62 </pre>
64 =end html
66 =cut
68 end synopsis
70 start see_also
72 =head1 See also
74 =begin html
76 <a HREF="data.html">data</a>, <a HREF="output.html">output</a>
78 =end html
80 =begin man
82 data, output
84 =end man
86 =cut
88 end see_also
90 =head1 Methods
92 =cut
94 # }}}
96 # {{{ new
98 =head2 new
100 Usage:
102 =for html <pre>
104 $model = model -> new( filename => 'run1.mod' )
106 =for html </pre>
108 This is the simplest and most common way to create a model
109 object and it requires a file on disk.
111 =for html <pre>
113 $model = model -> new( filename => 'run1.mod',
114 target => 'mem' )
116 =for html </pre>
118 If the target parameter is set to anything other than I<mem>
119 the output object (with file name given by the model
120 attribute I<outputfile>) and the data objects (identified by
121 the data file names in the $DATA NONMEM model file section)
122 will be initialized but will contain no information from
123 their files. If information from them are requiered later
124 on, they are read and parsed and the appropriate attributes
125 of the data and output objects are set.
127 =cut
129 start new
132 if ( defined $parm{'problems'} ) {
133 $this -> {'problems'} = $parm{'problems'};
134 } else {
135 ($this -> {'directory'}, $this -> {'filename'}) =
136 OSspecific::absolute_path( $this -> {'directory'}, $this -> {'filename'} );
137 $this -> _read_problems;
138 $this -> {'synced'} = 1;
141 if ( defined $parm{'active_problems'} ) {
142 $this -> {'active_problems'} = $parm{'active_problems'};
143 } elsif ( defined $this -> {'problems'} ) {
144 my @active = ();
145 for ( @{$this -> {'problems'}} ) {
146 push( @active, 1 );
148 $this -> {'active_problems'} = \@active;
151 if ( defined $this -> {'extra_data_files'} ){
152 for( my $i; $i < scalar @{$this -> {'extra_data_files'}}; $i++ ){
153 my ( $dir, $file ) = OSspecific::absolute_path( $this -> {'directory'}, $this -> {'extra_data_files'} -> [$i]);
154 $this -> {'extra_data_files'} -> [$i] = $dir . $file;
158 my $subroutine_files = $this -> subroutine_files;
159 if( defined $subroutine_files and scalar @{$subroutine_files} > 0 ){
160 push( @{$this -> {'extra_files'}}, @{$subroutine_files} );
163 if ( defined $this -> {'extra_files'} ){
164 for( my $i; $i < scalar @{$this -> {'extra_files'}}; $i++ ){
165 my ( $dir, $file ) = OSspecific::absolute_path( $this -> {'directory'}, $this -> {'extra_files'} -> [$i]);
166 $this -> {'extra_files'} -> [$i] = $dir . $file;
170 # Read datafiles, if any.
171 unless( defined $this -> {'datas'} and not $this -> {'quick_reload'} ){
172 my @idcolumns = @{$this -> idcolumns};
173 my @datafiles = @{$this -> datafiles('absolute_path' => 1)};
174 # my @datafiles = @{$this -> datafiles('absolute_path' => 0)};
175 for ( my $i = 0; $i <= $#datafiles; $i++ ) {
176 my $datafile = $datafiles[$i];
177 my $idcolumn = $idcolumns[$i];
178 my ( $cont_column, $wrap_column ) = $this -> {'problems'} -> [$i] -> cont_wrap_columns;
179 my $ignoresign = defined $this -> ignoresigns ? $this -> ignoresigns -> [$i] : undef;
180 my @model_header = @{$this -> {'problems'} -> [$i] -> header};
181 if ( defined $idcolumn ) {
182 push ( @{$this -> {'datas'}}, data ->
183 new( idcolumn => $idcolumn,
184 filename => $datafile,
185 cont_column => $cont_column,
186 wrap_column => $wrap_column,
187 #model_header => \@model_header,
188 ignoresign => $ignoresign,
189 directory => $this -> {'directory'},
190 ignore_missing_files => $this -> {'ignore_missing_files'} ||
191 $this -> {'ignore_missing_data'},
192 target => $this -> {'target'}) );
193 } else {
194 'debug' -> die( message => "Model -> new: Both idcolumn and datafile must ".
195 "be specified to create a model object." );
200 # Read outputfile, if any.
201 if( ! defined $this -> {'outputs'} ) {
202 unless( defined $this -> {'outputfile'} ){
203 ($this -> {'outputfile'} = $this -> {'filename'}) =~ s/\.mod$/.lst/;
205 push ( @{$this -> {'outputs'}}, output ->
206 new( filename => $this -> {'outputfile'},
207 directory => $this -> {'directory'},
208 ignore_missing_files =>
209 $this -> {'ignore_missing_files'} || $this -> {'ignore_missing_output_files'},
210 target => $this -> {'target'},
211 model_id => $this -> {'model_id'} ) );
214 end new
216 # }}} new
218 # {{{ register_in_database
220 start register_in_database
222 if ( $PsN::config -> {'_'} -> {'use_database'} ) {
223 # Backslashes messes up the sql syntax
224 my $file_str = $self->{'filename'};
225 my $dir_str = $self->{'directory'};
226 $file_str =~ s/\\/\//g;
227 $dir_str =~ s/\\/\//g;
229 # md5sum
230 my $md5sum = md5_hex(OSspecific::slurp_file($self-> full_name ));
232 my $dbh = DBI -> connect("DBI:mysql:host=".$PsN::config -> {'_'} -> {'database_server'}.
233 ";databse=".$PsN::config -> {'_'} -> {'project'},
234 $PsN::config -> {'_'} -> {'user'},
235 $PsN::config -> {'_'} -> {'password'},
236 {'RaiseError' => 1});
238 my $sth;
240 my $select_arr = [];
242 if ( not $force ) {
243 my $sth = $dbh -> prepare( "SELECT model_id FROM ".$PsN::config -> {'_'} -> {'project'}.
244 ".model ".
245 "WHERE filename = '$file_str' AND ".
246 "directory = '$dir_str' AND ".
247 "md5sum = '".$md5sum."'" );
248 $sth -> execute or 'debug' -> die( message => $sth->errstr ) ;
250 $select_arr = $sth -> fetchall_arrayref;
253 if ( scalar @{$select_arr} > 0 ) {
254 'debug' -> warn( level => 1,
255 message => "Found an old entry in the database matching the ".
256 "current model file" );
257 if ( scalar @{$select_arr} > 1 ) {
258 'debug' -> warn( level => 1,
259 message => "Found more than one matching entry in database".
260 ", using the first" );
262 $self -> {'model_id'} = $select_arr->[0][0];
263 } else {
264 my ( $date_str, $time_str );
265 if( $Config{osname} eq 'MSWin32' ){
266 $date_str = `date /T`;
267 $time_str = ' '.`time /T`;
268 } else {
269 # Assuming UNIX
270 $date_str = `date`;
272 chomp($date_str);
273 chomp($time_str);
274 my $date_time = $date_str.$time_str;
275 $sth = $dbh -> prepare("INSERT INTO ".$PsN::config -> {'_'} -> {'project'}.
276 ".model (filename,date,directory,md5sum) ".
277 "VALUES ('$file_str', '$date_time', '$dir_str','".
278 $md5sum."' )");
279 $sth -> execute;
280 $self -> {'model_id'} = $sth->{'mysql_insertid'};
282 $sth -> finish if ( defined $sth );
283 $dbh -> disconnect;
285 $model_id = $self -> {'model_id'} # return the model_id;
287 end register_in_database
289 # }}} register_in_database
291 # {{{ shrinkage_stats
293 start shrinkage_stats
295 if ( $#problem_numbers > 0 and ref $enabled eq 'ARRAY' ){
296 if ( $#problem_numbers != ( scalar @{$enabled} - 1 ) ) {
297 'debug' -> die( message => "The number of problem_numbers ".($#problem_numbers+1).
298 "and enabled/disabled shrinkage_stats ".scalar @{$enabled}.
299 " do not match" );
302 unless( $#problem_numbers > 0 ){
303 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
305 my @en_arr;
306 if( ref \$enabled eq 'SCALAR' ) {
307 for ( @problem_numbers ) {
308 push( @en_arr, $enabled );
310 } elsif ( not ref $enabled eq 'ARRAY' ) {
311 debug -> die( message => 'enabled must be a scalar or a reference to an array, '.
312 'not a reference to a '.ref($enabled).'.' );
315 my @problems = @{$self -> {'problems'}};
316 my $j = 0;
317 foreach my $i ( @problem_numbers ) {
318 if ( defined $problems[ $i-1 ] ) {
319 if ( defined $en_arr[ $j ] ) {
320 if( $en_arr[ $j ] ) {
321 $problems[ $i-1 ] -> shrinkage_module -> enable;
322 } else {
323 $problems[ $i-1 ] -> shrinkage_module -> disable;
325 # my $eta_file = $self -> filename.'_'.$i.'.etas';
326 # my $eps_file = $self -> filename.'_'.$i.'.wres';
327 # $problems[ $i-1 ] -> {'eta_shrinkage_table'} = $eta_file;
328 # $problems[ $i-1 ] -> {'wres_shrinkage_table'} = $eps_file;
329 } else {
330 push( @indicators, $problems[ $i-1 ] -> shrinkage_module -> status );
332 } else {
333 'debug' -> die( message => "Problem number $i does not exist!" );
335 $j++;
338 end shrinkage_stats
340 # }}} shrinkage_stats
342 # {{{ wres_shrinkage
344 =head2 wres_shrinkage
346 Usage:
348 =for html <pre>
350 my $wres_shrink = $model_object -> wres_shrinkage();
352 =for html </pre>
354 Description:
356 Calculates wres shrinkage, a table file with wres is necessary. The
357 return value is reference of and array with one an array per problem
358 in it.
360 =cut
362 start wres_shrinkage
364 my @problems = @{$self -> {'problems'}};
365 foreach my $problem ( @problems ) {
366 push( @wres_shrinkage, $problem -> wres_shrinkage );
369 end wres_shrinkage
371 # }}} wres_shrinkage
373 # {{{ eta_shrinkage
375 =head2 eta_shrinkage
377 Usage:
379 =for html <pre>
381 my $eta_shrink = $model_object -> eta_shrinkage();
383 =for html </pre>
385 Description:
387 Calculates eta shrinkage, a table file with eta is necessary. The
388 return value is reference of and array with one an array per problem
389 in it.
391 =cut
393 start eta_shrinkage
395 my @problems = @{$self -> {'problems'}};
396 foreach my $problem ( @problems ) {
397 push( @eta_shrinkage, $problem -> eta_shrinkage );
400 end eta_shrinkage
402 # }}} eta_shrinkage
404 # {{{ nonparametric_code
406 start nonparametric_code
408 if ( $#problem_numbers > 0 and $#enabled > 0 ){
409 if ( $#problem_numbers != $#enabled ) {
410 'debug' -> die( message => "The number of problem_numbers ".($#problem_numbers+1).
411 "and enabled/disabled nonparametric_code ".($#enabled+1).
412 "do not match" );
415 unless( $#problem_numbers > 0 ){
416 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
418 my @problems = @{$self -> {'problems'}};
419 my $j = 0;
420 foreach my $i ( @problem_numbers ) {
421 if ( defined $problems[ $i-1 ] ) {
422 if ( defined $enabled[ $j ] ) {
423 $problems[ $i-1 ] -> nonparametric_code( $enabled[ $j ] );
424 } else {
425 push( @indicators, $problems[ $i-1 ] -> nonparametric_code );
427 } else {
428 'debug' -> die( message => "Problem number $i does not exist!" );
430 $j++;
433 end nonparametric_code
435 # }}} nonparametric_code
437 # {{{ add_nonparametric_code
439 start add_nonparametric_code
441 $self -> set_records( type => 'nonparametric',
442 record_strings => [ 'MARGINALS UNCONDITIONAL' ] );
443 $self -> set_option( record_name => 'estimation',
444 option_name => 'POSTHOC' );
445 my ( $msfo_ref, $junk ) = $self ->
446 _get_option_val_pos( name => 'MSFO',
447 record_name => 'estimation' );
448 my @nomegas = @{$self -> nomegas};
450 for( my $i = 0; $i <= $#nomegas; $i++ ) { # loop the problems
451 my $marg_str = 'ID';
452 for( my $j = 0; $j <= $nomegas[$i]; $j++ ) {
453 $marg_str = $marg_str.' COM('.($j+1).')=MG'.($j+1);
455 $marg_str = $marg_str.' FILE='.$self->filename.'.marginals'.
456 ' NOAPPEND ONEHEADER NOPRINT';
457 $self -> add_records( problem_numbers => [($i+1)],
458 type => 'table',
459 record_strings => [ $marg_str ] );
460 $self -> remove_option( record_name => 'abbreviated',
461 option_name => 'COMRES' );
462 $self -> add_option( record_name => 'abbreviated',
463 option_name => 'COMRES',
464 option_value => ($nomegas[$i]+1),
465 add_record => 1 ); #Add $ABB if not existing
467 $self -> add_marginals_code( problem_numbers => [($i+1)],
468 nomegas => [ $nomegas[$i] ] );
471 if( not defined $msfo_ref ) {
472 for( my $i = 0; $i < $self -> nproblems; $i++ ) {
473 $self -> add_option( record_name => 'estimation',
474 option_name => 'MSFO',
475 option_value => $self -> filename.'.msfo'.($i+1) );
477 } else {
478 for( my $i = 0; $i < scalar @{$msfo_ref}; $i++ ) {
479 if( not defined $msfo_ref->[$i] or not defined $msfo_ref->[$i][0] ) {
480 $self -> add_option( record_name => 'estimation',
481 option_name => 'MSFO',
482 option_value => $self -> filename.'.msfo'.($i+1) );
487 end add_nonparametric_code
489 # }}} add_nonparametric_code
491 # {{{ flush_data
493 =head2 flush_data
495 Usage:
497 =for html <pre>
499 $model_object -> flush_data();
501 =for html </pre>
503 Description:
505 flush data calls the same method on each data object (usually one)
506 which causes it to write data to disk and remove its data from memory.
508 =cut
510 start flush_data
512 if ( defined $self -> {'datas'} ) {
513 foreach my $data ( @{$self -> {'datas'}} ) {
514 $data -> flush;
518 end flush_data
520 # }}} flush_data
522 # {{{ full_name
524 =head2 full_name
526 Usage:
528 C<< my $file_name = $model_object -> full_name(); >>
530 Description:
532 full_name will return the name of the modelfile and its directory in a
533 string. For example: "/users/guest/project/model.mod".
535 =cut
537 start full_name
539 $full_name = $self -> {'directory'} . $self -> {'filename'};
541 end full_name
543 # }}}
545 # {{{ sync_output
547 This function is unused and should probably be removed.
549 # start __sync_output
551 unless( defined $self -> {'outputfile'} ){
552 'debug' -> die( message => "No output file is set, cannot synchronize output" );
554 @{$self -> {'outputs'}} = ();
555 push ( @{$self -> {'outputs'}}, output ->
556 new( filename => $self -> {'outputfile'},
557 ignore_missing_files => $self -> {'ignore_missing_files'},
558 target => $self -> {'target'},
559 model_id => $self -> {'model_id'} ) );
561 # end __sync_output
563 # }}} sync_output
565 # {{{ add_marginals_code
567 start add_marginals_code
569 # add_marginals_code takes two arguments.
571 # - problem_numbers is an array holding the numbers of the problems in
572 # which code should be added.
574 # - nomegas which is an array holding the number of (diagonal-element)
575 # omegas of each problem given by problem_numbers.
577 # For each omega in each problem, verbatim code is added to make the
578 # marginals available for printing (e.g. to a table file). COM(1) will
579 # hold the nonparametric density, COM(2) the marginal cumulative value
580 # for the first eta, COM(2) the marginal cumulative density for the
581 # second eta and so on.
583 unless( $#problem_numbers >= 0 ){
584 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
587 my @problems = @{$self -> {'problems'}};
588 my $j = 0;
589 foreach my $i ( @problem_numbers ) {
590 if ( defined $problems[ $i-1 ] ) {
591 $problems[$i-1] -> add_marginals_code( nomegas => $nomegas[ $j ] );
592 } else {
593 'debug' -> die( message => "Problem number $i does not exist.");
595 $j++;
598 end add_marginals_code
600 # }}} add_marginals_code
602 # {{{ add_records
604 =head2 add_records
606 Usage:
608 =for html <pre>
610 $model_object -> add_records( type => 'THETA',
611 record_strings => ['(0.1,15,23)'] );
613 =for html </pre>
615 Arguments:
617 =over 3
619 =item type
621 string
623 =item record_strings
625 array of strings
627 =item problem_numbers
629 array of integers
631 =back
633 Description:
635 add_records is used to add NONMEM control file records to the model
636 object. The "type" argument is mandatory and must be a valid NONMEM
637 record name, such as "PRED" or "THETA". Otherwise an error will be
638 output and the program terminated (this is object to change, ideally
639 we would only report an error and let the caller deal with it). The
640 "record_strings" argument is a mandatory array of valid NONMEM record
641 code. Each array corresponds to a line of the record code. There
642 "problem_numbers" argument is optional and is an array of problems
643 numbered from 1 for which the record is added, by default the record
644 is added to all problems.
646 Notice that the records are appended to those that allready exists,
647 which makes sence for records that do not exist and for initial
648 values. For records like "DATA" or "PRED" you probably want to use
649 "set_records".
651 =cut
653 start add_records
655 unless( $#problem_numbers >= 0 ){
656 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
659 my @problems = @{$self -> {'problems'}};
660 foreach my $i ( @problem_numbers ) {
661 if ( defined $problems[ $i-1 ] ) {
662 # if( defined $self -> {'problems'} ){
663 # if( defined @{$self -> {'problems'}}[$problem_number - 1] ){
664 # my $problem = @{$self -> {'problems'}}[$problem_number - 1];
665 # $problem -> add_records( 'type' => $type,
666 # 'record_strings' => \@record_strings );
667 $problems[$i-1] -> add_records( 'type' => $type,
668 'record_strings' => \@record_strings );
669 } else {
670 'debug' -> die( message => "Problem number $i does not exist.");
673 # else {
674 # 'debug' -> die( message => "Model -> add_records: No Problems in model object.") ;
677 end add_records
679 # }}} add_records
681 # {{{ set_records
683 =head2 set_records
685 Usage:
687 =for html <pre>
689 $model_object -> set_records( type => 'THETA',
690 record_strings => ['(0.1,15,23)'] );
692 =for html </pre>
694 Arguments:
696 =over 3
698 =item type
700 string
702 =item record_strings
704 array of strings
706 =item problem_numbers
708 array of integers
710 =back
712 Description:
714 set_records works just like add_records but will replace any existing
715 records in the model object.
717 =cut
719 start set_records
721 unless( $#problem_numbers >= 0 ){
722 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
725 my @problems = @{$self -> {'problems'}};
726 foreach my $i ( @problem_numbers ) {
727 if ( defined $problems[ $i-1 ] ) {
728 # if( defined $self -> {'problems'} ){
729 # if( defined @{$self -> {'problems'}}[$problem_number - 1] ){
730 # my $problem = @{$self -> {'problems'}}[$problem_number - 1];
731 # $problem -> set_records( 'type' => $type,
732 # 'record_strings' => \@record_strings );
733 $problems[$i-1] -> set_records( 'type' => $type,
734 'record_strings' => \@record_strings );
735 } else {
736 'debug' -> die( "Problem number $i does not exist." );
739 # else {
740 # 'debug' -> die( "No Problems in model object.") ;
743 end set_records
745 # }}} set_records
747 # {{{ remove_records
749 =head2 remove_records
751 Usage:
753 =for html <pre>
755 $model_object -> remove_records( type => 'THETA' )
757 =for html </pre>
759 Arguments:
761 =over 3
763 =item type
765 string
767 =item problem_numbers
769 array of integers
771 =back
773 Description:
775 remove_records removes the record given in the "type" argument which
776 must be a valid NONMEM record name.
778 =cut
780 start remove_records
782 unless( $#problem_numbers >= 0 ){
783 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
786 my @problems = @{$self -> {'problems'}};
787 foreach my $i ( @problem_numbers ) {
788 if ( defined $problems[ $i-1 ] ) {
789 # if( defined $self -> {'problems'} ){
790 # if( defined @{$self -> {'problems'}}[$problem_number - 1] ){
791 # my $problem = @{$self -> {'problems'}}[$problem_number - 1];
792 # $problem -> remove_records( 'type' => $type );
793 $problems[$i-1] -> remove_records( 'type' => $type );
794 } else {
795 'debug' -> die( message => "Problem number $i, does not exist" );
798 # else {
799 # 'debug' -> die( message => "No Problems in model object." );
802 end remove_records
804 # }}} remove_records
806 # {{{ copy
808 =head2 copy
810 Usage:
812 =for html <pre>
814 $model_object -> copy( filename => 'copy.mod',
815 copy_data => 1,
816 copy_output => 0 )
818 =for html </pre>
820 Arguments:
822 =over 3
824 =item filename
826 string
828 =item copy_data
830 boolean
832 =item copy_output
834 boolean
836 =item directory
838 string
840 =item data_file_names
842 array of strings
844 =item target
846 string with value 'disk' or 'mem'
848 =item extra_data_file_names
850 array of strings
852 =item update_shrinkage_tables
854 boolean
856 =back
858 Description:
860 copy produces a new modelfile object and a new file on disk whose name
861 is given by the "filename" argument. To create copies of data file the
862 copy_data options may be set to 1. The values of "data_file_names",
863 unless given, will be the model file name but with '.mod' exchanged
864 for '_$i.dta', where $i is the problem number. If data is not copied,
865 a new data object will be intialized from the same data file as the
866 previous model and "data_file_names" WILL BE IGNORED. This has the
867 side effect that the data file can be modified from both the original
868 model and the copy. The same holds for "extra_data_files". It is
869 possible to set "copy_output" to 1 as well, which then copies the
870 output object instead of reading the output file from disk, which is
871 slower. Since output objects are meant to be read-only, no
872 output_filename can be specified and the output object copy will
873 reside in memory only.
875 The "target" option has no effect.
877 =cut
879 start copy
881 # PP_TODO fix a nice copying of modelfile data
882 # preferably in memory copy. Perhaps flush data ?
884 # Check sanity of the length of data file names argument
885 if ( scalar @data_file_names > 0 ) {
886 'debug' -> die( message => "model -> copy: The number of specified new data file " .
887 "names ". scalar @data_file_names. "must\n match the number".
888 " of data objects connected to the model object".
889 scalar @{$self -> {'datas'}} )
890 unless ( scalar @data_file_names == scalar @{$self -> {'datas'}} );
891 } else {
892 my $d_filename;
893 ($d_filename = $filename) =~ s/\.mod$//;
894 for ( my $i = 1; $i <= scalar @{$self -> {'datas'}}; $i++ ) {
895 # Data filename is created in this directory (no directory needed).
896 push( @data_file_names, $d_filename."_data_".$i."_copy.dta" );
900 # Check sanity of the length of extra_data file names argument
901 if ( scalar @extra_data_file_names > 0 ) {
902 'debug' -> die( message => "The number of specified new extra_data file ".
903 "names ". scalar @extra_data_file_names, "must\n match the number".
904 " of problems (one extra_data file per prolem)".
905 scalar @{$self -> {'extra_data_files'}} )
906 unless( scalar @extra_data_file_names == scalar @{$self -> {'extra_data_files'}} );
907 } else {
908 if ( defined $self -> {'extra_data_files'} ) {
909 my $d_filename;
910 ($d_filename = $filename) =~ s/\.mod$//;
911 for ( my $i = 1; $i <= scalar @{$self -> {'extra_data_files'}}; $i++ ) {
912 # Extra_Data filename is created in this directory (no directory needed).
913 push( @extra_data_file_names, $d_filename."_extra_data_".$i."_copy.dta" );
918 ($directory, $filename) = OSspecific::absolute_path( $directory, $filename );
920 # New copy:
922 # save references to own data and output objects
923 my $datas = $self -> {'datas'};
924 # $Data::Dumper::Maxdepth = 2;
925 # die "MC1: ", Dumper $datas -> [0] -> {'individuals'};
926 my $outputs = $self -> {'outputs'};
927 my %extra_datas;
928 my @problems = @{$self -> {'problems'}};
929 for ( my $i = 0; $i <= $#problems; $i++ ) {
930 if ( defined $problems[$i] -> {'extra_data'} ) {
931 $extra_datas{$i} = $problems[$i] -> {'extra_data'};
935 my ( @new_datas, @new_extra_datas, @new_outputs );
937 $self -> synchronize if not $self -> {'synced'};
939 # remove ref to data and output object to speed up the
940 # cloning
941 $self -> {'datas'} = undef;
942 $self -> {'outputs'} = undef;
943 for ( my $i = 0; $i <= $#problems; $i++ ) {
944 $problems[$i] -> {'extra_data'} = undef;
947 # Copy the data objects if so is requested
948 if ( defined $datas ) {
949 my $i = 0;
950 foreach my $data ( @{$datas} ) {
951 if ( $copy_data == 1 ) {
952 push( @new_datas, $data ->
953 copy( filename => $data_file_names[$i]) );
954 } else {
955 # This line assumes one data per problem! May be a source of error.
956 my ( $cont_column, $wrap_column ) = $self -> problems -> [$i] -> cont_wrap_columns;
957 my $ignoresign = defined $self -> ignoresigns ? $self -> ignoresigns -> [$i] : undef;
958 my @model_header = @{$self -> problems -> [$i] -> header};
959 push @new_datas, data ->
960 new( filename => $data -> filename,
961 directory => $data -> directory,
962 cont_column => $cont_column,
963 wrap_column => $wrap_column,
964 #model_header => \@model_header,
965 target => 'disk',
966 ignoresign => $ignoresign,
967 idcolumn => $data -> idcolumn );
969 $i++;
973 # Copy the extra_data objects if so is requested
974 for ( my $i = 0; $i <= $#problems; $i++ ) {
975 my $extra_data = $extra_datas{$i};
976 if ( defined $extra_data ) {
977 if ( $copy_data == 1 ) {
978 push( @new_extra_datas, $extra_data ->
979 copy( filename => $extra_data_file_names[$i]) );
980 } else {
981 push( @new_extra_datas, extra_data ->
982 new( filename => $extra_data -> filename,
983 directory => $extra_data -> directory,
984 target => 'disk',
985 idcolumn => $extra_data -> idcolumn ) );
991 # Clone self into new model object and set synced to 0 for
992 # the copy
993 $new_model = Storable::dclone( $self );
994 $new_model -> {'synced'} = 0;
996 # $Data::Dumper::Maxdepth = 3;
997 # die Dumper $new_datas[0] -> {'individuals'};
999 # Restore the data and output objects for self
1000 $self -> {'datas'} = $datas;
1001 $self -> {'outputs'} = $outputs;
1002 for ( my $i = 0; $i <= $#problems; $i++ ) {
1003 if( defined $extra_datas{$i} ){
1004 $problems[$i] -> {'extra_data'} = $extra_datas{$i};
1008 # Set the new file name for the copy
1009 $new_model -> directory( $directory );
1010 $new_model -> filename( $filename );
1012 # {{{ update the shrinkage modules
1014 my @problems = @{$new_model -> problems};
1015 for( my $i = 1; $i <= scalar @problems; $i++ ) {
1016 $problems[ $i-1 ] -> shrinkage_module -> model( $new_model );
1019 # }}} update the shrinkage modules
1021 # Copy the output object if so is requested (only one output
1022 # object defined per model object)
1023 if ( defined $outputs ) {
1024 foreach my $output ( @{$outputs} ) {
1025 if ( $copy_output == 1 ) {
1026 push( @new_outputs, $output -> copy );
1027 } else {
1028 my $new_out = $filename;
1029 $new_out =~ s/\.mod$/\.lst/;
1030 push( @new_outputs, output ->
1031 new ( filename => $new_out,
1032 directory => $directory,
1033 target => 'disk',
1034 ignore_missing_files => 1,
1035 model_id => $new_model -> {'model_id'} ) );
1040 # Add the copied data and output objects to the model copy
1041 $new_model -> datas( \@new_datas );
1043 if ( $#new_extra_datas >= 0 ) {
1044 my @new_problems = @{$new_model -> problems};
1045 for ( my $i = 0; $i <= $#new_problems; $i++ ) {
1046 $new_problems[$i] -> {'extra_data'} = $new_extra_datas[$i];
1047 if ( $copy_data == 1 ){
1048 $new_problems[$i] -> {'extra_data_file_name'} = $extra_data_file_names[$i];
1053 $new_model -> {'outputs'} = \@new_outputs;
1055 $new_model -> _write;
1057 $new_model -> synchronize if $target eq 'disk';
1059 end copy
1061 # }}} copy
1063 # {{{ covariance
1065 =head2 covariance
1067 Usage:
1069 =for html <pre>
1071 my $indicators = $model_object -> covariance( enabled => [1] );
1073 =for html </pre>
1075 Arguments:
1077 =over 3
1079 =item enabled
1081 array of booleans
1083 =item problem_numbers
1085 array of integers
1087 =back
1089 Description:
1091 covariance will let you turn the covariance step on and off per
1092 problem. The "enabled" argument is an array which must have a length
1093 equal to the number of problems. Each element set to 0 will disable
1094 the covariance step for the corresponding problem. And conversely each
1095 element set to nonzero will enable the covariance step.
1097 covariance will return an array with an element for each problem, the
1098 element will indicate whether the covariance step is turned on or not.
1100 =cut
1102 start covariance
1104 if ( $#problem_numbers > 0 ){
1105 if ( $#problem_numbers != $#enabled ) {
1106 'debug' -> die( message => "The number of problem_numbers ".($#problem_numbers+1).
1107 "and enabled/disabled covariance records ".($#enabled+1).
1108 "do not match" );
1111 unless( $#problem_numbers > 0 ){
1112 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
1114 my @problems = @{$self -> {'problems'}};
1115 my $j = 0;
1116 foreach my $i ( @problem_numbers ) {
1117 if ( defined $problems[ $i-1 ] ) {
1118 if ( defined $enabled[ $j ] ) {
1119 $problems[ $i-1 ] -> covariance( enabled => $enabled[ $j ] );
1120 } else {
1121 push( @indicators, $problems[ $i-1 ] -> covariance );
1123 } else {
1124 'debug' -> die( message => "Problem number $i does not exist!" );
1126 $j++;
1129 end covariance
1131 # }}} covariance
1133 # {{{ datas
1135 =head2 datas
1137 Usage:
1139 =for html <pre>
1141 $model_object -> datas( [$data_obj] );
1143 my $data_objects = $model_object -> data;
1145 =for html </pre>
1147 Arguments:
1149 The argument is an unnamed array of data objects.
1151 Description:
1153 If data is used without argument the data objects connected to the
1154 model object is returned. If an argument is given it must be an array
1155 of length equal to the number of problems with data objects. Those
1156 objects will replace any existing data objects and their filenames
1157 will be put in the model files records.
1159 =cut
1161 start datas
1163 my $nprobs = scalar @{$self -> {'problems'}};
1164 if ( defined $parm ) {
1165 if ( ref($parm) eq 'ARRAY' ) {
1166 my @new_datas = @{$parm};
1167 # Check that new_headers and problems match
1168 'debug' -> die( message => "The number of problems $nprobs and".
1169 " new data ". $#new_datas+1 ." don't match in ".
1170 $self -> full_name ) unless ( $#new_datas + 1 == $nprobs );
1171 if ( defined $self -> {'problems'} ) {
1172 for( my $i = 0; $i < $nprobs; $i++ ) {
1173 $self -> _option_name( position => 0,
1174 record => 'data',
1175 problem_number => $i+1,
1176 new_name => $new_datas[$i] -> filename);
1178 } else {
1179 'debug' -> die( message => "No problems defined in ".
1180 $self -> full_name );
1182 } else {
1183 'debug' -> die( message => "Supplied new value is not an array" );
1187 end datas
1189 # }}}
1191 # {{{ datafile
1193 # TODO 2006-03-22
1194 # I have removed this because it was only used in the bootstrap. I
1195 # fixed the bootstrap to use datafiles instead. Also the bootstrap
1196 # methods who used this was very old and should probably be removed as
1197 # well.
1199 # start datafile
1201 # datafile either retrieves or sets a new name for the datafile in the first problem of the
1202 # model. This method is only here for compatibility reasons. Don't use it. Use L</datafiles> instead.
1204 if( defined $new_name ){
1205 $self -> _option_name( position => 0,
1206 record => 'data',
1207 problem_number => $problem_number,
1208 new_name => $new_name);
1209 my ( $cont_column, $wrap_column ) = $self -> problems -> [$problem_number-1] ->
1210 cont_wrap_columns;
1211 my $ignoresign = defined $self -> ignoresigns ?
1212 $self -> ignoresigns -> [$problem_number-1] : undef;
1213 my @model_header = @{$self -> problems -> [$problem_number-1] -> header};
1214 $self -> {'datas'} -> [$problem_number-1] = data ->
1215 new( idcolumn => $self -> idcolumn( problem_number => $problem_number ),
1216 ignoresign => $ignoresign,
1217 filename => $new_name,
1218 cont_column => $cont_column,
1219 wrap_column => $wrap_column,
1220 #model_header => \@model_header,
1221 ignore_missing_files => $self -> {'ignore_missing_files'},
1222 target => $self -> {'target'} );
1223 } else {
1224 $name = $self -> _option_name( position => 0, record => 'data', problem_number => $problem_number );
1227 # end datafile
1229 # }}} datafile
1231 # {{{ datafiles
1233 =head2 datafiles
1235 Usage:
1237 =for html <pre>
1239 $model_object -> datafiles( new_names => ['datafile.dta'] );
1241 =for html </pre>
1243 Arguments:
1245 =over 2
1247 =item new_names
1249 array of strings
1251 =item problem_numbers
1253 array of integer
1255 =item absolute_path
1257 boolean
1259 =back
1261 Description:
1263 datafiles changes the names of the data files in a model file. The
1264 "new_names" argument is an array of strings, where each string gives
1265 the file name of a problem data file. The length of "new_names" must
1266 be equal to the "problem_numbers" argument. "problem_numbers" is by
1267 default containing all of the models problems numbers. In the example
1268 above we only have one problem in the model file and therefore only
1269 need to give on new file name.
1271 Unless new_names is given datafiles returns the names of the data
1272 files used by the model file. If the optional "absolute_path" argument
1273 is given, the returned file names will have the path to file as well.
1275 =cut
1277 start datafiles
1279 # The datafiles method retrieves or sets the names of the
1280 # datafiles specified in the $DATA record of each problem. The
1281 # problem_numbers argument can be used to control which
1282 # problem that is affected. If absolute_path is set to 1, the
1283 # returned file names are given with absolute paths.
1285 unless( $#problem_numbers > 0 ){
1286 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
1288 if ( scalar @new_names > 0 ) {
1289 my $i = 0;
1290 my @idcolumns = @{$self ->
1291 idcolumns( problem_numbers => \@problem_numbers )};
1292 foreach my $new_name ( @new_names ) {
1293 if ( $absolute_path ) {
1294 my $tmp;
1295 ($tmp, $new_name) = OSspecific::absolute_path('', $new_name );
1296 $new_name = $tmp . $new_name;
1299 $self -> _option_name( position => 0,
1300 record => 'data',
1301 problem_number => $problem_numbers[$i],
1302 new_name => $new_name);
1303 my ( $cont_column, $wrap_column ) = $self -> problems ->
1304 [$problem_numbers[$i]-1] -> cont_wrap_columns;
1305 my $ignoresign = defined $self -> ignoresigns ? $self -> ignoresigns -> [$i] : undef;
1306 my @model_header = @{$self -> problems -> [$i] -> header};
1307 $self -> {'datas'} -> [$problem_numbers[$i]-1] = data ->
1308 new( idcolumn => $idcolumns[$i],
1309 ignoresign => $ignoresign,
1310 filename => $new_name,
1311 cont_column => $cont_column,
1312 wrap_column => $wrap_column,
1313 #model_header => \@model_header,
1314 ignore_missing_files => $self -> {'ignore_missing_files'},
1315 target => $self -> {'target'} );
1316 $i++;
1318 } else {
1319 foreach my $prob_num ( @problem_numbers ) {
1320 if ( $absolute_path ) {
1321 my ($d_dir, $d_name);
1322 ($d_dir, $d_name) =
1323 OSspecific::absolute_path($self -> {'directory'}, $self ->_option_name( position => 0,
1324 record => 'data',
1325 problem_number => $prob_num ) );
1326 push( @names, $d_dir . $d_name );
1327 } else {
1328 my $name = $self -> _option_name( position => 0,
1329 record => 'data',
1330 problem_number => $prob_num );
1331 $name =~ s/.*[\/\\]//;
1332 push( @names, $name );
1337 end datafiles
1339 # }}} datafiles
1341 # {{{ des
1343 # TODO 2006-03-22
1344 # This method is renamed __des in dia but not here. If nothing broke
1345 # until now I think we can safely remove it.
1347 start des
1349 # Returns the des part specified subproblem.
1350 # TODO: Even though new_des can be specified, they wont be set
1351 # in to the object.
1353 my @prob = @{$self -> problems};
1354 my @des = @{$prob[$problem_number - 1] -> get_record('des') -> code}
1355 if ( defined $prob[$problem_number - 1] -> get_record('des') );
1357 end des
1359 # }}} des
1361 # {{{ eigen
1362 start eigen
1364 $self -> {'problems'} -> [0] -> eigen;
1366 end eigen
1367 # }}} eigen
1369 # {{{ error
1371 # TODO 2006-03-22
1372 # This method is renamed __error in dia but not here. If nothing broke
1373 # until now I think we can safely remove it.
1375 start error
1377 # Usage:
1379 # @error = $modelObject -> error;
1381 # Returns the error part specified subproblem.
1382 # TODO: Even though new_error can be specified, they wont be set
1383 # in to the object.
1384 my @prob = @{$self -> problems};
1385 my @error = @{$prob[0] -> get_record('error') -> code}
1386 if ( defined $prob[0] -> get_record('error') );
1388 end error
1390 # }}} error
1392 # {{{ extra_data_files
1394 =head2 extra_data_files
1396 Usage:
1398 =for html <pre>
1400 $model_object -> extra_data_files( ['extra_data.dta'] );
1402 my $extra_file_name = $model_object -> extra_data_files;
1404 =for html </pre>
1406 Arguments:
1408 The argument is an unnamed array of strings
1410 Description:
1412 If extra_data_files is used without argument the names of any extra
1413 data files connected to the model object is returned. If an argument
1414 is given it must be an array of length equal to the number of problems
1415 in the model. Then the names of the extra data files will be changed
1416 to those in the array.
1418 =cut
1420 start extra_data_files
1422 my @file_names;
1423 # Sets or retrieves extra_data_file_name on problem level
1424 my $nprobs = scalar @{$self -> {'problems'}};
1425 if ( defined $parm ) {
1426 if ( ref($parm) eq 'ARRAY' ) {
1427 my @new_file_names = @{$parm};
1428 # Check that new_file_names and problems match
1429 'debug' -> die( message => "model -> extra_data_files: The number of problems $nprobs and" .
1430 " new_file_names " . $#new_file_names+1 . " don't match in ".
1431 $self -> full_name ) unless ( $#new_file_names + 1 == $nprobs );
1432 if ( defined $self -> {'problems'} ) {
1433 for( my $i = 0; $i < $nprobs; $i++ ) {
1434 $self -> {'problems'} -> [$i] -> extra_data_file_name( $new_file_names[$i] );
1436 } else {
1437 'debug' -> die( message => "No problems defined in " .
1438 $self -> full_name );
1440 } else {
1441 'debug' -> die(message => "Supplied new value is not an array.");
1443 } else {
1444 if ( defined $self -> {'problems'} ) {
1445 for( my $i = 0; $i < $nprobs; $i++ ) {
1446 if( defined $self -> {'problems'} -> [$i] -> extra_data_file_name ) {
1447 push ( @file_names ,$self -> {'problems'} -> [$i] -> extra_data_file_name );
1452 return \@file_names;
1454 end extra_data_files
1456 # }}}
1458 # {{{ extra_data_headers
1460 =head2 extra_data_headers
1462 Usage:
1464 =for html <pre>
1466 $model_object -> extra_data_headers( [$data_obj] );
1468 my $data_objects = $model_object -> extra_data_headers;
1470 =for html </pre>
1472 Arguments:
1474 The argument is an unnamed array of arrays of strings.
1476 Description:
1478 If extra_data_files is used without argument the headers of any extra
1479 data files connected to the model object is returned. If an argument
1480 is given it must be an array of length equal to the number of problems
1481 in the model. Then the headers of the extra data files will be changed
1482 to those in the array.
1484 =cut
1486 start extra_data_headers
1488 my @headers;
1489 # Sets or retrieves extra_data_header on problem level
1490 my $nprobs = scalar @{$self -> {'problems'}};
1491 if ( defined $parm ) {
1492 if ( ref($parm) eq 'ARRAY' ) {
1493 my @new_headers = @{$parm};
1494 # Check that new_headers and problems match
1495 'debug' -> die( message => "The number of problems $nprobs and".
1496 " new_headers " . $#new_headers+1 . " don't match in ".
1497 $self -> full_name) unless ( $#new_headers + 1 == $nprobs );
1498 if ( defined $self -> {'problems'} ) {
1499 for( my $i = 0; $i < $nprobs; $i++ ) {
1500 $self -> {'problems'} -> [$i] -> extra_data_header( $new_headers[$i] );
1502 } else {
1503 'debug' -> die( message => "No problems defined in " . $self -> full_name );
1505 } else {
1506 'debug' -> die( message => "Supplied new value is not an array" );
1508 } else {
1509 if ( defined $self -> {'problems'} ) {
1510 for( my $i = 0; $i < $nprobs; $i++ ) {
1511 push ( @headers, $self -> {'problems'} -> [$i] -> extra_data_header );
1515 return \@headers;
1517 end extra_data_headers
1519 # }}} extra_data_headers
1521 # {{{ factors
1523 =head2 factors
1525 Usage:
1527 =for html <pre>
1529 my $factors = $model_object -> factors;
1531 =for html </pre>
1533 Arguments:
1535 =over 2
1537 =item colunm
1539 number
1541 =item column_head
1543 string
1545 =item problem_number
1547 integer
1549 =item return_occurences
1551 boolean
1553 =item unique_in_individual
1555 boolean
1557 =back
1559 Description:
1561 The following text comes from the documentation of
1562 data::factors. model::factors will call data::factors for the given
1563 problem number in the model object. Also it will take try to find
1564 "column_head" in the $INPUT record instead of the data file header.
1566 Either column (number, starting at 1) or column_head must be
1567 specified. The default behaviour is to return a hash with the factors
1568 as keys referencing arrays with the order numbers (not the ID numbers)
1569 of the individuals that contain this factor.
1571 If unique_in_individual is true (1), the returned hash will contain an
1572 element with key 'Non-unique values found' and value 1 if any
1573 individual contain more than one value in the specified column.
1575 Return occurences will calculate the occurence of each factor
1576 value. Several occurences in one individual counts as one
1577 occurence. The elements of the returned hash will have the factors as
1578 keys and the number of occurences as values.
1580 =cut
1582 start factors
1584 # Calls <I>factors</I> on the data object of a specified
1585 # problem. See <I>data -> factors</I> for details.
1586 my $column_number;
1587 my $extra_data_column;
1588 if ( defined $column_head ) {
1589 # Check normal data object first
1590 my ( $values_ref, $positions_ref ) = $self ->
1591 _get_option_val_pos ( problem_numbers => [$problem_number],
1592 name => $column_head,
1593 record_name => 'input',
1594 global_position => 1 );
1595 $column_number = $positions_ref -> [0];
1596 # Next, check extra_data
1597 my $extra_data_headers = $self -> extra_data_headers;
1598 if ( defined $extra_data_headers and defined $extra_data_headers -> [0] ) {
1599 for ( my $i = 1; $i <= scalar @{$extra_data_headers->[0]}; $i++ ) {
1600 $extra_data_column = $i if ( $column_head eq $extra_data_headers->[0][$i-1] );
1603 'debug' -> die( message => "Unknown column \"$column_head\"" )
1604 unless ( defined $column_number or defined $extra_data_column );
1605 } else {
1606 $column_number = $column;
1608 if ( defined $column_number) {
1609 %factors = %{$self -> {'datas'} -> [$problem_number-1] ->
1610 factors( column => $column_number,
1611 unique_in_individual => $unique_in_individual,
1612 return_occurences => $return_occurences )};
1613 } else {
1614 %factors = %{$self -> {'problems'} -> [$problem_number-1] -> extra_data
1615 -> factors( column => $extra_data_column,
1616 unique_in_individual => $unique_in_individual,
1617 return_occurences => $return_occurences )};
1620 end factors
1622 # }}}
1624 # {{{ fractions
1626 =head2 fractions
1628 Usage:
1630 =for html <pre>
1632 my $fractions = $model_object -> fractions;
1634 =for html </pre>
1636 Arguments:
1638 =over 2
1640 =item colunm
1642 number
1644 =item column_head
1646 string
1648 =item problem_number
1650 integer
1652 =item return_occurences
1654 boolean
1656 =item ignore_missing
1658 boolean
1660 =back
1662 Description:
1664 fractions will return the fractions from data::fractions. It will find
1665 "column_head" in the $INPUT record instead of that data header as
1666 data::fractions does.
1668 =cut
1670 start fractions
1672 # Calls <I>fractions</I> on the data object of a specified
1673 # problem. See <I>data -> fractions</I> for details.
1674 my $column_number;
1675 my $extra_data_column;
1676 if ( defined $column_head ) {
1677 # Check normal data object first
1678 my ( $values_ref, $positions_ref ) = $self ->
1679 _get_option_val_pos ( problem_numbers => [$problem_number],
1680 name => $column_head,
1681 record_name => 'input',
1682 global_position => 1 );
1683 $column_number = $positions_ref -> [0];
1684 # Next, check extra_data
1685 my $extra_data_headers = $self -> extra_data_headers;
1686 if ( defined $extra_data_headers and defined $extra_data_headers -> [0] ) {
1687 for ( my $i = 1; $i <= scalar @{$extra_data_headers->[0]}; $i++ ) {
1688 $extra_data_column = $i if ( $column_head eq $extra_data_headers->[0][$i-1] );
1691 'debug' -> die( "Unknown column \"$column_head\"" )
1692 unless ( defined $column_number or defined $extra_data_column );
1693 } else {
1694 $column_number = $column;
1696 if ( defined $column_number) {
1697 %fractions = %{$self -> {'datas'} -> [$problem_number-1] ->
1698 fractions( column => $column_number,
1699 unique_in_individual => $unique_in_individual,
1700 ignore_missing => $ignore_missing )};
1701 } else {
1702 %fractions = %{$self -> {'problems'} -> [$problem_number-1] -> extra_data
1703 -> fractions( column => $extra_data_column,
1704 unique_in_individual => $unique_in_individual,
1705 ignore_missing => $ignore_missing )};
1708 end fractions
1710 # }}}
1712 # {{{ fixed
1714 =head2 fractions
1716 Usage:
1718 =for html <pre>
1720 my $fractions = $model_object -> fractions;
1722 =for html </pre>
1724 Arguments:
1726 =over 2
1728 =item colunm
1730 number
1732 =item column_head
1734 string
1736 =item problem_number
1738 integer
1740 =item return_occurences
1742 boolean
1744 =item ignore_missing
1746 boolean
1748 =back
1750 Description:
1752 fractions will return the fractions from data::fractions. It will find
1753 "column_head" in the $INPUT record instead of that data header as
1754 data::fractions does.
1756 =cut
1758 start fixed
1760 # Sets or gets the 'fixed' status of a (number of)
1761 # parameter(s). 1 correspond to a parameter being fixed and
1762 # 0 not fixed. The returned parameter is a reference to a
1763 # two-dimensional array, indexed by problems and parameter
1764 # numbers.
1765 # Valid parameter types are 'theta', 'omega' and 'sigma'.
1767 @fixed = @{ $self -> _init_attr
1768 ( parameter_type => $parameter_type,
1769 parameter_numbers => \@parameter_numbers,
1770 problem_numbers => \@problem_numbers,
1771 new_values => \@new_values,
1772 attribute => 'fix')};
1774 end fixed
1776 # }}} fixed
1778 # {{{ have_missing_data
1780 =head2 fractions
1782 Usage:
1784 =for html <pre>
1786 my $fractions = $model_object -> fractions;
1788 =for html </pre>
1790 Arguments:
1792 =over 2
1794 =item colunm
1796 number
1798 =item column_head
1800 string
1802 =item problem_number
1804 integer
1806 =item return_occurences
1808 boolean
1810 =item ignore_missing
1812 boolean
1814 =back
1816 Description:
1818 fractions will return the fractions from data::fractions. It will find
1819 "column_head" in the $INPUT record instead of that data header as
1820 data::fractions does.
1822 =cut
1824 start have_missing_data
1826 # Calls <I>have_missing_data</I> on the data object of a specified
1827 # problem. See <I>data -> have_missing_data</I> for details.
1828 my $column_number;
1829 my $extra_data_column;
1830 if ( defined $column_head ) {
1831 # Check normal data object first
1832 my ( $values_ref, $positions_ref ) = $self ->
1833 _get_option_val_pos ( problem_numbers => [$problem_number],
1834 name => $column_head,
1835 record_name => 'input',
1836 global_position => 1 );
1837 $column_number = $positions_ref -> [0];
1838 # Next, check extra_data
1839 my $extra_data_headers = $self -> extra_data_headers;
1840 if ( defined $extra_data_headers and defined $extra_data_headers -> [0] ) {
1841 for ( my $i = 1; $i <= scalar @{$extra_data_headers->[0]}; $i++ ) {
1842 $extra_data_column = $i if ( $column_head eq $extra_data_headers->[0][$i-1] );
1845 'debug' -> die( message => "Unknown column \"$column_head\"" )
1846 unless ( defined $column_number or defined $extra_data_column );
1847 } else {
1848 $column_number = $column;
1850 if ( defined $column_number) {
1851 $return_value = $self -> {'datas'} -> [$problem_number-1] ->
1852 have_missing_data( column => $column_number );
1853 } else {
1854 $return_value = $self -> {'problems'} -> [$problem_number-1] ->
1855 extra_data -> have_missing_data( column => $extra_data_column );
1858 end have_missing_data
1860 # }}}
1862 # {{{ idcolumn
1864 =head2 fractions
1866 Usage:
1868 =for html <pre>
1870 my $fractions = $model_object -> fractions;
1872 =for html </pre>
1874 Arguments:
1876 =over 2
1878 =item colunm
1880 number
1882 =item column_head
1884 string
1886 =item problem_number
1888 integer
1890 =item return_occurences
1892 boolean
1894 =item ignore_missing
1896 boolean
1898 =back
1900 Description:
1902 fractions will return the fractions from data::fractions. It will find
1903 "column_head" in the $INPUT record instead of that data header as
1904 data::fractions does.
1906 =cut
1908 start idcolumn
1910 # Usage:
1912 # @idcolumns = @{$modelObject -> idcolumns( problem_numbers => [2,3] );
1914 # idcolumns returns the idcolumn index in the datafile for the
1915 # specified problem.
1917 my $junk_ref;
1918 ( $junk_ref, $col ) = $self ->
1919 _get_option_val_pos( name => 'ID',
1920 record_name => 'input',
1921 problem_numbers => [$problem_number] );
1923 if ( $problem_number ne 'all' ) {
1924 $col = @{$col}[0];
1927 end idcolumn
1929 # }}} idcolumn
1931 # {{{ idcolumns
1933 =head2 fractions
1935 Usage:
1937 =for html <pre>
1939 my $fractions = $model_object -> fractions;
1941 =for html </pre>
1943 Arguments:
1945 =over 2
1947 =item colunm
1949 number
1951 =item column_head
1953 string
1955 =item problem_number
1957 integer
1959 =item return_occurences
1961 boolean
1963 =item ignore_missing
1965 boolean
1967 =back
1969 Description:
1971 fractions will return the fractions from data::fractions. It will find
1972 "column_head" in the $INPUT record instead of that data header as
1973 data::fractions does.
1975 =cut
1977 start idcolumns
1979 # Usage:
1981 # @column_numbers = @{$modelObject -> idcolumns( problem_numbers => [2] )};
1983 # idcolumns returns the idcolumn indexes in the datafile for the
1984 # specified problems.
1986 my ( $junk_ref, $col_ref ) = $self ->
1987 _get_option_val_pos( name => 'ID',
1988 record_name => 'input',
1989 problem_numbers => \@problem_numbers );
1990 # There should only be one instance of $INPUT and hence we collapse
1991 # the two-dim return from _get_option_pos_val to a one-dim array:
1993 foreach my $prob ( @{$col_ref} ) {
1994 foreach my $inst ( @{$prob} ) {
1995 push( @column_numbers, $inst );
1999 end idcolumns
2001 # }}} idcolumns
2003 # {{{ ignoresigns
2004 =head2 fractions
2006 Usage:
2008 =for html <pre>
2010 my $fractions = $model_object -> fractions;
2012 =for html </pre>
2014 Arguments:
2016 =over 2
2018 =item colunm
2020 number
2022 =item column_head
2024 string
2026 =item problem_number
2028 integer
2030 =item return_occurences
2032 boolean
2034 =item ignore_missing
2036 boolean
2038 =back
2040 Description:
2042 fractions will return the fractions from data::fractions. It will find
2043 "column_head" in the $INPUT record instead of that data header as
2044 data::fractions does.
2046 =cut
2048 start ignoresigns
2050 # Usage:
2052 # @ignore_signs = @{$modelObject -> ignoresigns( problem_numbers => [2,4] )};
2054 # ignoresigns returns the ignore signs in the datafile for the
2055 # specified problems
2057 my ( $ignore_opt_ref, $junk_ref ) = $self ->
2058 _get_option_val_pos( name => 'IGNORE',
2059 record_name => 'data',
2060 problem_numbers => \@problem_numbers );
2062 # There should only be one instance of $DATA and hence we collapse
2063 # the two-dim return from _get_option_pos_val to a one-dim array:
2064 foreach my $prob ( @{$ignore_opt_ref} ) {
2065 foreach my $inst ( @{$prob} ) {
2066 $inst = '#' unless defined $inst;
2067 push( @ignore, $inst );
2071 end ignoresigns
2073 # }}} ignoresigns
2075 # {{{ indexes
2077 =head2 fractions
2079 Usage:
2081 =for html <pre>
2083 my $fractions = $model_object -> fractions;
2085 =for html </pre>
2087 Arguments:
2089 =over 2
2091 =item colunm
2093 number
2095 =item column_head
2097 string
2099 =item problem_number
2101 integer
2103 =item return_occurences
2105 boolean
2107 =item ignore_missing
2109 boolean
2111 =back
2113 Description:
2115 fractions will return the fractions from data::fractions. It will find
2116 "column_head" in the $INPUT record instead of that data header as
2117 data::fractions does.
2119 =cut
2121 start indexes
2123 # Usage:
2125 # @indexArray = @{$modelObject -> indexes( 'parameter_type' => 'omega' )};
2127 # A call to I<indexes> returns the indexes of all parameters
2128 # specified in I<parameter_numbers> from the subproblems
2129 # specified in I<problem_numbers>. The method returns a reference to an array that has
2130 # the same structure as parameter_numbers but for each
2131 # array of numbers is instead an array of indices. The method
2132 # uses a method from the model::problem class to format the
2133 # indices, so here are a few lines from the code comments in
2134 # model/problem.pm that describes the returned value:
2136 # <snip>
2137 # The Indexes method calculates the index for a
2138 # parameter. Off-diagonal elements will get a index 'i_j', where i
2139 # is the row number and j is the column number
2140 # </snip>
2142 unless( $#problem_numbers > 0 ){
2143 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
2145 my @problems = @{$self -> {'problems'}};
2146 foreach my $i ( @problem_numbers ) {
2147 if ( defined $problems[ $i-1 ] ) {
2148 push( @indexes,
2149 $problems[ $i-1 ] ->
2150 indexes( parameter_type => $parameter_type,
2151 parameter_numbers => $parameter_numbers[ $i-1 ] ) );
2152 } else {
2153 'debug' -> die( message => "Problem number $i does not exist!" );
2157 end indexes
2159 # }}} indexes
2161 # {{{ initial_values
2163 =head2 fractions
2165 Usage:
2167 =for html <pre>
2169 my $fractions = $model_object -> fractions;
2171 =for html </pre>
2173 Arguments:
2175 =over 2
2177 =item colunm
2179 number
2181 =item column_head
2183 string
2185 =item problem_number
2187 integer
2189 =item return_occurences
2191 boolean
2193 =item ignore_missing
2195 boolean
2197 =back
2199 Description:
2201 fractions will return the fractions from data::fractions. It will find
2202 "column_head" in the $INPUT record instead of that data header as
2203 data::fractions does.
2205 =cut
2207 start initial_values
2209 # initial_values either sets or gets the initial values of
2210 # the parameter specified in "parameter_type" for each
2211 # problem specified in problem_numbers. For each element
2212 # in problem_numbers there must be a reference in
2213 # parameter_numbers to an array that specify the indices
2214 # of the parameters in the subproblem for which the initial
2215 # values are set, replaced or retrieved.
2217 # The add_if_absent argument tells the method to add an init
2218 # (theta,omega,sigma) if the parameter number points to a
2219 # non-existing parameter with parameter number one higher
2220 # than the highest presently included. Only applicable if
2221 # new_values are set. Valid parameter types are 'theta',
2222 # 'omega' and 'sigma'.
2224 @initial_values = @{ $self -> _init_attr
2225 ( parameter_type => $parameter_type,
2226 parameter_numbers => \@parameter_numbers,
2227 problem_numbers => \@problem_numbers,
2228 new_values => \@new_values,
2229 attribute => 'init',
2230 add_if_absent => $add_if_absent )};
2232 end initial_values
2234 # }}} initial_values
2236 # {{{ is_option_set
2239 =head2 fractions
2241 Usage:
2243 =for html <pre>
2245 my $fractions = $model_object -> fractions;
2247 =for html </pre>
2249 Arguments:
2251 =over 2
2253 =item colunm
2255 number
2257 =item column_head
2259 string
2261 =item problem_number
2263 integer
2265 =item return_occurences
2267 boolean
2269 =item ignore_missing
2271 boolean
2273 =back
2275 Description:
2277 fractions will return the fractions from data::fractions. It will find
2278 "column_head" in the $INPUT record instead of that data header as
2279 data::fractions does.
2281 =cut
2283 start is_option_set
2285 # Usage:
2287 # if( $modelObject -> is_option_set( record => 'recordName', name => 'optionName' ) ){
2288 # print "problem_number 1 has option optionName set in record recordName";
2291 # is_option_set checks if an option is set in a given record in given problem.
2293 my ( @problems, @records, @options );
2294 my $accessor = $record.'s';
2295 if ( defined $self -> {'problems'} ) {
2296 @problems = @{$self -> {'problems'}};
2297 } else {
2298 'debug' -> die( message => "No problems defined in model" );
2300 unless( defined $problems[$problem_number - 1] ){
2301 'debug' -> warn( level => 2,
2302 message => "model -> is_option_set: No problem number $problem_number defined in model" );
2303 return 0; # No option can be set if no problem exists.
2306 if ( defined $problems[$problem_number - 1] -> $accessor ) {
2307 @records = @{$problems[$problem_number - 1] -> $accessor};
2308 } else {
2309 'debug' -> warn( level => 2,
2310 message => "model -> is_option_set: No record $record defined" .
2311 " in problem number $problem_number." );
2312 return 0;
2315 unless(defined $records[$instance - 1] ){
2316 'debug' -> warn( level => 2,
2317 message => "model -> is_option_set: No record instance number $instance defined in model." );
2318 return 0;
2321 if ( defined $records[$instance - 1] -> options ) {
2322 @options = @{$records[$instance - 1] -> options};
2323 } else {
2324 'debug' -> warn( level => 2,
2325 message => "No option defined in record: $record in problem number $problem_number." );
2326 return 0;
2328 foreach my $option ( @options ) {
2329 $found = 1 if ( defined $option and $option -> name eq $name );
2332 end is_option_set
2334 # }}} is_option_set
2336 # {{{ is_run
2339 =head2 fractions
2341 Usage:
2343 =for html <pre>
2345 my $fractions = $model_object -> fractions;
2347 =for html </pre>
2349 Arguments:
2351 =over 2
2353 =item colunm
2355 number
2357 =item column_head
2359 string
2361 =item problem_number
2363 integer
2365 =item return_occurences
2367 boolean
2369 =item ignore_missing
2371 boolean
2373 =back
2375 Description:
2377 fractions will return the fractions from data::fractions. It will find
2378 "column_head" in the $INPUT record instead of that data header as
2379 data::fractions does.
2381 =cut
2383 start is_run
2385 # Usage:
2387 # is_run returns true if the outputobject owned by the
2388 # modelobject has valid outpudata either in memory or on disc.
2389 if( defined $self -> {'outputs'} ){
2390 if( @{$self -> {'outputs'}}[0] -> have_output ){
2391 $return_value = 1;
2393 } else {
2394 $return_value = 0;
2397 end is_run
2398 # }}} is_run
2400 # {{{ is_simulation
2403 =head2 fractions
2405 Usage:
2407 =for html <pre>
2409 my $fractions = $model_object -> fractions;
2411 =for html </pre>
2413 Arguments:
2415 =over 2
2417 =item colunm
2419 number
2421 =item column_head
2423 string
2425 =item problem_number
2427 integer
2429 =item return_occurences
2431 boolean
2433 =item ignore_missing
2435 boolean
2437 =back
2439 Description:
2441 fractions will return the fractions from data::fractions. It will find
2442 "column_head" in the $INPUT record instead of that data header as
2443 data::fractions does.
2445 =cut
2447 start is_simulation
2449 my $problems = $self -> {'problems'};
2450 if( defined $problems -> [$problem_number - 1] ) {
2451 my $problem = $problems -> [$problem_number - 1];
2452 # If we don't have an ESTIMATION record we are simulating.
2453 $is_sim = 1 unless( defined $problem -> {'estimations'} and
2454 scalar( @{$problem-> {'estimations'}} ) > 0 );
2456 # If we have a ONLYSIM option in the simulation record.
2457 $is_sim = 1 if( $self -> is_option_set ( name => 'ONLYSIM',
2458 record => 'simulation',
2459 problem_number => $problem_number ));
2461 # If max evaluations is zero we are simulating
2462 $is_sim = 1 if( defined $self -> maxeval(problem_numbers => [$problem_number]) and
2463 defined $self -> maxeval(problem_numbers => [$problem_number])->[0][0] and
2464 $self -> maxeval(problem_numbers => [$problem_number])->[0][0] == 0 );
2466 # Anything else?
2468 # If non of the above is true, we are estimating.
2469 } else {
2470 'debug' -> warn( level => 1,
2471 message => 'Problem nr. $problem_number not defined. Assuming no simulation' );
2472 $is_sim = 0;
2475 end is_simulation
2477 # }}}
2479 # {{{ lower_bounds
2481 =head2 fractions
2483 Usage:
2485 =for html <pre>
2487 my $fractions = $model_object -> fractions;
2489 =for html </pre>
2491 Arguments:
2493 =over 2
2495 =item colunm
2497 number
2499 =item column_head
2501 string
2503 =item problem_number
2505 integer
2507 =item return_occurences
2509 boolean
2511 =item ignore_missing
2513 boolean
2515 =back
2517 Description:
2519 fractions will return the fractions from data::fractions. It will find
2520 "column_head" in the $INPUT record instead of that data header as
2521 data::fractions does.
2523 =cut
2525 start lower_bounds
2527 # lower_bounds either sets or gets the initial values of the
2528 # parameter specified in the argument parameter_type for
2529 # each problem specified in problem_numbers. See L</fixed>.
2531 @lower_bounds = @{ $self -> _init_attr
2532 ( parameter_type => $parameter_type,
2533 parameter_numbers => \@parameter_numbers,
2534 problem_numbers => \@problem_numbers,
2535 new_values => \@new_values,
2536 attribute => 'lobnd')};
2538 end lower_bounds
2540 # }}} lower_bounds
2542 # {{{ labels
2544 =head2 fractions
2546 Usage:
2548 =for html <pre>
2550 my $fractions = $model_object -> fractions;
2552 =for html </pre>
2554 Arguments:
2556 =over 2
2558 =item colunm
2560 number
2562 =item column_head
2564 string
2566 =item problem_number
2568 integer
2570 =item return_occurences
2572 boolean
2574 =item ignore_missing
2576 boolean
2578 =back
2580 Description:
2582 fractions will return the fractions from data::fractions. It will find
2583 "column_head" in the $INPUT record instead of that data header as
2584 data::fractions does.
2586 =cut
2588 start labels
2590 # Usage:
2592 # @labels = @{$modobj -> labels( parameter_type => 'theta' )};
2594 # This basic usage takes one arguments and returns matched names and
2595 # estimated values of the specified parameter. The parameter_type argument
2596 # is mandatory. It returns the labels of all parameters of type given by
2597 # $parameter_type.
2598 # @labels will be a two-dimensional array:
2599 # [[label1][label2][label3]...]
2601 # $labels -> labels( parameter_type => 'theta',
2602 # problem_numbers => [2,4] );
2604 # To get labels of specific problems, the problem_numbers argument can be used.
2605 # It should be a reference to an array containing the numbers
2606 # of all problems whos labels should be retrieved.
2608 # $modobj -> labels( parameter_type => 'theta',
2609 # problem_numbers => [2,4],
2610 # parameter_numbers => [[1,3][4,6]]);
2612 # The retrieval can be even more specific by using the parameter_numbers
2613 # argument. It should be a reference to a two-dimensional array, where
2614 # the inner arrays holds the numbers of the parameters that should be
2615 # fetched. In the example above, parameters one and three from problem two
2616 # plus parameters four and six from problem four are retrieved.
2618 # $modobj -> labels( parameter_type => 'theta',
2619 # problem_numbers => [2,4],
2620 # parameter_numbers => [[1,3][4,6]],
2621 # generic => 1 );
2623 # To get generic labels for the parameters - e.g. OM1, OM2_1, OM2 etc -
2624 # set the generic argument to 1.
2626 # $modobj -> labels( parameter_type => 'theta',
2627 # problem_numbers => [2],
2628 # parameter_numbers => [[1,3]],
2629 # new_values => [['Volume','Clearance']] );
2631 # The new_values argument can be used to give parameters new labels. In
2632 # the above example, parameters one and three in problem two are renamed
2633 # Volume and Clearance.
2636 my ( @index, $idx );
2637 @labels = @{ $self -> _init_attr
2638 ( parameter_type => $parameter_type,
2639 parameter_numbers => \@parameter_numbers,
2640 problem_numbers => \@problem_numbers,
2641 new_values => \@new_values,
2642 attribute => 'label' )};
2644 # foreach my $prl ( @labels ) {
2645 # foreach my $label ( @{$prl} ) {
2646 # print "Label: $label\n";
2651 @index = @{$self -> indexes( parameter_type => $parameter_type,
2652 parameter_numbers => \@parameter_numbers,
2653 problem_numbers => \@problem_numbers )};
2654 for ( my $i = 0; $i <= $#labels; $i++ ) {
2655 for ( my $j = 0; $j < scalar @{$labels[$i]}; $j++ ) {
2656 $idx = $index[$i][$j];
2657 $labels[$i][$j] = uc(substr($parameter_type,0,2)).$idx
2658 unless ( defined $labels[$i][$j] and not $generic );
2662 end labels
2664 # }}} labels
2666 # {{{ maxeval
2668 =head2 fractions
2670 Usage:
2672 =for html <pre>
2674 my $fractions = $model_object -> fractions;
2676 =for html </pre>
2678 Arguments:
2680 =over 2
2682 =item colunm
2684 number
2686 =item column_head
2688 string
2690 =item problem_number
2692 integer
2694 =item return_occurences
2696 boolean
2698 =item ignore_missing
2700 boolean
2702 =back
2704 Description:
2706 fractions will return the fractions from data::fractions. It will find
2707 "column_head" in the $INPUT record instead of that data header as
2708 data::fractions does.
2710 =cut
2712 start maxeval
2714 # Usage:
2716 # @maxev = @{$modobj -> maxeval};
2718 # This basic usage takes no arguments and returns the value of the
2719 # MAXEVAL option in the $ESTIMATION record of each problem.
2720 # @maxev will be a two dimensional array:
2721 # [[maxeval_prob1][maxeval_prob2][maxeval_prob3]...]
2723 # $modobj -> maxeval( new_values => [[0],[999]];
2725 # If the new_values argument of maxeval is given, the values of the
2726 # MAXEVAL options will be changed. In this example, MAXEVAL will be
2727 # set to 0 in the first problem and to 999 in the second.
2728 # The number of elements in new_values must match the number of problems
2729 # in the model object $modobj.
2731 # $modobj -> maxeval( new_values => [[0],[999]],
2732 # problem_numbers => [2,4] );
2734 # To set the MAXEVAL of specific problems, the problem_numbers argument can
2735 # be used. It should be a reference to an array containing the numbers
2736 # of all problems where the MAXEVAL should be changed or retrieved.
2737 # If specified, the size of new_values must be the same as the size
2738 # of problem_numbers.
2743 my ( $val_ref, $junk ) = $self ->
2744 _option_val_pos( name => 'MAX',
2745 record_name => 'estimation',
2746 problem_numbers => \@problem_numbers,
2747 new_values => \@new_values,
2748 exact_match => $exact_match );
2749 @values = @{$val_ref};
2751 end maxeval
2753 # }}} maxeval
2755 # {{{ median
2757 =head2 fractions
2759 Usage:
2761 =for html <pre>
2763 my $fractions = $model_object -> fractions;
2765 =for html </pre>
2767 Arguments:
2769 =over 2
2771 =item colunm
2773 number
2775 =item column_head
2777 string
2779 =item problem_number
2781 integer
2783 =item return_occurences
2785 boolean
2787 =item ignore_missing
2789 boolean
2791 =back
2793 Description:
2795 fractions will return the fractions from data::fractions. It will find
2796 "column_head" in the $INPUT record instead of that data header as
2797 data::fractions does.
2799 =cut
2801 start median
2803 # Calls <I>median</I> on the data object of a specified
2804 # problem. See <I>data -> median</I> for details.
2805 my $column_number;
2806 my $extra_data_column;
2807 if ( defined $column_head ) {
2808 # Check normal data object first
2809 my ( $values_ref, $positions_ref ) = $self ->
2810 _get_option_val_pos ( problem_numbers => [$problem_number],
2811 name => $column_head,
2812 record_name => 'input',
2813 global_position => 1 );
2814 $column_number = $positions_ref -> [0];
2815 if ( not defined $column_number ) {
2816 # Next, check extra_data
2817 my $extra_data_headers = $self -> extra_data_headers;
2818 if ( defined $extra_data_headers and defined $extra_data_headers -> [0] ) {
2819 for ( my $i = 1; $i <= scalar @{$extra_data_headers->[0]}; $i++ ) {
2820 $extra_data_column = $i if ( $column_head eq $extra_data_headers->[0][$i-1] );
2824 'debug' -> die( message => "Unknown column \"$column_head\"" )
2825 unless ( defined $column_number or defined $extra_data_column );
2826 } else {
2827 $column_number = $column;
2830 if ( defined $column_number) {
2831 $median = $self -> {'datas'} -> [$problem_number-1] ->
2832 median( column => $column_number,
2833 unique_in_individual => $unique_in_individual );
2834 } else {
2835 $median = $self -> {'problems'} -> [$problem_number-1] -> extra_data ->
2836 median( column => $extra_data_column,
2837 unique_in_individual => $unique_in_individual );
2840 end median
2842 # }}}
2844 # {{{ max
2846 =head2 fractions
2848 Usage:
2850 =for html <pre>
2852 my $fractions = $model_object -> fractions;
2854 =for html </pre>
2856 Arguments:
2858 =over 2
2860 =item colunm
2862 number
2864 =item column_head
2866 string
2868 =item problem_number
2870 integer
2872 =item return_occurences
2874 boolean
2876 =item ignore_missing
2878 boolean
2880 =back
2882 Description:
2884 fractions will return the fractions from data::fractions. It will find
2885 "column_head" in the $INPUT record instead of that data header as
2886 data::fractions does.
2888 =cut
2890 start max
2892 # Calls <I>max</I> on the data object of a specified
2893 # problem. See <I>data -> max</I> for details.
2894 my $column_number;
2895 my $extra_data_column;
2896 if ( defined $column_head ) {
2897 # Check normal data object first
2898 my ( $values_ref, $positions_ref ) = $self ->
2899 _get_option_val_pos ( problem_numbers => [$problem_number],
2900 name => $column_head,
2901 record_name => 'input',
2902 global_position => 1 );
2903 $column_number = $positions_ref -> [0];
2904 if ( not defined $column_number ) {
2905 # Next, check extra_data
2906 my $extra_data_headers = $self -> extra_data_headers;
2907 if ( defined $extra_data_headers and defined $extra_data_headers -> [0] ) {
2908 for ( my $i = 1; $i <= scalar @{$extra_data_headers->[0]}; $i++ ) {
2909 $extra_data_column = $i if ( $column_head eq $extra_data_headers->[0][$i-1] );
2913 'debug' -> die( message => "Unknown column \"$column_head\"" )
2914 unless ( defined $column_number or defined $extra_data_column );
2915 } else {
2916 $column_number = $column;
2919 if ( defined $column_number) {
2920 $max = $self -> {'datas'} -> [$problem_number-1] ->
2921 max( column => $column_number );
2922 } else {
2923 $max = $self -> {'problems'} -> [$problem_number-1] -> extra_data ->
2924 max( column => $extra_data_column );
2927 end max
2929 # }}}
2931 # {{{ min
2933 =head2 fractions
2935 Usage:
2937 =for html <pre>
2939 my $fractions = $model_object -> fractions;
2941 =for html </pre>
2943 Arguments:
2945 =over 2
2947 =item colunm
2949 number
2951 =item column_head
2953 string
2955 =item problem_number
2957 integer
2959 =item return_occurences
2961 boolean
2963 =item ignore_missing
2965 boolean
2967 =back
2969 Description:
2971 fractions will return the fractions from data::fractions. It will find
2972 "column_head" in the $INPUT record instead of that data header as
2973 data::fractions does.
2975 =cut
2977 start min
2979 # Calls <I>min</I> on the data object of a specified
2980 # problem. See <I>data -> min</I> for details.
2981 my $column_number;
2982 my $extra_data_column;
2983 if ( defined $column_head ) {
2984 # Check normal data object first
2985 my ( $values_ref, $positions_ref ) = $self ->
2986 _get_option_val_pos ( problem_numbers => [$problem_number],
2987 name => $column_head,
2988 record_name => 'input',
2989 global_position => 1 );
2990 $column_number = $positions_ref -> [0];
2991 if ( not defined $column_number ) {
2992 # Next, check extra_data
2993 my $extra_data_headers = $self -> extra_data_headers;
2994 if ( defined $extra_data_headers and defined $extra_data_headers -> [0] ) {
2995 for ( my $i = 1; $i <= scalar @{$extra_data_headers->[0]}; $i++ ) {
2996 $extra_data_column = $i if ( $column_head eq $extra_data_headers->[0][$i-1] );
3000 'debug' -> die( message => "Unknown column \"$column_head\"" )
3001 unless ( defined $column_number or defined $extra_data_column );
3002 } else {
3003 $column_number = $column;
3006 if ( defined $column_number) {
3007 $min = $self -> {'datas'} -> [$problem_number-1] ->
3008 min( column => $column_number );
3009 } else {
3010 $min = $self -> {'problems'} -> [$problem_number-1] -> extra_data ->
3011 min( column => $extra_data_column );
3014 end min
3016 # }}}
3018 # {{{ name_val
3021 =head2 fractions
3023 Usage:
3025 =for html <pre>
3027 my $fractions = $model_object -> fractions;
3029 =for html </pre>
3031 Arguments:
3033 =over 2
3035 =item colunm
3037 number
3039 =item column_head
3041 string
3043 =item problem_number
3045 integer
3047 =item return_occurences
3049 boolean
3051 =item ignore_missing
3053 boolean
3055 =back
3057 Description:
3059 fractions will return the fractions from data::fractions. It will find
3060 "column_head" in the $INPUT record instead of that data header as
3061 data::fractions does.
3063 =cut
3065 start name_val
3067 # Usage:
3069 # @name_val = @{$modobj -> name_val( parameter_type => 'theta' )};
3071 # This basic usage takes one arguments and returns matched names and
3072 # estimated values of the specified parameter. The parameter_type argument
3073 # is mandatory.
3074 # The names are taken from
3075 # the labels of the parameters (se the labels method for specifications of
3076 # default labels) and the values are aquired from the output object bound
3077 # to the model object. If no output exists, the name_val method returns
3078 # undef.
3079 # @name_val will be a two-dimensional array of references to hashes using
3080 # the names from each problem as keys:
3081 # [[ref to hash1 ][ref to hash2][ref to hash3]...]
3083 # $modobj -> name_val( parameter_type => 'theta',
3084 # problem_numbers => [2,4] );
3086 # To get matched names and values of specific problems, the problem_numbers argument
3087 # can be used. It should be a reference to an array containing the numbers
3088 # of all problems whos names and values should be retrieved.
3090 # $modobj -> name_val( parameter_type => 'theta',
3091 # problem_numbers => [2,4],
3092 # parameter_numbers => [[1,3][4,6]]);
3094 # The retrieval can be even more specific by using the parameter_numbers
3095 # argument. It should be a reference to a two-dimensional array, where
3096 # the inner arrays holds the numbers of the parameters that should be
3097 # fetched. In the example above, parameters one and three from problem two
3098 # plus parameters four and six from problem four are retrieved.
3101 unless( $#problem_numbers > 0 ){
3102 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
3104 my @names = @{$self -> labels( parameter_type => $parameter_type,
3105 parameter_numbers => \@parameter_numbers,
3106 problem_numbers => \@problem_numbers )};
3107 my @values;
3108 if ( defined $self -> outputs -> [0] ) {
3109 my $accessor = $parameter_type.'s';
3110 @values = @{$self -> outputs -> [0] ->
3111 $accessor( problems => \@problem_numbers,
3112 parameter_numbers => \@parameter_numbers )};
3113 # my @problems = @{$self -> {'problems'}};
3114 # foreach my $i ( @problem_numbers ) {
3115 # if ( defined $problems[ $i-1 ] ) {
3116 # my $pn_ref = $#parameter_numbers >= 0 ? \@{$parameter_numbers[ $i-1 ]} : [];
3117 # push( @names_values,
3118 # $problems[ $i-1 ] ->
3119 # name_val( parameter_type => $parameter_type,
3120 # parameter_numbers => $pn_ref ) );
3121 # } else {
3122 # die "Model -> name_val: Problem number $i does not exist!\n";
3126 # if ( scalar @{$self -> {'outputs'}} > 0 ) {
3127 # my $outobj = $self -> {'outputs'} -> [0];
3130 'debug' -> die( message => "The number of problems retrieved from the model" .
3131 " do not match the ones retrived from the output" ) unless( $#names == $#values );
3132 for( my $i = 0; $i <= $#names; $i++ ) {
3133 'debug' -> die( message => "Problem " . $i+1 .
3134 " The number of parameters retrieved from the model (".scalar @{$names[$i]}.
3135 ") do not match the ones retrived from the output (".
3136 scalar @{$values[$i][0]}.")" )
3137 unless( scalar @{$names[$i]} == scalar @{$values[$i][0]} );
3138 my @prob_nv = ();
3139 for( my $j = 0; $j < scalar @{$values[$i]}; $j++ ){
3140 my %nv = ();
3141 for( my $k = 0; $k < scalar @{$names[$i]}; $k++ ){
3142 $nv{$names[$i][$k]} = $values[$i][$j][$k];
3144 push( @prob_nv, \%nv );
3146 push( @names_values, \@prob_nv );
3149 end name_val
3151 # }}} name_val
3153 # {{{ nproblems
3155 =head2 fractions
3157 Usage:
3159 =for html <pre>
3161 my $fractions = $model_object -> fractions;
3163 =for html </pre>
3165 Arguments:
3167 =over 2
3169 =item colunm
3171 number
3173 =item column_head
3175 string
3177 =item problem_number
3179 integer
3181 =item return_occurences
3183 boolean
3185 =item ignore_missing
3187 boolean
3189 =back
3191 Description:
3193 fractions will return the fractions from data::fractions. It will find
3194 "column_head" in the $INPUT record instead of that data header as
3195 data::fractions does.
3197 =cut
3199 start nproblems
3201 # nproblems returns the number of problems in the modelobject.
3203 $number_of_problem = scalar @{$self -> {'problems'}};
3205 end nproblems
3207 # }}} nproblems
3209 # {{{ nthetas
3211 =head2 fractions
3213 Usage:
3215 =for html <pre>
3217 my $fractions = $model_object -> fractions;
3219 =for html </pre>
3221 Arguments:
3223 =over 2
3225 =item colunm
3227 number
3229 =item column_head
3231 string
3233 =item problem_number
3235 integer
3237 =item return_occurences
3239 boolean
3241 =item ignore_missing
3243 boolean
3245 =back
3247 Description:
3249 fractions will return the fractions from data::fractions. It will find
3250 "column_head" in the $INPUT record instead of that data header as
3251 data::fractions does.
3253 =cut
3255 start nthetas
3257 # returns the number of thetas in the model for the given
3258 # problem number.
3259 $nthetas = $self -> _parameter_count( 'record' => 'theta', 'problem_number' => $problem_number );
3261 end nthetas
3263 # }}} nthetas
3265 # {{{ nomegas
3267 =head2 fractions
3269 Usage:
3271 =for html <pre>
3273 my $fractions = $model_object -> fractions;
3275 =for html </pre>
3277 Arguments:
3279 =over 2
3281 =item colunm
3283 number
3285 =item column_head
3287 string
3289 =item problem_number
3291 integer
3293 =item return_occurences
3295 boolean
3297 =item ignore_missing
3299 boolean
3301 =back
3303 Description:
3305 fractions will return the fractions from data::fractions. It will find
3306 "column_head" in the $INPUT record instead of that data header as
3307 data::fractions does.
3309 =cut
3311 start nomegas
3313 # returns the number of omegas in the model for the given
3314 # problem number.
3315 # $nomegas = $self -> _parameter_count( 'record' => 'omega', 'problem_number' => $problem_number );
3316 unless( $#problem_numbers >= 0 ){
3317 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
3320 my @problems = @{$self -> {'problems'}};
3321 foreach my $i ( @problem_numbers ) {
3322 if ( defined $problems[ $i-1 ] ) {
3323 push( @nomegas, $problems[ $i-1 ] -> nomegas );
3324 } else {
3325 'debug' -> die( "Problem number $i does not exist." );
3329 end nomegas
3331 # }}} nomegas
3333 # {{{ nsigmas
3335 =head2 fractions
3337 Usage:
3339 =for html <pre>
3341 my $fractions = $model_object -> fractions;
3343 =for html </pre>
3345 Arguments:
3347 =over 2
3349 =item colunm
3351 number
3353 =item column_head
3355 string
3357 =item problem_number
3359 integer
3361 =item return_occurences
3363 boolean
3365 =item ignore_missing
3367 boolean
3369 =back
3371 Description:
3373 fractions will return the fractions from data::fractions. It will find
3374 "column_head" in the $INPUT record instead of that data header as
3375 data::fractions does.
3377 =cut
3379 start nsigmas
3381 # returns the number of sigmas in the model for the given problem number.
3383 # $nsigmas = $self -> _parameter_count( 'record' => 'sigma', 'problem_number' => $problem_number );
3385 unless( $#problem_numbers >= 0 ){
3386 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
3389 my @problems = @{$self -> {'problems'}};
3390 foreach my $i ( @problem_numbers ) {
3391 if ( defined $problems[ $i-1 ] ) {
3392 push( @nsigmas, $problems[ $i-1 ] -> nsigmas );
3393 } else {
3394 'debug' -> die( "Problem number $i does not exist." );
3398 end nsigmas
3400 # }}} nsigmas
3402 # {{{ outputfile
3404 =head2 fractions
3406 Usage:
3408 =for html <pre>
3410 my $fractions = $model_object -> fractions;
3412 =for html </pre>
3414 Arguments:
3416 =over 2
3418 =item colunm
3420 number
3422 =item column_head
3424 string
3426 =item problem_number
3428 integer
3430 =item return_occurences
3432 boolean
3434 =item ignore_missing
3436 boolean
3438 =back
3440 Description:
3442 fractions will return the fractions from data::fractions. It will find
3443 "column_head" in the $INPUT record instead of that data header as
3444 data::fractions does.
3446 =cut
3448 start outputfile
3450 # Usage:
3452 # This method is a (partially) automatically generated accessor for the
3453 # outputfile attribute of the model class. Since no named argument is needed
3454 # for accessors, the two possible ways of calling outputfile are:
3456 # $modelObject -> outputfile( 'newfilename.lst' );
3458 # $outputfilename = $modelObject -> outputfile;
3460 # The first alternative sets a new name for the output file, and the second
3461 # retrieves the value.
3463 # The extra feature for this accessor, compared to other accessors, is that
3464 # if a new name is given, the accessor tries to create a new output object
3465 # based on this.
3467 if( defined $parm ) {
3468 $self -> {'outputs'} =
3469 [ output ->
3470 new( filename => $parm,
3471 ignore_missing_files => $self -> {'ignore_missing_files'},
3472 target => $self -> {'target'},
3473 model_id => $self -> {'model_id'} ) ];
3476 end outputfile
3478 # }}} outputfile
3480 # {{{ pk
3482 =head2 fractions
3484 Usage:
3486 =for html <pre>
3488 my $fractions = $model_object -> fractions;
3490 =for html </pre>
3492 Arguments:
3494 =over 2
3496 =item colunm
3498 number
3500 =item column_head
3502 string
3504 =item problem_number
3506 integer
3508 =item return_occurences
3510 boolean
3512 =item ignore_missing
3514 boolean
3516 =back
3518 Description:
3520 fractions will return the fractions from data::fractions. It will find
3521 "column_head" in the $INPUT record instead of that data header as
3522 data::fractions does.
3524 =cut
3526 start pk
3528 # sets or gets the pk code for a given problem in the
3529 # model object. The new_pk argument should be an array where
3530 # each element contains a row of a valid NONMEM $PK block,
3532 my @prob = @{$self -> problems};
3534 unless( defined $prob[$problem_number - 1] ){
3535 'debug' -> die( message => "Problem number $problem_number does not exist" );
3538 my $pks = $prob[$problem_number - 1] -> pks;
3539 if( scalar @new_pk > 0 ) {
3540 if( defined $pks and scalar @{$pks} > 0 ){
3541 $prob[$problem_number - 1] -> pks -> [0] -> code(\@new_pk);
3542 } else {
3543 'debug' -> die( message => "No \$PK record" );
3545 } else {
3546 if ( defined $pks and scalar @{$pks} > 0 ) {
3547 @pk = @{$prob[$problem_number - 1] -> pks -> [0] -> code};
3551 end pk
3553 # }}} pk
3555 # {{{ pred
3557 =head2 fractions
3559 Usage:
3561 =for html <pre>
3563 my $fractions = $model_object -> fractions;
3565 =for html </pre>
3567 Arguments:
3569 =over 2
3571 =item colunm
3573 number
3575 =item column_head
3577 string
3579 =item problem_number
3581 integer
3583 =item return_occurences
3585 boolean
3587 =item ignore_missing
3589 boolean
3591 =back
3593 Description:
3595 fractions will return the fractions from data::fractions. It will find
3596 "column_head" in the $INPUT record instead of that data header as
3597 data::fractions does.
3599 =cut
3601 start pred
3603 # Sets or gets the pred code for a given problem in the model
3604 # object. See L</pk> for details.
3605 my @prob = @{$self -> problems};
3607 unless( defined $prob[$problem_number - 1] ){
3608 'debug' -> die( message => "problem number $problem_number does not exist" );
3611 if( scalar @new_pred > 0 ) {
3612 if( defined $prob[$problem_number - 1] -> preds ){
3613 $prob[$problem_number - 1] -> preds -> [0] -> code(\@new_pred);
3614 } else {
3615 'debug' -> die( message => "No \$PRED record" );
3617 } else {
3618 if ( defined $prob[$problem_number - 1] -> preds ) {
3619 @pred = @{$prob[$problem_number - 1] -> preds -> [0] -> code};
3620 } else {
3621 'debug' -> die( message => "No \$PRED record" );
3625 end pred
3627 # }}} pred
3629 # {{{ print
3631 =head2 fractions
3633 Usage:
3635 =for html <pre>
3637 my $fractions = $model_object -> fractions;
3639 =for html </pre>
3641 Arguments:
3643 =over 2
3645 =item colunm
3647 number
3649 =item column_head
3651 string
3653 =item problem_number
3655 integer
3657 =item return_occurences
3659 boolean
3661 =item ignore_missing
3663 boolean
3665 =back
3667 Description:
3669 fractions will return the fractions from data::fractions. It will find
3670 "column_head" in the $INPUT record instead of that data header as
3671 data::fractions does.
3673 =cut
3675 start print
3677 # Prints the formatted model to standard out.
3679 my ( @formatted );
3680 foreach my $problem ( @{$self -> {'problems'}} ) {
3681 push( @formatted, $problem -> format_problem );
3683 for ( @formatted ) {
3684 print;
3687 end print
3689 # }}} print
3691 # {{{ problem_structure
3693 start problem_structure
3695 my ( $val, $pos ) = $self -> _option_val_pos( record_name => 'simulation',
3696 name => 'SUBPROBLEMS' );
3697 if( defined $val ) {
3698 my @vals = @{$val};
3699 for( my $i = 0; $i <= $#vals; $i++ ) {
3700 if( defined $vals[$i] ) {
3701 if( scalar @{$vals[$i]} > 0 ) {
3702 $subproblems[$i] = $vals[$i][0];
3703 } else {
3704 $subproblems[$i] = 1;
3706 } else {
3707 $subproblems[$i] = 1;
3712 end problem_structure
3714 # }}} problem_structure
3716 # {{{ randomize_inits
3718 =head2 fractions
3720 Usage:
3722 =for html <pre>
3724 my $fractions = $model_object -> fractions;
3726 =for html </pre>
3728 Arguments:
3730 =over 2
3732 =item colunm
3734 number
3736 =item column_head
3738 string
3740 =item problem_number
3742 integer
3744 =item return_occurences
3746 boolean
3748 =item ignore_missing
3750 boolean
3752 =back
3754 Description:
3756 fractions will return the fractions from data::fractions. It will find
3757 "column_head" in the $INPUT record instead of that data header as
3758 data::fractions does.
3760 =cut
3762 start randomize_inits
3764 foreach my $prob ( @{$self -> {'problems'}} ) {
3765 $prob -> set_random_inits ( degree => $degree );
3768 end randomize_inits
3770 # }}}
3773 # {{{ record
3775 =head2 fractions
3777 Usage:
3779 =for html <pre>
3781 my $fractions = $model_object -> fractions;
3783 =for html </pre>
3785 Arguments:
3787 =over 2
3789 =item colunm
3791 number
3793 =item column_head
3795 string
3797 =item problem_number
3799 integer
3801 =item return_occurences
3803 boolean
3805 =item ignore_missing
3807 boolean
3809 =back
3811 Description:
3813 fractions will return the fractions from data::fractions. It will find
3814 "column_head" in the $INPUT record instead of that data header as
3815 data::fractions does.
3817 =cut
3819 start record
3821 # If the argument new_data is given, record sets new_data in
3822 # the model objects member specified with record_name. The
3823 # format of new_data is an array of strings, where each
3824 # element corresponds to a line of code as it would have
3825 # looked like in a valid NONMEM modelfile. If new_data is left
3826 # undefined, record returns lines of code belonging to the
3827 # record specified by record_name in a format that is valid in
3828 # a NONMEM modelfile.
3830 my @problems = @{$self -> {'problems'}};
3831 my $records;
3833 if ( defined $problems[ $problem_number - 1 ] ) {
3834 if ( scalar(@new_data) > 0 ){
3835 my $rec_class = "model::problem::$record_name";
3836 my $record = $rec_class -> new('record_arr' => \@new_data );
3837 } else {
3838 $record_name .= 's';
3839 $records = $problems[ $problem_number - 1 ] -> {$record_name};
3840 foreach my $record( @{$records} ){
3841 push(@data, $record -> _format_record);
3846 end record
3848 # }}} record
3850 # {{{ remove_inits
3852 =head2 fractions
3854 Usage:
3856 =for html <pre>
3858 my $fractions = $model_object -> fractions;
3860 =for html </pre>
3862 Arguments:
3864 =over 2
3866 =item colunm
3868 number
3870 =item column_head
3872 string
3874 =item problem_number
3876 integer
3878 =item return_occurences
3880 boolean
3882 =item ignore_missing
3884 boolean
3886 =back
3888 Description:
3890 fractions will return the fractions from data::fractions. It will find
3891 "column_head" in the $INPUT record instead of that data header as
3892 data::fractions does.
3894 =cut
3896 start remove_inits
3898 # Usage
3900 # $model -> remove_inits( type => 'theta',
3901 # indexes => [1,2,5,6] )
3904 # In all cases the type must be set to theta. Removing Omegas in
3905 # Sigmas is not allowed, (If need that feature, send us a
3906 # mail). In the above example the thetas 1, 2, 5 and 6 will be
3907 # removed from the modelfile. Notice that this alters the theta
3908 # numbering, so if you later decide that theta number 7 must be
3909 # removed as well, you must calculate its new position in the
3910 # file. In this case the new number would be 3. Also notice that
3911 # numbering starts with 1.
3913 # $model -> remove_inits( type => 'theta',
3914 # labels => ['V', 'CL'] )
3917 # If you have specified labels in you modelfiles(a label is
3918 # string inside a comment on the same row as the theta) you can
3919 # specify an array with labels, and the corresponding theta, if
3920 # it exists, will be removed. This is a much better approach
3921 # since you don't need to know where in order the theta you wish
3922 # to remove appears. If you specify both labels and indexes, the
3923 # indexes will be ignored.
3925 'debug' -> die( message => 'does not have the functionality for removing $OMEGA or $SIGMA options yet' )
3926 if ( $type eq 'omega' or $type eq 'sigma' );
3927 my $accessor = $type.'s';
3929 # First pick out a referens to the theta records array.
3930 my $inits_ref = $self -> problems -> [$problem_number -1] -> $accessor;
3932 # If we have any thetas at all:
3933 if ( defined $inits_ref ) {
3934 my @inits = @{$inits_ref};
3936 # If labels are specified, we translate the labels into
3937 # indexes.
3938 if ( scalar @labels > 0 ) {
3939 @indexes = ();
3940 my $i = 1;
3941 # Loop over theta records
3942 foreach my $init ( @inits ) {
3943 # Loop over the individual thetas inside
3944 foreach my $option ( @{$init -> options} ) {
3945 # Loop over all given labels.
3946 foreach my $label ( @labels ) {
3947 # Push the index number if a given label match the
3948 # theta label
3949 push( @indexes, $i ) if ( $option -> label eq $label);
3951 # $i is the count of thetas so far
3952 $i++;
3957 # We don't really remove thetas, we do a loop over all thetas
3958 # and recording which we like to keep. We do that by selecting
3959 # an index, from @indexes, that shall be removed and loop over
3960 # the thetas, all thetas that doesn't match the index are
3961 # stored in @keep_options. When we find a theta that matches,
3962 # we pick a new index and continue the loop. So by makeing
3963 # sure that @indexes is sorted, we only need to loop over the
3964 # thetas once.
3966 @indexes = sort {$a <=> $b} @indexes;
3968 my $index = 0;
3969 my $nr_options = 1;
3970 my @keep_records;
3972 # Loop over all records
3973 RECORD_LOOP: foreach my $record ( @inits ){
3974 my @keep_options = ();
3975 # Loop over all thetas
3976 foreach my $option ( @{$record -> options} ) {
3977 if( $indexes[ $index ] == $nr_options ){
3978 # If a theta matches an index, we take the next index
3979 # and forget the theta.
3980 unless( $index > $#indexes ){
3981 $index++;
3983 } else {
3984 # Otherwise we rember it.
3985 push(@keep_options,$option);
3987 $nr_options++;
3989 if( scalar(@keep_options) > 0 ){
3990 # If we remember some thetas, we must also remember the
3991 # record which they are in.
3992 $record -> options( \@keep_options );
3993 push( @keep_records, $record );
3997 # Set the all kept thetas back into the modelobject.
3998 @{$inits_ref} = @keep_records;
4000 } else {
4001 'debug' -> die( message => "No init of type $type defined" );
4004 end remove_inits
4006 # }}}
4008 # {{{ restore_inits
4010 =head2 fractions
4012 Usage:
4014 =for html <pre>
4016 my $fractions = $model_object -> fractions;
4018 =for html </pre>
4020 Arguments:
4022 =over 2
4024 =item colunm
4026 number
4028 =item column_head
4030 string
4032 =item problem_number
4034 integer
4036 =item return_occurences
4038 boolean
4040 =item ignore_missing
4042 boolean
4044 =back
4046 Description:
4048 fractions will return the fractions from data::fractions. It will find
4049 "column_head" in the $INPUT record instead of that data header as
4050 data::fractions does.
4052 =cut
4054 start restore_inits
4056 # restore_inits brings back initial values previously stored
4057 # using store_inits. This method pair allows a user to store
4058 # the currents initial values in a backup, replace them with
4059 # temporary values and later restore them.
4061 if ( defined $self -> {'problems'} ) {
4062 foreach my $problem ( @{$self -> {'problems'}} ){
4063 $problem -> restore_inits;
4067 end restore_inits
4069 # }}} restore_inits
4071 # {{{ store_inits
4073 =head2 fractions
4075 Usage:
4077 =for html <pre>
4079 my $fractions = $model_object -> fractions;
4081 =for html </pre>
4083 Arguments:
4085 =over 2
4087 =item colunm
4089 number
4091 =item column_head
4093 string
4095 =item problem_number
4097 integer
4099 =item return_occurences
4101 boolean
4103 =item ignore_missing
4105 boolean
4107 =back
4109 Description:
4111 fractions will return the fractions from data::fractions. It will find
4112 "column_head" in the $INPUT record instead of that data header as
4113 data::fractions does.
4115 =cut
4117 start store_inits
4119 # store_inits stores initial values that can later be
4120 # brought back using restore_inits. See L</restore_inits>.
4122 if ( defined $self -> {'problems'} ) {
4123 foreach my $problem ( @{$self -> {'problems'}} ){
4124 $problem -> store_inits;
4128 end store_inits
4130 # }}} store_inits
4132 # {{{ synchronize
4134 start synchronize
4136 # Synchronize checks the I<synced> object attribute to see
4137 # if the model is in sync with its corresponding file, given
4138 # by the objetc attribute I<filename>. If not, it checks if
4139 # the model contains any defined problems and if it does, it
4140 # writes the formatted model to disk, overwriting any
4141 # existing file of name I<filename>. If no problem is
4142 # defined, synchronize tries to parse the file I<filename>
4143 # and set the object internals to match it.
4144 unless( $self -> {'synced'} ){
4145 if( defined $self -> {'problems'} and
4146 scalar @{$self -> {'problems'}} > 0 ){
4147 $self -> _write;
4148 } else {
4149 if( -e $self -> full_name ){
4150 $self -> _read_problems;
4151 } else {
4152 return;
4156 $self -> {'synced'} = 1;
4158 end synchronize
4160 # }}} synchronize
4162 # {{{ flush
4163 start flush
4164 # synchronizes the object with the file on disk and empties
4165 # most of the objects attributes to save memory.
4166 if( defined $self -> {'problems'} and
4167 ( !$self -> {'synced'} or $force ) ) {
4168 $self -> _write;
4170 $self -> {'problems'} = undef;
4171 $self -> {'synced'} = 0;
4173 end flush
4174 # }}} flush
4176 # {{{ target
4177 start target
4179 if ( $parm eq 'disk' ) {
4180 $self -> {'target'} = 'disk';
4181 $self -> flush;
4182 } elsif ( $parm eq 'mem' and $self -> {'target'} eq 'disk' ) {
4183 $self -> {'target'} = 'mem';
4184 $self -> synchronize;
4187 end target
4188 # }}}
4190 # {{{ table_names
4192 =head2 fractions
4194 Usage:
4196 =for html <pre>
4198 my $fractions = $model_object -> fractions;
4200 =for html </pre>
4202 Arguments:
4204 =over 2
4206 =item colunm
4208 number
4210 =item column_head
4212 string
4214 =item problem_number
4216 integer
4218 =item return_occurences
4220 boolean
4222 =item ignore_missing
4224 boolean
4226 =back
4228 Description:
4230 fractions will return the fractions from data::fractions. It will find
4231 "column_head" in the $INPUT record instead of that data header as
4232 data::fractions does.
4234 =cut
4236 start table_names
4238 # Usage:
4240 # @tableNames = @{$modobj -> table_names};
4242 # This basic usage takes no arguments and returns the value of
4243 # the FILE option in the $TABLE NONMEM record of each
4244 # problem. @tableNames will be a two dimensional array:
4246 # [[tableName_prob1][tableName_prob2][tableName_prob3]...]
4249 # If the I<new_names> argument of table_names is given, the
4250 # values of the FILE options will be changed.
4252 # To set the FILE of specific problems, the I<problem_numbers>
4253 # argument can be used. It should be a reference to an array
4254 # containing the numbers of all problems where the FILE should
4255 # be changed or retrieved. If specified, the size of
4256 # I<new_names> must be the same as the size of
4257 # I<problem_numbers>.
4259 # The I<ignore_missing_files> boolean argument can be used to
4260 # set names of table that does not exist yet (e.g. before a
4261 # run has been performed).
4263 my ( $name_ref, $junk ) = $self ->
4264 _option_val_pos( name => 'FILE',
4265 record_name => 'table',
4266 problem_numbers => \@problem_numbers,
4267 new_values => \@new_names );
4268 if ( $#new_names >= 0 ) {
4269 my @problems = @{$self -> {'problems'}};
4270 unless( $#problem_numbers > 0 ){
4271 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
4273 foreach my $i ( @problem_numbers ) {
4274 $problems[$i-1] -> _read_table_files( ignore_missing_files => $ignore_missing_files || $self -> {'ignore_missing_output_files'});
4277 @names = @{$name_ref};
4279 end table_names
4281 # }}} table_names
4283 # {{{ table_files
4285 =head2 fractions
4287 Usage:
4289 =for html <pre>
4291 my $fractions = $model_object -> fractions;
4293 =for html </pre>
4295 Arguments:
4297 =over 2
4299 =item colunm
4301 number
4303 =item column_head
4305 string
4307 =item problem_number
4309 integer
4311 =item return_occurences
4313 boolean
4315 =item ignore_missing
4317 boolean
4319 =back
4321 Description:
4323 fractions will return the fractions from data::fractions. It will find
4324 "column_head" in the $INPUT record instead of that data header as
4325 data::fractions does.
4327 =cut
4329 start table_files
4331 # Usage:
4333 # @table_files = @{$modobj -> table_files};
4335 # This basic usage takes no arguments and returns the table
4336 # files objects for all problems. @table_files will be a
4337 # two dimensional array:
4339 # [[table_file_object_prob1][table_file_object_prob2]...]
4342 # To retrieve the table file objects from specific problems,
4343 # the I<problem_numbers> argument can be used. It should be
4344 # a reference to an array containing the numbers of all
4345 # problems from which the table file objects should be
4346 # retrieved.
4348 unless( $#problem_numbers > 0 ){
4349 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
4351 my @problems = @{$self -> {'problems'}};
4352 foreach my $i ( @problem_numbers ) {
4353 if ( defined $problems[ $i-1 ] ) {
4354 push( @table_files, $problems[$i-1] -> table_files );
4355 } else {
4356 'debug' -> die( message => "Problem number $i does not exist!" );
4360 end table_files
4362 # }}}
4364 # {{{ units
4366 =head2 fractions
4368 Usage:
4370 =for html <pre>
4372 my $fractions = $model_object -> fractions;
4374 =for html </pre>
4376 Arguments:
4378 =over 2
4380 =item colunm
4382 number
4384 =item column_head
4386 string
4388 =item problem_number
4390 integer
4392 =item return_occurences
4394 boolean
4396 =item ignore_missing
4398 boolean
4400 =back
4402 Description:
4404 fractions will return the fractions from data::fractions. It will find
4405 "column_head" in the $INPUT record instead of that data header as
4406 data::fractions does.
4408 =cut
4410 start units
4412 # Sets or gets the units of a (number of) parameter(s). The
4413 # unit is not a proper NONMEM syntax but is recognized by
4414 # the PsN model class. A unit (and a label) can be specified
4415 # as a comments after a parameter definition. e.g.:
4417 # $THETA (0,13.2,100) ; MTT; h
4419 # which will give this theta the label I<MTT> and unit I<h>.
4420 @units = @{ $self -> _init_attr( parameter_type => $parameter_type,
4421 parameter_numbers => \@parameter_numbers,
4422 problem_numbers => \@problem_numbers,
4423 new_values => \@new_values,
4424 type => 'unit')};
4426 end units
4428 # }}} units
4430 # {{{ update_inits
4433 =head2 fractions
4435 Usage:
4437 =for html <pre>
4439 my $fractions = $model_object -> fractions;
4441 =for html </pre>
4443 Arguments:
4445 =over 2
4447 =item colunm
4449 number
4451 =item column_head
4453 string
4455 =item problem_number
4457 integer
4459 =item return_occurences
4461 boolean
4463 =item ignore_missing
4465 boolean
4467 =back
4469 Description:
4471 fractions will return the fractions from data::fractions. It will find
4472 "column_head" in the $INPUT record instead of that data header as
4473 data::fractions does.
4475 =cut
4477 start update_inits
4479 # Usage:
4481 # $modobj -> update_inits ( from_output => $outobj );
4483 # alt
4485 # $modobj -> update_inits ( from_output_file => $outfile );
4487 # This basic usage takes the parameter estimates from the
4488 # output object I<$outobj> or from the output file I<$outfile>
4489 # and updates the initial estimates in the model object
4490 # I<$modobj>. The number of problems and parameters must be
4491 # the same in the model and output objects. If there exist
4492 # more than one subproblem per problem in the output object,
4493 # only the estimates from the first subproblem will be
4494 # transferred.
4496 # $modobj -> update_inits ( from_output => $outobj,
4497 # ignore_missing_parameters => 1 );
4499 # If the ignore_missing_parameters argument is set to 1, the number of
4500 # parameters in the model and output objects do not need to match. The
4501 # parameters that exist in both objects are used for the update of the
4502 # model object.
4504 # $modobj -> update_inits ( from_output => $outobj,
4505 # from_model => $from_modobj );
4507 # If the from_model argument is given, update_inits tries to match the
4508 # parameter names (labels) given in $from_modobj and $modobj and
4509 # and thereafter updating the $modobj object. See L</units> and L</labels>.
4512 my ( %labels, @own_labels, @from_labels );
4513 'debug' -> die( message => "No output object defined and" .
4514 " no output object found through the model object specified." )
4515 unless ( ( defined $from_model and
4516 ( defined $from_model -> outputs and
4517 defined @{$from_model -> outputs}[0] ) ) or
4518 defined $from_output or
4519 defined $from_output_file );
4520 if ( defined $from_output ) {
4521 'debug' -> warn( level => 2,
4522 message => "using output object ".
4523 "specified as argument\n" );
4524 } elsif ( defined $from_output_file ) {
4525 $from_output = output -> new( filename => $from_output_file );
4526 } else {
4527 $from_output = @{$from_model -> outputs}[0];
4530 my @params = ();
4531 if( $update_thetas ){
4532 push( @params, 'theta' );
4534 if( $update_omegas ) {
4535 push( @params, 'omega' );
4537 if( $update_sigmas ) {
4538 push( @params, 'sigma' );
4541 foreach my $param ( @params ) {
4542 # Get own labels and from labels
4543 if ( defined $from_model ) {
4544 @own_labels = @{$self -> labels( parameter_type => $param )};
4546 @from_labels = @{$from_model -> labels( parameter_type => $param )};
4547 'debug' -> die( message => "The number of problems are not the same in from-model ".
4548 $from_model -> full_name." (".
4549 ($#from_labels+1).")".
4550 " and the model to be updated ".
4551 $self -> full_name." (".
4552 ($#own_labels+1).")" ) unless ( $#own_labels == $#from_labels );
4553 } else {
4554 @own_labels = @{$self -> labels( parameter_type => $param,
4555 generic => 1 )};
4556 @from_labels = @{$from_output -> labels( parameter_type => $param )};
4557 'debug' -> die( message => "The number of problems are not the same in from-output ".
4558 $from_output -> full_name." (".
4559 ($#from_labels+1).")".
4560 " and the model to be updated ".
4561 $self -> full_name." (".
4562 ($#own_labels+1).")" ) unless ( $#own_labels == $#from_labels );
4565 # Loop over the problems:
4566 my $accessor = $param.'s';
4567 # Since initial estimates are specified on the problem level and not on
4568 # the subproblem level we use the estimates from the outputs first subproblem
4569 my @from_values = @{$from_output -> $accessor ( subproblems => [1] )};
4570 # {{{ Omega and Sigma update section
4572 # The functionality that has been commented out because it
4573 # fails when omegas are zero. This functionality should be
4574 # moved to output::problem::subproblem (2005-02-09) TODO
4576 # if ($param eq 'omega' or $param eq 'sigma')
4578 # #print "FL: ", Dumper @from_labels;
4579 # #print "OL: ", Dumper @own_labels;
4580 # print "FV: $param Before " . Dumper(@from_values) . "\n";
4581 # #Fix omegas and sigmas so that the correlation between elements <=1
4582 # my $raw_accessor = "raw_" . $accessor;
4583 # my @raw_from_values = @{$from_output -> $raw_accessor(subproblems => [1])};
4584 # my ($i,$j);
4585 # for (my $a=0; $a<scalar(@from_values); $a++)
4587 # my $prob_values = $from_values[$a];
4588 # my $raw_prob_values = $raw_from_values[$a];
4589 # for (my $b=0; $b<scalar(@{$prob_values}); $b++)
4591 # my $values = $prob_values->[$b];
4592 # my $raw_values = $raw_prob_values->[$b];
4593 # my $counter = 0;
4594 # #Find out the n*n-matrix size (pq-formula)
4595 # my $n = int(sqrt(1/4+scalar(@{$raw_values})*2)-1/2);
4596 # for ($i=0; $i<$n; $i++)
4598 # for ($j=0; $j<$n; $j++)
4600 # if ( $j<=$i && $raw_values->[$i*($i+1)/2+$j]!=0 )
4602 # #print "Omega value = " . @other_val[$counter] . "\n";
4603 # $counter++;
4605 # #Only check the low-triangular off-diagonals of the omega matrix
4606 # #omega(i,j)>=sqrt(omega(i,i)*omega(j,j))
4607 # if ($j<=$i && $j!=$i &&
4608 # ($raw_values->[$i*($i+1)/2+$j]>=sqrt(
4609 # $raw_values->[$i*($i+1)/2+$i]*$raw_values->[$j*($j+1)/2+$j])))
4611 # print "Changing raw value at $i, $j: " . $raw_values->[$i*($i+1)/2+$j] ." to ".
4612 # (int(10000*sqrt($raw_values->[$i*($i+1)/2+$i]*$raw_values->[$j*($j+1)/2+$j]))/10000) ."\n";
4613 # #print "At index ($i,$j)\n" if ($self->{'debug'});
4614 # $raw_values->[$i*($i+1)/2+$j]=int(10000*sqrt(
4615 # $raw_values->[$i*($i+1)/2+$i]*$raw_values->[$j*($j+1)/2+$j]))/10000;
4616 # print "Changing omega value " . $values->[$counter-1] . " to " . $raw_values->[$i*($i+1)/2+$j] ."\n";
4617 # $values->[$counter-1] = $raw_values->[$i*($i+1)/2+$j];
4623 # #print "FL: ", Dumper @from_labels;
4624 # #print "OL: ", Dumper @own_labels;
4625 # print "FV: $param After ", Dumper(@from_values), "\n";
4626 # die;
4629 # }}}
4631 for ( my $i = 0; $i <= $#own_labels; $i++ ) {
4632 unless ( $ignore_missing_parameters ) {
4633 my $from_name = defined $from_model ? $from_model -> filename :
4634 $from_output -> filename;
4635 'debug' -> die( message => "Model -> update_inits: The number of ".$param.
4636 "s are not the same in from-model (" . $from_name .
4637 "): " . scalar @{$from_labels[$i]} .
4638 ", and the model to be updated (" . $self -> {'filename'} .
4639 "): " . scalar @{$own_labels[$i]} )
4640 unless ( scalar @{$own_labels[$i]} ==
4641 scalar @{$from_labels[$i]} );
4644 for ( my $j = 0; $j < scalar @{$from_labels[$i]}; $j++ ) {
4645 for ( my $k = 0; $k < scalar @{$own_labels[$i]}; $k++ ) {
4646 if ( $from_labels[$i][$j] eq $own_labels[$i][$k] ){
4647 $labels{$k+1} = $from_values[$i][0][$j];
4652 my @own_idxs = keys( %labels );
4653 my @from_vals;
4654 for(my $i=0; $i <= $#own_idxs; $i++){
4655 @from_vals[$i] = $labels{ $own_idxs[$i] };
4658 $self -> initial_values( problem_numbers => [$i+1],
4659 parameter_type => $param,
4660 parameter_numbers => [\@own_idxs],
4661 new_values => [\@from_vals] );
4665 end update_inits
4667 # }}} update_inits
4669 # {{{ upper_bounds
4671 start upper_bounds
4673 # upper_bounds either sets or gets the initial values of the
4674 # parameter specified in I<parameter_type> for each
4675 # subproblem specified in I<problem_numbers>. For each
4676 # element in I<problem_numbers> there must be an array in
4677 # I<parameter_numbers> that specify the indices of the
4678 # parameters in the subproblem for which the upper bounds
4679 # are set, replaced or retrieved.
4681 @upper_bounds = @{ $self -> _init_attr
4682 ( parameter_type => $parameter_type,
4683 parameter_numbers => \@parameter_numbers,
4684 problem_numbers => \@problem_numbers,
4685 new_values => \@new_values,
4686 attribute => 'upbnd')};
4688 end upper_bounds
4690 # }}} upper_bounds
4692 # {{{ clean_extra_data_code
4693 start clean_extra_data_code
4696 # This method cleans out old code for extra data. It searches
4697 # all subroutine statements in all problems for external
4698 # subroutines named "get_sub" and "reader" which are added by
4699 # "add_extra_data_code".
4701 foreach my $problem( @{$self -> {'problems'}} ){
4702 if ( defined $problem -> subroutines and defined $problem -> subroutines -> [0] -> options) {
4703 foreach my $option ( @{$problem -> subroutines -> [0] -> options} ){
4704 if( lc($option -> name) eq 'other'){
4705 if( lc($option -> value) =~ /get_sub|reader/ ){
4707 # If we find "get_sub" or "reader" we remove
4708 # everything between "IMPORTING COVARIATE DATA" and
4709 # "IMPORTING COVARIATE DATA END" by finding the
4710 # indexes in the code array and and splicing it out.
4712 my $code;
4713 if( $problem -> pks ){
4714 # If the code is in a pk block:
4715 $code = $problem -> pks -> [0] -> code;
4716 } else {
4717 $code = $problem -> preds -> [0] -> code;
4720 my $start_idx;
4721 my $end_idx;
4722 for( my $i = 0; $i <= $#{$code}; $i++ ){
4723 if( $code -> [$i] eq ";***IMPORTING COVARIATE DATA*******\n" ){
4724 $start_idx = $i-1;
4726 if( $code -> [$i] eq ";***IMPORTING COVARIATE DATA END***\n" ){
4727 $end_idx = $i+1;
4730 @{$code} = ( @{$code}[0..$start_idx] , @{$code}[$end_idx..$#{$code}] );
4732 if( $problem -> pks ){
4733 # Put the cut down code back in the right place:
4734 $problem -> pks -> [0] -> code( $code );
4735 } else {
4736 $problem -> preds -> [0] -> code( $code );
4739 last;
4746 end clean_extra_data_code
4747 # }}} clean_extra_data_code
4749 # {{{ add_extra_data_code
4751 start add_extra_data_code
4753 # This method adds fortran code that will handle wide datasets
4754 # (that is data sets with more than 20 columns). It adds code to
4755 # each problems pk or pred.
4757 my @code_lines;
4759 # Get the headers of the columns that have been moved to another
4760 # data file.
4762 # unless( defined $self -> extra_data_headers ){
4763 # die "ERROR model::add_extra_data_code: No headers for the extra data file\n";
4766 # extra_data_headers is a two dimensional array. One array of
4767 # headers for each problem in the modelfile.
4768 for( my $i = 0; $i <= $#{$self -> {'problems'}}; $i++ ){
4769 if ( defined $self -> {'problems'} -> [$i] -> {'extra_data_header'} ) {
4770 my $problem_headers = $self -> {'problems'} -> [$i] -> {'extra_data_header'};
4772 my $length = 0;
4773 my @headers;
4774 my $header_string;
4775 # Loop over the problem specific headers and make a string
4776 # that will go into the fortran code. Assume that the
4777 # first column holds the ID, hence the $i=1
4778 for (my $i = 1; $i <= $#{$problem_headers}; $i++ ) {
4779 my $header = $problem_headers -> [$i];
4780 push( @headers, $header );
4781 # Chopp the string at 40 characters, to be nice to g77 :)
4782 if ( $length + length($header) > 40 ) {
4783 $header_string .= "\n\"& ";
4784 $length = 0
4786 if ( $i < $#{$problem_headers} ) {
4787 $header_string .= 'I' . $header . ', ';
4788 $length += length( 'I' . $header . ', ' );
4789 } else {
4790 $header_string .= 'I' . $header;
4791 $length += length( 'I' . $header );
4795 my @code_lines = ('',
4796 ';***IMPORTING COVARIATE DATA*******',
4797 '" FIRST',
4798 '" REAL CURID, MID,',
4799 '"& '.$header_string,
4800 '" LOGICAL READ',
4801 '"',
4802 '" IF (.NOT.READ) THEN',
4803 '" CALL READER()',
4804 '" CURID = 1',
4805 '" READ = .TRUE.',
4806 '" END IF',
4807 '"',
4808 '" IF (NEWIND.LT.2) THEN',
4809 '" CALL GET_SUB (NEWIND,ID,CURID,MID,',
4810 '"& '.$header_string. ')',
4811 '" END IF',
4812 ' CID = MID',
4813 ' IF (CID.NE.ID) THEN',
4814 ' PRINT *, \'ERROR CHECKING FAILED, CID = \', CID ,\' ID = \', ID',
4815 ' END IF',
4816 '');
4818 foreach my $header ( @headers ) {
4819 push( @code_lines, " $header = I$header" );
4822 push( @code_lines, (';***IMPORTING COVARIATE DATA END***','','') );
4824 my $problem = $self -> {'problems'} -> [$i];
4825 if ( defined $problem -> {'subroutines'} ) {
4826 $problem -> subroutines -> [0] -> _add_option( option_string => 'OTHER=get_sub' . $i );
4827 $problem -> subroutines -> [0] -> _add_option( option_string => 'OTHER=reader' . $i );
4828 } else {
4829 $problem -> add_records( type => 'subroutines', record_strings => ['OTHER=get_sub', 'OTHER=reader'] );
4832 if ( defined $problem -> pks ) {
4833 unshift( @{$problem -> pks -> [0] -> code}, join("\n", @code_lines ));
4834 } else {
4835 unshift( @{$problem -> preds -> [0] -> code},join("\n", @code_lines ));
4840 end add_extra_data_code
4842 # }}}
4844 # {{{ drop_dropped
4846 start drop_dropped
4848 for( my $i = 0; $i < scalar @{$self -> {'problems'}}; $i++ ) {
4849 $self -> {'datas'}[$i] -> drop_dropped( model_header => $self -> {'problems'}[$i] -> header );
4850 $self -> {'problems'}[$i] -> drop_dropped( );
4851 #$self -> {'datas'}[$i] -> model_header( $self -> {'problems'}[$i] -> header );
4854 end drop_dropped
4856 # }}} drop_dropped
4858 # {{{ wrap_data
4860 start wrap_data
4862 my $default_wrap = 18;
4864 my ( @wrap_columns, @cont_columns );
4865 if ( not defined $wrap_column ) {
4866 for( my $i = 0; $i < scalar @{$self -> {'problems'}}; $i++ ) {
4867 my $columns = scalar @{$self -> {'problems'}[$i] -> dropped_columns}-1; #skip ID
4868 my $modulus = $columns%($default_wrap-2); # default_wrap-2 to account for ID and CONT
4869 my $rows = (($columns-$modulus)/($default_wrap-2))+1;
4870 if ( $rows == 1 ) {
4871 push( @wrap_columns, undef );
4872 } else {
4873 push( @wrap_columns, (ceil( $columns/$rows )+2) ); #Must use #cols + ID and CONT
4876 } else {
4877 for( my $i = 0; $i < scalar @{$self -> {'problems'}}; $i++ ) {
4878 push( @wrap_columns, $wrap_column );
4882 for( my $i = 0; $i < scalar @{$self -> {'problems'}}; $i++ ) {
4883 next if ( not defined $wrap_columns[$i] );
4884 $wrap_column = $wrap_columns[$i];
4885 $cont_column = $wrap_columns[$i] if( not defined $cont_column );
4886 my ( $prim, $sec ) =
4887 $self -> {'datas'}[$i] -> wrap( cont_column => $cont_column,
4888 wrap_column => $wrap_column,
4889 model_header => $self -> {'problems'}[$i] -> header );
4890 $self -> {'problems'}[$i] -> primary_columns( $prim );
4891 $self -> {'problems'}[$i] -> secondary_columns( $sec );
4892 $self -> {'data_wrapped'}++;
4895 end wrap_data
4897 # }}} wrap_data
4899 # {{{ unwrap_data
4900 start unwrap_data
4902 for( my $i = 0; $i < scalar @{$self -> {'problems'}}; $i++ ) {
4903 $self -> {'datas'}[$i] -> unwrap;
4904 $self -> {'problems'}[$i] -> primary_columns( [] );
4905 $self -> {'problems'}[$i] -> secondary_columns( [] );
4907 $self -> {'data_wrapped'} = 0;
4909 end unwrap_data
4910 # }}} unwrap_data
4912 # {{{ write_get_subs
4914 start write_get_subs
4916 for( my $i = 0; $i <= $#{$self -> {'problems'}}; $i++ ){
4917 if ( defined $self -> {'problems'} -> [$i] -> {'extra_data_header'} and
4918 defined $self -> problems -> [$i] -> extra_data ) {
4919 my @problem_header = @{$self -> {'problems'} -> [$i] -> {'extra_data_header'}};
4920 my @headers;
4921 my $length = 0;
4922 my $header_string;
4924 my $rows = $self -> problems -> [$i] -> extra_data -> count_ind;
4926 # Assume that first column holds the ID. Get rid of it.
4927 shift( @problem_header );
4928 for ( my $i = 0; $i <= $#problem_header; $i++ ) {
4929 my $header = $problem_header[$i];
4930 push( @headers, $header );
4931 # Chop the string at 40 characters, to be nice to g77 :)
4932 if ( $length + length($header) > 40 ) {
4933 $header_string .= "\n & ";
4934 $length = 0
4936 if ( $i < $#problem_header ) {
4937 $header_string .= $header . ', ';
4938 $length += length( $header . ', ' );
4939 } else {
4940 $header_string .= $header;
4941 $length += length( $header );
4945 open( FILE, '>', 'get_sub' . $i . '.f' );
4946 print FILE (" SUBROUTINE GET_SUB (NEWIND,ID,CURID,MID,\n",
4947 " & $header_string)\n",
4948 " COMMON /READ/ TID,TCOV\n",
4949 "\n",
4950 " REAL ID,CURID,MID,\n",
4951 " & $header_string\n",
4952 "\n",
4953 " INTEGER NEWIND\n",
4954 "\n",
4955 " REAL TID($rows), TCOV($rows,",scalar @problem_header,")\n",
4956 " CURID = 1\n",
4957 "\n",
4958 "C START AT TOP EVERY TIME\n",
4959 " IF (NEWIND.EQ.1) THEN \n",
4960 "12 CONTINUE\n",
4961 " IF (CURID.GT.$rows) THEN \n",
4962 " PRINT *, \"Covariate data not found for\", ID\n",
4963 " MID = -9999\n",
4964 " RETURN\n",
4965 " END IF\n",
4966 "\n",
4967 " IF (ID.GT.TID (CURID)) THEN\n",
4968 " CURID = CURID + 1\n",
4969 " GOTO 12\n",
4970 " END IF\n",
4971 " ELSEIF (NEWIND.EQ.0) THEN\n",
4972 " CURID = 1\n",
4973 " END IF\n",
4974 "\n" );
4975 my $length = 0;
4976 for ( my $i = 1; $i <= $#headers+1; $i++ ) {
4977 $length += length("TCOV(I,$i),");
4978 if ( $length > 40 ) {
4979 print FILE "\n";
4980 $length = 0;
4982 print FILE " ".$headers[$i-1] . " = TCOV(CURID,$i)\n";
4985 print FILE (" MID = TID(CURID)\n",
4986 " END\n",
4987 "\n" );
4989 close FILE;
4992 close( FILE );
4994 end write_get_subs
4996 # }}}
4998 # {{{ write_readers
5000 start write_readers
5002 for( my $i = 0; $i <= $#{$self -> {'problems'}}; $i++ ){
5003 if ( defined $self -> {'problems'} -> [$i] -> {'extra_data_header'} and
5004 defined $self -> {'problems'} -> [$i] -> {'extra_data'} ) {
5005 my @problem_header = @{$self -> {'problems'} -> [$i] -> {'extra_data_header'}};
5006 my @headers;
5007 my $length = 0;
5009 my $rows = $self -> problems -> [$i] -> extra_data -> count_ind;
5010 my $filename = $self -> problems -> [$i] -> extra_data -> filename;
5011 # Assume that first column holds the ID. Get rid of it.
5012 shift( @problem_header );
5014 'debug' -> warn( level => 2,
5015 message => "Writing reader".$i.".f to directory".cwd );
5016 open( FILE, '>', 'reader' . $i . '.f' );
5017 print FILE (" SUBROUTINE READER()\n",
5018 "\n",
5019 " COMMON /READ/ TID,TCOV\n",
5020 "\n",
5021 " REAL TID ($rows), TCOV ($rows, ",scalar @problem_header,")\n",
5022 "\n",
5023 " OPEN (UNIT = 77,FILE = '$filename')\n",
5024 " REWIND 77\n",
5025 " DO 11,I = 1,$rows\n",
5026 " READ (77,*) TID(I)," );
5028 my $length = 0;
5029 for ( my $i = 1; $i <= $#problem_header+1; $i++ ) {
5030 $length += length("TCOV(I,$i),");
5031 if ( $length > 40 ) {
5032 print FILE "\n & ";
5033 $length = 0;
5035 if ( $i <= $#problem_header ) {
5036 print FILE "TCOV(I,$i),";
5037 } else {
5038 print FILE "TCOV(I,$i)\n";
5042 print FILE ( "11 CONTINUE\n",
5043 " END\n" );
5047 end write_readers
5049 # }}}
5051 # {{{ _write
5053 start _write
5056 # $model -> _write( filename => 'model.mod' );
5058 # Writes the content of the modelobject to disk. Either to the
5059 # filename given, or to the string returned by model::full_name.
5061 my @formatted;
5063 # An element in the active_problems array is a boolean that
5064 # corresponds to the element with the same index in the problems
5065 # array. If the boolean is true, the problem will be run. All
5066 # other will be commented out.
5067 my @active = @{$self -> {'active_problems'}};
5069 # loop over all problems.
5070 for ( my $i = 0; $i < scalar @{$self -> {'problems'}}; $i++ ) {
5071 # Call on the problem object to format it as text. The
5072 # filename and problem numbers are needed to make some
5073 # autogenerated files (msfi, tabels etc...) unique to the
5074 # model and problem
5075 my @preformatted = @{$self -> {'problems'} -> [$i] ->
5076 # _format_problem };
5077 _format_problem( filename => $self -> filename,
5078 problem_number => ($i+1) ) };
5079 # Check if the problem is NOT active, if so comment it out.
5080 unless ( $active[$i] ) {
5081 for ( my $j = 0; $j <= $#preformatted; $j++ ) {
5082 $preformatted[$j] = '; '.$preformatted[$j];
5085 # Add extra line to avoid problems with execution of NONMEM
5086 push(@preformatted,"\n");
5087 push( @formatted, @preformatted );
5090 # Open a file and print the formatted problems.
5091 # TODO Add some errorchecking.
5092 open( FILE, '>'. $filename );
5093 for ( @formatted ) {
5094 chomp;
5095 print FILE;
5096 print FILE "\n";
5098 close( FILE );
5100 if ( $write_data ) {
5101 foreach my $data ( @{$self -> {'datas'}} ) {
5102 $data -> _write;
5106 end _write
5108 # }}} _write
5110 # {{{ filename
5111 start filename
5113 if ( defined $parm and $parm ne $self -> {'filename'} ) {
5114 $self -> {'filename'} = $parm;
5115 $self -> {'model_id'} = undef;
5116 # $self -> _write;
5119 end filename
5120 # }}} filename
5122 # {{{ _get_option_val_pos
5124 start _get_option_val_pos
5126 # Usage:
5128 # ( $values_ref, $positions_ref ) ->
5129 # _get_option_val_pos ( name => 'ID',
5130 # record_name => 'input' );
5131 # my @values = @{$values_ref};
5132 # my @positions = @{$positions_ref};
5134 # This basic usage returns the name of the third option in the first
5135 # instance of the record specified by I<record_name> for all problems
5137 # If global_position is set to 1, only one value and position
5138 # pair is returned per problem. If there are more than one
5139 # match in the model; the first will be returned for each
5140 # problem.
5142 # Private method, should preferably not be used outside model.pm
5144 # my ( @records, @instances );
5145 my $accessor = $record_name.'s';
5146 my @problems = @{$self -> {'problems'}};
5147 unless( $#problem_numbers > 0 ){
5148 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
5150 foreach my $i ( @problem_numbers ) {
5151 my $rec_ref = $problems[ $i-1 ] -> $accessor;
5152 if ( defined $problems[ $i-1 ] and defined $rec_ref ) {
5153 my @records = @{$rec_ref};
5154 unless( $#instances > 0 ){
5155 @instances = (1 .. $#records+1);
5158 my @inst_values = ();
5159 my @inst_positions = ();
5160 my $glob_pos = 1;
5161 my ( $glob_value, $glob_position );
5162 INSTANCES: foreach my $j ( @instances ) {
5163 if ( defined $records[ $j-1 ] ) {
5164 my $k = 1;
5165 my ( $value, $position );
5166 foreach my $option ( @{$records[$j-1] -> {'options'}} ) {
5167 if ( defined $option and $option -> name eq $name) {
5168 if ( $global_position ) {
5169 $glob_value = $option -> value;
5170 $glob_position = $glob_pos;
5171 last INSTANCES;
5172 } else {
5173 $value = $option -> value;
5174 $position = $k;
5177 $k++;
5178 $glob_pos++;
5180 push( @inst_values, $value );
5181 push( @inst_positions, $position );
5182 } else {
5183 'debug' -> die( message => "Instance $j in problem number $i does not exist!" )
5186 if ( $global_position ) {
5187 push( @values, $glob_value );
5188 push( @positions, $glob_position );
5189 } else {
5190 push( @values, \@inst_values );
5191 push( @positions, \@inst_positions );
5193 } else {
5194 'debug' -> die( message => "Problem number $i does not exist!" );
5197 # if( defined $problem_number ) {
5198 # if( $problem_number < 1 or $problem_number > scalar @problems ) {
5199 # die "model -> _get_option_val_pos: No such problem number, ",
5200 # $problem_number,", in this model!\n";
5203 # my $i;
5204 # die "modelfile -> _get_option_val_pos: No problem $problem_number exists\n"
5205 # if( (scalar @problems < 1) and ($problem_number ne 'all') );
5206 # my $j = 1;
5207 # foreach my $problem ( @problems ) {
5208 # @records = @{$problem -> $accessor};
5209 # @records = ($records[$instance-1]) if ( $instance ne 'all' );
5210 # die "modelfile -> _get_option_val_pos: No record instance $instance ".
5211 # "of record $record_name in problem $problem_number exists\n"
5212 # if( (scalar @records < 1) and ($instance ne 'all') );
5213 # foreach my $record ( @records ) {
5214 # $i = 1;
5215 # foreach my $option ( @{$record -> {'options'}} ) {
5216 # if ( defined $option and $option -> name eq $name) {
5217 # print "Found $name at $i\n" if ( $self -> {'debug'} );
5218 # push( @values, $option -> value );
5219 # push( @positions, $i );
5221 # $i++;
5226 end _get_option_val_pos
5228 # }}} _get_option_val_pos
5230 # {{{ _init_attr
5232 start _init_attr
5234 # The I<add_if_absent> argument tells the method to add an init (theta,omega,sigma)
5235 # if the parameter number points to a non-existing parameter with parameter number
5236 # one higher than the highest presently included. Only applicatble if
5237 # I<new_values> are set. Default value = 0;
5239 unless( scalar @problem_numbers > 0 ){
5240 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
5242 my @problems = @{$self -> {'problems'}};
5243 if ( $#new_values >= 0 ) {
5244 'debug' -> die( message => "The number of new value sets " .
5245 $#new_values+1 . " do not" .
5246 " match the number of problems " . $#problem_numbers+1 . " specified" )
5247 unless(($#new_values == $#problem_numbers) );
5248 if ( $#parameter_numbers > 0 ) {
5249 'debug' -> die( message => "The number of parameter number sets do not" .
5250 " match the number of problems specified" )
5251 unless(($#parameter_numbers == $#problem_numbers) );
5255 my $new_val_idx = 0;
5256 foreach my $i ( @problem_numbers ) {
5257 if ( defined $problems[ $i-1 ] ) {
5258 if ( scalar @new_values > 0) {
5259 # {{{ Update values
5260 # Use attribute parameter_values to collect diagnostic outputs
5261 push( @parameter_values,
5262 $problems[ $i-1 ] ->
5263 _init_attr( parameter_type => $parameter_type,
5264 parameter_numbers => $parameter_numbers[ $new_val_idx ],
5265 new_values => \@{$new_values[ $new_val_idx ]},
5266 attribute => $attribute,
5267 add_if_absent => $add_if_absent ) );
5268 # }}} Update values
5269 } else {
5270 # {{{ Retrieve values
5271 push( @parameter_values,
5272 $problems[ $i-1 ] ->
5273 _init_attr( parameter_type => $parameter_type,
5274 parameter_numbers => $parameter_numbers[ $i-1 ],
5275 attribute => $attribute ) );
5276 # }}} Retrieve values
5278 } else {
5279 'debug' -> die( message => "Problem number $i does not exist!" );
5281 $new_val_idx++;
5284 end _init_attr
5286 # }}} _init_attr
5288 # {{{ _option_name
5290 start _option_name
5292 # Usage:
5294 # $modobj -> _option_name ( record => $record_name,
5295 # position => 3 );
5297 # This basic usage returns the name of the third option in the first
5298 # instance of the record specified by I<record>.
5301 my ( @problems, @records, @options, $i );
5302 my $accessor = $record.'s';
5303 if ( defined $self -> {'problems'} ) {
5304 @problems = @{$self -> {'problems'}};
5305 } else {
5306 'debug' -> die( message => "No problems defined in model" );
5308 if ( defined $problems[$problem_number - 1] -> $accessor ) {
5309 @records = @{$problems[$problem_number - 1] -> $accessor};
5310 } else {
5311 'debug' -> die( message => "No record $record defined in ".
5312 "problem number $problem_number." );
5314 if ( defined $records[$instance - 1] -> options ) {
5315 @options = @{$records[$instance - 1] -> options};
5316 } else {
5317 'debug' -> die( message => "model -> _option_name: No option defined in record ".
5318 "$record in problem number $problem_number." );
5320 $i = 0;
5321 foreach my $option ( @options ) {
5322 if ( $i == $position ) {
5323 if ( defined $new_name ){
5324 $option -> name($new_name) if ( defined $option );
5325 }else{
5326 $name = $option -> name if ( defined $option );
5329 $i++;
5332 end _option_name
5334 # }}} _option_name
5336 # {{{ _parameter_count
5337 start _parameter_count
5339 if( defined $self -> {'problems'} ){
5340 my $problems = $self -> {'problems'};
5341 if( defined @{$problems}[$problem_number - 1] ){
5342 $count = @{$problems}[$problem_number - 1] -> record_count( 'record_name' => $record );
5346 end _parameter_count
5347 # }}} _parameter_count
5349 # {{{ _read_problems
5351 start _read_problems
5354 # To read problems from a modelfile we need its full name
5355 # (meaning filename and path). And we need an array for the
5356 # modelfile lines and an array with indexes telling where
5357 # problems start in the modelfile array.
5360 my $file = $self -> full_name;
5361 my ( @modelfile, @problems );
5362 my ( @problem_start_index );
5364 # Check if the file is missing, and if that is ok.
5365 # TODO Check accessor what happens if the file is missing.
5367 return if( not (-e $file) && $self -> {'ignore_missing_files'} );
5369 # Open the file, slurp it and close it
5370 open( FILE, "$file" ) ||
5371 'debug' -> die( message => "Model -> _read_problems: Could not open $file".
5372 " for reading" );
5373 @modelfile = <FILE>;
5374 close( FILE );
5376 my @extra_data_files = defined $self ->{'extra_data_files'} ?
5377 @{$self -> {'extra_data_files'}} : ();
5378 my @extra_data_headers = defined $self ->{'extra_data_headers'} ?
5379 @{$self -> {'extra_data_headers'}} : ();
5382 # # Find the indexes where the problems start
5383 # for ( my $i = 0; $i <= $#modelfile; $i++ ) {
5384 # push( @problem_start_index, $i )if ( $modelfile[$i] =~ /\$PROB/ );
5387 # # Loop over the number of problems. Copy the each problems lines
5388 # # and create a problem object.
5390 # for( my $i = 0; $i <= $#problem_start_index; $i++ ) {
5391 # my $start_index = $problem_start_index[$i];
5392 # my $end_index = defined $problem_start_index[$i+1] ? $problem_start_index[$i+1] - 1: $#modelfile ;
5393 # # Line copy
5394 # my @problem_lines = @modelfile[$start_index .. $end_index];
5396 # # Problem object creation.
5397 # push( @problems, model::problem -> new ( debug => $self -> {'debug'},
5398 # ignore_missing_files => $self -> {'ignore_missing_files'},
5399 # prob_arr => \@problem_lines,
5400 # extra_data_file_name => $extra_data_files[$i],
5401 # extra_data_header => $extra_data_headers[$i]) );
5403 my $start_index = 0;
5404 my $end_index;
5405 my $first = 1;
5406 my $prob_num = 0;
5408 # It may look like the loop takes one step to much, but its a
5409 # trick that helps parsing the last problem.
5410 for ( my $i = 0; $i <= @modelfile; $i++ ) {
5411 if( $i <= $#modelfile ){
5412 $_ = $modelfile[$i];
5415 # In this if statement we use the lazy evaluation of logical
5416 # or to make sure we only execute search pattern when we have
5417 # a line to search. Which is all cases but the very last loop
5418 # iteration.
5420 if( $i > $#modelfile or /\$PROB/ ){
5421 $end_index = $i;
5423 # The if statement here is only necessary in the first loop
5424 # iteration. When start_index == end_index == 0 we want to
5425 # skip to the next iteration looking for the actual end of
5426 # the first problem.
5428 if( $end_index > $start_index and not $first ){
5429 # extract lines of code:
5430 my @problem_lines = @modelfile[$start_index .. $end_index-1];
5431 # reset the search for problems by moving the problem start
5432 # forwards:
5433 $start_index = $i;
5435 my $sh_mod = model::shrinkage_module -> new ( model => $self,
5436 temp_problem_number => ($#problems+2));
5437 push( @problems, model::problem ->
5438 new ( directory => $self -> {'directory'},
5439 ignore_missing_files => $self -> {'ignore_missing_files'},
5440 ignore_missing_output_files => $self -> {'ignore_missing_output_files'},
5441 sde => $self -> {'sde'},
5442 prob_arr => \@problem_lines,
5443 extra_data_file_name => $extra_data_files[$prob_num],
5444 extra_data_header => $extra_data_headers[$prob_num],
5445 shrinkage_module => $sh_mod ) );
5447 $sh_mod -> problem( $problems[$#problems] );
5448 $prob_num++;
5450 $first = 0;
5454 # Set the problems in the modelobject.
5455 $self -> problems(\@problems);
5457 end _read_problems
5459 # }}} _read_problems
5461 # {{{ set_option
5463 start set_option
5465 unless( $#problem_numbers >= 0 ){
5466 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
5469 my @problems = @{$self -> {'problems'}};
5470 foreach my $i ( @problem_numbers ) {
5471 if ( defined $problems[ $i-1 ] ) {
5472 my $found = $self -> is_option_set( 'problem_number' => $i,
5473 'record' => $record_name,
5474 'name' => $option_name );
5475 $problems[$i-1] -> remove_option( record_name => $record_name,
5476 option_name => $option_name ) if ( $found );
5477 $problems[$i-1] -> add_option( record_name => $record_name,
5478 option_name => $option_name,
5479 option_value => $option_value );
5483 end set_option
5485 # }}} set_option
5487 # {{{ add_option
5489 start add_option
5491 unless( $#problem_numbers >= 0 ){
5492 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
5495 my @problems = @{$self -> {'problems'}};
5496 foreach my $i ( @problem_numbers ) {
5497 if ( defined $problems[ $i-1 ] ) {
5498 $problems[$i-1] -> add_option( record_name => $record_name,
5499 option_name => $option_name,
5500 option_value => $option_value,
5501 add_record => $add_record );
5505 end add_option
5507 # }}} add_option
5509 # {{{ remove_option
5511 start remove_option
5513 unless( $#problem_numbers >= 0 ){
5514 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
5517 my @problems = @{$self -> {'problems'}};
5518 foreach my $i ( @problem_numbers ) {
5519 if ( defined $problems[ $i-1 ] ) {
5520 $problems[$i-1] -> remove_option( record_name => $record_name,
5521 option_name => $option_name );
5525 end remove_option
5527 # }}} remove_option
5529 # {{{ _option_val_pos
5531 start _option_val_pos
5533 unless( $#problem_numbers >= 0 ){
5534 @problem_numbers = (1 .. $#{$self -> {'problems'}}+1);
5536 my @problems = @{$self -> {'problems'}};
5537 if ( $#new_values >= 0 ) {
5538 'debug' -> die( message => "Trying to set option $name in record $record_name but the ".
5539 "number of new value sets (".
5540 ($#new_values+1).
5541 "), do not match the number of problems specified (".
5542 ($#problem_numbers+1).")" )
5543 unless(($#new_values == $#problem_numbers) );
5544 if ( $#instance_numbers > 0 ) {
5545 'debug' -> die( message => "The number of instance number sets (".
5546 ($#instance_numbers+1).
5547 "),do not match the number of problems specified (".
5548 ($#problem_numbers+1).")" )
5549 unless(($#instance_numbers == $#problem_numbers) );
5553 foreach my $i ( @problem_numbers ) {
5554 if ( defined $problems[ $i-1 ] ) {
5555 my $rn_ref = $#instance_numbers >= 0 ? \@{$instance_numbers[ $i-1 ]} : [];
5556 if ( scalar @new_values > 0) {
5557 # {{{ Update values
5559 if( not defined $new_values[ $i-1 ] ) {
5560 debug -> die( message => " The specified new_values was undefined for problem $i" );
5563 if( not ref( $new_values[ $i-1 ] ) eq 'ARRAY' ) {
5564 debug -> die( message => " The specified new_values for problem $i is not an array as it should be but a ".
5565 ( defined ref( $new_values[ $i-1 ] ) ?
5566 ref( $new_values[ $i-1 ] ) : 'undef' ) );
5569 $problems[ $i-1 ] ->
5570 _option_val_pos( record_name => $record_name,
5571 instance_numbers => $rn_ref,
5572 new_values => \@{$new_values[ $i-1 ]},
5573 name => $name,
5574 exact_match => $exact_match );
5576 # }}} Update values
5577 } else {
5578 # {{{ Retrieve values
5579 my ( $val_ref, $pos_ref ) =
5580 $problems[ $i-1 ] ->
5581 _option_val_pos( record_name => $record_name,
5582 instance_numbers => $rn_ref,
5583 name => $name,
5584 exact_match => $exact_match );
5585 push( @values, $val_ref );
5586 push( @positions, $pos_ref );
5587 # }}} Retrieve values
5589 } else {
5590 'debug' -> die( message => "Problem number $i does not exist!" );
5594 end _option_val_pos
5596 # }}} _option_val_pos
5598 # {{{ subroutine_files
5600 start subroutine_files
5602 my %fsubs;
5603 foreach my $subr( 'PRED','CRIT', 'CONTR', 'CCONTR', 'MIX', 'CONPAR', 'OTHER', 'PRIOR' ){
5604 my ( $model_fsubs, $junk ) = $self -> _option_val_pos( record_name => 'subroutine',
5605 name => $subr );
5606 if( @{$model_fsubs} > 0 ){
5607 foreach my $prob_fsubs ( @{$model_fsubs} ){
5608 foreach my $fsub( @{$prob_fsubs} ){
5609 $fsubs{$fsub} = 1;
5615 @fsubs = keys %fsubs;
5616 if( @fsubs > 0 ){
5617 for( my $i = 0; $i <= $#fsubs; $i ++ ){
5618 unless( $fsubs[$i] =~ /\.f$/ ){
5619 $fsubs[$i] .= '.f';
5624 end subroutine_files
5626 # }}}