=head1 NAME

CFX5::Simulation::Configuration::Remeshing::Remesh::Preprocessing - Preprocessing module for remeshing

=head1 SYNOPSIS

Functions for preprocessing step of the remesh process

$Id$

=cut

package CFX5::Simulation::Configuration::Remeshing::Remesh::Preprocessing ;

use strict ;

use vars qw($CVSVersion);
$CVSVersion='$Id$ ';

use Exporter ; 
use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
@ISA=qw(Exporter);
@EXPORT_OK=qw(Preprocess);
%EXPORT_TAGS = (
  'all' => [qw(Preprocess)],  
);

use CFX5::Output qw(:all error_text);
use CFX5::PathManip qw(split_mult_files join_file);
use CFX5::PkgLoader qw(cfx5_require);
use CFX5::Config qw(%Config) ;
use CFX5::Commands qw(do_cmd parse_rv);

use CFX5::Simulation::Configuration::Remeshing::Remesh::ICEMCFD_rpl ;
use CFX5::Simulation::Util qw(GetDataSet);

#
#---- Load the LPNQUERY API
#     --> This code was pilfered from CCL2MF.pm
#
sub _load_lpnquery_load {
  my $from=shift ;

  _debug("Attempting to load LPNQUERY from $from.") ;
  #_debug("Before use lib, \@INC=", @INC) ;
  'lib'->import($from) ;
  # on NT under the Perl compiler, the lib->import fails.  Not sure
  # why.  So we also put the directory onto the lib path.
  unshift @INC, $from ;
  #_debug("After use lib, \@INC=", @INC) ;
  eval { require LPNQUERY ; } ;
  if ($@) {
    internal_error "Error loading lpnquery from $from:\n\n $@" ;
  }
  1 ;
}

{
  my $lpnquery_loaded=0 ;
  sub _load_lpnquery {
    if ($lpnquery_loaded) {
      return 1 ;
    } elsif (exists $ENV{CFX5LIBDIR} and -d "$ENV{CFX5LIBDIR}/../ccl") {
      _load_lpnquery_load("$ENV{CFX5LIBDIR}/../ccl")
        and $lpnquery_loaded=1 ;
    } elsif (defined $Config{CFX5LIBDIR} and -d "$Config{CFX5LIBDIR}/../ccl") {
      _load_lpnquery_load("$Config{CFX5LIBDIR}/../ccl")
        and $lpnquery_loaded=1 ;
    }
    return $lpnquery_loaded ;
  }
}                             


#
################################################################################
# Preprocess - High level Preprocessing routine
################################################################################
#
sub Preprocess {
  my ($self) = @_ ; 
#
#===============================================================================
# Gather required information from the remesh object
#===============================================================================
#
  my $rundir = $self->Get("RunDir") ; 
  if (!defined $rundir) {
    _error "Preprocess: Run directory not available" ;
    return 0; 
  }
  my $rundir_dir = $rundir->{dir} ;
  my $cclapi=$self->Get("CCLAPI");
  unless (defined $cclapi) {
    _error "Preprocess: Could not connect to CCL" ;
    return 0; 
  }
  my $CCLPath = $self->Get("CCLPath");
  unless (defined $CCLPath) {
    _error "Preprocess: CCLPath not available" ;
    return 0; 
  }
  my $resfile = $self->Get("ResFile");
  unless (defined $resfile) {
    _error "Preprocess: ResFile not available" ;
    return 0; 
  }

#
#===============================================================================
#  ICEM CFD Replay
#===============================================================================
#   
  if ($self->Get("Option") eq "ICEM CFD Replay") {
#
#----------------------------------------------------------------------- 
# Setup
#----------------------------------------------------------------------- 
#
    my $path ; 
    my @CCLChildren ;
    my $child ; 
    my $name ; 
    my $value ; 
    my $option ;
    my @boundaryList ;  
#
# Create cfx_params.rpl replay file in the run directory
# --> Get rid of it if it exists (otherwise we keep appending)
#
    my $cfxParamsFile = "cfx_params.rpl" ;
    if (-e join_file($rundir_dir,$cfxParamsFile)) {
      $rundir->remove_file($cfxParamsFile); 
    }
    $path = $rundir->creatable_file($cfxParamsFile) ;
    my $cfxParams_obj = 
      CFX5::Simulation::Configuration::Remeshing::Remesh::ICEMCFD_rpl->new($path) ;
#
#----------------------------------------------------------------------- 
# Set file name parameters
#----------------------------------------------------------------------- 
#
    my $ICEM_ACN = $self->Get("Parent")->icem_install_root() ;
    unless (defined $ICEM_ACN) {
      _error "Preprocessing: ICEM CFD installation not found.  ".
             "Please install and/or set the ICEM_ACN environment variable appropriately." ;
      return 0; 
    }

    $cfxParams_obj->Set_Parameter(
      ( "ResFile"        => '{'.$resfile.'}', 
        "MeshFile"       => '{'.$self->Get("MeshFile").'}',
        "GeomFile"       => '{'.$self->Get("Geometry File").'}',
        "MeshReplayFile" => '{'.$self->Get("Mesh Replay File").'}',
        "ICEMCFDInst"    => '{'.$ICEM_ACN.'}',
      )
    ) ;
#
#----------------------------------------------------------------------- 
# Figure out what (if any) monitor dat is required and gather it
# in one shot (this is possibly an expensive step)
#----------------------------------------------------------------------- 
#
    my %monData ; 
    my $monVar ;
#
#---- Look for SCALAR PARAMETER definitions
#
    $cclapi->getChildObjectList($CCLPath,\@CCLChildren) ;
    foreach $child (@CCLChildren) {
      next unless ($child =~/(.*):(.*)/) ;  
      next unless (defined $1 && defined $2) ; 
      next unless ($1 eq "SCALAR PARAMETER") ; 
      $cclapi->getStringValue("$CCLPath/$child/Monitor Point",\$monVar) ;
      $monData{$monVar} = undef; 
    }     
#
#---- Look for ICEM CFD MESH CONTROL definitions
#
    my $meshCtrl_dir = "$CCLPath/ICEM CFD MESH CONTROL" ;
    $cclapi->getChildObjectList($meshCtrl_dir,\@CCLChildren) ;
    foreach $child (@CCLChildren) {
      next unless ($child =~/(.*):(.*)/) ;  
      next unless (defined $1 && defined $2) ; 
      next unless ($1 eq "ICEM CFD PART PARAMETER") ; 
      $cclapi->getStringValue("$meshCtrl_dir/$child/Monitor Point",\$monVar) ;
      $monData{$monVar} = undef; 
    }  
#
#---- Grab any mon data that is required 
#
    if (%monData) {

      #
      # Create and issue the mon data extraction command
      my $monVars = '"USER POINT,'.join('";"USER POINT,',keys(%monData)).'"';
      my $cmd      = "$Config{CFX_CMD_DIR}/cfx5mondata" ;
      my $dataFile = "cfx5mondata.out" ;

      my @cmdArgs ; 
      push (@cmdArgs, "-mon"); 
      push (@cmdArgs, "mon"); 
      push (@cmdArgs, "-lastvaluesonly"); 
      push (@cmdArgs, "-varlist"); 
      push (@cmdArgs, $monVars); 
      push (@cmdArgs, "-output"); 
      push (@cmdArgs, $dataFile); 

      $rundir->enter(); 
      if (-e $dataFile) {
        unlink $dataFile; 
      }
      my $rv = CFX5::Simulation::Configuration::Remeshing::run_and_redirect_Remeshing(
                 $self->Get("RemeshOutFile"),
                 $cmd,@cmdArgs);
      unless ($rv->is_success()) {
        $cmd = unshift (@cmdArgs,$cmd) ;
        $cmd = join(" ",@cmdArgs); 
        _error($rv->errstr("Preprocess: Failed during cfx5mondata call for scalars:\n$cmd\n"));
        $rundir->leave(); 
        return 0; 
      }
      $rundir->leave(); 

      #
      # Read the extracted monitor data
      unless (open( MONDATA, "<".join_file($rundir_dir,$dataFile))) {
        print "Cant open input file: $dataFile\n" ;
        exit 0; 
      }
      my $varsMonDataS = <MONDATA> ;
      my $valsMonDataS = <MONDATA> ;
      close (MONDATA); 
      
      #
      # Parse the data returned, and store it in the hash
      %monData = (); 
      $varsMonDataS =~ s/(USER POINT,)//g ; 
      my @varsMonData = split(',',$varsMonDataS);
      my @valsMonData = split(',',$valsMonDataS);
      for (my $ii=0; $ii<scalar(@varsMonData); $ii++) {
        $varsMonData[$ii] =~ /(")(.*)(")/; 
        $monData{$2} = $valsMonData[$ii] ; 
      }    
    }
#
#----------------------------------------------------------------------- 
# Set generic scalar parameters
#----------------------------------------------------------------------- 
#
    $cclapi->getChildObjectList($CCLPath,\@CCLChildren) ;
    foreach $child (@CCLChildren) {
      next unless ($child =~/(.*):(.*)/) ;  
      next unless (defined $1 && defined $2) ; 
      next unless ($1 eq "SCALAR PARAMETER") ; 
      my $name = $2 ; 
      $cclapi->getStringValue("$CCLPath/$child/Monitor Point",\$monVar) ;
      unless (defined $monVar) {
        _error "Preprocess: Mesh control monitor point not found." ;
        return 0 ;
      }
      
      $cfxParams_obj->Set_ScalarArray("ScalarParam",
                                      $name,$monData{$monVar});
    }     
#
#----------------------------------------------------------------------- 
# Set displacement parameters for specified parts 
#----------------------------------------------------------------------- 
#
# Get the geometry control option
#
    my $geomCtrl_dir = "$CCLPath/ICEM CFD GEOMETRY CONTROL" ;
    $option = undef; 
    $cclapi->getStringValue("$geomCtrl_dir/Option",\$option) ;
    unless (defined $option) {
      _error "Preprocess: ICEM CFD geometry control unset." ;
      return 0; 
    }

#
# Specify the appropriate geometry control file
#
    my $geomCtrlFile  ;
    if    ($option eq "Automatic") {
      $geomCtrlFile = join_file($Config{CFX_DATA_DIR},'Remeshing',
                                'icemcfd_GeomCtrl.rpl') ;
    }
    elsif ($option eq "User Defined Replay File") {
      $cclapi->getStringValue("$geomCtrl_dir/File Name",\$geomCtrlFile) ;
    }
    
    if (defined $geomCtrlFile) {    
      $rundir->enter(); 
      unless (-e $geomCtrlFile) {
        _error "Missing geometry control file: $geomCtrlFile\n" ;
        return 0; 
      }
      $rundir->leave(); 
      $cfxParams_obj->Set_Parameter("GeomCtrlFile"=>'{'.$geomCtrlFile.'}') ; 
    }
#
# Gather mesh motion data from results if geometry controls active
#
    if ($option ne "None") {

      # Load up the lpnquery API and connect to the res file
      _load_lpnquery() 
        or _error "Unabel to find LPNQUERY" ;   
      my $lpnq=LPNQUERY::new($resfile);

      # Loop over part controls  
      $cclapi->getChildObjectList($geomCtrl_dir,\@CCLChildren) ;
      foreach $child (@CCLChildren) {
        next unless ($child =~/(.*):(.*)/) ;  
        next unless (defined $1 && defined $2) ; 
        next unless ($1 eq "ICEM CFD PART MAP") ; 
        $name = $2 ;

        # Learn about the part control
        my ($partsList, $physRegion) ;  
        $cclapi->getStringValue("$geomCtrl_dir/$child/ICEM CFD Parts List",
                                \$partsList) ;
        unless (defined $partsList) {
          _error "Preprocess: PART MAP $name - ICEM CFD Parts List not found." ;
          return 0; 
        }
        $cclapi->getStringValue("$geomCtrl_dir/$child/Boundary",
                                \$physRegion) ;
        unless (defined $physRegion) {
          _error "Preprocess: PART MAP $name - Boundary not found." ;
          return 0; 
        }

        # Extract the displacement
        # - Figure out the WHERE for the current physics region
        # - read the displacement data from the results file

        my $info=$lpnq->getPhysicsWhereInformation($physRegion);
        my $where = "ZN".$info->{zoneId0}."/".$info->{physicsType}.$info->{physicsIndex} ; 
        my $what = "G/MESHMOTION/LINEAR/CENTDISPTOT"; 
        my $when = "-1" ;      

        my @data = GetDataSet($resfile,$what,$where,$when) ; 
        unless (ref $data[0]) {
          _error "Preprocess: Mesh Motion dataset missing for $where" ;
          return 0; 
        }

        # Generate the displacement set commands
        my @parts = split_mult_files($partsList) ;
        foreach my $part (@parts) {
          $cfxParams_obj->Set_VectorArray("Displacement",$part,@{$data[0]});
        }
      }
    }  
#
#----------------------------------------------------------------------- 
# Set mesh control parameters for specified parts
#----------------------------------------------------------------------- 
#
    $option = undef; 
    $cclapi->getStringValue("$meshCtrl_dir/Option",\$option) ;
    unless (defined $option) {
      _error "Preprocess: ICEM CFD mesh control unset." ;
      return 0; 
    }
#
# Specify the appropriate mesh control file
#
    my $meshCtrlFile  ;
    if    ($option eq "Automatic") {
      $meshCtrlFile = join_file($Config{CFX_DATA_DIR},'Remeshing',
                                'icemcfd_MeshCtrl.rpl') ;
    }
    elsif ($option eq "User Defined Replay File") {
      $cclapi->getStringValue("$meshCtrl_dir/File Name",\$meshCtrlFile) ;
    }
    
    if (defined $meshCtrlFile) {
      $rundir->enter(); 
      unless (-e $meshCtrlFile) {
        _error "Missing mesh control file: $meshCtrlFile\n" ;
      }
      $rundir->leave(); 
      $cfxParams_obj->Set_Parameter("MeshCtrlFile"=>'{'.$meshCtrlFile.'}') ; 
    }
#
# Gather mesh control data from results if controls are active
#
    if ($option ne "None") {
      my $meshCtrl_dir = "$CCLPath/ICEM CFD MESH CONTROL" ;

      # Loop over mesh controls  
      $cclapi->getChildObjectList($meshCtrl_dir,\@CCLChildren) ;
      foreach $child (@CCLChildren) {
        next unless ($child =~/(.*):(.*)/) ;  
        next unless (defined $1 && defined $2) ; 
        next unless ($1 eq "ICEM CFD PART PARAMETER") ; 

        # Learn about the part control
        my ($partsList, $param, $value) ;  
        $cclapi->getStringValue("$meshCtrl_dir/$child/ICEM CFD Parts List",
                                \$partsList) ;
        $cclapi->getStringValue("$meshCtrl_dir/$child/ICEM CFD Parameter",
                                \$param) ;
        $cclapi->getStringValue("$meshCtrl_dir/$child/Monitor Point",
                                \$monVar) ;
        unless (defined $partsList) {
          _error "Preprocess: Mesh control parts list not found." ;
          return 0 ;
        }
        unless (defined $param) {
          _error "Preprocess: Mesh control parameter not found." ;
          return 0 ;
        }
        unless (defined $monVar) {
          _error "Preprocess: Mesh control monitor point not found." ;
          return 0 ;
        }

        my $ICEMParm ; 
        if ($param eq 'ehgt') {
          $ICEMParm = 'PartEHeight' ;
        }
        elsif ($param eq 'emax') {
          $ICEMParm = 'PartEMaxSize' ;
        }
        else {
          _error "Preprocess: Unrecognised ICEM CFD Parameter: $param" ;
          return 0 ;
        }

        # Clean parts list and write commands to rpl file
        $partsList =~ s/^[ ]*//g ;
        $partsList =~ s/[ ]*$//g ;
        foreach my $part (split(/[ ]*,[ ]*/,$partsList)) {
          $cfxParams_obj->Set_ScalarArray($ICEMParm,$part,$monData{$monVar});
        }
      }
    }  
#
#----------------------------------------------------------------------- 
# Closure
#----------------------------------------------------------------------- 
#
  }
#
#===============================================================================
#  User Defined
#===============================================================================
#   
  elsif ($self->Get("Option") eq "User Defined") {
#
# Nothing to do here
#
  }
#
#===============================================================================
#  Unhandled Option
#===============================================================================
#   
  else {
    _error "Preprocess: Invalid Remesh Option found" ;
    return 0 ; 
  }

  1 ;
}

1 ;
