=head1 NAME

  CFX5::Simulation::Configuration::PPE - Solver Input File Input handling for CFX5::Simulation::Configuration

=head1 SYNOPSIS

This object is used by L<CFX5::Simulation::Configuration>, to manage the process of setting
up the initial problem definition files.

=head1 DESCRIPTION

$Id$

=cut

package CFX5::Simulation::Configuration::PPE;

use base 'CFX5::Simulation::Subtask';

use strict;
use CFX5::Output qw(:all error_text);
use CFX5::FileTests;

use CFX5::PathManip qw(dirname basename fullpath join_file);
use CFX5::FileOps qw(set_file_contents append_to_file);

use CFX5::Config qw(%Config export_conf);

use CFX5::CCLI;

use CFX5::Simulation::Util '_check_defined';

use Carp;

=head3 SetupHostList

Reads the host list.

=cut

sub SetupHostList {
  my $self=shift;

  #
  # we need to set up the host list based on our input arguments
  #
  #if ($self->parallel()) {
    unless ($self->host_list_is_set_up()) {
      _trace("Setting up parallel host list");

      my $hstlist=$self->hstlist();
      my $hstfile=$self->hstfile();

      # we now read the hostinfo.ccl at the same time as the standard
      # RULES files

      if (not $self->ppe_obj()->explicit_host_list()) {
	#
	# fast setup using the local host
	#

        _debug("Setting up hosts for Local Parallel");

	# make sure the process count appears in the CCL if we print it
	$self->processes();

      } elsif (defined $hstlist) {
	#
	# have an explicit list of hosts to use (distributed)
	#

        _debug("Setting up host list from $hstlist");

	# read the hst file if there is one, so we can get information
	# from it
	if (defined $hstfile) {
	  CFX5::FileTests::datafile($hstfile, "Host file $hstfile") 
	    or return 0;
          _debug("Reading supplementary host information from $hstfile");
	  $self->ppe_obj()->read_hst($hstfile);
	}
      } elsif (defined $hstfile) {
	#
	# have an explicit 'hst' file to use
	#

	_check_defined($hstfile,"host file") or return 0;
	CFX5::FileTests::datafile($hstfile, "Host file $hstfile") or return 0;

        _debug("Reading host information file $hstfile");

	# for MPI mode, we need to read the hst file and get the list
	# of hosts out of it.  So we'll always do that, and re-write
	# it ourselves explicitly later, to make sure that the
	# information reported in the CCL is consistent.
	my @hosts=$self->ppe_obj()->read_hst($hstfile);
        # we know there wasn't a host list argument, or we wouldn't be here!
	$self->hstlist(join(', ',@hosts));
        _debug("Host list is", $self->hstlist());
      } else {
	_error("A host list or host file is required for a Distributed Parallel run, but none has been specified.");
      }

      # don't go through all this again
      $self->host_list_is_set_up(1)
    }

    # we will create a hst file later

    # make sure these are included in the CCL returned with -print-ccl
    $self->ppe_obj()->mark_hosts();
  #}
  1;
}

=head1 PPE

=cut

# make sure we have the MPI shared libraries on the library path if
# necessary
sub _mpipath {
  my $self=shift;

  if ($Config{windows_nt} and lc($self->ppe_mode()) eq 'mpi') {
    my $libdir=join_file($Config{CFX5_MPI_DIR},'lib');
    CFX5::FileTests::datadir($libdir,
                             "MPI Library directory $libdir")
        or return 0;
    append_to_libpath($libdir);
  }
  return 1;
}

sub _Do {
  my $self=shift;
  my $do=shift;

  # do nothing in serial
  #return 1 unless $self->parallel();

  # make sure we've got something sensible here
  internal_error("First argument must be 0 or 1") 
    unless defined $do and $do eq 0 or $do eq 1;

  #
  # make sure the host list is set up fully
  #
  $self->SetupHostList()
    or return 0;

  my $ppe=$self->ppe_obj();
  #
  # Custom solver exe?
  # 
  if(defined $self->solver_exec()){
    my $localhost=$ppe->local_host();
    $localhost->executable($self->solver_exec());
  }

  # Check installation root
  if ($do) {
    $ppe->check_root();
  }

  #
  # do some local checks to make sure we're going to be OK
  #
  $ppe->local_check()
    or return 0;

  # if we're going to run the solver, check that the start method is
  # OK.  If we're only being set up because we're partitioning --
  # still necessary, for the automatic partition weighting -- we don't
  # want to check the start method, as it's probably wrong.
  if ($self->do_solve()) {
    $ppe->start_method()->local_check($ppe)
      or return 0;
  }

  #$self->_mpipath();

  # XXX set up for IRIX MPI, this should not be here
  if ($Config{CFX5OS} eq 'irix') {
    # don't try to do this with %Config, it's too scary!
    my @hits=grep { m/^MPI_USE_/ } keys %ENV;
    unless (@hits) {
      $Config{MPI_USE_TCP}=1;
      export_conf('MPI_USE_TCP');
    }
  } elsif ($Config{CFX_OS} eq 'Linux') {

#  Export SGI MPI environment variables if set
    export_conf('MPI_ARRAY');
    export_conf('MPI_USE_TCP');
    export_conf('MPI_USE_XPMEM');
    export_conf('MPI_USE_IB');
    export_conf('MPI_BUFFER_MAX');
    export_conf('MPI_MSGS_MAX');
    export_conf('MPI_DSM_CPULIST');
    export_conf('MPI_DSM_DISTRIBUTE');
    export_conf('MPI_MAPPED_STACK_SIZE');
    export_conf('MPI_MAPPED_HEAP_SIZE');

#  Export HP MPI environment variables if set
    export_conf('MP_GANG');
    export_conf('MPI_2BCOPY');
    export_conf('MPI_COMMD');
    export_conf('MPI_DLIB_FLAGS');
    export_conf('MPI_ELANLOCK');
    export_conf('MPI_FLAGS');
    export_conf('MPI_GLOBMEMSIZE');
    export_conf('MPI_HMP');
    export_conf('MPI_IC_ORDER');
    export_conf('MPI_ICLIB_ELAN');
    export_conf('MPI_ICLIB_GM');
    export_conf('MPI_ICLIB_MX');
    export_conf('MPI_ICLIB_ITAPI');
    export_conf('MPI_ICLIB_UDAPL');
    export_conf('MPI_ICLIB_VAPI');
    export_conf('MPI_ICLIB_VAPIDIR');
    export_conf('MPI_INSTR');
    export_conf('MPI_LOCALIP');
    export_conf('MPI_LONG_DOUBLE_IS8');
    export_conf('MPI_MAX_REMSH');
    export_conf('MPI_MAX_WINDOW');
    export_conf('MPI_MT_FLAGS');
    export_conf('MPI_NO_MALLOCLIB');
    export_conf('MPI_NOBACKTRACE');
    export_conf('MPI_PAGE_ALIGN_MEM');
    export_conf('MPI_PHYSICAL_MEMORY');
    export_conf('MPI_PIN_PERCENTAGE');
    export_conf('MPI_PRUNOPTIONS');
    export_conf('MPI_RANKMEMSIZE');
    export_conf('MPI_RDMA_MSGSIZE');
    export_conf('MPI_RDMA_NENVELOPE');
    export_conf('MPI_RDMA_NFRAGMENT');
    export_conf('MPI_RDMA_NFRAGMENT');
    export_conf('MPI_RDMA_NONESIDED');
    export_conf('MPI_RDMA_NSRQRECV');

    if ($ppe->explicit_host_list()) {
      export_conf('MPI_REMSH');
    }
    
    export_conf('MPI_ROOT');
    export_conf('MPI_SOCKBUFSIZE');
    export_conf('MPI_SHMEMCNTL');
    export_conf('MPI_SPAWN_PRUNOPTIONS');
    export_conf('MPI_SPAWN_SRUNOPTIONS');
    export_conf('MPI_SRUNOPTIONS');
    export_conf('MPI_TCP_CORECVLIMIT');
    export_conf('MPI_TMPDIR');
    export_conf('MPI_USE_LIBELAN');
    export_conf('MPI_USE_LIBELAN_SUB');
    export_conf('MPI_USE_MALLOPT_SBRK_PROTECTION');
    export_conf('MPI_USEPRUN');
    export_conf('MPI_USERUN_IGNORE_ARGS');
    export_conf('MPI_USESRUN');
    export_conf('MPI_USESRUN_IGNORE_ARGS');
    export_conf('MPI_VAPI_QPPARAMS');
    export_conf('MPI_WORKDIR');
    export_conf('MPIRUN_OPTIONS');
    export_conf('MPIRUN_SYSTEM_OPTIONS');
  }

  # Export MPICH 1.2.6 environment variables if set
  export_conf('P4_GLOBMEMSIZE');
  export_conf('P4_RSHCOMMAND');
  export_conf('P4_SOCKBUFSIZE');

  #  Export Intel MPI environment variables if set
  export_conf('I_MPI_DAT_LIBRARY');
  export_conf('I_MPI_DEBUG');
  export_conf('I_MPI_FABRICS');
  export_conf('I_MPI_FABRICS_LIST');
  export_conf('I_MPI_ROOT');
  export_conf('I_MPI_MPD_RSH');
  export_conf('I_MPI_OFA_LIBRARY');
  export_conf('I_MPI_TMI_LIBRARY');

  #
  # start up PVM
  #
  if ($do) {
    unless ($self->ppe_running()) {
      my $workdir=$self->rundir_obj();
      $ppe->workdir($workdir);
      unless ($ppe->start()) {
        # failed properly to start PVM
        return 0;
      }
      $self->ppe_running(1);
    }
  }

  return 1;
}

=head3 Interrogate

Ensure we have architecture information etc for all required hosts

=cut

sub Interrogate {
  my $self=shift;

  # do nothing in serial
  #return 1 unless $self->parallel();

  $self->Check();

  my $ppe=$self->ppe_obj();

  #
  # start up PVM
  #
  return ($ppe->interrogate());
}



1;
