#!/usr/bin/env perl
#!/usr/bin/perl
#
# Albert Danial May 20 2004
#
use warnings;
use strict;
use File::Basename;
use Getopt::Std;
use vars qw ( $opt_p );
use DBI;
use DBD::SQLite 1.00;
use Data::Dumper;

$opt_p = 0;
getopts('p:');
my $script = basename $0;
die "$script [-p <# partitions>]  <model .db file>  [<results>]]
                  Creates gmsh .msh (mesh) and .pos (post processing)
                  files using the model data from the .db file, and 
                  optionally the results data from <results>.
                  At the moment only understands tri3 elements.
" unless scalar @ARGV >= 1;

my $model_db_file = "";
my $partn_db_file = "";
my $results_file  = "";
die "Number of partitions must be an integer\n" if $opt_p =~ /\D/;

$model_db_file = shift @ARGV;
$model_db_file .= ".db" unless -r $model_db_file;
die "Cannot read $model_db_file" unless -r $model_db_file;
die "$model_db_file -- expect database file to have .db extension" 
    unless $model_db_file =~ /\.db$/;
if ($opt_p) {
    ($partn_db_file = $model_db_file) =~ s/\.db$//;
    $partn_db_file .= sprintf("_P%03d.db", $opt_p);
    die "Cannot read $partn_db_file" unless -r $partn_db_file;
    die "$partn_db_file -- expect database file to have .db extension" 
        unless $partn_db_file =~ /\.db$/;
}
$results_file = shift @ARGV if @ARGV;

(my $pos_file = $model_db_file) =~ s/\.db$/.pos/;
(my $msh_file = $model_db_file) =~ s/\.db$/.msh/;

my $dbh = DBI->connect("dbi:SQLite:dbname=$model_db_file","","");

my $node_ref = $dbh->selectall_hashref( 
                        "select seq_no,x1,x2,x3 from node", "seq_no");
my $e_n_ref  = $dbh->selectall_arrayref(
                        "select eid,nid from element_node order by seq_no");
my $rc = $dbh->disconnect;

my $partn_ref = {};
if ($partn_db_file) {
    $dbh       = DBI->connect("dbi:SQLite:dbname=$partn_db_file","","");
    $partn_ref = $dbh->selectall_hashref(
                            "select eid,pid from element_partition;", "eid");
    $rc        = $dbh->disconnect;
}
#print Dumper($node_ref);
#print Dumper($e_n_ref);
#print Dumper($partn_ref);
#die;

write_msh($msh_file, $node_ref, $e_n_ref, $partn_ref);
#need to rewrite to get rid of \%node_xyz, \%connect, \%can_nid,  \%can_eid
#if ($results_file) {
#    # write vector triangles with vertex offsets from the results file
#    my @solution_vector = load_results($results_file); # sorted by orig node ID
#    #write_modal_pos($pos_file, \%node_xyz, \%connect, 
#    #                $renum_ref, \@solution_vector);
#    write_modal_pos_parsed($pos_file, \%node_xyz, \%connect, 
#                          \%can_nid,  \%can_eid,
#                           $renum_ref, \@solution_vector);
#} else {
#    # write scalar triangles with bogus time step data
#    write_pos($pos_file, \%node_xyz, \%connect);
#}

sub write_msh { # {{{1
    my ($msh_file     , # in file to write to
        $rhh_node_xyz , # in node_xyz{NID}{x1|x2|x3} = value
        $raa_connect  , # in connect        = [ [eid,nid_1], [eid,nid_2] ... ]
        $rh_partition , # in partition{EID} = partition ID
       ) = @_;

    unlink $msh_file if -r $msh_file;
    open  OUT, ">$msh_file" or die "Cannot write to $msh_file:  $!\n";

    printf OUT "\$NOD\n";
    printf OUT "%d\n", scalar keys %{$rhh_node_xyz};   # number of nodes
    foreach my $NID (sort {$a <=> $b} keys %{$rhh_node_xyz}) {
        printf OUT "%d % 12.6e % 12.6e % 12.6e\n", 
                    $NID                     ,
                    $rhh_node_xyz->{$NID}{x1},
                    $rhh_node_xyz->{$NID}{x2},
                    $rhh_node_xyz->{$NID}{x3};
    }
    printf OUT "\$ENDNOD\n";

    my %connect = (); # connect{eid} = [node_1, node_2, ... ]
    foreach my $pair (@{$raa_connect}) {
        push @{$connect{$pair->[0]}}, $pair->[1];
    }
    printf OUT "\$ELM\n";
    printf OUT "%d\n", scalar keys %connect;

    my $elem_type     = 2; # file:///usr/share/doc/gmsh/html/gmsh_10.html#SEC62
    my $partition_ID  = 10000;  # arbitrary base value
    my $elementary_ID = $partition_ID;
    my $elem_seq_no   = 0;

    foreach my $EID (sort {$a <=> $b} keys %connect) {
        my $nNodes = scalar @{$connect{$EID}};
        my $offset = 0;
        $offset = $rh_partition->{$EID}{pid}
            if defined $rh_partition->{$EID}{pid};   # pid = partition ID
        printf OUT "%d %d %d %d",
                   $EID          , 
                   $elem_type    , 
                   $partition_ID , # + $offset,  ??
                   $elementary_ID + $offset;  # show element's partition 
        printf OUT " %d", $nNodes;
        for (my $i = 0; $i < $nNodes; $i++) {
            printf OUT " %d", $connect{$EID}[$i];
        }
        printf OUT "\n";
    } 
    printf OUT "\$ENDELM\n";

    close  OUT;
    print "Wrote $msh_file\n";
} # 1}}}
sub write_pos { # {{{1
    my ($pos_file     , # in file to write to
        $rha_xyz      , # in node_xyz{canonical NID} = [x, y, z]
        $rha_connect  , # in connect{canonical EID} = [list of can. NID's]
       ) = @_;

    my $binary_format          = 0;  # 0 => text       1 => binary
    my $sizeof_double          = 8;

    # assign header values {{{2
    my $view_name              = "vname_A"; 
       $view_name              =~ s/\s+/^/g;
    my $n_time_steps           = 3;
    my $n_scalar_points        = 0; 
    my $n_vector_points        = 0; 
    my $n_tensor_points        = 0;
    my $n_scalar_lines         = 0; 
    my $n_vector_lines         = 0; 
    my $n_tensor_lines         = 0;
    my $n_scalar_triangles     = scalar keys %{$rha_connect}; 
    my $n_vector_triangles     = 0; 
    my $n_tensor_triangles     = 0;
    my $n_scalar_quadrangles   = 0; 
    my $n_vector_quadrangles   = 0; 
    my $n_tensor_quadrangles   = 0;
    my $n_scalar_tetrahedra    = 0; 
    my $n_vector_tetrahedra    = 0; 
    my $n_tensor_tetrahedra    = 0;
    my $n_scalar_hexahedra     = 0; 
    my $n_vector_hexahedra     = 0; 
    my $n_tensor_hexahedra     = 0;
    my $n_scalar_prisms        = 0; 
    my $n_vector_prisms        = 0; 
    my $n_tensor_prisms        = 0;
    my $n_scalar_pyramids      = 0; 
    my $n_vector_pyramids      = 0; 
    my $n_tensor_pyramids      = 0;
    my $n_text2d               = 0; 
    my $n_text2d_chars         = 0; 
    my $n_text3d               = 0; 
    my $n_text3d_chars         = 0; 
    # 2}}}

    unlink $pos_file if -r $pos_file;
    open  OUT, ">$pos_file" or die "Cannot write to $pos_file:  $!\n";

    # print header {{{2
    printf OUT "\$PostFormat\n";
    printf OUT "%g %d %d\n", 1.2, $binary_format, $sizeof_double;
    printf OUT "\$EndPostFormat\n";
    printf OUT "\$View\n";
    printf OUT "%s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", 
            $view_name, 
            $n_time_steps,
            $n_scalar_points, 
            $n_vector_points, 
            $n_tensor_points,
            $n_scalar_lines, 
            $n_vector_lines, 
            $n_tensor_lines,
            $n_scalar_triangles, 
            $n_vector_triangles, 
            $n_tensor_triangles,
            $n_scalar_quadrangles, 
            $n_vector_quadrangles, 
            $n_tensor_quadrangles,
            $n_scalar_tetrahedra, 
            $n_vector_tetrahedra, 
            $n_tensor_tetrahedra,
            $n_scalar_hexahedra, 
            $n_vector_hexahedra, 
            $n_tensor_hexahedra,
            $n_scalar_prisms, 
            $n_vector_prisms, 
            $n_tensor_prisms,
            $n_scalar_pyramids, 
            $n_vector_pyramids, 
            $n_tensor_pyramids,
            $n_text2d, 
            $n_text2d_chars, 
            $n_text3d, 
            $n_text3d_chars;
    # 2}}}

    foreach (0..($n_time_steps-1)) {
        print OUT " $_";
    }
    foreach my $EID (sort {$rha_connect->{$a} <=>
                           $rha_connect->{$b}} keys %{$rha_connect}) {
        my @values = qw(1 2 3 4 5 6 7 8 9);  
        # length of values = $n_time_steps x n vertices in element
        my $n1 = $rha_connect->{$EID}[0];
        my $n2 = $rha_connect->{$EID}[1];
        my $n3 = $rha_connect->{$EID}[2];
        printf OUT " %f %f %f %f %f %f %f %f %f",
               # sequence is x1, x2, x3, y1, y2, y3, z1, z2, z3
               $rha_xyz->{$n1}[0], $rha_xyz->{$n2}[0], $rha_xyz->{$n3}[0], 
               $rha_xyz->{$n1}[1], $rha_xyz->{$n2}[1], $rha_xyz->{$n3}[1], 
               $rha_xyz->{$n1}[2], $rha_xyz->{$n2}[2], $rha_xyz->{$n3}[2];
        printf OUT " @values";
    } 

    printf OUT   "\n\$EndView\n";
    close  OUT;
    print "Wrote $pos_file\n";
} # 1}}}
sub write_modal_pos { # {{{1
    my ($pos_file     , # in file to write to
        $rha_xyz      , # in node_xyz{canonical NID} = [x, y, z]
        $rha_connect  , # in connect{canonical EID}  = [list of can. NID's]
        $rhh_renum_id , # in renum_id{canonical NID} = renumbered NID
        $ra_eigvector , # in 0..nDof-1 
       ) = @_;

    my $binary_format          = 0;  # 0 => text       1 => binary
    my $sizeof_double          = 8;

    my $nNodes  = scalar keys %{$rha_xyz};
    my $nVecDOF = scalar @{$ra_eigvector};
    die "Number of rows in the eigenvector, $nVecDOF != 6 x number of nodes," .
        " $nNodes" unless 6*$nNodes == $nVecDOF;

    # assign header values {{{2
    my $view_name              = "vname_A"; 
       $view_name              =~ s/\s+/^/g;
    my $n_time_steps           = 1;
    my $n_scalar_points        = 0; 
    my $n_vector_points        = 0; 
    my $n_tensor_points        = 0;
    my $n_scalar_lines         = 0; 
    my $n_vector_lines         = 0; 
    my $n_tensor_lines         = 0;
    my $n_scalar_triangles     = 0;
    my $n_vector_triangles     = scalar keys %{$rha_connect}; 
    my $n_tensor_triangles     = 0;
    my $n_scalar_quadrangles   = 0; 
    my $n_vector_quadrangles   = 0; 
    my $n_tensor_quadrangles   = 0;
    my $n_scalar_tetrahedra    = 0; 
    my $n_vector_tetrahedra    = 0; 
    my $n_tensor_tetrahedra    = 0;
    my $n_scalar_hexahedra     = 0; 
    my $n_vector_hexahedra     = 0; 
    my $n_tensor_hexahedra     = 0;
    my $n_scalar_prisms        = 0; 
    my $n_vector_prisms        = 0; 
    my $n_tensor_prisms        = 0;
    my $n_scalar_pyramids      = 0; 
    my $n_vector_pyramids      = 0; 
    my $n_tensor_pyramids      = 0;
    my $n_text2d               = 0; 
    my $n_text2d_chars         = 0; 
    my $n_text3d               = 0; 
    my $n_text3d_chars         = 0; 
    # 2}}}

    unlink $pos_file if -r $pos_file;
    open  OUT, ">$pos_file" or die "Cannot write to $pos_file:  $!\n";

    # print header {{{2
    printf OUT "\$PostFormat\n";
    printf OUT "%g %d %d\n", 1.2, $binary_format, $sizeof_double;
    printf OUT "\$EndPostFormat\n";
    printf OUT "\$View\n";
    printf OUT "%s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", 
            $view_name, 
            $n_time_steps,
            $n_scalar_points, 
            $n_vector_points, 
            $n_tensor_points,
            $n_scalar_lines, 
            $n_vector_lines, 
            $n_tensor_lines,
            $n_scalar_triangles, 
            $n_vector_triangles, 
            $n_tensor_triangles,
            $n_scalar_quadrangles, 
            $n_vector_quadrangles, 
            $n_tensor_quadrangles,
            $n_scalar_tetrahedra, 
            $n_vector_tetrahedra, 
            $n_tensor_tetrahedra,
            $n_scalar_hexahedra, 
            $n_vector_hexahedra, 
            $n_tensor_hexahedra,
            $n_scalar_prisms, 
            $n_vector_prisms, 
            $n_tensor_prisms,
            $n_scalar_pyramids, 
            $n_vector_pyramids, 
            $n_tensor_pyramids,
            $n_text2d, 
            $n_text2d_chars, 
            $n_text3d, 
            $n_text3d_chars;
    # 2}}}
    foreach (0..($n_time_steps-1)) {
        print OUT " $_";
    }
    foreach my $EID (sort {$rha_connect->{$a} <=>
                           $rha_connect->{$b}} keys %{$rha_connect}) {
        # length of values = $n_time_steps x n vertices in element
        my $n1 = $rha_connect->{$EID}[0];
        my $n2 = $rha_connect->{$EID}[1];
        my $n3 = $rha_connect->{$EID}[2];
        # vertex coordinates
        printf OUT " %f %f %f %f %f %f %f %f %f",
               # sequence is x1, x2, x3, y1, y2, y3, z1, z2, z3
               $rha_xyz->{$n1}[0], $rha_xyz->{$n2}[0], $rha_xyz->{$n3}[0], 
               $rha_xyz->{$n1}[1], $rha_xyz->{$n2}[1], $rha_xyz->{$n3}[1], 
               $rha_xyz->{$n1}[2], $rha_xyz->{$n2}[2], $rha_xyz->{$n3}[2];
        # vertex displacements
#print "n1=$n1  n2=$n2  n3=$n3\n";
#die Dumper($rhh_renum_id->{$n1});
        printf OUT " %f %f %f %f %f %f %f %f %f",
               $ra_eigvector->[dof($n1, "Tx", $rhh_renum_id)],
               $ra_eigvector->[dof($n1, "Ty", $rhh_renum_id)],
               $ra_eigvector->[dof($n1, "Tz", $rhh_renum_id)],
               $ra_eigvector->[dof($n2, "Tx", $rhh_renum_id)],
               $ra_eigvector->[dof($n2, "Ty", $rhh_renum_id)],
               $ra_eigvector->[dof($n2, "Tz", $rhh_renum_id)],
               $ra_eigvector->[dof($n3, "Tx", $rhh_renum_id)],
               $ra_eigvector->[dof($n3, "Ty", $rhh_renum_id)],
               $ra_eigvector->[dof($n3, "Tz", $rhh_renum_id)];
#       printf OUT " %f %f %f %f %f %f %f %f %f",
#              $ra_eigvector->[dof($n1, "Tx", $rhh_renum_id)],
#              $ra_eigvector->[dof($n2, "Tx", $rhh_renum_id)],
#              $ra_eigvector->[dof($n3, "Tx", $rhh_renum_id)],
#              $ra_eigvector->[dof($n1, "Ty", $rhh_renum_id)],
#              $ra_eigvector->[dof($n2, "Ty", $rhh_renum_id)],
#              $ra_eigvector->[dof($n3, "Ty", $rhh_renum_id)],
#              $ra_eigvector->[dof($n1, "Tz", $rhh_renum_id)],
#              $ra_eigvector->[dof($n2, "Tz", $rhh_renum_id)],
#              $ra_eigvector->[dof($n3, "Tz", $rhh_renum_id)];
    } 

    printf OUT   "\n\$EndView\n";
    close  OUT;
    print "Wrote $pos_file\n";
} # 1}}}
sub write_modal_pos_parsed { # {{{1
    # ie, starts with 'View "vname" {'
    my ($pos_file     , # in file to write to
        $rha_xyz      , # in node_xyz{canonical NID} = [x, y, z]
        $rha_connect  , # in connect{canonical EID}  = [list of can. NID's]
        $rh_nid_can   , # in nid_can{NID} = canonical NID
        $rh_eid_can   , # in eid_can{EID} = canonical EID
        $rhh_renum_id , # in renum_id{canonical NID} = renumbered NID
        $ra_eigvector , # in 0..nDof-1 
       ) = @_;

    my $nNodes  = scalar keys %{$rha_xyz};
    my $nVecDOF = scalar @{$ra_eigvector};
    die "Number of rows in the eigenvector, $nVecDOF != 6 x number of nodes," .
        " $nNodes" unless 6*$nNodes == $nVecDOF;

    my $view_name              = "vname_A"; 
       $view_name              =~ s/\s+/^/g;

    unlink $pos_file if -r $pos_file;
    open  OUT, ">$pos_file" or die "Cannot write to $pos_file:  $!\n";

    printf OUT "View \"$view_name\" {\n";

#   foreach my $EID (sort {$rha_connect->{$a} <=>
#                          $rha_connect->{$b}} keys %{$rha_connect}) {
    foreach my $orig_eid (sort {$a <=> $b} keys %{$rh_eid_can}) {
        my $EID = $rh_eid_can->{$orig_eid};
        # length of values = $n_time_steps x n vertices in element
        my $n1 = $rha_connect->{$EID}[0];
        my $n2 = $rha_connect->{$EID}[1];
        my $n3 = $rha_connect->{$EID}[2];
        # vertex coordinates
        printf OUT "VT(% 8.4f,% 8.4f,% 8.4f,% 8.4f,% 8.4f,% 8.4f,% 8.4f,% 8.4f,% 8.4f) ",
               # sequence is x1, x2, x3, y1, y2, y3, z1, z2, z3
               $rha_xyz->{$n1}[0], $rha_xyz->{$n1}[1], $rha_xyz->{$n1}[2], 
               $rha_xyz->{$n2}[0], $rha_xyz->{$n2}[1], $rha_xyz->{$n2}[2], 
               $rha_xyz->{$n3}[0], $rha_xyz->{$n3}[1], $rha_xyz->{$n3}[2];
        # vertex displacements
#print "orig_eid=$orig_eid  EID=$EID  n1=$n1  n2=$n2  n3=$n3\n";
#die Dumper($rhh_renum_id->{$n1});
        printf OUT "{% 8.4f,% 8.4f,% 8.4f,% 8.4f,% 8.4f,% 8.4f,% 8.4f,% 8.4f,% 8.4f};\n",
               $ra_eigvector->[dof($n1, "Tx", $rhh_renum_id)],
               $ra_eigvector->[dof($n1, "Ty", $rhh_renum_id)],
               $ra_eigvector->[dof($n1, "Tz", $rhh_renum_id)],
               $ra_eigvector->[dof($n2, "Tx", $rhh_renum_id)],
               $ra_eigvector->[dof($n2, "Ty", $rhh_renum_id)],
               $ra_eigvector->[dof($n2, "Tz", $rhh_renum_id)],
               $ra_eigvector->[dof($n3, "Tx", $rhh_renum_id)],
               $ra_eigvector->[dof($n3, "Ty", $rhh_renum_id)],
               $ra_eigvector->[dof($n3, "Tz", $rhh_renum_id)];
    } 

    printf OUT "};\n";
    close  OUT;
    print "Wrote $pos_file\n";
} # 1}}}
sub dof { # {{{1
    my ($canonical_node_id, # in 
        $nodal_dof         , # in  Tx | Ty | Tz | Rx | Ry | Rz
        $rhh_renumbered_id , # in renumbered_id{canonical id}{new_id} 
       ) = @_;

    my $offset;
    if      ($nodal_dof eq "Tx") {
        $offset = 0;
    } elsif ($nodal_dof eq "Ty") {
        $offset = 1;
    } elsif ($nodal_dof eq "Tz") {
        $offset = 2;
    } elsif ($nodal_dof eq "Rx") {
        $offset = 3;
    } elsif ($nodal_dof eq "Ry") {
        $offset = 4;
    } elsif ($nodal_dof eq "Rz") {
        $offset = 5;
    } else {
        die "Bad input to dof():  no such nodal_dof '$nodal_dof'";
    }

    return $offset + 6*($rhh_renumbered_id->{$canonical_node_id}{new_id} - 1);
} # 1}}}
sub load_results { # {{{1
    my ($file     , # in file
       ) = @_;

    my @vector = ();
    open  IN, $file or die "Cannot read $file:  $!\n";
    while (<IN>) {
        next if /^\s*$/ or /^\s*#/;
        chomp;
        s/^\s+//; s/\s+$//;  # remove leading, trailing ws
        push @vector, $_;
    }
    close IN;

    # normalize it
    my $peak = 0;
    foreach (@vector) {
        $peak = $_ if $peak < abs($_);
    }
    foreach (@vector) {
        $_ /= $peak;
    }

    return @vector;
} # 1}}}
__END__
# Sample contents for $node_ref, $e_n_ref, $partn_ref  {{{1

$node_ref = {
          '6' => {
                   'x3' => '0',
                   'seq_no' => 6,
                   'x2' => '0.6666667',
                   'x1' => '1'
                 },
          '3' => {
                   'x3' => '0',
                   'seq_no' => 3,
                   'x2' => '0.3333333',
                   'x1' => '1'
                 },
          '7' => {
                   'x3' => '0',
                   'seq_no' => 7,
                   'x2' => '1',
                   'x1' => '0.3333333'
                 },
          '9' => {
                   'x3' => '0',
                   'seq_no' => 9,
                   'x2' => '1',
                   'x1' => '1'
                 },
          '2' => {
                   'x3' => '0',
                   'seq_no' => 2,
                   'x2' => '0.3333333',
                   'x1' => '0.6666667'
                 },
          '8' => {
                   'x3' => '0',
                   'seq_no' => 8,
                   'x2' => '1',
                   'x1' => '0.6666667'
                 },
          '1' => {
                   'x3' => '0',
                   'seq_no' => 1,
                   'x2' => '0.3333333',
                   'x1' => '0.3333333'
                 },
          '4' => {
                   'x3' => '0',
                   'seq_no' => 4,
                   'x2' => '0.6666667',
                   'x1' => '0.3333333'
                 },
          '5' => {
                   'x3' => '0',
                   'seq_no' => 5,
                   'x2' => '0.6666667',
                   'x1' => '0.6666667'
                 }
        };
$e_n_ref = [
          [
            1,
            1
          ],
          [
            1,
            2
          ],
          [
            1,
            5
          ],
          [
            2,
            1
          ],
          [
            2,
            5
          ],
          [
            2,
            4
          ],
          [
            3,
            2
          ],
          [
            3,
            3
          ],
          [
            3,
            6
          ],
          [
            4,
            2
          ],
          [
            4,
            6
          ],
          [
            4,
            5
          ],
          [
            5,
            4
          ],
          [
            5,
            5
          ],
          [
            5,
            8
          ],
          [
            6,
            4
          ],
          [
            6,
            8
          ],
          [
            6,
            7
          ],
          [
            7,
            5
          ],
          [
            7,
            6
          ],
          [
            7,
            9
          ],
          [
            8,
            5
          ],
          [
            8,
            9
          ],
          [
            8,
            8
          ]
        ];
$partn_ref = {
          '6' => {
                   'pid' => 1,
                   'eid' => 6
                 },
          '3' => {
                   'pid' => 2,
                   'eid' => 3
                 },
          '7' => {
                   'pid' => 2,
                   'eid' => 7
                 },
          '2' => {
                   'pid' => 1,
                   'eid' => 2
                 },
          '8' => {
                   'pid' => 1,
                   'eid' => 8
                 },
          '1' => {
                   'pid' => 2,
                   'eid' => 1
                 },
          '4' => {
                   'pid' => 2,
                   'eid' => 4
                 },
          '5' => {
                   'pid' => 1,
                   'eid' => 5
                 }
        };
1}}}
