#!/usr/bin/env perl
#!/usr/local/bin/perl -w
#
# Generates an arbitrarily large rectangular mesh made of triangular 
# elements.  Nodes on the left edge are constrained; nodes on the 
# right edge are loaded.
#
# Albert Danial Oct 6 2003
#
use File::Basename;
use Getopt::Long;
use POSIX;          # acos
use strict;

my $TWOPI  = 2*acos(-1);
my $script = basename $0;
my $nid_offset = 100000;
my $eid_offset = 200000;

my @known_xforms = qw( cylinder torus rect );
my ($nx, $ny, $xform, $ri, $ro, );
$xform = "rect";
GetOptions ("nx=i"    => \$nx,
            "ny=i"    => \$ny,
            "xform=s" => \$xform,
            "ri=s"    => \$ri,
            "ro=s"    => \$ro,
           );
my $R = $ri if  defined $ri;
   $R = $ro if !defined($R) and defined($ro);

die "$script -nx <X>  -ny <Y> [options]  <file name>

              Generates a rectangular mesh made of X*Y tiles, where each
              tile is made of two triangular elements.  The output is a
              comma separated value (.csv) file.  Each element has unit
	      dimension so large meshes have larger X,Y coordinates.

              Some model sizes:

               nx      ny      # elements    # nodes      # dof
              ---     ---      ----------    -------    --------
                5       5              50         36         216
               10      10             200        121         726
               30      30            1800        961        5766
               50      50            5000       2601       15606
              100      50           10000       5151       30906
              200     200           80000      40401      242406
              400     400          320000     160801      964806
              500     500          500000     251001     1506006

              Options:
                -xform=XXX   Where XXX is one of the following shapes:
                               @known_xforms
                             The default is rect
                             Depending on the shape one or more of the
                             other options must be specified.
                -ro=XXX      Outer  radius for a torus, or radius for cylinder.
                -ri=XXX      Innner radius for a torus, or radius for cylinder.

" unless @ARGV and $nx and $ny;

die "For a cylinder specify a radius with either -ri=X  or  -ro=X\n" 
    if $xform eq "cylinder" and (!defined $R);
die "For a torus specify both innner and outer radii with -ri=<Ri>  and  -ro=<Ro>\n" 
    if $xform eq "torus"    and (!defined $ri or !defined $ro);

my $file  = shift @ARGV;
my $nElem = 2*$nx*$ny;
my $nNode = ($nx+1)*($ny+1);
printf "%s:  %d elements, %d nodes, %d DOF, %d lines\n", 
        $file, $nElem, $nNode, 6*$nNode, $nElem + $nNode + 3;

my $el_type   = "tri3";
my $pid       = "45_mil_alum";
my $schema    = "schema,../db/csv_schema.yaml";
my $coord     = coordinate_systems();
my $property  = "shell_prop, 45_mil_alum, al_6061, 4.5e-2";
my $material  = "material,al_6061, 9.9E+06 [E; psi], 3.72180450E+06 [G; psi], .33 [nu], 0.098 [density; lbm/in^3], 20000 [max stress]";
my $spc_set   = 'set, left_nodes, sql, " \
  select seq_no from node where x1 <= (select min(x1) from node)    -- \
"';
my $load_set   = 'set, right_nodes, sql, " \
  select seq_no from node where x1 >= (select max(x1) from node)    -- \
"';

# corner_set isn't used but shows an example of a longer SQL expression
my $corner_set   = 'set, corner_nodes, sql, " \
  select seq_no from node where x1 <= (select min(x1) from node)   -- \
                            and x2 <= (select min(x2) from node)   -- \
    union                                                      -- \
  select seq_no from node where x1 <= (select min(x1) from node)   -- \
                            and x2 >= (select max(x2) from node)   -- \
    union                                                      -- \
  select seq_no from node where x1 >= (select max(x1) from node)   -- \
                            and x2 <= (select min(x2) from node)   -- \
    union                                                      -- \
  select seq_no from node where x1 >= (select max(x1) from node)   -- \
                            and x2 >= (select max(x2) from node)   -- \
"';

my $spc       = "spc, constrain_left_nodes, left_nodes, 123456";
my $load_cid  =   "";  # load vector coordinate system
my $load_Mag  = 10.0;  # load vector magnitude
my $load_n1   =  0.0;  # load vector direction 1 value
my $load_n2   =  0.0;  # load vector direction 2 value
my $load_n3   =  1.0;  # load vector direction 3 value
my $load      = "force, load_right_nodes, right_nodes, " .
                "$load_cid, $load_Mag, $load_n1, $load_n2, $load_n3";
my $nid_bl    = 0;  # bottom left  node
my $nid_br    = 0;  # bottom right node
my $nid_tl    = 0;  # top    left  node
my $nid_tr    = 0;  # top    right node
my $eid       = $eid_offset;

open  OUT, ">$file" or die "Cannot write to $file: $!\n";
print OUT "$schema\n";  # must be first line of the .csv file
for (my $iy = 1; $iy <= $ny; $iy++) {
    for (my $ix = 1; $ix <= $nx; $ix++) {
        ++$eid;
        $nid_bl = $nid_offset + $ix + ($iy - 1)*($nx + 1);   
        $nid_br = $nid_bl + 1;       
        $nid_tl = $nid_bl + 1 + $nx; 
        $nid_tr = $nid_tl + 1;       
        printf OUT "%s,%s,%s,%d,%d,%d\n", 
                    $el_type, $eid, $pid, $nid_bl, $nid_br, $nid_tr;
        ++$eid;
        printf OUT "%s,%s,%s,%d,%d,%d\n", 
                    $el_type, $eid, $pid, $nid_bl, $nid_tr, $nid_tl;
    }
}

my $nid       = $nid_offset;
my $max_dim   = 1;
if ($xform eq "rect") {
    $max_dim   = $nx;
    $max_dim   = $ny if $ny > $nx;
}
my $X         = 0;   my $dX = 1.0/($max_dim+1);  # normalize to the
my $Y         = 0;   my $dY = 1.0/($max_dim+1);  # larger dimension
my $Z         = 0;   my $dZ = 0.0;
for (my $iy = 1; $iy <= $ny+1; $iy++) {
    for (my $ix = 1; $ix <= $nx+1; $ix++) {
        $X = $ix*$dX;
        $Y = $iy*$dY;
        ++$nid;
        if      ($xform eq "cylinder") {
            printf OUT "node,%s, 1,%e,%e,%e,\n", $nid, 
                            cylinder_xyz($X, $Y, $Z, $R, $nx*$dX);
        } elsif ($xform eq "torus") {
            printf OUT "node,%s, 1,%e,%e,%e,\n", $nid, 
                            torus_xyz(   $X, $Y, $Z, $ri, $ro, $nx*$dX, $ny*$dY);
        } elsif ($xform eq "rect") {
            printf OUT "node,%s, 1,%e,%e,%e,\n", $nid, 
                                         $X, $Y, $Z;
        } else {
            die "'$xform' is not a recognized transformation. Try one of\n" .
                "@known_xforms\n";
        }
    }
}
print OUT "$coord\n";
print OUT "$property\n";
print OUT "$material\n";
print OUT "$spc_set\n";
print OUT "$spc\n";
print OUT "$load_set\n";
print OUT "$load\n";
close OUT;

sub cylinder_xyz { # {{{1
    my ($in_x, $in_y, $in_z, $radius, $x_width) = @_;

    my $out_x = $radius*cos($TWOPI*$in_x/$x_width);
    my $out_z = $radius*sin($TWOPI*$in_x/$x_width);

    return ($out_x, $in_y, $out_z);
} # 1}}}
sub torus_xyz { # {{{1
    my ($in_x, $in_y, $in_z, $rad_in, $rad_out, $x_width, $y_height) = @_;

    my $Rm = 0.5*($rad_out + $rad_in);
    my $Rc = 0.5*($rad_out - $rad_in);

    # first map to cylinder
    my $out_x =   $Rm*cos($TWOPI*$in_x/$x_width);
    my $out_z = 2*$Rc*sin($TWOPI*$in_x/$x_width);

    # then map to torus
    my $out_y  = ($Rm + $out_x/$Rm)*sin($TWOPI*$in_y/$y_height); 
       $out_x  = ($Rm + $out_x/$Rm)*cos($TWOPI*$in_y/$y_height);


    return ($out_x, $out_y, $out_z);
} # 1}}}
sub coordinate_systems { # {{{1
return '
# coordinate system 1 is the same as the basic coordinae system
#       ID   IDrel type       point A            point B               point C
coord,    1,   0,   1,   ,  0.00,0.00,0.00, ,  0.00, 0.00,1.00, ,  1.00, 0.00,1.00
coord,  101,   1,   1,   ,  0.05,0.07,0.09, ,  0.05,-0.03,1.09, ,  1.05,-0.03,1.09
coord,  102,   1,   1,   ,  0.05,0.07,0.09, ,  0.15, 0.07,1.09, ,  1.05, 0.07,1.09
coord,  103,   1,   1,   ,  0.05,0.07,0.09, ,  0.05, 0.07,1.09, ,  1.05, 0.17,1.09
coord, 1101, 101,   1,   ,  0.05,0.07,0.09, ,  0.05,-0.03,1.09, ,  1.05,-0.03,1.09
coord, 1102, 101,   1,   ,  0.05,0.07,0.09, ,  0.15, 0.07,1.09, ,  1.05, 0.07,1.09
coord, 1103, 101,   1,   ,  0.05,0.07,0.09, ,  0.05, 0.07,1.09, ,  1.05, 0.17,1.09
coord, 2101, 102,   1,   ,  0.05,0.07,0.09, ,  0.05,-0.03,1.09, ,  1.05,-0.03,1.09
coord, 2102, 102,   1,   ,  0.05,0.07,0.09, ,  0.15, 0.07,1.09, ,  1.05, 0.07,1.09
coord, 2103, 102,   1,   ,  0.05,0.07,0.09, ,  0.05, 0.07,1.09, ,  1.05, 0.17,1.09
coord, 3101, 103,   1,   ,  0.05,0.07,0.09, ,  0.05,-0.03,1.09, ,  1.05,-0.03,1.09
coord, 3102, 103,   1,   ,  0.05,0.07,0.09, ,  0.15, 0.07,1.09, ,  1.05, 0.07,1.09
coord, 3103, 103,   1,   ,  0.05,0.07,0.09, ,  0.05, 0.07,1.09, ,  1.05, 0.17,1.09
';
} # 1}}}
