#! perl -w
##
## Name:
##
## cfx5export
##
## Purpose:
##
## Export data in a CFX-5 Definition or Results File for use with third-party
## products.
##
## Synopsis:
##
## cfx5export -ansysfsi [-domain <number>] [-help] [-name <file>] 
##            [-offset-flow <number>] [-offset-val <number>] 
##            [-scale-flow <number>] [-scale-val <number>] 
##            [-summary] [-timestep <number>] [-verbose] 
##            [-eltype <element type>] [-regions <region1><,region2>...] <file> 
##
## cfx5export -cgns [-boundary] [-corrected] [-domain <number>]
##            [-cgns-version <2.4|3.0>] [-HDF5] [-ADF]
##            [-geometry] [-help] [-include] [-name <file>] [-summary]
##            [-timestep <number>] [-user <level>] [-norotate] 
##            [-boundaries-as-nodes] [-boundaries-as-faces] 
##            [-one-file-for-transient-cases] [-cgns-names] 
##            [-no-cfx-variable-description]
##            [-verbose] <file>
##
## cfx5export -ensight [-5|-6|-7] [-ascii|-binary] 
##            [-corrected] [-domain <number>] [-help] [-include] [-long] 
##            [-name <file>] [-summary] [-timestep <number>] [-user <level>] 
##            [-norotate] [-verbose] <file>
##
## cfx5export -fieldview [-split-grid-and-results] 
##            [-UNS <2.4|2.6|2.7|3.0>] [-REG <1.0|2.0>]
##            [-ascii] [-corrected] [-noparticles]
##            [-domain <number>]  [-include] [-name <file>] [-summary]
##            [-timestep <number>] [-user <level>] [-norotate] 
##            [-verbose] <file>
##
## cfx5export -patran [-boundary] [-corrected] [-domain <number>]
##            [-geometry] [-help] [-include] [-name <file>] [-nodes]
##            [-summary] [-timestep <number>] [-user <level>] [-norotate]
##            [-verbose] <file>
##
## cfx5export [-exec <executable>] -custom [<options>]
##
## []  denotes an optional argument.
## |   separates mutually exclusive arguments.
## ... following an argument indicates that it may be repeated.
## <>  and # denote that substitution of a suitable value is required. All 
##     other arguments are keywords, some of which may have a short form.
##
## N.B. the -ansysfsi -ensight, -patran, -fieldview or -custom option should be
##      the first option on the command line.
##
## Arguments:
##
## -5
##     Write an EnSight version 5 case file instead of an EnSight
##     version 7 results file.
##
## -6
##     Write an EnSight version 6 case file instead of an EnSight
##     version 7 results file.
##
## -7
##     Write an EnSight version 7 case file instead of an EnSight
##     version 7 results file.
##
#A -ADF
## -adf
##     Write CGNS data as an ADF formatted file.
##
## -ansysfsi
##     Export data in the ansys fsi CDB format.
##
#A -ascii
## -s
##     Write the output file in ASCII format, rather than unstructured
##     binary format.
##
#A -binary
##     Write the output file in unstructured binary format, rather than
##     ASCII format.
##
## -boundaries-as-faces
##     Write boundary condition patches as collections of faces rather
##     than as collections of nodes.
##
## -boundaries-as-nodes
##     Write boundary condition patches as collections of nodes rather
##     than as collections of faces.
##
#A -boundary
## -b
##     Boundary data only.
##
## -cgns
##     Export data in the CGNS format.
##
## -cgns-names
##     Map CFX variables to CGNS variable names (where possible) 
##
#A -cgns-version <2.4|3.0>
## -cv <2.4|3.0>
##     Write CGNS file as version specified
##
#A -corrected
## -c
##     Use corrected boundary node data.
##
## -custom <options>
##     Use custom export program defined by the CFX_EXPORT_EXEC 
##     variable environment. If this option is specified no further 
##     argument checking is done and all remaining options on the 
##     command line are passed straight to the export program. 
##
#A -domain <number>
## -d <number>
##     Specifies the domain of interest.  If <number> is non-zero,
##     cfx5export will export just the given domain.  If <number> is
##     zero, the data is combined and exported as a single domain.
##     <number> defaults to zero.
##
## -eltype <number>
##     Specifies ANSYS element type to use for ANSYS FSI CDB file output
##
## -ensight
##     Export data for use with Ensight.
##
#A -exec <executable>
##     Because environment variables are not read under Windows NT,
##     this option provides an alternative way of specifying the
##     custom export executable dynamically, without having to create
##     a cfx5rc file.  Note that it must appear before the -custom
##     switch.
##
#A -fieldview
## -fv
##     Export data for use with FieldView.
##
#A -geometry
## -g
##     Geometry data only.
##
#A -help
## -h
##     Print this message.
##
#A -HDF5
#A -hdf5
##     Write CGNS data as an HDF5 formatted file.
##
#A -include
## -i
##     Include boundary node data only.
##
#A -summary
## -f
##     Displays a summary of the domains and timesteps contained in
##     the CFX-5 Results File.
##
#A -long
## -l
##     Use long variable names.
##
## -one-file-for-transient-cases
##     Write all transient data to a single file using BaseIterative_t and 
##     ZoneIterative_t nodes
##
#A -name <base>
#A -output <base>
## -out <base>
##     Set the basename for the output files to <base> instead
##     of the name of the input file.
##
## -no-cfx-variable-description
##     Don't write a description of the full CFX Solver Variable Name
##
#A -nodes
#A -n
##     Use nodes when exporting packet 21 data (groups).
##
## -noparticles
##     Don't write particle tracks.
##
## -norotate
##     Don't write rotated mesh in rotating domains.
##
#A -offset-flow <number>
## -Of <number>
##     Offset flows written to ANSYS FSI CDB files by <number>
##
#A -offset-val <number>
## -Ov <number>
##     Offset values written to ANSYS FSI CDB files by <number>
##
## -patran
##     Export data for use with MSC/Patran.
##
## -regions <region1><,region2>...
##     Export data for comma separated list of regions to ANSYS FSI CDB file
##
## -REG <version>
##     Write Fieldview Region (FVREG) Files as version specified.
##
#A -scale-flow <number>
## -Sf <number>
##     Scale flows written to ANSYS FSI CDB files by <number>
##
#A -scale-val <number>
## -Sv <number>
##     Scale values written to ANSYS FSI CDB files by <number>
##
## -split-grid-and-results
##     Write Fieldview unstructured results files in Split Grid/Results format.
##
#A -timestep <number>
#A -time <number>
## -t <number>
##     If the <number> is non-zero, exports data for the given
##     timestep from a transient run.  If <number> is -1, data from
##     all timesteps is exported.  If the -timestep switch is not
##     given, data from the last timestep will be exported.
##
#A -user <level>
## -u <level>
##     User level of interest.
##
## -UNS <version>
##     Write Fieldview Unstructured (FVUNS) Files as version specified.
##
#A -verbose
## -v
##     Specifying this option may result in additional output being sent to
##     the UNIX standard output file (normally the screen).
##
## <file> 
##     Name of file from which data is to be exported. The names of the file(s)
##     created depends on the format being written:
##
##     ANSYS FSI
##     ---------
##     The FSI data will be written to the file <file>.cdb as an ANSYS coded 
##     database.  If the option -name is used, then its argument will be used
##     as the base name for the output file. 
##
##     Ensight
##     -------
##     The Ensight geometry will be written to <file>.geom, the
##     results file to <file>.res, and the variables to <file>.s## or 
##     <file>.v## where ## is the variable number and s indicates a 
##     scalar and v a vector unless the -name option is used.
##
##     Patran 
##     ------
##     The Patran geometry will be written to <file>.out as a Neutral file, 
##     the results template to <file>.res_tmpl, and the nodal results
##     to <basename>.nod unless the -name option is used.
##
##     FieldView
##     ---------
##     The FieldView geometry will be written to <file>.fv unless the
##     -name option is used.
##     
##     N.B. The files will be created in the same directory as the 
##     original files, not necessarily in the current working directory.
##
#U ****************************************************************************
#U *                                                                          *
#U *     The following features are supported only within CFDS and are not    *
#U *     shown by the -help option when the command is run elsewhere.         *
#U *                                                                          *
#U ****************************************************************************
#U
#U Unsupported Features:
#U
#U Arguments:
#U
#U -debug
#U      Run version of the code with debugging enabled.
#U
#U -test
#U      Run test program.
#U
#U -absolute
#U -a
#U     Use the absolute pressure instead of the relative pressure for
#U     output.  This does not currently work.
#U

# get the standard CFX-5 library directory
BEGIN {
  if ($0 =~ m!^([a-z]:)?[\\/]!i) {
    # it's an absolute path, use it as-is
    (my $nd=$0) =~ s!(perllib[\\/])?[^\\/]*$!!i;
    unshift @INC, "$nd/perllib";
  } else {
    # wrap this in an eval, and if it fails, pray ... for example,
    # this can fail on Windows if we're running off a share.
    eval {
      require FindBin;
      FindBin->import();
      unshift @INC, "$FindBin::Bin/perllib";
      my $dummy=$FindBin::Bin; # kill used only once warning
    };
    warn $@ if $@;
  }
  if (defined $ENV{CFX_LOCAL_PERLLIB}) {
     unshift @INC, $ENV{CFX_LOCAL_PERLLIB};
  }

}

use CFX5::Init;
CFX5::Init::init();

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

use strict;

use CFX5::Output qw(:all);
use CFX5::Config qw(%Config export_conf);
use CFX5::PrintConfig;

use CFX5::CmdLine qw(:util parse_cmdline);

use CFX5::PathManip qw(join_file split_file fullpath basename dirname);

use CFX5::Commands qw(do_cmd handle_rv parse_rv);

use CFX5::CCLI;

#
# read the configuration
#
CFX5::Config::read_config();

#
# parse the command line
#
use vars qw(@args $infile $outfile $debug $format $fileversion $cgformat $cgversion
            $elttype @regions @nonopt);

$cgversion=2.4;

use vars qw(%ArgsHash);
%ArgsHash = (
    -NoConsole  => \&handle_std_arg,
    -5          => sub { $fileversion = '5'; },
    -6          => sub { $fileversion = '6'; },
    -7          => sub { $fileversion = '7'; },
    -8          => sub { $fileversion = '8'; },

    -a          => \@args,
    -absolute   => '-a',

    -ansysfsi   => sub { $format = 'ANSYSFSI'; },

    -ADF        => sub { $cgformat = 'ADF'; },
    -adf        => '-ADF',

    -b          => \@args,
    -boundary   => '-b',

    -cgns       => sub { $format = 'CGNS'; },

    '-cgns-version+'  => sub { $cgversion = $_[1]; },
    '-cv+'      => '-cgns-version',

    -c          => \@args,
    -corrected  => '-c',

    -custom     => sub { $format = 'Custom'; $CFX5::CmdLine::Break = 1; },

    -C          => \@args,
    '-one-file-for-transient-cases' => '-C',

    -debug      => \&handle_std_arg,

    '-d+'       => sub { push @args, '-d'.$_[1]; },
    '-domain+'  => '-d',

    '-elttype+' => \$elttype,
    '-eltype+'  => '-elttype',

    -ensight    => sub { $format = 'Ensight'; },

    '-exec+'    => sub { conf_execfile('CFX_EXPORT_EXEC', 'CFX-5 Custom Export executable %%', @_); },

    -fieldview  => '-fv',
    -fv         => sub { $format = 'Fieldview'; },

    -g          => \@args,
    -geometry   => '-g',

    -h          => \&handle_std_arg,
    -help       => '-h',

    -HDF5       => sub { $cgformat = 'HDF5'; },
    -hdf5       => '-HDF5',

    -i          => \@args,
    -include    => '-i',

    -l          => \@args,
    -long       => '-l',

    -M          => \@args,
    '-cgns-names' => '-M',

    '-name+'    => sub { conf_datafile_w(\$outfile, undef, @_); },
    '-out+'     => '-name',
    '-output+'  => '-name',

    -n          => \@args,
    -nodes      => '-n',

    -P          => \@args,
    -noparticles => '-P',

    -x          => \@args,
    '-no-cfx-variable-description' => '-x',

    -G          => \@args,
    -norotate   => '-G',

    '-offset-flow-' => '-Of',
    '-offsetflow-' => '-Of',
    '-Of-'      => sub { push @args, '-Of'.$_[1]; },

    '-offset-val-' => '-Ov',
    '-offsetval-' => '-Ov',
    '-Ov-'      => sub { push @args, '-Ov'.$_[1]; },

    -patran     => sub { $format = 'Patran'; },

    '-regions+' => \@regions,

    -s          => '-Fa',
    -ascii      => '-Fa',
    -binary     => '-Fb',
    -Fa         => \@args,
    -Fb         => \@args,

    '-boundaries-as-nodes' => '-N',
    '-boundaries-as-faces' => '-F',

    '-N'       => \@args,
    '-F'       => \@args,

    '-REG+'    => sub { $_[1] =~ s/\.//; push @args, '-r'.$_[1]; },

    '-scale-flow-' => '-Sf',
    '-scaleflow-' => '-Sf',
    '-Sf-'       => sub { push @args, '-Sf'.$_[1]; },

    '-scale-val-' => '-Sv',
    '-scaleval-' => '-Sv',
    '-Sv-'       => sub { push @args, '-Sv'.$_[1]; },

    '-split-grid-and-results' => '-Fs',
    '-Fs'       => \@args,

    -summary    => '-f',
    -f          => \@args,

    -test       => sub { $format = 'Test'; },

    '-timestep-' => sub { push @args, '-t'.$_[1]; },
    '-time-'    => '-timestep',
    '-t-'       => '-timestep',

    '-UNS+'     => sub { $_[1] =~ s/\.//; push @args, '-v'.$_[1]; },

    -v          => \&handle_std_arg,
    -verbose    => '-v',

    '-u+'       => sub { push @args, '-u'.$_[1]; },
    '-user+'    => '-u',
    '-zone+'    => '-domain',
);

@nonopt = parse_cmdline( \@ARGV, 'ALLOW_NON_OPTION', %ArgsHash );

exit_if_error();

# we can assume any executable if -summary is given, they all do the
# same in this case (apart from the ANSYS one!  So we have to respect
# the format if supplied).
my $issummary = grep { $_ eq '-f' or $_ eq '-summary' } @args;
if (!defined $format and $issummary) {
  $format='Ensight';
}

_abort("Please specify which format file you want to export.")
  unless defined $format;

use CFX5::Config::def
  CFX_EXPORT_DIR => sub { $Config{CFX5EXECDIR}; };

CFX5::CCLI::export_parser_conf();

#
# is this a custom export?
#
if ($format eq "Custom") {
  _abort("CFX_EXPORT_EXEC not defined, unable to do custom export")
    unless defined $Config{CFX_EXPORT_EXEC};

  if (CFX5::FileTests::execfile($Config{CFX_EXPORT_EXEC}, "CFX-5 custom export executable $Config{CFX_EXPORT_EXEC}")) {
    my $rv=handle_rv(do_cmd($Config{CFX_EXPORT_EXEC}, @args,@nonopt));
    _warn("CFX-5 custom export executable $Config{CFX_EXPORT_EXEC}\nexited with return code $rv")
      if ($rv);
    exit(defined $rv ? $rv : 1);
  }
  exit_if_error();

  # we should never get here
  internal_error("Unexpected execution path");
}

#
# check @nonopt for validness (OK, validity)
#
$infile=shift(@nonopt)
  unless defined $infile;

if (!defined $infile) {
  _error("You must specify a CFX-5 Results File from which to export data.\n");
} elsif (scalar(@nonopt) > 0) {
  _error("Unexpected non-option arguments: ", join(" ",@nonopt));
}

#
# map long names to short names where possible in @args
#
for my $a (@args) {
  $a = $ArgsHash{$a} if (defined $ArgsHash{$a} and !ref($ArgsHash{$a}));
}

#
# append appropriate version number argument
#

if (defined($fileversion)) {
  if ($format eq "Fieldview") {
    if ($fileversion eq "6") {
      push @args, '-v24';
      push @args, '-R';
    } elsif ($fileversion eq "7"  || $fileversion eq "8") {
      push @args, '-v25';
      push @args, '-r10';
    } else {
      # This forces an error for an invalid version number
      push @args, '-'.$fileversion;
    }
  } elsif ($format eq "Ensight" && $fileversion ne "") {
    push @args, "-".$fileversion;
  }
}


# append appropriate version number argument
#

if (defined($cgversion) || defined($cgformat) ) {
  if ($format eq "CGNS") {
    if ($cgversion eq "2.4") {
      $format = "CGNSV2";
      push @args, '-A';
    }
  }
  if ($format eq "CGNS") {
    if ($cgformat eq "ADF" ) {
      push @args, '-A';
    } else { 
      push @args, '-H';
    }
  }
}

#
# check that these options are allowed for this export type
#

use vars qw(%DisallowedOpts);
%DisallowedOpts =
  (
   ANSYSFSI     => [qw(-UNS -REG -5 -6 -7 -8 -Fa -Fb -Fs -l -n -i -b -g -P -G -N -F -C -M -A -H -x)],
   CGNSV2    => [qw(-UNS -REG -5 -6 -7 -8 -Fa -Fb -Fs -l -n -P -H)],
   CGNS      => [qw(-UNS -REG -5 -6 -7 -8 -Fa -Fb -Fs -l -n -P )],
   Ensight   => [qw(-UNS -REG -8 -b -g -n -P -N -F -Fs -C -M -A -H -x)],
   Fieldview => [qw(-b -g -l -n -N -F -C -M -A -H -x)],
   Patran    => [qw(-UNS -REG -5 -6 -7 -8 -Fa -Fb -Fs -l -P -N -F -C -M -A -H -x)],
   Test      => [qw(-UNS -REG -5 -6 -7 -8 -Fa -Fb -Fs -b -g -i -n -P -G -N -F -C -M -A -H -x)],
  );
for my $o (@{$DisallowedOpts{$format}}) {
  _error("Option $o is not allowed for $format export")
    if grep(m/^$o$/, @args);
}

unless ($format eq 'ANSYSFSI') {
  if (@regions) {
    _error("The -regions option is not valid with $format export");
  }
  if (defined $elttype) {
    _error("The -elttype option is not valid with $format export");
  }
}

if ($format eq 'ANSYSFSI' and not $issummary) {
  # handle some of the options specially
  unless (defined $elttype) {
    _error("You must specify an element type for $format export");
  }
}

exit_if_error();

if ($format eq 'ANSYSFSI') {
  # we always need to pass an outfile argument to the ANSYSFSI export, as
  # some of the other things come after it.
  unless (defined $outfile) {
    $outfile=basename($infile);
    $outfile =~ s!\.(res|def)$!!;
  }
}

if ($format eq 'ANSYSFSI' and not $issummary) {
  # handle some of the options specially
  push @args, $elttype;
}

push @args, $infile;
push @args, $outfile if defined $outfile;

if (@regions) {
  push @args, join(",", (map { split(/\s*,\s*/, $_) } @regions));
}

#
# run the export stuff
#
{
  my $exec=join_file($Config{CFX_EXPORT_DIR},"Export".$format.".exe");
  if (CFX5::FileTests::execfile($exec, "CFX-5 export program for $format")) {
    my $rv=handle_rv(do_cmd($exec, @args));
    _warn("CFX-5 export program for $format exited with return code $rv")
      if ($rv);
    exit(defined $rv ? $rv : 1);
  }
  exit_if_error();
  exit(1);
}
