//============================================//
// The intention of this V&V script is to have it run for each new version of OpenVSP and
// VSPAERO. Some of the test cases are expected to run more smoothly than others. One of 
// the main issues that seems to occur is segment intersection errors. One of the ways to
// get around this problem is to adjust the AR or another geometric parameter by a very 
// small amount (0.01-0.000001 works usually). Normally, it's best to start at 0.000001 
// and go up by halves until CompGeom or VSPERO stops crashing (e.g. 0.00001 -> 0.00005 
// -> 0.0001 -> 0.0005).
//
// Common settings for all V&V studies, such as the number of wake iterations can be 
//  adjusted in the VV_Base class. Search for "V&V Base Class" to locate the class.
//
// Author: Justin Gravett, ESAero
// Created: 2018-02-07

//============= Define Global Entities ==============//
file fid;
int m_FigCount = 1;

//============= Main Functions ==============//
void main()
{
    Print( string("Begin Master VSP V&V Script\n") );

    const string html_fname = GetVSPVersion() + string("_VV.html");
    
    // Open the file in 'write' mode and check for success
    if( fid.open(html_fname, "w") < 0 ) 
    {
      string message =  string("Error: Failed to Open ") + html_fname + string("\n");
      Print( message );
      return;
    }
    
    HTML html = HTML(); // Initialize HTML class

    html.WriteHTMLHeader();
    
    TestAll( html );
    
    html.AddResizeEvent();
    html.WriteHTMLBody();

    fid.writeString( string("</html>") );
    fid.close();
    
    //==== Check For API Errors ====//
    while ( GetNumTotalErrors() > 0 )
    {
        ErrorObj err = PopLastError();
        Print( err.GetErrorString() );
    }
    
    Print( string( "End Master VSP V&V Script\n" ) );
}

void TestAll(HTML @ html)
{
    // Set the flags here for which studies to run
    const bool HersheyFlag = true;
    const bool SweptFlag = true;
    const bool BertinFlag = true;
    const bool WarrenFlag = true;
    const bool VKTFlag = true;
    const bool EllipseFlag = true;
    const bool SuperDeltaFlag = true;
    
    if ( HersheyFlag )
    {
        html.m_hvv = HersheyVV(); // Initialize HersheyVV class

        //====Execute Hershey Study====//
        html.m_hvv.HersheyBarStudy();
        
        // Note: To control which Hershey Bar studies to run, comment the functions in
        //  GenerateHersheyBarWings() and TestHersheyBarWings()
    }
    
    if ( SweptFlag )
    {
        html.m_svv = SweepVV(); // Initialize SweepVV class
        
        //====Execute Swept Wing Sweep Study====//
        html.m_svv.SweptWingStudy();
        
        // Note: To control which Swept Wing studies to run, comment the functions in
        //  GenerateSweepStudyWings() and TestSweepStudyWings()
    }
    
    if ( BertinFlag )
    {
        html.m_bvv = BertinVV(); // Initialize BertinVV class
        
        //====Execute Bertin-Smith Study====//
        html.m_bvv.BertinSmithStudy();
    }
    
    if ( WarrenFlag )
    {
        html.m_wvv = WarrenVV(); // Initialize WarrenVV class
        
        //====Execute Warren-12 Study====//
        html.m_wvv.WarrrenStudy();
    }
    
    if ( VKTFlag )
    {
        html.m_vktvv = VKTVV(); // Initialize VKTVV class
        
        //====Execute VKT Epsilon Kappa Tau Tess Study====//
        html.m_vktvv.VKTStudy();
        
        // Note: To control which VKT studies to run, comment the functions in
        //  GenerateVKTWings() and TestVKTWings()
    }
    
    if ( EllipseFlag )
    {
        html.m_evv = EllipsoidVV(); // Initialize EllipsoidVV class
        
        //====Execute Ellipsoid Study====//
        html.m_evv.EllipsoidStudy();
    }
    
    if ( SuperDeltaFlag )
    {
        html.m_sdvv = SDVV(); // Initialize SDVV class
        
        //====Execute Supersonic Delta Wing Study====//
        html.m_sdvv.TestSupersonicDeltaWing();
    }
}

//============= Helper Functions ==============//
array<vec3d> ReadCpDistFile( string file_name )
{
    // This function reads in an XFoil CpDistribution file and returns an array<vec3d>
    //  in the format (x,y,Cp)
    array<vec3d> x_y_cp_vec;
    file@ cp_file = file();

    if( cp_file.open(file_name, "r") < 0 )
    {
      string message =  string("Error: Failed to Open ") + file_name + string("\n");
      Print( message );
      return x_y_cp_vec;
    }

    string line = cp_file.readLine();
    uint row_cnt = 1;
    
    while ( !cp_file.isEndOfFile() )
    {
        if ( row_cnt > 3 )
        {
            array<string> line_array = line.split(" ");
            
            vec3d x_y_cp;
            uint index = 0;
            
            for ( uint i = 0; i < uint(line_array.length()); i++ )
            {
                if ( ( line_array[i].length() > 1 ) && index < 3 )
                {
                    x_y_cp[index] = parseFloat(line_array[i]);
                    index++;
                }
            }
            
            x_y_cp_vec.push_back(x_y_cp);
        }
        
        line = cp_file.readLine();
        row_cnt++;
    }

    cp_file.close();
    
    return x_y_cp_vec;
}

array<vec3d> ReadAVLFSFile( string file_name )
{
    // This function reads in an AVL Strip Force file and returns an array<vec3d>
    //  in the format (y,Cl,Cd)
    array<vec3d> y_Cl_Cd_vec;
    file@ fs_file = file();

    if( fs_file.open(file_name, "r") < 0 )
    {
      string message =  string("Error: Failed to Open ") + file_name + string("\n");
      Print( message );
      return y_Cl_Cd_vec;
    }

    string line = fs_file.readLine();

    while ( !fs_file.isEndOfFile() )
    {
        if ( line.findFirst("Strip Forces referred to Strip Area, Chord",0) > 0 )
        {
            line = fs_file.readLine();
            line = fs_file.readLine();
            
            while ( ( line.length() > 10 ) && line.findFirst("--------",0) < 0 )
            {
                array<string> line_array = line.split("   ");
                
                vec3d y_Cl_Cd;
                y_Cl_Cd[0] = parseFloat(line_array[2]);
                y_Cl_Cd[1] = parseFloat(line_array[8]);
                y_Cl_Cd[2] = parseFloat(line_array[9]);
                
                y_Cl_Cd_vec.push_back(y_Cl_Cd);
                
                line = fs_file.readLine();
            }
        }
        
        line = fs_file.readLine();
    }

    fs_file.close();
    
    return y_Cl_Cd_vec;
}

//============= HTML Support Functions ==============//
class HTML
{
    HTML()
    {
    }
    
    void WriteHTMLHeader()
    {
        fid.writeString( string("<!DOCTYPE html>\n") );
        fid.writeString( string("<html lang='en'>\n") );
        
        fid.writeString( string("  <head>\n") );
        fid.writeString( string("\t<title>OpenVSP Verification and Validation</title>\n") );
        fid.writeString( string("\t<meta charset='utf-8'>\n") );
        fid.writeString( string("\t<meta name='viewport' content='width=device-width, initial-scale=1.0'>\n") );
        
        // CSS 
        fid.writeString( string("\t<style>\n") );
        fid.writeString( string("\t  * { box-sizing: border-box; }\n") );
        fid.writeString( string("\t  body { margin: 0; counter-reset: section; }\n") );
        fid.writeString( string("\t  .column.content p { font-size: 110%; }\n") );
        fid.writeString( string("\t  h1 { text-align: center; counter-reset: subsection; }\n") );
        fid.writeString( string("\t  h1::before { counter-increment: section; content: 'Case ' counter(section) ': ';}\n") );
        fid.writeString( string("\t  .topnav { overflow: hidden; background-color: #333; }\n") );
        fid.writeString( string("\t  .topnav a { float: left; display: block; color: #f2f2f2; text-align: center; padding: 14px 16px; text-decoration: none; }\n") );
        fid.writeString( string("\t  .topnav a:hover { background-color: #ddd; color: black; }\n") );
        fid.writeString( string("\t  .chartl { float: left; margin-left: 0.5vmin; margin-right: 0.5vmin; }\n") );
        fid.writeString( string("\t  .chartr { float: right; margin-left: 0.5vmin; margin-right: 0.5vmin; }\n") );
        fid.writeString( string("\t  .chartc { display: table; margin: 0 auto }\n") );
        fid.writeString( string("\t  .column { float: left; }\n") );
        fid.writeString( string("\t  .column.toc { width: 10%; position: sticky; position: -webkit-sticky; top: 0; padding-left: 3px; padding-right: 0px; padding-top: 3px; }\n") );
        fid.writeString( string("\t  .toc_title { font-size: 2.6vmin; font-weight: 800; text-align: center; }\n") );
        fid.writeString( string("\t  #toc_container { background: #f9f9f9; border: 2px solid #aaa; padding: 0.6vmin; width: 98%; word-wrap: break-word; }\n") );
        fid.writeString( string("\t  ul { list-style-position: outside; padding-left: 2vmin; font-size: 1.8vmin; }\n") );
        fid.writeString( string("\t  ul ul { padding-left: 1.35vmin; font-size: 1.5vmin; }\n") );
        fid.writeString( string("\t  li { margin: 5px 0; }\n") );
        fid.writeString( string("\t  table.center { margin: auto; border-collapse: collapse; } \n") );
        fid.writeString( string("\t  table.center.three { width: 30.0%; } \n") );
        fid.writeString( string("\t  table.center.four { width: 40.0%; } \n") );
        fid.writeString( string("\t  table.center.five { width: 50.0%; } \n") );
        fid.writeString( string("\t  table.center.six { width: 60.0%; } \n") );
        fid.writeString( string("\t  table.center.seven { width: 70.0%; } \n") );
        fid.writeString( string("\t  table.center.eight { width: 80.0%; } \n") );
        fid.writeString( string("\t  caption { text-align: center; font-weight: bold; font-size: 115%; } \n") );
        fid.writeString( string("\t  th, td { padding: 8px; border: 1px solid black; } \n") );
        fid.writeString( string("\t  th { text-align: left; background-color: #333; color: white; } \n") );
        fid.writeString( string("\t  tr:nth-child(even){background-color: #f2f2f2;} \n") );
        fid.writeString( string("\t  .column.content { counter-reset: section; width: 90%; background-color: #ddd; padding: 5px 25px 15px 25px }\n") );
        fid.writeString( string("\t  .clearfix { clear: both; }\n") );
        fid.writeString( string("\t  .row:after, .row:before { clear: both; display: table; content: ' '; }\n") );
        fid.writeString( string("\t  .footer { overflow: auto; text-align: center; font-size: 115%; background-color: #f1f1f1; padding: 10px; }\n") );
        fid.writeString( string("\t  @media (max-width: 600px) { .column.toc, .column.content { width: 100%; } }\n") ); // Remove?
        fid.writeString( string("\t</style>\n") );
        
        // JavaScript
        //fid.writeString( string("\t<script type='text/x-mathjax-config'>MathJax.Hub.Config({ TeX: { equationNumbers: { autoNumber: 'all' } } });</script>\n") ); // Auto equation #s
        fid.writeString( string("\t<script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js?config=TeX-MML-AM_CHTML'></script>\n") ); // MathJax JavaScript
        fid.writeString( string("\t<script src='https://www.gstatic.com/charts/loader.js'></script>\n") ); // Google Charts JavaScript
        fid.writeString( string("\t<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>\n") ); // Include jQuery Javascript library
        fid.writeString( string("\t<script >\n") );
        fid.writeString( string("\t  // Load the Visualization API and the chart package.\n") );
        fid.writeString( string("\t  google.charts.load('current', {'packages':['corechart']});\n\n") );
    }
    
    void AddResizeEvent()
    {
        fid.writeString( string("\t  $(window).resize(function() {\n") );
        fid.writeString( string("\t\tif(this.resizeTO) clearTimeout(this.resizeTO);\n") );
        fid.writeString( string("\t\tthis.resizeTO = setTimeout(function() {\n") );
        fid.writeString( string("\t\t  $(this).trigger('resizeEnd');\n") );
        fid.writeString( string("\t\t}, 500);\n") ); // Reduce the time?
        fid.writeString( string("\t  });\n\n") );
        
        fid.writeString( string("\t  $(window).on('resizeEnd', function() {\n") );
        
        for ( uint i = 0; i < uint(m_hvv.m_HersheyARIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_hvv.m_HersheyARIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_hvv.m_HersheyAlphaErrorIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_hvv.m_HersheyAlphaErrorIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_hvv.m_HersheyTessIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_hvv.m_HersheyTessIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_hvv.m_HersheyTimeIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_hvv.m_HersheyTimeIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_hvv.m_HersheyLDTCIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_hvv.m_HersheyLDTCIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_hvv.m_HersheyLDUTessIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_hvv.m_HersheyLDUTessIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_hvv.m_HersheyLDWTessIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_hvv.m_HersheyLDWTessIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_hvv.m_HersheyDDUTessIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_hvv.m_HersheyDDUTessIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_hvv.m_HersheyDDWTessIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_hvv.m_HersheyDDWTessIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_hvv.m_HersheyLDWakeIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_hvv.m_HersheyLDWakeIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_hvv.m_HersheyLDAdvancedIDVec.length()); i++ )
        {
            for ( uint j = 0; j < uint(m_hvv.m_HersheyLDAdvancedIDVec[i].length()); j++ )
            {
                string fun = string("\t\t") + m_hvv.m_HersheyLDAdvancedIDVec[i][j] + string("();\n");
                fid.writeString( fun );
            }
        }
        for ( uint i = 0; i < uint(m_hvv.m_HersheyAdvancedTimeIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_hvv.m_HersheyAdvancedTimeIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_svv.m_SweepTessIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_svv.m_SweepTessIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        // for ( uint i = 0; i < uint(m_svv.m_SweepSweepLAvCRIDVec.length()); i++ )
        // {
            // string fun = string("\t\t") + m_svv.m_SweepSweepLAvCRIDVec[i] + string("();\n");
            // fid.writeString( fun );
        // }
        for ( uint i = 0; i < uint(m_svv.m_SweepSweepClavARIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_svv.m_SweepSweepClavARIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_svv.m_SweepSweepErrorIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_svv.m_SweepSweepErrorIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_bvv.m_BSErrorIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_bvv.m_BSErrorIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_bvv.m_BSClAIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_bvv.m_BSClAIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_wvv.m_W12ClaErrorIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_wvv.m_W12ClaErrorIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_wvv.m_W12CmaErrorIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_wvv.m_W12CmaErrorIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_wvv.m_W12ClAIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_wvv.m_W12ClAIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_wvv.m_W12CmAIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_wvv.m_W12CmAIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_vktvv.m_VKTEpsKapTauIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_vktvv.m_VKTEpsKapTauIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_vktvv.m_VKTUTessIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_vktvv.m_VKTUTessIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_vktvv.m_VKTWTessIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_vktvv.m_VKTWTessIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_evv.m_EllipsoidIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_evv.m_EllipsoidIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        for ( uint i = 0; i < uint(m_sdvv.m_SuperDeltaIDVec.length()); i++ )
        {
            string fun = string("\t\t") + m_sdvv.m_SuperDeltaIDVec[i] + string("();\n");
            fid.writeString( fun );
        }
        
        fid.writeString( string("\t  });\n\n") );
    }
    
    void WriteHTMLBody()
    {
        // End header
        fid.writeString( string("\t</script>\n") ); 
        fid.writeString( string("  </head>\n") );

        fid.writeString( string("  <body>\n") ); // Begin Body
        
        WriteHTMLTopNav();
        WriteHTMLContent();
        WriteHTMLFooter();
        
        fid.writeString( string("  </body>\n") ); // End Body
    }

    private void WriteHTMLTopNav()
    {
        // Header
        fid.writeString( string("\t<div class='topnav'>\n") );
        fid.writeString( string("\t  <a href='http://www.openvsp.org'>OpenVSP Home</a>\n") );
        fid.writeString( string("\t  <a href='http://www.openvsp.org/wiki/doku.php'>OpenVSP Wiki</a>\n") );
        fid.writeString( string("\t  <h2 style='color: white; text-align: center; padding-right: 5vmin; font-size: 120%; font-weight: bold;'>OpenVSP Verification & Validation Studies</h2>\n") );
        fid.writeString( string("\t</div>\n") );
    }

    private void WriteHTMLTOC()
    {
        // Table of Contents
        fid.writeString( string("\t  <div class='column toc'>\n") );
        fid.writeString( string("\t\t<div id='toc_container'>\n") );
        fid.writeString( string("\t\t<h3 class='toc_title'>Contents</h3>\n") );
        fid.writeString( string("\t\t<ul>\n") );
        fid.writeString( string("\t\t  <li><a href='#INT'>Introduction</a></li>\n") );
        fid.writeString( string("\t\t  <li><a href='#NOT'>Notation</a></li>\n") );
        fid.writeString( string("\t\t  <li><a href='#EQU'>Equations</a></li>\n") );
        if ( m_hvv.m_runflag )
        {
            fid.writeString( string("\t\t  <li><a href='#HBW'>Hershey Bar</a>\n") );
            fid.writeString( string("\t\t\t<ul>\n") );
            if ( m_hvv.m_HersheyARIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t\t  <li><a href='#HAR'>AR Study</a></li>\n") );
            }
            if ( m_hvv.m_HersheyAlphaErrorIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t\t  <li><a href='#HAlpha'>Angle of Attack Study</a></li>\n") );
            }
            if ( m_hvv.m_HersheyTessIDVec.length() > 0 || m_hvv.m_HersheyTimeIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t\t  <li><a href='#HTess'>Tesselation Study</a></li>\n") );
            }
            if ( m_hvv.m_HersheyLDTCIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t\t  <li><a href='#HTC'>Tip Clustering Study</a></li>\n") );
            }
            if ( m_hvv.m_HersheyLDWTessIDVec.length() > 0 || m_hvv.m_HersheyDDWTessIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t\t  <li><a href='#HCT'>Chord Tesselation Study</a></li>\n") );
            }
            if ( m_hvv.m_HersheyLDUTessIDVec.length() > 0 || m_hvv.m_HersheyDDWTessIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t\t  <li><a href='#HST'>Span Tesselation Study</a></li>\n") );
            }
            if ( m_hvv.m_HersheyLDWakeIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t\t  <li><a href='#HWake'>Wake Iteration Study</a></li>\n") );
            }
            if ( m_hvv.m_HersheyLDAdvancedIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t\t  <li><a href='#HAdv'>Advanced Settings Study</a></li>\n") );
            }
            fid.writeString( string("\t\t\t</ul>\n") );
            fid.writeString( string("\t\t  </li>\n") );
        }
        if ( m_svv.m_runflag )
        {
            fid.writeString( string("\t\t  <li><a href='#SW'>Swept Wing</a>\n") );
            fid.writeString( string("\t\t\t<ul>\n") );
            if ( m_svv.m_SweepTessIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t\t  <li><a href='#STess'>Tesselation Study</a></li>\n") );
            }
            if ( m_svv.m_SweepSweepClavARIDVec.length() > 0 || m_svv.m_SweepSweepErrorIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t\t  <li><a href='#SSS'>Sweep Study</a></li>\n") );
            }
            fid.writeString( string("\t\t\t</ul>\n") );
            fid.writeString( string("\t\t  </li>\n") );
        }
        if ( m_bvv.m_runflag )
        {
            fid.writeString( string("\t\t  <li><a href='#BSW'>Bertin-Smith</a>\n") );
            fid.writeString( string("\t\t  </li>\n") );
        }
        if ( m_wvv.m_runflag )
        {
            fid.writeString( string("\t\t  <li><a href='#WW'>Warren-12</a>\n") );
            fid.writeString( string("\t\t  </li>\n") );
        }
        if ( m_vktvv.m_runflag )
        {
            fid.writeString( string("\t\t  <li><a href='#VKTW'>Von Karman-Trefftz</a>\n") );
            fid.writeString( string("\t\t\t<ul>\n") );
            if ( m_vktvv.m_VKTEpsKapTauIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t\t  <li><a href='#VKTE'>Epsilon Kappa Tau Study</a></li>\n") );
            }
            if ( m_vktvv.m_VKTWTessIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t\t  <li><a href='#VKTCT'>Chord Tesselation Study</a></li>\n") );
            }
            if ( m_vktvv.m_VKTUTessIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t\t  <li><a href='#VKTST'>Span Tesselation Study</a></li>\n") );
            }
            fid.writeString( string("\t\t\t</ul>\n") );
            fid.writeString( string("\t\t  </li>\n") );
        }
        if ( m_evv.m_runflag && m_evv.m_EllipsoidIDVec.length() > 0 )
        {
            fid.writeString( string("\t\t  <li><a href='#EW'>Ellipsoid</a>\n") );
            fid.writeString( string("\t\t  </li>\n") );
        }
        if ( m_sdvv.m_runflag )
        {
            fid.writeString( string("\t\t  <li><a href='#SDW'>Supersonic Delta Wing</a>\n") );
            fid.writeString( string("\t\t  </li>\n") );
        }
        
        fid.writeString( string("\t\t  <li><a href='#REF'>References</a>\n") );
        fid.writeString( string("\t\t  </li>\n") );
        
        fid.writeString( string("\t\t</ul>\n") );
        fid.writeString( string("\t\t</div>\n") );
        fid.writeString( string("\t  </div>\n") );
    }

    private void WriteHTMLContent()
    {
        // Content Section
        fid.writeString( string("\t<div class='row'>\n") );

        // Table of Contents
        WriteHTMLTOC();
        
        // Main Content
        fid.writeString( string("\t  <div class='column content'>\n") );
        
        fid.writeString( string( "\t\t<h2 id='INT' style='text-align: center; font-size: 200%; font-weight: bold;'>Introduction</h2>\n" ) );
        fid.writeString( string("\t\t<div>\n") );
        
        string text = string("This document outlines a series of verification and validation (V&V) studies that have been conducted for the VSPAERO aerodynamic solver. This HTML was generated from a single *.vspscript file named \"Master_VSP_VV_Script.vspscript\" located in the scripts directory distributed with the OpenVSP software. In each of the V&V test cases listed below, there exists published theoretical or experimental results that can be verified for a particular geometric model. The results from VSPAERO are then able to be validated against the published results to determine the accuracy of the solver. Each test begins by modeling a wing or series of wing geometries through the OpenVSP API. The geometric parameters are listed in a table for each study. If a parameter is not listed in the table the default value in OpenVSP should be assumed. Each generated model is saved, and therefore can be loaded in OpenVSP to verify the model accurately represents the geometry of the V&V study. Depending on the particular test case, the results needed for validation must then be identified or calculated. Once results for validation are established, VSPAERO is setup and executed through the Analysis Manager. The VSPAERO analysis inputs are listed in a table for each study, with default values used for items not identified in the tables. Following VSPAERO execution, results are obtained through the Results Manager, compared to the published results, and displayed.");

        WriteParagraph( text );
        
        fid.writeString( string("\t\t</div>\n") );
        
        fid.writeString( string( "\t\t<h2 id='NOT' style='text-align: center; font-size: 200%; font-weight: bold;'>Notation</h2>\n" ) );
        fid.writeString( string("\t\t<div style='font-weight: bold;'>\n") );
        
        text = string("AR\\(\\equiv\\)\tAspect Ratio");
        WriteParagraph( text );
        
        text = string("\\(\\alpha\\equiv\\)\tAngle of Attack");
        WriteParagraph( text );
        
        text = string("\\(\\alpha_0\\equiv\\)\tZero-Lift Angle of Attack");
        WriteParagraph( text );
        
        text = string("B\\(\\equiv\\)\tMach Parameter");
        WriteParagraph( text );
        
        text = string("b\\(\\equiv\\)\tWing Span");
        WriteParagraph( text );
        
        text = string("\\(\\beta\\equiv\\)\tSideslip Angle");
        WriteParagraph( text );
        
        text = string("c\\(\\equiv\\)\tWing Chord");
        WriteParagraph( text );
        
        text = string("\\(C_{Di}\\equiv\\)\tCoefficient of Induced Drag"); // per rad?
        WriteParagraph( text );
        
        text = string("\\(C_L\\equiv\\)\tCoefficient of Lift"); // per rad?
        WriteParagraph( text );
        
        text = string("\\(C_{L\\alpha}\\equiv\\)\tLift Curve Slope");
        WriteParagraph( text );
        
        text = string("\\(C_M\\equiv\\)\tPitching Moment Coefficient");
        WriteParagraph( text );
        
        text = string("\\(C_{M\\alpha}\\equiv\\)\tPitching Moment Coefficient with Respect to \\(\\alpha\\)");
        WriteParagraph( text );
        
        // text = string("\\(\\frac{\\partial \\alpha}{\\partial C_L}\\equiv\\)\tLift Angle");
        // WriteParagraph( text );
        
        text = string("\\(\\Gamma\\equiv\\)\tCirculation");
        WriteParagraph( text );
        
        text = string("K\\(\\equiv\\)\tRatio of \\(C_{L\\alpha}\\) to \\(2\\pi\\)");
        WriteParagraph( text );

        text = string("\\(\\Lambda\\equiv\\)\tSweep");
        WriteParagraph( text );
        
        text = string("\\(\\Lambda_{c/2}\\equiv\\)\tSweep of Half Chord Line");
        WriteParagraph( text );
        
        text = string("M\\(\\equiv\\)\tMach Number");
        WriteParagraph( text );
        
        text = string("m\\(\\equiv\\)\tSubsonic (m < 1) or Supersonic (m > 1) Character of Wing Leading Edge");
        WriteParagraph( text );
        
        text = string("S\\(\\equiv\\)\tPlanform Area");
        WriteParagraph( text );
        
        text = string("s\\(\\equiv\\)\tHalf Span");
        WriteParagraph( text );
        
        text = string("\\(V_{\\infty}\\equiv\\)\tFreestream Velocity");
        WriteParagraph( text );
        
        text = string("w\\(\\equiv\\)\tDownwash Velocity");
        WriteParagraph( text );
        
        text = string("y\\(\\equiv\\)\tLocation Along Wing Span");
        WriteParagraph( text );
        
        fid.writeString( string("\t\t</div>\n") );
        fid.writeString( string( "\t\t<h2 id='EQU' style='text-align: center; font-size: 200%; font-weight: bold;'>Equations</h2>\n" ) );
        fid.writeString( string("\t\t<div>\n") );
        
        text = string("Lifting Line Theory (LLT) provides the following equations outlined in the NACA TN 3911 <span style='font-style: italic'>A Method for Predicting Lift Increments Due to Flap Deflection at Low Angles of Attack in Incompressible Flow</span> by Lowry and Polhamus:");
        WriteParagraph( text );
        
        fid.writeString( string( "\t\t  <p>$$C_{L\\alpha} = \\frac{2\\pi AR }{2 + \\sqrt{(\\frac{AR^2 B^2}{K^2})(1 + \\frac{tan^2 \\Lambda_{c/2}}{B^2}) + 4}}$$</p>\n" ) );
        
        fid.writeString( string( "\t\t  <p>$$B = \\sqrt{1-M^2}$$</p>\n" ) );
        
        fid.writeString( string( "\t\t  <p>$$K = C_{L\\alpha2D}(\\frac{180}{\\pi})/(2\\pi)$$</p>\n" ) );
        
        text = string("Lowry and Polhamus also present a method of determining the downwash velocity and circulation distribution across a wing span:");
        WriteParagraph( text );
        
        fid.writeString( string( "\t\t  <p>$$w(y)=\\int_{-s}^{s} \\frac{[-d\\Gamma(y_0)/dy]dy_0}{4\\pi(y - y_0)}$$</p>\n" ) );
        
        fid.writeString( string( "\t\t  <p>$$\\Gamma(y) = {\\pi}{V_\\infty}(\\alpha - \\alpha_0)c + {\\pi}w(y)c$$</p>\n" ) );
        
        text = string("In <span style='font-style: italic'>Aerodynamics for Engineers</span>, Bertin and Smith develop the monoplane equation:");
        WriteParagraph( text );
        
        fid.writeString( string( "\t\t  <p>$$\\frac{{\\pi}c}{4s}(\\alpha - \\alpha_0)sin(\\theta) = \\sum_{n=1, odd}^{\\infty}A_n sin(n\\theta)[\\frac{{\\pi}cn}{4s}+sin(\\theta)]$$</p>\n" ) );
        
        text = string("Glauert's method can be used to solve the monoplane equation for \\(A_n\\), which can be used to determine the distribution of lift, drag, circulation, and downwash velocity:");
        WriteParagraph( text );
        
        fid.writeString( string( "\t\t  <p>$$\\Gamma(y) = 4{V_\\infty}s \\sum_{n=1, odd}^{\\infty}A_n sin(n\\theta)$$</p>\n" ) );
        
        fid.writeString( string( "\t\t  <p>$$w(y) = \\frac{V_\\infty}{sin(\\theta)} {\\sum_{n=1, odd}^{\\infty}{{A_n}{sin(n\\theta)}}}$$</p>\n" ) );
        
        fid.writeString( string( "\t\t  <p>$${C_L}(y) = \\frac{2}{{V_\\infty}c} \\int_{-s}^{s} {\\Gamma}(y)dy$$</p>\n" ) );
        
        fid.writeString( string( "\t\t  <p>$${C_{Di}}(y) = \\frac{2}{{V_\\infty}^{2}S} \\int_{-s}^{s} w(y){\\Gamma}(y)dy$$</p>\n" ) );
        
        text = string("The next group of equations are presented for three-dimensional wings in steady supersonic flow in <span style='font-style: italic'>Aerodynamics of Wings and Bodies</span> by Ashley and Landahl:");
        WriteParagraph( text );
        
        fid.writeString( string( "\t\t  <p>$$m = \\frac{B}{tan(\\Lambda)}$$</p>\n" ) );
        
        fid.writeString( string( "\t\t  <p>$$C_{L\\alpha}tan(\\Lambda) = f(m)$$</p>\n" ) );
        
        text = string("This final group of equations are general equations used throughout this V&V study:");
        WriteParagraph( text );
        
        fid.writeString( string( "\t\t  <p>$$\\%_{error} = |\\frac{experimental - theoretical}{theoretical}| \\times 100\\%$$</p>\n" ) );
        
        text = string("The central differencing formula:");
        WriteParagraph( text );
        
        fid.writeString( string( "\t\t  <p>$$f'(x)\\approx\\frac{f(x+h)-f(x-h)}{2h}$$</p>\n" ) );
        
        fid.writeString( string("\t\t</div>\n") );
        
        if ( m_hvv.m_runflag )
        {
            fid.writeString( string( "\t\t<h1 id='HBW'>Hershey Bar</h1>\n" ) );
            
            text = string("Various Hershey Bar wings with AR ranging from 10 to 60 are modeled through the OpenVSP API for this V&V case. The Hershey Bar wing has been studied and used for aerodynamic verification and validation studies extensively. The Hershey Bar wing has a unit length chord and no sweep. For the studies presented below, a symmetric NACA0012 airfoil is modeled for each Heshey Bar wing. A series of studies are conducted that investigate qualities such as tesselation, aspect ratio, and advanced settings, and their effect on VSPAERO accuracy. ");

            WriteParagraph( text );
            
            if ( m_hvv.m_HersheyARIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t<div>\n") );
                fid.writeString( string("\t\t  <h2 id='HAR'>Aspect Ratio Study</h2>\n") );
                
                text = string("In the first Hershey Bar study, the resulting coefficient of lift (\\(C_L\\)) from VSPAERO's vortex lattice solver is compared to the approximate \\(C_L\\) determined for each angle of attack from Lifting Line Theory. The first plot listed below displays these results.");
                
                WriteParagraph( text );
                
                text = string("Next, the lift curve slope (\\(C_{L\\alpha}\\)) is calculated for each vortex lattice and panel method VSPAERO result, defined by a particular AR, by subtracting \\(C_L\\) at two angle of attack flow condition cases and dividing by the total change in \\(\\alpha\\). This method, known as central differencing, cannot be used at the minimum or maximum angle of attack values. For those cases, \\(C_{L\\alpha}\\) is identified as the slope of the \\(C_L\\) vs \\(\\alpha\\) curve immediately before or after the angle of attack endpoint. These methods are known as forward and backward differencing. The calculated \\(C_{L\\alpha}\\) values are compared to \\(C_{L\\alpha}\\) determined by Lifting Line Theory at the associated aspect ratios. A 2D lift curve slope of 0.1096622 per degree is assumed, given a theoretical K ratio of 1.0. This plot can be seen in the second graph below.");

                WriteParagraph( text );
                
                // text = string("The \\(C_{L\\alpha}\\) results from VSPAERO are then used to calculate the lift angle (\\(\\frac{\\partial \\alpha}{\\partial C_L}\\)) at each chord ratio (\\(\\frac{1}{AR}\\)). These values are then compared to the theoretical lift angle determined by Lifting Line Theory. Lift angle is simply the inverse of \\(C_{L\\alpha}\\) for an angle of attack of 1.0°, so can therefore be determine for the theoretical \\(C_{L\\alpha}\\) as well. This comparison is displayed in the third plot below.");
                
                // WriteParagraph( text );
                
                HTMLTable t1 = HTMLTable(); // Initialize Table
                array<string> head_str(10);
                head_str[0] = string("Airfoil");
                head_str[1] = string("AR");
                head_str[2] = string("Root Chord");
                head_str[3] = string("Tip Chord");
                head_str[4] = string("\\(\\Lambda\\) (°)");
                head_str[5] = string("Span Tess (U)");
                head_str[6] = string("Chord Tess (W)");
                head_str[7] = string("LE Clustering");
                head_str[8] = string("TE Clustering");
                head_str[9] = string("Tip Clustering");
                t1.m_HeaderStringVec = head_str;
                
                array<array<string>> data_mat(1);
                data_mat[0].resize(10);
                data_mat[0][0] = string("NACA0012");
                data_mat[0][1] = string("5 to 60");
                data_mat[0][2] = string("1.0");
                data_mat[0][3] = string("1.0");
                data_mat[0][4] = string("0.0");
                data_mat[0][5] = string("41");
                data_mat[0][6] = string("51");
                data_mat[0][7] = string("0.2");
                data_mat[0][8] = string("1.0");
                data_mat[0][9] = string("1.0");
                
                t1.m_TableDataMat = data_mat;
                t1.m_Width = 6;
                t1.m_Caption = string("Aspect Ratio Study Geometry Setup");
                
                t1.WriteHTMLTable();
                
                HTMLTable t2 = HTMLTable(); // Initialize Table
                head_str.resize(7);
                head_str[0] = string("Case #");
                head_str[1] = string("Analysis");
                head_str[2] = string("Method");
                head_str[3] = string("\\(\\alpha\\) (°)");
                head_str[4] = string("\\(\\beta\\) (°)");
                head_str[5] = string("M");
                head_str[6] = string("Wake Iterations");
                t2.m_HeaderStringVec = head_str;
                
                data_mat.resize(2);
                data_mat[0].resize(7);
                data_mat[0][0] = string("1");
                data_mat[0][1] = string("Sweep");
                data_mat[0][2] = string("VLM");
                data_mat[0][3] = string("-20.0 to 20.0, npts: 8");
                data_mat[0][4] = string("0.0");
                data_mat[0][5] = m_hvv.m_MachVec[0];
                data_mat[0][6] = m_hvv.m_WakeIterVec[0];
                
                data_mat[1].resize(7);
                data_mat[1][0] = string("2");
                data_mat[1][1] = string("Single Point");
                data_mat[1][2] = string("Panel");
                data_mat[1][3] = string("1.0");
                data_mat[1][4] = string("0.0");
                data_mat[1][5] = m_hvv.m_MachVec[0];
                data_mat[1][6] = m_hvv.m_WakeIterVec[0];
                
                t2.m_TableDataMat = data_mat;
                t2.m_Width = 5;
                t2.m_Caption = string("Aspect Ratio Study VSPAERO Setup");
                
                t2.WriteHTMLTable();
                
                AddChartsToBody(m_hvv.m_HersheyARIDVec);
            }
            
            if ( m_hvv.m_HersheyAlphaErrorIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t<div>\n") );
                fid.writeString( string("\t\t  <h2 id='HAlpha'>Angle of Attack Study</h2>\n") );
                
                text = string("Using the same OpenVSP Hershey Bar wing geomentry as the previous study, aspect ratio is held constant and a VSPAERO alpha sweep analysis is conducted. The goal of this study is to demonstrate how the error in \\(C_{L\\alpha}\\) changes at various angles of attack.");
                
                WriteParagraph( text );
                
                HTMLTable t1 = HTMLTable(); // Initialize Table
                array<string> head_str(10);
                head_str[0] = string("Airfoil");
                head_str[1] = string("AR");
                head_str[2] = string("Root Chord");
                head_str[3] = string("Tip Chord");
                head_str[4] = string("\\(\\Lambda\\) (°)");
                head_str[5] = string("Span Tess (U)");
                head_str[6] = string("Chord Tess (W)");
                head_str[7] = string("LE Clustering");
                head_str[8] = string("TE Clustering");
                head_str[9] = string("Tip Clustering");
                t1.m_HeaderStringVec = head_str;
                
                array<array<string>> data_mat(1);
                data_mat[0].resize(10);
                data_mat[0][0] = string("NACA0012");
                data_mat[0][1] = string("10");
                data_mat[0][2] = string("1.0");
                data_mat[0][3] = string("1.0");
                data_mat[0][4] = string("0.0");
                data_mat[0][5] = string("12");
                data_mat[0][6] = string("17");
                data_mat[0][7] = string("0.2");
                data_mat[0][8] = string("1.0");
                data_mat[0][9] = string("1.0");
                
                t1.m_TableDataMat = data_mat;
                t1.m_Width = 6;
                t1.m_Caption = string("Angle of Attack Study Geometry Setup");
                
                t1.WriteHTMLTable();
                
                HTMLTable t2 = HTMLTable(); // Initialize Table
                head_str.resize(7);
                head_str[0] = string("Case #");
                head_str[1] = string("Analysis");
                head_str[2] = string("Method");
                head_str[3] = string("\\(\\alpha\\) (°)");
                head_str[4] = string("\\(\\beta\\) (°)");
                head_str[5] = string("M");
                head_str[6] = string("Wake Iterations");
                t2.m_HeaderStringVec = head_str;
                
                data_mat.resize(1);
                data_mat[0].resize(7);
                data_mat[0][0] = string("1");
                data_mat[0][1] = string("Sweep");
                data_mat[0][2] = string("VLM");
                string cell = string("-20.0 to 20.0, npts:") + m_hvv.m_AlphaNpts;
                data_mat[0][3] = cell;
                data_mat[0][4] = string("0.0");
                data_mat[0][5] = m_hvv.m_MachVec[0];
                data_mat[0][6] = m_hvv.m_WakeIterVec[0];
                
                t2.m_TableDataMat = data_mat;
                t2.m_Width = 5;
                t2.m_Caption = string("Angle of Attack Study VSPAERO Setup");
                
                t2.WriteHTMLTable();
                
                AddChartsToBody(m_hvv.m_HersheyAlphaErrorIDVec);
            }
            
            if ( m_hvv.m_HersheyTessIDVec.length() > 0 || m_hvv.m_HersheyTimeIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t<div>\n") );
                fid.writeString( string("\t\t  <h2 id='HTess'>Tesselation Study</h2>\n") );
                
                text = string("The next Hershey Bar study investigates the effects of tesselation on VSPAERO VLM results by varying U and W tesselation for a particular AR case. Two charts are generated and can be seen below. The chart on the left displays the percent error between VSPAERO and theoretical lifting line \\(C_{L\\alpha}\\) results as chord tesselation increases at various span tesselations. The charts on the right display the same error, but as span tesselation increases at various chord tesselations.");
                
                WriteParagraph( text );
                
                HTMLTable t1 = HTMLTable(); // Initialize Table
                array<string> head_str(10);
                head_str[0] = string("Airfoil");
                head_str[1] = string("AR");
                head_str[2] = string("Root Chord");
                head_str[3] = string("Tip Chord");
                head_str[4] = string("\\(\\Lambda\\) (°)");
                head_str[5] = string("Span Tess (U)");
                head_str[6] = string("Chord Tess (W)");
                head_str[7] = string("LE Clustering");
                head_str[8] = string("TE Clustering");
                head_str[9] = string("Tip Clustering");
                t1.m_HeaderStringVec = head_str;
                
                array<array<string>> data_mat(1);
                data_mat[0].resize(10);
                data_mat[0][0] = string("NACA0012");
                data_mat[0][1] = string("10");
                data_mat[0][2] = string("1.0");
                data_mat[0][3] = string("1.0");
                data_mat[0][4] = string("0.0");
                data_mat[0][5] = string("5 to 41");
                data_mat[0][6] = string("9 to 51");
                data_mat[0][7] = string("0.2");
                data_mat[0][8] = string("1.0");
                data_mat[0][9] = string("1.0");
                
                t1.m_TableDataMat = data_mat;
                t1.m_Width = 6;
                t1.m_Caption = string("Tesselation Study Geometry Setup");
                
                t1.WriteHTMLTable();
                
                HTMLTable t2 = HTMLTable(); // Initialize Table
                head_str.resize(6);
                head_str[0] = string("Analysis");
                head_str[1] = string("Method");
                head_str[2] = string("\\(\\alpha\\) (°)");
                head_str[3] = string("\\(\\beta\\) (°)");
                head_str[4] = string("M");
                head_str[5] = string("Wake Iterations");
                t2.m_HeaderStringVec = head_str;
                
                data_mat.resize(1);
                data_mat[0].resize(6);
                data_mat[0][0] = string("Single Point");
                data_mat[0][1] = string("VLM");
                data_mat[0][2] = string("1.0");
                data_mat[0][3] = string("0.0");
                data_mat[0][4] = m_hvv.m_MachVec[0];
                data_mat[0][5] = m_hvv.m_WakeIterVec[0];
                
                t2.m_TableDataMat = data_mat;
                t2.m_Width = 5;
                t2.m_Caption = string("Tesselation Study VSPAERO Setup");
                
                t2.WriteHTMLTable();
                
                AddChartsToBody(m_hvv.m_HersheyTessIDVec);
                
                fid.writeString( string("\t\t<div>\n") );
                
                text = string("Next, the VSPAERO execution time is considered at various chord and span tesselation values. In conjunction with the plots above, the plots below allow for a comparison between percent error and VSPAERO execution time as tesselation is varied in both the chordwise and spanwise directions.");
                
                WriteParagraph( text );
                
                AddChartsToBody(m_hvv.m_HersheyTimeIDVec);
            }
            
            if ( m_hvv.m_HersheyLDTCIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t<div>\n") );
                fid.writeString( string("\t\t  <h2 id='HTC'>Tip Clustering Study</h2>\n") );
                
                text = string("This next study looks at the influence of tip clustering on \\(C_L\\) distribution along the Hershey Bar wing span. Tip clustering is varied while aspect ratio, chord tesselation, and span tesselation are held constant. The \\(C_L\\) distribution across the span is compared to the lifting line approximate \\(C_L\\) distribution found using Glauert's method. In addition, lift distribution results are generated in Athena Vortex Lattice v3.37 (AVL) for the Hershey Bar wing using the same setup conditions as VSPAERO. The AVL input file, \"Hershey_AR10.avl\", and results file, \"Hershey_AR10_AVL.dat\", can be found in the same directory as the Master V&V Script. The plots below display how tip clustering effects the error between VSPAERO VLM and LLT.");
                
                WriteParagraph( text );
                
                HTMLTable t1 = HTMLTable(); // Initialize Table
                array<string> head_str(10);
                head_str[0] = string("Airfoil");
                head_str[1] = string("AR");
                head_str[2] = string("Root Chord");
                head_str[3] = string("Tip Chord");
                head_str[4] = string("\\(\\Lambda\\) (°)");
                head_str[5] = string("Span Tess (U)");
                head_str[6] = string("Chord Tess (W)");
                head_str[7] = string("LE Clustering");
                head_str[8] = string("TE Clustering");
                head_str[9] = string("Tip Clustering");
                t1.m_HeaderStringVec = head_str;
                
                array<array<string>> data_mat(1);
                data_mat[0].resize(10);
                data_mat[0][0] = string("NACA0012");
                data_mat[0][1] = string("10");
                data_mat[0][2] = string("1.0");
                data_mat[0][3] = string("1.0");
                data_mat[0][4] = string("0.0");
                data_mat[0][5] = string("12");
                data_mat[0][6] = string("17");
                data_mat[0][7] = string("0.2");
                data_mat[0][8] = string("1.0");
                data_mat[0][9] = string("0.1, 0.5, 1.0");
                
                t1.m_TableDataMat = data_mat;
                t1.m_Width = 6;
                t1.m_Caption = string("Tip Clustering Study Geometry Setup");
                
                t1.WriteHTMLTable();
                
                HTMLTable t2 = HTMLTable(); // Initialize Table
                head_str.resize(6);
                head_str[0] = string("Analysis");
                head_str[1] = string("Method");
                head_str[2] = string("\\(\\alpha\\) (°)");
                head_str[3] = string("\\(\\beta\\) (°)");
                head_str[4] = string("M");
                head_str[5] = string("Wake Iterations");
                t2.m_HeaderStringVec = head_str;
                
                data_mat.resize(1);
                data_mat[0].resize(6);
                data_mat[0][0] = string("Single Point");
                data_mat[0][1] = string("VLM");
                data_mat[0][2] = string("1.0");
                data_mat[0][3] = string("0.0");
                data_mat[0][4] = m_hvv.m_MachVec[0];
                data_mat[0][5] = m_hvv.m_WakeIterVec[0];
                
                t2.m_TableDataMat = data_mat;
                t2.m_Width = 5;
                t2.m_Caption = string("Tip Clustering Study VSPAERO Setup");
                
                t2.WriteHTMLTable();
                
                HTMLTable t3 = HTMLTable(); // Initialize Table
                head_str.resize(5);
                head_str[0] = string("Nchord");
                head_str[1] = string("Cspace");
                head_str[2] = string("Nspan");
                head_str[3] = string("Sspan");
                head_str[4] = string("M");
                t3.m_HeaderStringVec = head_str;

                data_mat.resize(1);
                data_mat[0].resize(5);
                data_mat[0][0] = string("30");
                data_mat[0][1] = string("1.0");
                data_mat[0][2] = string("20");
                data_mat[0][3] = string("-3.0");
                data_mat[0][4] = m_hvv.m_MachVec[0];
                
                t3.m_TableDataMat = data_mat;
                t3.m_Width = 4;
                t3.m_Caption = string("Tip Clustering Study AVL Setup");
                
                t3.WriteHTMLTable();
                
                AddChartsToBody(m_hvv.m_HersheyLDTCIDVec);
            }
            
            if ( m_hvv.m_HersheyLDUTessIDVec.length() > 0 || m_hvv.m_HersheyDDUTessIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t<div>\n") );
                fid.writeString( string("\t\t  <h2 id='HST'>Span Tesselation Study</h2>\n") );
                
                text = string("The impact of span tesselation on \\(C_L\\) distribution along the Hershey Bar wing span is investigated first in this study. While holding aspect ratio, chord tesselation, and tip clustering constant, span tesselation is varied. The first plot below displays the \\(C_L\\) distribution across the span compared to the lifting line approximate \\(C_L\\) distribution found using Glauert's method. In addition, the lift distribution results computed by AVL for an identical geometry and flow condition input are plotted.");
                
                WriteParagraph( text );
                
                HTMLTable t1 = HTMLTable(); // Initialize Table
                array<string> head_str(10);
                head_str[0] = string("Airfoil");
                head_str[1] = string("AR");
                head_str[2] = string("Root Chord");
                head_str[3] = string("Tip Chord");
                head_str[4] = string("\\(\\Lambda\\) (°)");
                head_str[5] = string("Span Tess (U)");
                head_str[6] = string("Chord Tess (W)");
                head_str[7] = string("LE Clustering");
                head_str[8] = string("TE Clustering");
                head_str[9] = string("Tip Clustering");
                t1.m_HeaderStringVec = head_str;
                
                array<array<string>> data_mat(1);
                data_mat[0].resize(10);
                data_mat[0][0] = string("NACA0012");
                data_mat[0][1] = string("10");
                data_mat[0][2] = string("1.0");
                data_mat[0][3] = string("1.0");
                data_mat[0][4] = string("0.0");
                data_mat[0][5] = string("5 to 41");
                data_mat[0][6] = string("17");
                data_mat[0][7] = string("0.2");
                data_mat[0][8] = string("1.0");
                data_mat[0][9] = string("1.0");
                
                t1.m_TableDataMat = data_mat;
                t1.m_Width = 6;
                t1.m_Caption = string("Span Tesselation Study Geometry Setup");
                
                t1.WriteHTMLTable();
                
                HTMLTable t2 = HTMLTable(); // Initialize Table
                head_str.resize(6);
                head_str[0] = string("Analysis");
                head_str[1] = string("Method");
                head_str[2] = string("\\(\\alpha\\) (°)");
                head_str[3] = string("\\(\\beta\\) (°)");
                head_str[4] = string("M");
                head_str[5] = string("Wake Iterations");
                t2.m_HeaderStringVec = head_str;
                
                data_mat.resize(1);
                data_mat[0].resize(6);
                data_mat[0][0] = string("Single Point");
                data_mat[0][1] = string("VLM");
                data_mat[0][2] = string("1.0");
                data_mat[0][3] = string("0.0");
                data_mat[0][4] = m_hvv.m_MachVec[0];
                data_mat[0][5] = m_hvv.m_WakeIterVec[0];
                
                t2.m_TableDataMat = data_mat;
                t2.m_Width = 5;
                t2.m_Caption = string("Span Tesselation Study VSPAERO Setup");
                
                t2.WriteHTMLTable();
                
                HTMLTable t3 = HTMLTable(); // Initialize Table
                head_str.resize(5);
                head_str[0] = string("Nchord");
                head_str[1] = string("Cspace");
                head_str[2] = string("Nspan");
                head_str[3] = string("Sspan");
                head_str[4] = string("M");
                t3.m_HeaderStringVec = head_str;

                data_mat.resize(1);
                data_mat[0].resize(5);
                data_mat[0][0] = string("30");
                data_mat[0][1] = string("1.0");
                data_mat[0][2] = string("20");
                data_mat[0][3] = string("-3.0");
                data_mat[0][4] = m_hvv.m_MachVec[0];
                
                t3.m_TableDataMat = data_mat;
                t3.m_Width = 4;
                t3.m_Caption = string("Span Tesselation Study AVL Setup");
                
                t3.WriteHTMLTable();
                
                AddChartsToBody(m_hvv.m_HersheyLDUTessIDVec);
                
                fid.writeString( string("\t\t<div>\n") );
                
                text = string("The impact of span tesselation on \\(C_{Di}\\) distribution along the Hershey Bar wing span is looked at next. As in the plot above, aspect ratio, chord tesselation, and tip clustering are held constant as span tesselation is varied. The plot below displays the \\(C_{Di}\\) distribution across the span compared to the lifting line approximate \\(C_{Di}\\) distribution, and the drag distribution determined by AVL. The AVL input and results file are located in the same directory as the Master V&V Script (\"Hershey_AR10.avl\" and \"Hershey_AR10_AVL.dat\" respectively).");
                
                WriteParagraph( text );
                
                AddChartsToBody(m_hvv.m_HersheyDDUTessIDVec);
            }
            
            if ( m_hvv.m_HersheyLDWTessIDVec.length() > 0 || m_hvv.m_HersheyDDWTessIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t<div>\n") );
                fid.writeString( string("\t\t  <h2 id='HCT'>Chord Tesselation Study</h2>\n") );
                
                text = string("This next lift distribution study is similar to the previous one, except here the influence of chord tesselation is presented. Span tesselation, aspect ratio, and tip clustering are all held constant. The plots below compare the VSPAERO \\(C_L\\) distribution for the Hershey Bar wing to the lifting line theory approximate solution. The same geometry and flow condition inputs were run in AVL to generate lift distribution results. The AVL results are read in and plotted as well. The results file, \"Hershey_AR10_AVL.dat\", may be viewed in the scripts directory.");
                
                WriteParagraph( text );
                
                HTMLTable t1 = HTMLTable(); // Initialize Table
                array<string> head_str(10);
                head_str[0] = string("Airfoil");
                head_str[1] = string("AR");
                head_str[2] = string("Root Chord");
                head_str[3] = string("Tip Chord");
                head_str[4] = string("\\(\\Lambda\\) (°)");
                head_str[5] = string("Span Tess (U)");
                head_str[6] = string("Chord Tess (W)");
                head_str[7] = string("LE Clustering");
                head_str[8] = string("TE Clustering");
                head_str[9] = string("Tip Clustering");
                t1.m_HeaderStringVec = head_str;
                
                array<array<string>> data_mat(1);
                data_mat[0].resize(10);
                data_mat[0][0] = string("NACA0012");
                data_mat[0][1] = string("10");
                data_mat[0][2] = string("1.0");
                data_mat[0][3] = string("1.0");
                data_mat[0][4] = string("0.0");
                data_mat[0][5] = string("12");
                data_mat[0][6] = string("9 to 51");
                data_mat[0][7] = string("0.2");
                data_mat[0][8] = string("1.0");
                data_mat[0][9] = string("1.0");
                
                t1.m_TableDataMat = data_mat;
                t1.m_Width = 6;
                t1.m_Caption = string("Chord Tesselation Study Geometry Setup");
                
                t1.WriteHTMLTable();
                
                HTMLTable t2 = HTMLTable(); // Initialize Table
                head_str.resize(6);
                head_str[0] = string("Analysis");
                head_str[1] = string("Method");
                head_str[2] = string("\\(\\alpha\\) (°)");
                head_str[3] = string("\\(\\beta\\) (°)");
                head_str[4] = string("M");
                head_str[5] = string("Wake Iterations");
                t2.m_HeaderStringVec = head_str;
                
                data_mat.resize(1);
                data_mat[0].resize(6);
                data_mat[0][0] = string("Single Point");
                data_mat[0][1] = string("VLM");
                data_mat[0][2] = string("1.0");
                data_mat[0][3] = string("0.0");
                data_mat[0][4] = m_hvv.m_MachVec[0];
                data_mat[0][5] = m_hvv.m_WakeIterVec[0];
                
                t2.m_TableDataMat = data_mat;
                t2.m_Width = 5;
                t2.m_Caption = string("Chord Tesselation Study VSPAERO Setup");
                
                t2.WriteHTMLTable();
                
                HTMLTable t3 = HTMLTable(); // Initialize Table
                head_str.resize(5);
                head_str[0] = string("Nchord");
                head_str[1] = string("Cspace");
                head_str[2] = string("Nspan");
                head_str[3] = string("Sspan");
                head_str[4] = string("M");
                t3.m_HeaderStringVec = head_str;

                data_mat.resize(1);
                data_mat[0].resize(5);
                data_mat[0][0] = string("30");
                data_mat[0][1] = string("1.0");
                data_mat[0][2] = string("20");
                data_mat[0][3] = string("-3.0");
                data_mat[0][4] = m_hvv.m_MachVec[0];
                
                t3.m_TableDataMat = data_mat;
                t3.m_Width = 4;
                t3.m_Caption = string("Chord Tesselation Study AVL Setup");
                
                t3.WriteHTMLTable();
                
                AddChartsToBody(m_hvv.m_HersheyLDWTessIDVec);
                
                fid.writeString( string("\t\t<div>\n") );
                
                text = string("Chord tesselation's influence on induced drag coefficient distribution for the Hershey Bar wing is then looked at. Aspect ratio, span tesselation, and tip clustering are all held constant while chord tesselation is varied. The lifting line approximate \\(C_{Di}\\) distribution calculated using Glauert's method is plotted below alongside the results generated from VSPAERO. In addition, AVL's drag distribution for the Hershey Bar wing is displayed.");
                
                WriteParagraph( text );
                
                AddChartsToBody(m_hvv.m_HersheyDDWTessIDVec);
            }
            
            if ( m_hvv.m_HersheyLDWakeIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t<div>\n") );
                fid.writeString( string("\t\t  <h2 id='HWake'>Wake Iteration Study</h2>\n") );
                
                text = string("This next lift distribution study demonstrates the effect of the number of wake iterations on VSPAERO lift coefficient distribution. Span tesselation, chord tesselation, aspect ratio, and tip clustering are all held constant. In the first plot below, the lift distribution is plotted for each wake iteration case. Below the lift distribution plot, the VSPAERO total computation time is plotted for each wake iteration case. Combined, these plots demonstrate the increase in VSPAERO accuracy as the number of wake iterations is increased, but at a cost to overall computation time.");
                
                WriteParagraph( text );
                
                HTMLTable t1 = HTMLTable(); // Initialize Table
                array<string> head_str(10);
                head_str[0] = string("Airfoil");
                head_str[1] = string("AR");
                head_str[2] = string("Root Chord");
                head_str[3] = string("Tip Chord");
                head_str[4] = string("\\(\\Lambda\\) (°)");
                head_str[5] = string("Span Tess (U)");
                head_str[6] = string("Chord Tess (W)");
                head_str[7] = string("LE Clustering");
                head_str[8] = string("TE Clustering");
                head_str[9] = string("Tip Clustering");
                t1.m_HeaderStringVec = head_str;
                
                array<array<string>> data_mat(1);
                data_mat[0].resize(10);
                data_mat[0][0] = string("NACA0012");
                data_mat[0][1] = string("10");
                data_mat[0][2] = string("1.0");
                data_mat[0][3] = string("1.0");
                data_mat[0][4] = string("0.0");
                data_mat[0][5] = string("12");
                data_mat[0][6] = string("17");
                data_mat[0][7] = string("0.2");
                data_mat[0][8] = string("1.0");
                data_mat[0][9] = string("1.0");
                
                t1.m_TableDataMat = data_mat;
                t1.m_Width = 6;
                t1.m_Caption = string("Wake Iteration Study Geometry Setup");
                
                t1.WriteHTMLTable();
                
                HTMLTable t2 = HTMLTable(); // Initialize Table
                head_str.resize(6);
                head_str[0] = string("Analysis");
                head_str[1] = string("Method");
                head_str[2] = string("\\(\\alpha\\) (°)");
                head_str[3] = string("\\(\\beta\\) (°)");
                head_str[4] = string("M");
                head_str[5] = string("Wake Iterations");
                t2.m_HeaderStringVec = head_str;
                
                data_mat.resize(1);
                data_mat[0].resize(6);
                data_mat[0][0] = string("Single Point");
                data_mat[0][1] = string("VLM");
                data_mat[0][2] = string("1.0");
                data_mat[0][3] = string("0.0");
                data_mat[0][4] = m_hvv.m_MachVec[0];
                data_mat[0][5] = string("1 to 5");;
                
                t2.m_TableDataMat = data_mat;
                t2.m_Width = 5;
                t2.m_Caption = string("Wake Iteration Study VSPAERO Setup");
                
                t2.WriteHTMLTable();
                
                AddChartsToBody(m_hvv.m_HersheyLDWakeIDVec);
            }
            
            if ( m_hvv.m_HersheyLDAdvancedIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t<div>\n") );
                fid.writeString( string("\t\t  <h2 id='HAdv'>Advanced Settings Study</h2>\n") );
                
                text = string("In this study, the various advanced VSPAERO settings are investigated and compared for the Hershey Bar wing. The settings of interest are the preconditioner and 2nd order Karman-Tsien Mach Correction. The three types of preconditioners available are matrix, Jacobi, and symmetric successive over-relaxation (SSOR). For each VSPAERO run case, only a single advanced setting is varied at a time while all others are left at their default value. The last column of the VSPAERO setup table displays the VSPAERO computation time for each case. The lift distribution is plotted for each case and compared to the theoretical lift distribution determined by Glauert's method. This process is repeated for one, two, and three wake iterations.");
                
                WriteParagraph( text );
                
                HTMLTable t1 = HTMLTable(); // Initialize Table
                array<string> head_str(10);
                head_str[0] = string("Airfoil");
                head_str[1] = string("AR");
                head_str[2] = string("Root Chord");
                head_str[3] = string("Tip Chord");
                head_str[4] = string("\\(\\Lambda\\) (°)");
                head_str[5] = string("Span Tess (U)");
                head_str[6] = string("Chord Tess (W)");
                head_str[7] = string("LE Clustering");
                head_str[8] = string("TE Clustering");
                head_str[9] = string("Tip Clustering");
                t1.m_HeaderStringVec = head_str;
                
                array<array<string>> data_mat(1);
                data_mat[0].resize(10);
                data_mat[0][0] = string("NACA0012");
                data_mat[0][1] = string("15");
                data_mat[0][2] = string("1.0");
                data_mat[0][3] = string("1.0");
                data_mat[0][4] = string("0.0");
                data_mat[0][5] = string("12");
                data_mat[0][6] = string("17");
                data_mat[0][7] = string("0.2");
                data_mat[0][8] = string("1.0");
                data_mat[0][9] = string("1.0");
                
                t1.m_TableDataMat = data_mat;
                t1.m_Width = 6;
                t1.m_Caption = string("Advanced Settings Study Geometry Setup");
                
                t1.WriteHTMLTable();

                for ( uint8 w = 0; w < uint8(m_hvv.m_AdvancedWakeVec.length()); w++ )
                {
                    fid.writeString( string("\t\t<div>\n") );
                    
                    HTMLTable t2 = HTMLTable(); // Initialize Table
                    head_str.resize(10);
                    head_str[0] = string("Case #");
                    head_str[1] = string("Analysis");
                    head_str[2] = string("Method");
                    head_str[3] = string("\\(\\alpha\\) (°)");
                    head_str[4] = string("\\(\\beta\\) (°)");
                    head_str[5] = string("M");
                    head_str[6] = string("Wake Iterations");
                    head_str[7] = string("Preconditioner");
                    head_str[8] = string("Mach Correction");
                    head_str[9] = string("Exe Time (sec)");
                    t2.m_HeaderStringVec = head_str;
                    
                    data_mat.resize(4);
                    for ( uint8 i = 0; i < 4; i++ )
                    {
                        data_mat[i].resize(10);
                        
                        if ( i == 0 )
                        {
                            data_mat[i][0] = string("Default");
                        }
                        else
                        {
                            data_mat[i][0] = int(i);
                        }

                        data_mat[i][1] = string("Single Point");
                        data_mat[i][2] = string("VLM");
                        data_mat[i][3] = string("1.0");
                        data_mat[i][4] = string("0.0");
                        data_mat[i][5] = m_hvv.m_MachVec[0];
                        data_mat[i][6] = m_hvv.m_AdvancedWakeVec[w];
                        
                        if ( i == 1 )
                        {
                            data_mat[i][7] = string("Jacobi");
                        }
                        else if ( i == 2 )
                        {
                            data_mat[i][7] = string("SSOR");
                        }
                        else
                        {
                            data_mat[i][7] = string("Matrix");
                        }
                        
                        if ( i == 3 )
                        {
                            data_mat[i][8] = string("On");
                        }
                        else
                        {
                            data_mat[i][8] = string("Off");
                        }
                        
                        data_mat[i][9] = m_hvv.m_AdvancedTimeVec[w][i];
                    }
                    
                    t2.m_TableDataMat = data_mat;
                    t2.m_Width = 7;
                    t2.m_Caption = string("Advanced Settings Study VSPAERO Setup: Wake Iterations = ") + int(m_hvv.m_AdvancedWakeVec[w]);
                    
                    t2.WriteHTMLTable();

                    AddChartsToBody(m_hvv.m_HersheyLDAdvancedIDVec[w]);
                }
                
                fid.writeString( string("\t\t</div>\n") );
                fid.writeString( string("\t\t<div>\n") );
                
                text = string("In the next plot below, the total VSPAERO computation time is plotted for each wake iteration and advanced settings case to provide a summary of the \"Exe Time (sec)\" column in each of the tables above.");
                
                WriteParagraph( text );
                
                AddChartsToBody(m_hvv.m_HersheyAdvancedTimeIDVec);
            }
        }
        if ( m_svv.m_runflag )
        {
            fid.writeString( string( "\t\t<h1 id='SW'>Swept Wing</h1>\n" ) );
            
            text = string("Swept Hershey Bar wings with AR ranging from 10 to 60 are modeled through the OpenVSP API for this V&V case. The wing sweep is measured from the half-chord line for each generated geometry. A series of studies are conducted, with some similarities to the previous Hershey Bar studies. The studies investigate VSPAERO VLM and panel method results generated from variations in aspect ratio, tesselation, and sweep.");

            WriteParagraph( text );
            
            if ( m_svv.m_SweepTessIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t<div>\n") );
                fid.writeString( string("\t\t  <h2 id='STess'>Tesselation Study</h2>\n") );
                
                text = string("In the first swept wing study, the influence of chordwise and spanwise tesselation on VSPAERO results is investigated for a particular aspect ratio. A test matrix of wings is created by varying U and W tesselation. For each sweep case, two charts are presented below. First, the percent error between VSPAERO and theoretical lifting line \\(C_{L\\alpha}\\) results is presented as chord tesselation increases at various span tesselations. A similar error is displayed in the following chart, but as span tesselation increases at various chord tesselations.");

                WriteParagraph( text );
                
                HTMLTable t1 = HTMLTable(); // Initialize Table
                array<string> head_str(11);
                head_str[0] = string("Airfoil");
                head_str[1] = string("AR");
                head_str[2] = string("Root Chord");
                head_str[3] = string("Tip Chord");
                head_str[4] = string("\\(\\Lambda\\) (°)");
                head_str[5] = string("\\(\\Lambda\\) Location");
                head_str[6] = string("Span Tess (U)");
                head_str[7] = string("Chord Tess (W)");
                head_str[8] = string("LE Clustering");
                head_str[9] = string("TE Clustering");
                head_str[10] = string("Tip Clustering");
                t1.m_HeaderStringVec = head_str;
                
                array<array<string>> data_mat(1);
                data_mat[0].resize(11);
                data_mat[0][0] = string("NACA0012");
                data_mat[0][1] = string("10");
                data_mat[0][2] = string("1.0");
                data_mat[0][3] = string("1.0");
                data_mat[0][4] = string("30");
                data_mat[0][5] = string("0.5");
                data_mat[0][6] = string("5 to 41");
                data_mat[0][7] = string("9 to 51");
                data_mat[0][8] = string("0.2");
                data_mat[0][9] = string("1.0");
                data_mat[0][10] = string("1.0");
                
                t1.m_TableDataMat = data_mat;
                t1.m_Width = 6;
                t1.m_Caption = string("Tesselation Study Geometry Setup");
                
                t1.WriteHTMLTable();
                
                HTMLTable t2 = HTMLTable(); // Initialize Table
                head_str.resize(7);
                head_str[0] = string("Case #");
                head_str[1] = string("Analysis");
                head_str[2] = string("Method");
                head_str[3] = string("\\(\\alpha\\) (°)");
                head_str[4] = string("\\(\\beta\\) (°)");
                head_str[5] = string("M");
                head_str[6] = string("Wake Iterations");
                t2.m_HeaderStringVec = head_str;
                
                data_mat.resize(1);
                data_mat[0].resize(7);
                data_mat[0][0] = string("1");
                data_mat[0][1] = string("Single Point");
                data_mat[0][2] = string("VLM");
                data_mat[0][3] = string("1.0");
                data_mat[0][4] = string("0.0");
                data_mat[0][5] = m_hvv.m_MachVec[0];
                data_mat[0][6] = m_hvv.m_WakeIterVec[0];
                
                t2.m_TableDataMat = data_mat;
                t2.m_Width = 5;
                t2.m_Caption = string("Tesselation Study VSPAERO Setup");
                
                t2.WriteHTMLTable();
                
                AddChartsToBody(m_svv.m_SweepTessIDVec);
            }
            
            if ( m_svv.m_SweepSweepClavARIDVec.length() > 0 && m_svv.m_SweepSweepErrorIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t<div>\n") );
                fid.writeString( string("\t\t  <h2 id='SSS'>Sweep Study</h2>\n") );
                
                text = string("This next swept wing test looks to identify if sweep value plays a role in the accuracy between VSPAERO and 3D theoretical results. Sweep is varied to create a series of wing geometries. For each unique case, the results from VSPAERO VLM and panel method are used to identify the lift curve slope, along with the associated theoretical value. These results are plotted against aspect ratio in the charts below.");
                
                WriteParagraph( text );
                
                HTMLTable t1 = HTMLTable(); // Initialize Table
                array<string> head_str(11);
                head_str[0] = string("Airfoil");
                head_str[1] = string("AR");
                head_str[2] = string("Root Chord");
                head_str[3] = string("Tip Chord");
                head_str[4] = string("\\(\\Lambda\\) (°)");
                head_str[5] = string("\\(\\Lambda\\) Location");
                head_str[6] = string("Span Tess (U)");
                head_str[7] = string("Chord Tess (W)");
                head_str[8] = string("LE Clustering");
                head_str[9] = string("TE Clustering");
                head_str[10] = string("Tip Clustering");
                t1.m_HeaderStringVec = head_str;
                
                array<array<string>> data_mat(1);
                data_mat[0].resize(11);
                data_mat[0][0] = string("NACA0012");
                data_mat[0][1] = string("5 to 60");
                data_mat[0][2] = string("1.0");
                data_mat[0][3] = string("1.0");
                data_mat[0][4] = string("0 to 40");
                data_mat[0][5] = string("0.5");
                data_mat[0][6] = string("41");
                data_mat[0][7] = string("51");
                data_mat[0][8] = string("0.2");
                data_mat[0][9] = string("1.0");
                data_mat[0][10] = string("1.0");
                
                t1.m_TableDataMat = data_mat;
                t1.m_Width = 7;
                t1.m_Caption = string("Sweep Study Geometry Setup");
                
                t1.WriteHTMLTable();
                
                HTMLTable t2 = HTMLTable(); // Initialize Table
                head_str.resize(7);
                head_str[0] = string("Case #");
                head_str[1] = string("Analysis");
                head_str[2] = string("Method");
                head_str[3] = string("\\(\\alpha\\) (°)");
                head_str[4] = string("\\(\\beta\\) (°)");
                head_str[5] = string("M");
                head_str[6] = string("Wake Iterations");
                t2.m_HeaderStringVec = head_str;
                
                data_mat.resize(2);
                data_mat[0].resize(7);
                data_mat[0][0] = string("1");
                data_mat[0][1] = string("Single Point");
                data_mat[0][2] = string("VLM");
                data_mat[0][3] = string("1.0");
                data_mat[0][4] = string("0.0");
                data_mat[0][5] = m_hvv.m_MachVec[0];
                data_mat[0][6] = m_hvv.m_WakeIterVec[0];
                
                data_mat[1].resize(7);
                data_mat[1][0] = string("2");
                data_mat[1][1] = string("Single Point");
                data_mat[1][2] = string("Panel");
                data_mat[1][3] = string("1.0");
                data_mat[1][4] = string("0.0");
                data_mat[1][5] = m_hvv.m_MachVec[0];
                data_mat[1][6] = m_hvv.m_WakeIterVec[0];
                
                t2.m_TableDataMat = data_mat;
                t2.m_Width = 5;
                t2.m_Caption = string("Sweep Study VSPAERO Setup");
                
                t2.WriteHTMLTable();
                
                AddChartsToBody(m_svv.m_SweepSweepClavARIDVec);
                
                fid.writeString( string("\t\t<div>\n") );
                
                text = string("For each sweep value, the average percent error between VSPAERO and theoretical \\(C_{L\\alpha}\\) is calculated across all AR cases. The plot below identifies how average error changes as wing sweep increases.");
                
                WriteParagraph( text );
                
                AddChartsToBody(m_svv.m_SweepSweepErrorIDVec);
            }
        }
        if ( m_bvv.m_runflag )
        {
            fid.writeString( string("\t\t<div>\n") );
            fid.writeString( string( "\t\t<h1 id='BSW'>Bertin-Smith</h1>\n" ) );
            
            text = string("A Bertin-Smith Wing is modeled based off of the planform described in <span style='font-style: italic'>Aerodynamics for Engineers</span>, by Bertin and Smith. The book identifies a theoretical approximation for \\(C_{L\\alpha}\\) on page 202 to be 3.433/rad. A VSPAERO angle of attack sweep analysis is then run. For each \\(\\alpha\\) case, the lift coefficient is identified from VSPAERO and displayed alongside the theoretical lift coefficient in the first plot below. \\(C_{L\\alpha}\\) is then calculated for each angle of attack by using central differencing. However, central differencing cannot be used for the angle of attack endpoints, so forward differencing or backward differencing is used. These results are compared to the theoretical value to calculate a percent difference, which can be seen in the second plot and results table below.");

            WriteParagraph( text );
            
            HTMLTable t1 = HTMLTable(); // Initialize Table
            array<string> head_str(9);
            head_str[0] = string("Airfoil");
            head_str[1] = string("AR");
            head_str[2] = string("Root Chord");
            head_str[3] = string("Tip Chord");
            head_str[4] = string("\\(\\Lambda\\) (°)");
            head_str[5] = string("\\(\\Lambda\\) Location");
            head_str[6] = string("Span Tess (U)");
            head_str[7] = string("Chord Tess (W)");
            head_str[8] = string("Tip Clustering");
            t1.m_HeaderStringVec = head_str;
            
            array<array<string>> data_mat(1);
            data_mat[0].resize(9);
            data_mat[0][0] = string("NACA0012");
            data_mat[0][1] = string("5");
            data_mat[0][2] = string("0.2");
            data_mat[0][3] = string("0.2");
            data_mat[0][4] = string("45.0");
            data_mat[0][5] = string("0.0");
            data_mat[0][6] = string("6");
            data_mat[0][7] = string("33");
            data_mat[0][8] = string("1.0");
            t1.m_TableDataMat = data_mat;
            t1.m_Width = 5;
            t1.m_Caption = string("Bertin-Smith Geometry Setup");
            
            t1.WriteHTMLTable();
            
            HTMLTable t2 = HTMLTable(); // Initialize Table
            head_str.resize(6);
            head_str[0] = string("Analysis");
            head_str[1] = string("Method");
            head_str[2] = string("\\(\\alpha\\) (°)");
            head_str[3] = string("\\(\\beta\\) (°)");
            head_str[4] = string("M");
            head_str[5] = string("Wake Iterations");
            t2.m_HeaderStringVec = head_str;

            data_mat.resize(1);
            data_mat[0].resize(6);
            data_mat[0][0] = string("Sweep");
            data_mat[0][1] = string("VLM");
            string cell = string("-20.0 to 20.0, npts: ") + m_bvv.m_AlphaNpts;
            data_mat[0][2] = cell;
            data_mat[0][3] = string("0.0");
            data_mat[0][4] = m_bvv.m_MachVec[0];
            data_mat[0][5] = m_bvv.m_WakeIterVec[0];
            t2.m_TableDataMat = data_mat;
            t2.m_Width = 5;
            t2.m_Caption = string("Bertin-Smith VSPAERO Setup");
            
            t2.WriteHTMLTable();
            
            AddChartsToBody(m_bvv.m_BSClAIDVec);
            
            fid.writeString( string("\t\t<div>\n") );
            
            AddChartsToBody(m_bvv.m_BSErrorIDVec);
            
            HTMLTable t3 = HTMLTable(); // Initialize Table
            head_str.resize(4);
            head_str[0] = string("\\(\\alpha\\) (°)");
            head_str[1] = string("\\(C_{L\\alpha}\\) Expected (rad)");
            head_str[2] = string("\\(C_{L\\alpha}\\) Result (rad)");
            head_str[3] = string("% Difference");
            t3.m_HeaderStringVec = head_str;

            // Sweep Results
            data_mat.resize(m_bvv.m_AlphaNpts);
            for ( uint8 i = 0; i < uint8(m_bvv.m_AlphaNpts); i++ )
            {
                data_mat[i].resize(4);
                data_mat[i][0] = m_bvv.m_AlphaSweepVec[i];
                data_mat[i][1] = m_bvv.m_Cl_alpha_expected;
                data_mat[i][2] = m_bvv.m_Cl_alpha_res[i];
                data_mat[i][3] = m_bvv.m_Cl_alpha_error[i];
            }
            
            t3.m_TableDataMat = data_mat;
            t3.m_Width = 5;
            t3.m_Caption = string("Bertin-Smith Results");
            
            t3.WriteHTMLTable();
        }
        if ( m_wvv.m_runflag )
        {
            fid.writeString( string("\t\t<div>\n") );
            fid.writeString( string( "\t\t<h1 id='WW'>Warren-12</h1>\n" ) );
            
            text = string("<span style='font-style: italic'>A New Non-Linear Vortex Lattice Method: Applications to Wing Aerodynamic Optimizations</span> by Oliviu, Andreea, and Ruxandra provides the dimensions for a Warren-12 Wing, a geometry that has been commonly used to verify VLM solvers. This geometry is modeled through the OpenVSP API. Theoretical approximations for \\(C_{L\\alpha}\\) and \\(C_{M\\alpha}\\) are published to be 2.743/rad and -3.10/rad respectively. Also like the Bertin-Smith Study, a VSPAERO sweep analysis is then run. In the first plot below, the lift coefficient determined by VSPAERO is plotted with the theoretical value for each angle of attack case. \\(C_{L\\alpha}\\) is calculated from these results using central differencing where possible, and forward or backward differencing where it is not possible. The percent difference in \\(C_{L\\alpha}\\) is displayed for each \\(\\alpha\\) case in the second plot. This process is then repeated for \\(C_M\\) and \\(C_{M\\alpha}\\). Last, the values and percent differences for \\(C_{L\\alpha}\\) and \\(C_{M\\alpha}\\) are summarizes in a results table.");

            WriteParagraph( text );
            
            HTMLTable t1 = HTMLTable(); // Initialize Table
            array<string> head_str(9);
            head_str[0] = string("Airfoil");
            head_str[1] = string("AR");
            head_str[2] = string("Root Chord");
            head_str[3] = string("Tip Chord");
            head_str[4] = string("\\(\\Lambda\\) (°)");
            head_str[5] = string("\\(\\Lambda\\) Location");
            head_str[6] = string("Span Tess (U)");
            head_str[7] = string("Chord Tess (W)");
            head_str[8] = string("Tip Clustering");
            t1.m_HeaderStringVec = head_str;
            
            array<array<string>> data_mat(1);
            data_mat[0].resize(9);
            data_mat[0][0] = string("NACA0012");
            data_mat[0][1] = string("\\(2 \\sqrt{2}\\)");
            data_mat[0][2] = string("1.5");
            data_mat[0][3] = string("0.5");
            data_mat[0][4] = string("53.54");
            data_mat[0][5] = string("0.0");
            data_mat[0][6] = string("6");
            data_mat[0][7] = string("33");
            data_mat[0][8] = string("1.0");
            t1.m_TableDataMat = data_mat;
            t1.m_Width = 5;
            t1.m_Caption = string("Warren-12 Geometry Setup");
            
            t1.WriteHTMLTable();
            
            HTMLTable t2 = HTMLTable(); // Initialize Table
            head_str.resize(6);
            head_str[0] = string("Analysis");
            head_str[1] = string("Method");
            head_str[2] = string("\\(\\alpha\\) (°)");
            head_str[3] = string("\\(\\beta\\) (°)");
            head_str[4] = string("M");
            head_str[5] = string("Wake Iterations");
            t2.m_HeaderStringVec = head_str;

            data_mat.resize(1);
            data_mat[0].resize(6);
            data_mat[0][0] = string("Sweep");
            data_mat[0][1] = string("VLM");
            string cell = string("-20.0 to 20.0, npts: ") + m_wvv.m_AlphaNpts;
            data_mat[0][2] = cell;
            data_mat[0][3] = string("0.0");
            data_mat[0][4] = m_wvv.m_MachVec[0];
            data_mat[0][5] = m_wvv.m_WakeIterVec[0];
            t2.m_TableDataMat = data_mat;
            t2.m_Width = 5;
            t2.m_Caption = string("Warren-12 VSPAERO Setup");
            
            t2.WriteHTMLTable();
            
            AddChartsToBody(m_wvv.m_W12ClAIDVec);
            
            fid.writeString( string("\t\t<div>\n") );
            
            AddChartsToBody(m_wvv.m_W12ClaErrorIDVec);
            
            fid.writeString( string("\t\t<div>\n") );
            
            AddChartsToBody(m_wvv.m_W12CmAIDVec);
            
            fid.writeString( string("\t\t<div>\n") );
            
            AddChartsToBody(m_wvv.m_W12CmaErrorIDVec);
           
            HTMLTable t3 = HTMLTable(); // Initialize Table
            head_str.resize(7);
            head_str[0] = string("\\(\\alpha\\) (°)");
            head_str[1] = string("\\(C_{L\\alpha}\\) Expected (rad)");
            head_str[2] = string("\\(C_{L\\alpha}\\) Result (rad)");
            head_str[3] = string("\\(C_{L\\alpha}\\) % Difference");
            head_str[4] = string("\\(C_{M\\alpha}\\) Expected (rad)");
            head_str[5] = string("\\(C_{M\\alpha}\\) Result (rad)");
            head_str[6] = string("\\(C_{M\\alpha}\\) % Difference");
            t3.m_HeaderStringVec = head_str;

            // Sweep Results
            data_mat.resize(m_wvv.m_AlphaNpts);
            for ( uint i = 0; i < uint(m_wvv.m_AlphaNpts); i++ )
            {
                data_mat[i].resize(7);
                data_mat[i][0] = m_wvv.m_AlphaSweepVec[i];
                data_mat[i][1] = m_wvv.m_Cl_alpha_expected;
                data_mat[i][2] = m_wvv.m_Cl_alpha_res[i];
                data_mat[i][3] = m_wvv.m_Cl_alpha_error[i];
                data_mat[i][4] = m_wvv.m_Cm_alpha_expected;
                data_mat[i][5] = m_wvv.m_Cm_alpha_res[i];
                data_mat[i][6] = m_wvv.m_Cm_alpha_error[i];
            }
            
            t3.m_TableDataMat = data_mat;
            t3.m_Width = 6;
            t3.m_Caption = string("Warren-12 Results");
            
            t3.WriteHTMLTable();
        }
        if ( m_vktvv.m_runflag )
        {
            fid.writeString( string( "\t\t<h1 id='VKTW'>Von Karman-Trefftz</h1>\n" ) );
            
            text = string("A series of Von Karman-Trefftz (VKT) wings are generated using the Karman-Trefftz airfoil available in OpenVSP. A series of studies are then conducted with the first varying the VKT airfoil shape and the second and third varying U and W tesselation. Two API functions, vsp::GetVKTAirfoilPnts and vsp::GetVKTAirfoilCpDist, are used to determine the theoretical VKT airfoil \\(C_P\\) distribution. The VSP Slicer is executed to identify the \\(C_P\\) distribution at a specified planar slice in each VSPAERO result.");
            
            WriteParagraph( text );
            
            if ( m_vktvv.m_VKTEpsKapTauIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t<div>\n") );
                fid.writeString( string("\t\t  <h2 id='VKTE'>Epsilon Kappa Tau Study</h2>\n") );

                text = string("In this study, the shape of the Von Karman-Trefftz airfoil is manipulated to create a series of unique VKT wings. Theoretical \\(C_P\\) distributions are determined for each VKT airfoil and compared to the results obtained by executing the CpSlicer analysis with an XZ slice placed at the root of the VKT wing. The theoretical and VSPAERO \\(C_P\\) distributions are plotted for each VKT airfoil variation below. Note that if the VKT airfoil is symmetric, there is no difference in the exact \\(C_P\\) solution for the upper and lower surface. When the airfoil is not symmetric, there is a noticeable difference in the \\(C_P\\) distribution between the upper and lower surfaces.");
                
                WriteParagraph( text );
                
                HTMLTable t1 = HTMLTable(); // Initialize Table
                array<string> head_str(12);
                head_str[0] = string("Airfoil");
                head_str[1] = string("\\(\\varepsilon\\)");
                head_str[2] = string("\\(\\kappa\\)");
                head_str[3] = string("\\(\\tau\\) (°)");
                head_str[4] = string("AR");
                head_str[5] = string("Root Chord");
                head_str[6] = string("Tip Chord");
                head_str[7] = string("\\(\\lambda\\) (°)");
                head_str[8] = string("Span Tess (U)");
                head_str[9] = string("Chord Tess (W)");
                head_str[10] = string("LE Clustering");
                head_str[11] = string("TE Clustering");
                t1.m_HeaderStringVec = head_str;
                
                array<array<string>> data_mat(1);
                data_mat[0].resize(12);
                data_mat[0][0] = string("VKT");
                data_mat[0][1] = string("0.1, 0.2");
                data_mat[0][2] = string("0.0, 0.1");
                data_mat[0][3] = string("0, 10");
                data_mat[0][4] = string("30");
                data_mat[0][5] = string("1.0");
                data_mat[0][6] = string("1.0");
                data_mat[0][7] = string("0.0");
                data_mat[0][8] = string("41");
                data_mat[0][9] = string("51");
                data_mat[0][10] = string("0.2");
                data_mat[0][11] = string("1.0");
                
                t1.m_TableDataMat = data_mat;
                t1.m_Width = 6;
                t1.m_Caption = string("VKT \\(\\varepsilon\\) \\(\\kappa\\) \\(\\tau\\) Study Geometry Setup");
                
                t1.WriteHTMLTable();
                
                HTMLTable t2 = HTMLTable(); // Initialize Table
                head_str.resize(6);
                head_str[0] = string("Analysis");
                head_str[1] = string("Method");
                head_str[2] = string("\\(\\alpha\\) (°)");
                head_str[3] = string("\\(\\beta\\) (°)");
                head_str[4] = string("M");
                head_str[5] = string("Wake Iterations");
                t2.m_HeaderStringVec = head_str;
                
                data_mat.resize(1);
                data_mat[0].resize(6);
                data_mat[0][0] = string("Single Point");
                data_mat[0][1] = string("Panel");
                data_mat[0][2] = string("0.0");
                data_mat[0][3] = string("0.0");
                data_mat[0][4] = m_vktvv.m_MachVec[0];
                data_mat[0][5] = m_vktvv.m_WakeIterVec[0];
                
                t2.m_TableDataMat = data_mat;
                t2.m_Width = 5;
                t2.m_Caption = string("VKT \\(\\varepsilon\\) \\(\\kappa\\) \\(\\tau\\) Study VSPAERO Setup");
                
                t2.WriteHTMLTable();
                
                AddChartsToBody(m_vktvv.m_VKTEpsKapTauIDVec);
            }
            
            if ( m_vktvv.m_VKTWTessIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t<div>\n") );
                fid.writeString( string("\t\t  <h2 id='VKTCT'>Chord Tesselation Study</h2>\n") );
                
                text = string("This test looks to determine the influence of chord tesselation on VSPAERO results for the VKT wing by varying chord tesselation while holding all other variables constant. A CpSlicer analysis is then performed on each VSPAERO result with an XZ slice defined at the wing root. The VSPAERO \\(C_P\\) distribution is then compared to the theoretical \\(C_P\\) distribution for the particular VKT airfoil, as seen in the plots below. In addition, XFoil's \\(C_P\\) solution for the VKT airfoil is plotted. This result is obtained by first writing a Selig airfoil file from the same coordinate points used to generate the theoretical VKT Cp distribution. This file is loaded in XFoil 6.99 and run with identical flow conditions as VSPAERO. The VKT airfoil coordinate file, \"VKT_e0.1_k0.1_t10.dat\", and \\(C_P\\) distribution results file from XFoil, \"XFoil_VKT_CpDist.txt\", can be found in the airfoil directory of the OpenVSP software. ");
                
                WriteParagraph( text );
                
                HTMLTable t1 = HTMLTable(); // Initialize Table
                array<string> head_str(12);
                head_str[0] = string("Airfoil");
                head_str[1] = string("\\(\\varepsilon\\)");
                head_str[2] = string("\\(\\kappa\\)");
                head_str[3] = string("\\(\\tau\\) (°)");
                head_str[4] = string("AR");
                head_str[5] = string("Root Chord");
                head_str[6] = string("Tip Chord");
                head_str[7] = string("\\(\\lambda\\) (°)");
                head_str[8] = string("Span Tess (U)");
                head_str[9] = string("Chord Tess (W)");
                head_str[10] = string("LE Clustering");
                head_str[11] = string("TE Clustering");
                t1.m_HeaderStringVec = head_str;
                
                array<array<string>> data_mat(1);
                data_mat[0].resize(12);
                data_mat[0][0] = string("VKT");
                data_mat[0][1] = string("0.1");
                data_mat[0][2] = string("0.1");
                data_mat[0][3] = string("10");
                data_mat[0][4] = string("15");
                data_mat[0][5] = string("1.0");
                data_mat[0][6] = string("1.0");
                data_mat[0][7] = string("0.0");
                data_mat[0][8] = string("12 to 41");
                data_mat[0][9] = string("17 to 51");
                data_mat[0][10] = string("0.2");
                data_mat[0][11] = string("1.0");
                
                t1.m_TableDataMat = data_mat;
                t1.m_Width = 6;
                t1.m_Caption = string("VKT Chord Tesselation Study Geometry Setup");
                
                t1.WriteHTMLTable();
                
                HTMLTable t2 = HTMLTable(); // Initialize Table
                head_str.resize(6);
                head_str[0] = string("Analysis");
                head_str[1] = string("Method");
                head_str[2] = string("\\(\\alpha\\) (°)");
                head_str[3] = string("\\(\\beta\\) (°)");
                head_str[4] = string("M");
                head_str[5] = string("Wake Iterations");
                t2.m_HeaderStringVec = head_str;
                
                data_mat.resize(1);
                data_mat[0].resize(6);
                data_mat[0][0] = string("Single Point");
                data_mat[0][1] = string("Panel");
                data_mat[0][2] = string("0.0");
                data_mat[0][3] = string("0.0");
                data_mat[0][4] = m_vktvv.m_MachVec[0];
                data_mat[0][5] = m_vktvv.m_WakeIterVec[0];
                
                t2.m_TableDataMat = data_mat;
                t2.m_Width = 5;
                t2.m_Caption = string("VKT Chord Tesselation Study VSPAERO Setup");
                
                t2.WriteHTMLTable();
                
                AddChartsToBody(m_vktvv.m_VKTWTessIDVec);
            }
            
            if ( m_vktvv.m_VKTUTessIDVec.length() > 0 )
            {
                fid.writeString( string("\t\t<div>\n") );
                fid.writeString( string("\t\t  <h2 id='VKTST'>Span Tesselation Study</h2>\n") );
                
                text = string("This next test is similar to the previous one, but instead looks to determine the influence of span tesselation on VSPAERO results for the VKT wing. Chord tesselation, leading and trailing edge clustering, aspect ratio, and airfoil shape are all held constant. A CpSlicer analysis with a slice located along the wing root is then performed on each VSPAERO result. The VSPAERO \\(C_P\\) distribution is then compared to the theoretical \\(C_P\\) distribution for the VKT airfoil and XFoil's \\(C_P\\) solution. This comparison can be seen in the plots below. The VKT coordinate file input to XFoil, \"VKT_e0.1_k0.1_t10.dat\", and \\(C_P\\) distribution results file, \"XFoil_VKT_CpDist.txt\", are located in the airfoil directory.");
                
                WriteParagraph( text );
                
                HTMLTable t1 = HTMLTable(); // Initialize Table
                array<string> head_str(12);
                head_str[0] = string("Airfoil");
                head_str[1] = string("\\(\\varepsilon\\)");
                head_str[2] = string("\\(\\kappa\\)");
                head_str[3] = string("\\(\\tau\\) (°)");
                head_str[4] = string("AR");
                head_str[5] = string("Root Chord");
                head_str[6] = string("Tip Chord");
                head_str[7] = string("\\(\\lambda\\) (°)");
                head_str[8] = string("Span Tess (U)");
                head_str[9] = string("Chord Tess (W)");
                head_str[10] = string("LE Clustering");
                head_str[11] = string("TE Clustering");
                t1.m_HeaderStringVec = head_str;
                
                array<array<string>> data_mat(1);
                data_mat[0].resize(12);
                data_mat[0][0] = string("VKT");
                data_mat[0][1] = string("0.1");
                data_mat[0][2] = string("0.1");
                data_mat[0][3] = string("10");
                data_mat[0][4] = string("15");
                data_mat[0][5] = string("1.0");
                data_mat[0][6] = string("1.0");
                data_mat[0][7] = string("0.0");
                data_mat[0][8] = string("12 to 41");
                data_mat[0][9] = string("17 to 51");
                data_mat[0][10] = string("0.2");
                data_mat[0][11] = string("1.0");
                
                t1.m_TableDataMat = data_mat;
                t1.m_Width = 6;
                t1.m_Caption = string("VKT Span Tesselation Study Geometry Setup");
                
                t1.WriteHTMLTable();
                
                HTMLTable t2 = HTMLTable(); // Initialize Table
                head_str.resize(6);
                head_str[0] = string("Analysis");
                head_str[1] = string("Method");
                head_str[2] = string("\\(\\alpha\\) (°)");
                head_str[3] = string("\\(\\beta\\) (°)");
                head_str[4] = string("M");
                head_str[5] = string("Wake Iterations");
                t2.m_HeaderStringVec = head_str;
                
                data_mat.resize(1);
                data_mat[0].resize(6);
                data_mat[0][0] = string("Single Point");
                data_mat[0][1] = string("Panel");
                data_mat[0][2] = string("0.0");
                data_mat[0][3] = string("0.0");
                data_mat[0][4] = m_vktvv.m_MachVec[0];
                data_mat[0][5] = m_vktvv.m_WakeIterVec[0];
                
                t2.m_TableDataMat = data_mat;
                t2.m_Width = 5;
                t2.m_Caption = string("VKT Span Tesselation Study VSPAERO Setup");
                
                t2.WriteHTMLTable();
                
                AddChartsToBody(m_vktvv.m_VKTUTessIDVec);
            }
        }
        if ( m_evv.m_runflag && m_evv.m_EllipsoidIDVec.length() > 0 )
        {
            fid.writeString( string("\t\t<div>\n") );
            fid.writeString( string( "\t\t<h1 id='EW'>Ellipsoid</h1>\n" ) );

            text = string("An ellipsoid geometry is modeled for this V&V test case. Four VSPAERO single point analyses are conducted in which angle of attack and slideslip angle are varied. To validate the VSPAERO results, the surface coordinate points along each major axis of the ellipsoid are first calculated using the API function vsp::GetFeatureLinePnts. The \\(C_P\\) is then calculated at each surface point with the API function vsp::GetEllipsoidCpDist with the freestream velocity set to 100 in the X direction, as is default in VSPAERO. The vsp::GetEllipsoidCpDist function is based off the algorithms presented in NACA TN 196 by Max M. Munk. Three plots are generated for each VSPAERO run case, displaying the VSPAERO and theoretical \\(C_P\\) distribution along each major axis of the ellipsoid.");
            
            WriteParagraph( text );
            
            HTMLTable t1 = HTMLTable(); // Initialize Table
            array<string> head_str(6);
            head_str[0] = string("A Radius");
            head_str[1] = string("B Radius");
            head_str[2] = string("C Radius");
            head_str[3] = string("Center");
            head_str[4] = string("Span Tess (U)");
            head_str[5] = string("Chord Tess (W)");
            t1.m_HeaderStringVec = head_str;
            
            array<array<string>> data_mat(1);
            data_mat[0].resize(6);
            data_mat[0][0] = string("1.0");
            data_mat[0][1] = string("2.0");
            data_mat[0][2] = string("3.0");
            data_mat[0][3] = string("(0.0, 0.0, 0.0)");
            data_mat[0][4] = string("40");
            data_mat[0][5] = string("41");
            
            t1.m_TableDataMat = data_mat;
            t1.m_Width = 5;
            t1.m_Caption = string("Ellipsoid Geometry Setup");
            
            t1.WriteHTMLTable();
            
            HTMLTable t2 = HTMLTable(); // Initialize Table
            head_str.resize(7);
            head_str[0] = string("Run Case #");
            head_str[1] = string("Analysis");
            head_str[2] = string("Method");
            head_str[3] = string("\\(\\alpha\\) (°)");
            head_str[4] = string("\\(\\beta\\) (°)");
            head_str[5] = string("M");
            head_str[6] = string("Wake Iterations");
            t2.m_HeaderStringVec = head_str;
            
            data_mat.resize(4);
            for ( uint i = 0; i < uint(data_mat.length()); i++ )
            {
                data_mat[i].resize(7);
                data_mat[i][0] = i + 1;
                data_mat[i][1] = string("Single Point");
                data_mat[i][2] = string("Panel");
                data_mat[i][5] = string("0.0");
                data_mat[i][6] = m_vktvv.m_WakeIterVec[0];
            }

            data_mat[0][3] = string("0.0");
            data_mat[0][4] = string("0.0");

            data_mat[1][3] = string("0.0");
            data_mat[1][4] = string("20.0");

            data_mat[2][3] = string("20.0");
            data_mat[2][4] = string("0.0");

            data_mat[3][3] = string("20.0");
            data_mat[3][4] = string("20.0");
            
            t2.m_TableDataMat = data_mat;
            t2.m_Caption = string("Ellipsoid VSPAERO Setup");
            t2.m_Width = 5;
            t2.WriteHTMLTable();
            
            AddChartsToBody(m_evv.m_EllipsoidIDVec);
        }
        if ( m_sdvv.m_runflag )
        {
            fid.writeString( string("\t\t<div>\n") );
            fid.writeString( string( "\t\t<h1 id='SDW'>Supersonic Delta Wing</h1>\n" ) );
            
            text = string("This final study is similar to a study presented by Dave Kinney at the 2016 OpenVSP Workshop, which compared VSPAERO results to the theory of three-dimensional wings in steady supersonic flow outlined in <span style='font-style: italic'>Aerodynamics of Wings and Bodies</span> by Ashley and Landahl. Two supersonic delta wings are modeled and analyzed with VSPAERO, and the results are displayed alongside experimental data below. The subsonic or supersonic character of the wing's leading edge (m) is identified as a function of the Mach parameter (B) and sweep:");
            
            WriteParagraph( text );
            
            fid.writeString( string( "\t\t  <p>$$B = \\sqrt{1-M^2}$$</p>\n" ) );
            
            fid.writeString( string( "\t\t  <p>$$m = \\frac{B}{tan(\\Lambda)}$$</p>\n" ) );
            
            HTMLTable t1 = HTMLTable(); // Initialize Table
            array<string> head_str(9);
            head_str[0] = string("Case #");
            head_str[1] = string("Root Airfoil");
            head_str[2] = string("Tip Airfoil");
            head_str[3] = string("Span");
            head_str[4] = string("Root Chord");
            head_str[5] = string("\\(\\Lambda\\) (°)");
            head_str[6] = string("\\(\\Lambda\\) Location");
            head_str[7] = string("Span Tess (U)");
            head_str[8] = string("Chord Tess (W)");
            t1.m_HeaderStringVec = head_str;
            
            array<array<string>> data_mat(2);
            for ( uint i = 0; i < uint(data_mat.length()); i++ )
            {
                data_mat[i].resize(10);
                data_mat[i][0] = i + 1;
                data_mat[i][3] = string("20");
                data_mat[i][1] = string("NACA004");
                data_mat[i][2] = string("NACA006");
                data_mat[i][4] = string("11.0");
                data_mat[i][6] = string("0.0");
                data_mat[i][7] = string("30");
                data_mat[i][8] = string("33");
            }
            
            data_mat[0][5] = m_sdvv.m_Sweep[0];
            data_mat[1][5] = m_sdvv.m_Sweep[1];
            
            t1.m_Width = 5;
            t1.m_TableDataMat = data_mat;
            t1.m_Caption = string("Supersonic Delta Wing Geometry Setup");
            
            t1.WriteHTMLTable();
            
            HTMLTable t2 = HTMLTable(); // Initialize Table
            head_str.resize(6);
            head_str[0] = string("Analysis");
            head_str[1] = string("Method");
            head_str[2] = string("\\(\\alpha\\) (°)");
            head_str[3] = string("\\(\\beta\\) (°)");
            head_str[4] = string("M");
            head_str[5] = string("Wake Iterations");
            t2.m_HeaderStringVec = head_str;
            
            data_mat.resize(1);
            data_mat[0].resize(6);
            data_mat[0][0] = string("Single Point");
            data_mat[0][1] = string("VLM");
            data_mat[0][2] = string("5.0");
            data_mat[0][3] = string("0.0");
            data_mat[0][4] = string("1.135, 1.366, 1.894, 2.386, 2.861, 3.369, 3.884, 4.404");
            data_mat[0][5] = m_sdvv.m_WakeIterVec[0];

            t2.m_Width = 5;
            t2.m_TableDataMat = data_mat;
            t2.m_Caption = string("Supersonic Delta Wing VSPAERO Setup");
            
            t2.WriteHTMLTable();
            
            AddChartsToBody(m_sdvv.m_SuperDeltaIDVec);
        }
        
        // References
        fid.writeString( string( "\t\t<h2 id='REF' style='text-align: center; font-size: 175%; font-weight: bold;'>References</h2>\n" ) );
        fid.writeString( string("\t\t<div>\n") );
        fid.writeString( string("\t\t  <ol>\n") );
        
        fid.writeString( string("\t\t\t<li>Abbott, I. & Doenhoff, A. (1959). <span style='font-style: italic'>Theory of Wing Sections: Including a Summary of Airfoil Data</span>. NY: Dover Publications, Inc.</li>\n") );
        fid.writeString( string("\t\t\t<li>Ashley, H. & Landahl, M. (1965). <span style='font-style: italic'>Aerodynamics of Wings and Bodies</span>. MA: Addison-Wesley Publishing Company, Inc.</li>\n") );
        fid.writeString( string("\t\t\t<li>Bertin, J. J., & Smith, M. L. (1994). <span style='font-style: italic'>Aerodynamics for Engineers</span>(2nd ed.). Englewood Cliffs, NJ: Prentice Hall.</li>\n") );
        fid.writeString( string("\t\t\t<li>Drela, M. & Youngren, H. (2017). <span style='font-style: italic'>AVL 3.36 User Primer</span>.<a href='http://web.mit.edu/drela/Public/web/avl/avl_doc.txt'>http://web.mit.edu/drela/Public/web/avl/avl_doc.txt</a>.</li>\n") );
        fid.writeString( string("\t\t\t<li>Drela, M. & Youngren, H. (2001). <span style='font-style: italic'>XFOIL 6.9 User Primer</span>.<a href='http://web.mit.edu/drela/Public/web/xfoil/xfoil_doc.txt'>http://web.mit.edu/drela/Public/web/xfoil/xfoil_doc.txt</a>.</li>\n") );
        fid.writeString( string("\t\t\t<li>Hoerner, S. F., & Borst, H. V. (1985). <span style='font-style: italic'>Fluid-Dynamic Lift: Practical Information on Aerodynamic and Hydrodynamic Lift</span> (2nd ed.). Albuquerque/N.M.: Hoerner.</li>\n") );
        fid.writeString( string("\t\t\t<li>Katz, J. & Plotkin, A. (2001). <span style='font-style: italic'>Low-Speed Aerodynamics</span> (2nd ed.). New York: Cambridge University Press.</li>\n") );
        fid.writeString( string("\t\t\t<li>Kinney, D. (2016, August 24). <span style='font-style: italic'>VSPAERO Verification & Next Steps</span>. Live presentation at NASA Ames Conference Center, Mountain View.</li>\n") );
        fid.writeString( string("\t\t\t<li>Lamb, H. (1932). Hydrodynamics (6th ed.). Cambridge, MA: Cambridge University Press.</li>\n") );
        fid.writeString( string("\t\t\t<li>Lowry, J. & Polhamus, E. (1957). <span style='font-style: italic'>A Method for Predicting Lift Increments Due to Flap Deflection at Low Angles of Attack in Incompressible Flow</span>. Virginia: National Advisory Committee for Aeronautics. <a href='https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19930084818.pdf'>https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19930084818.pdf</a>. doi:19930084818</li>\n") );
        fid.writeString( string("\t\t\t<li>Munk, M. (1924). <span style='font-style: italic'>Remarks on the Pressure Distribution over the Surface of an Ellipsoid, Moving Translationally Through a Perfect Fluid</span>. Washington, DC: National Advisory Committee for Aeronautics. <a href='https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19930080983.pdf'> https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19930080983.pdf</a>. doi:19930080983</li>\n") );
        fid.writeString( string("\t\t\t<li>Oliviu, S. G., Andreea K., & Ruxandra M. B. (2016). <span style='font-style: italic'>A New Non-Linear Vortex Lattice Method: Applications to Wing Aerodynamic Optimizations</span>. Quebec: LARCASE Laboratory of Applied Research in Active Controls, Avionics, and Aeroelasticity. <a href='https://ac.els-cdn.com/S1000936116300954/1-s2.0-S1000936116300954-main.pdf?_tid=ecb8e5ea-d93a-11e7-afba-00000aacb362&acdnat=1512423465_5a88062ebd97b0cac725b33f68055f9d'>https://ac.els-cdn.com/S1000936116300954/1-s2.0-S1000936116300954-main.pdf?_tid=ecb8e5ea-d93a-11e7-afba-00000aacb362&acdnat=1512423465_5a88062ebd97b0cac725b33f68055f9d</a>.</li>\n") ); 
        fid.writeString( string("\t\t  </ol>\n") );
        fid.writeString( string("\t\t</div>\n") );
        
        fid.writeString( string("\t  </div>\n") ); // end row
        fid.writeString( string("\t</div>\n") ); // end column content
    }

    private void WriteHTMLFooter()
    {
        // Footer
        fid.writeString( string("\t<div class='footer'>\n") );
        string footer = "\t  <p>Verification & Validation Studies Performed Using " + GetVSPVersion() + "</p>\n";
        fid.writeString( footer );
        fid.writeString( string("\t</div>\n") );
    }
    
    private void AddChartsToBody( array<string> chart_name_vec )
    {
        for ( uint i = 0; i < uint(chart_name_vec.length()); i++ )
        {
            string chart_str = "\t\t  <p><figure class='chartc' id='" + chart_name_vec[i] + "' style='border: 1px solid #ccc'></figure></p>\n"; // center chart
            fid.writeString( chart_str );
        }
        
        fid.writeString( string("\t\t</div>\n") ); // end section
    }
    
    private void WriteParagraph( string text )
    {
        string paragraph = string("\t\t  <p>") + text + string("</p>\n");
        fid.writeString( paragraph );
    }
    
    // HTML Class Properties
    HersheyVV m_hvv;
    SweepVV m_svv;
    BertinVV m_bvv;
    WarrenVV m_wvv;
    VKTVV m_vktvv;
    EllipsoidVV m_evv;
    SDVV m_sdvv;
}

//============= Chart Class ==============//
class HTMLChart
{
    HTMLChart()
    {
        m_PointSize = 5.0;
        m_LegendPos = string("right");
        m_Height = 0.7;
        m_Width = 0.65;
    }
    
    void WriteChartHead( array<string> data_str_vec )
    {
        if ( data_str_vec.length() == 0 )
        {
            return;
        }

        string callback_name = "\t  google.charts.setOnLoadCallback(" + m_ChartName + ");\n";
        fid.writeString( callback_name );
        string fun_name = "\t  function " + m_ChartName + "() {\n";
        fid.writeString( fun_name );
        fid.writeString( string("\t\tvar data = google.visualization.arrayToDataTable([\n") );
        string legend_name = string("\t\t  [") + m_Legend + string("],\n");
        fid.writeString( legend_name );
        
        // Write data
        for ( uint i = 0; i < uint(data_str_vec.length()); i++ )
        {
            fid.writeString( data_str_vec[i] );
        }
        
        fid.writeString( string("\t\t]);\n\n") );
        string width = string("\t\tvar w_size = ") + double(m_Width) + string(" * window.innerWidth;\n");
        fid.writeString( width );
        string height = string("\t\tvar h_size = ") + double(m_Height) + string(" * window.innerHeight;\n\n");
        fid.writeString( height );
        fid.writeString( string("\t\tvar options = {\n") );
        string title_name = string("\t\t  title: '") + m_Title + string("',\n");
        fid.writeString( title_name );
        string pnt_size = string("\t\t  pointSize: ") + double(m_PointSize) + string(",\n");
        fid.writeString( pnt_size );
        
        if ( m_Series.length() > 0 )
        {
            string series_str = string("\t\t  series: ") + m_Series + string(",\n");
            fid.writeString( series_str );
        }
        
        string haxis_name = string("\t\t  ") + m_HAxisString + string(",\n");
        fid.writeString( haxis_name );
        string vaxis_name = string("\t\t  ") + m_VAxisString + string(",\n");
        fid.writeString( vaxis_name );
        fid.writeString( string("\t\t  width: w_size,\n") );
        fid.writeString( string("\t\t  height: h_size,\n") );
        string legend_str = string("\t\t  legend: { position: '") + m_LegendPos + string("', alignment: 'center' },\n");
        fid.writeString( legend_str );
        fid.writeString( string("\t\t  backgroundColor: '#ffffff'\n") );
        fid.writeString( string("\t\t};\n") );

        string add_name = "\n\t\tvar chart = new google.visualization.LineChart(document.getElementById('" + m_ChartName + "'));\n";
        fid.writeString( add_name );
        fid.writeString( string("\n\t\tchart.draw(data, options);\n") );
        
        fid.writeString( string("\t  }\n\n") );
    }
    
    // HTML Chart Class Properties
    string m_ChartName;
    string m_Legend;
    string m_Title;
    string m_VAxisString;
    string m_HAxisString;
    double m_PointSize;
    string m_LegendPos;
    double m_Height;
    double m_Width;
    string m_Series;
}

//============= Table Class ==============//
class HTMLTable
{
    HTMLTable()
    {
        m_Width = 5; // Default value (50% width)
    }
    
    void WriteHTMLTable()
    {
        fid.writeString( string("\t\t<p>\n") );
        
        string class_str = string("class='center");
        
        if ( m_Width == 3 )
        {
            class_str += string(" three");
        }
        else if ( m_Width == 4 )
        {
            class_str += string(" four");
        }
        else if ( m_Width == 5 )
        {
            class_str += string(" five");
        }
        else if ( m_Width == 6 )
        {
            class_str += string(" six");
        }
        else if ( m_Width == 7 )
        {
            class_str += string(" seven");
        }
        else if ( m_Width == 8 )
        {
            class_str += string(" eight");
        }
        
        class_str = string("\t\t  <table ") + class_str + string("'>\n");
        fid.writeString( class_str );
        
        if ( m_Caption.length() > 0 )
        {
            string caption = string("\t\t\t<caption>") + m_Caption + string("</caption>\n");
            fid.writeString( caption );
        }
        
        if ( m_HeaderStringVec.length() > 0 )
        {
            fid.writeString( string("\t\t\t<tr>\n") );
            for ( uint i = 0; i < uint(m_HeaderStringVec.length()); i++ )
            {
                string header = string("\t\t\t  <th>") + m_HeaderStringVec[i] + string("</th>\n");
                fid.writeString( header );
            }
            fid.writeString( string("\t\t \t</tr>\n") );
        }
        
        for ( uint r = 0; r < uint(m_TableDataMat.length()); r++ )
        {
            fid.writeString( string("\t\t\t<tr>\n") );
            for ( uint c = 0; c < uint(m_TableDataMat[r].length()); c++ )
            {
                string data = string("\t\t\t  <td>") + m_TableDataMat[r][c] + string("</td>\n");
                fid.writeString( data );
            }
            fid.writeString( string("\t\t\t</tr>\n") );
        }
        
        fid.writeString( string("\t\t  </table>\n") );
        fid.writeString( string("\t\t</p>\n") );
    }
    
    // HTML Table Class Properties
    string m_Caption;
    int m_Width;
    array<array<string>> m_TableDataMat;
    array<string> m_HeaderStringVec;
}

//============= V&V Base Class ==============//
class VV_Base
{
    VV_Base()
    {
        m_CompGeomAnalysis = string("VSPAEROComputeGeometry");
        m_VSPSweepAnalysis = string("VSPAEROSweep");
        m_CpSliceAnalysis = string("CpSlicer");
        
        m_AlphaVec[0] = 1.0; //deg
        m_MachVec[0] = 0.1;
        m_GeomVec[0] = 0; // Set: All
        m_RefFlagVec[0] = 1; // Wing Reference
        m_WakeIterVec[0] = 3;
        m_SymFlagVec[0] = 1;
        
        pi = 3.14159265358979323846;
        b = 0.9949874371; // M = 0.1, b = (1-M^2)^0.5
        k_theo = 1.0;
        Vinf = 100;
        
        m_runflag = false;
    }
    
    // V&V Base Class Properties
    string m_CompGeomAnalysis;
    string m_VSPSweepAnalysis;
    string m_CpSliceAnalysis;
    
    array<double> m_AlphaVec(1);
    array<double> m_MachVec(1);
    array<int> m_GeomVec(1);
    array<int> m_RefFlagVec(1);
    array<int> m_WakeIterVec(1);
    array<int> m_SymFlagVec(1);
    
    double pi;
    double b;
    double k_theo;
    double Vinf;
    
    bool m_runflag;
}

//============= Hershey Bar AR Tess Study Functions ==============//
class HersheyVV : VV_Base
{
    HersheyVV()
    {
        m_halfAR[0] = 2.5;
        m_halfAR[1] = 5.0016;
        m_halfAR[2] = 7.5;
        m_halfAR[3] = 12.5;
        m_halfAR[4] = 20.0;
        m_halfAR[5] = 30.0;

        m_AlphaNpts = 9;
        
        m_Tip_Clus[0] = 0.1;
        m_Tip_Clus[1] = 0.5;
        m_Tip_Clus[2] = 1;

        m_Tess_U[0] = 5;
        m_Tess_U[1] = 12;
        m_Tess_U[2] = 20;
        m_Tess_U[3] = 41;

        m_Tess_W[0] = 9;
        m_Tess_W[1] = 17;
        m_Tess_W[2] = 29;
        m_Tess_W[3] = 51;
        
        m_WakeIter[0] = 1;
        m_WakeIter[1] = 2;
        m_WakeIter[2] = 3;
        m_WakeIter[3] = 4;
        m_WakeIter[4] = 5;
        
        m_AdvancedWakeVec[0] = 1;
        m_AdvancedWakeVec[1] = 2;
        m_AdvancedWakeVec[2] = 3;
        
        string AVL_file_name = GetVSPExePath() + string("/airfoil/Hershey_AR10_AVL.dat");
        m_AR10_Y_Cl_Cd_vec = ReadAVLFSFile(AVL_file_name);
    }
    
    void HersheyBarStudy()
    {
        m_runflag = true;;
        
        //====Generate Hershey Bar Wings====//
        GenerateHersheyBarWings();
        
        //====Test Hershey Bar Wings====//
        TestHersheyBarWings();
    }
    
    private void GenerateHersheyBarWings()
    {
        GenerateHersheyBarARWings();
        GenerateHersheyBarUWTessWings();
        GenerateHersheyBarTCWings();
        GenerateHersheyBarUTessWings();
        GenerateHersheyBarWTessWings();
        GenerateHersheyBarWakeWings();
        GenerateHersheyBarAdvancedWings();
    }
    
    private void TestHersheyBarWings()
    {
        TestHersheyBarARWings();
        TestHersheyBarUWTessWings();
        TestHersheyBarTCWings();
        TestHersheyBarUTessWings();
        TestHersheyBarWTessWings();
        TestHersheyBarWakeWings();
        TestHersheyBarAdvancedWings();
    }

    //============= Hershey Bar Wing Generation Functions ==============//
    
    private void GenerateHersheyBarARWings()
    {
        //==== Add Wing Geometry ====//
        string wing_id = AddGeom( "WING", "" );
        
        //==== Set Wing Section ====//
        SetDriverGroup( wing_id, 1, AR_WSECT_DRIVER, ROOTC_WSECT_DRIVER, TIPC_WSECT_DRIVER );
        
        Update();
        
        //==== Set NACA 0012 Airfoil and Common Parms 
        SetParmVal( wing_id, "ThickChord", "XSecCurve_0", 0.12 );
        SetParmVal( wing_id, "ThickChord", "XSecCurve_1", 0.12 );
        SetParmVal( wing_id, "Sweep", "XSec_1", 0 );
        SetParmVal( wing_id, "Root_Chord", "XSec_1", 1.0 );
        SetParmVal( wing_id, "Tip_Chord", "XSec_1", 1.0 );
        SetParmVal( wing_id, "TECluster", "WingGeom", 1.0 );
        SetParmVal( wing_id, "LECluster", "WingGeom", 0.2 );
        
        const uint8 u = 3; // UTess
        const uint8 w = 3; // WTess
        const uint8 t = 2; // Tip Clustering
        
        SetParmVal( wing_id, "SectTess_U", "XSec_1", m_Tess_U[u] ); // Constant U Tess
        SetParmVal( wing_id, "Tess_W", "Shape", m_Tess_W[w] ); // Constant W Tess
        SetParmVal( wing_id, "OutCluster", "XSec_1", m_Tip_Clus[t] ); // Constant Tip Clustering
        
        Update();
        
        for ( uint8 x = 0; x < uint8(m_halfAR.length()); x++ )
        {
            SetParmVal( wing_id, "Aspect", "XSec_1", m_halfAR[x] );

            Update();

            //==== Setup export filenames for AR Study ====//
            string fname = "Hershey_AR" + int(2*m_halfAR[x]) + ".vsp3";

            //==== Save Vehicle to File ====//
            string message = "-->Saving vehicle file to: " + fname + "\n";
            Print( message );
            WriteVSPFile( fname, SET_ALL );
            Print( "COMPLETE\n" );
        }
        
        ClearVSPModel();
    }
    
    private void GenerateHersheyBarUTessWings()
    {
        //==== Add Wing Geometry ====//
        string wing_id = AddGeom( "WING", "" );
        
        //==== Set Wing Section ====//
        SetDriverGroup( wing_id, 1, AR_WSECT_DRIVER, ROOTC_WSECT_DRIVER, TIPC_WSECT_DRIVER );
        
        Update();
        
        //==== Set NACA 0012 Airfoil and Common Parms 
        SetParmVal( wing_id, "ThickChord", "XSecCurve_0", 0.12 );
        SetParmVal( wing_id, "ThickChord", "XSecCurve_1", 0.12 );
        SetParmVal( wing_id, "Sweep", "XSec_1", 0 );
        SetParmVal( wing_id, "Root_Chord", "XSec_1", 1.0 );
        SetParmVal( wing_id, "Tip_Chord", "XSec_1", 1.0 );
        SetParmVal( wing_id, "TECluster", "WingGeom", 1.0 );
        SetParmVal( wing_id, "LECluster", "WingGeom", 0.2 );
        
        const uint8 x = 1; // AR
        const uint8 w = 1; // WTess
        const uint8 t = 2; // Tip Clustering
        
        SetParmVal( wing_id, "Aspect", "XSec_1", m_halfAR[x] ); // Constant AR
        SetParmVal( wing_id, "Tess_W", "Shape", m_Tess_W[w] ); // Constant W Tess
        SetParmVal( wing_id, "OutCluster", "XSec_1", m_Tip_Clus[t] ); // Constant Tip Clustering
        
        Update();
        
        for ( uint8 u = 0; u < uint8(m_Tess_U.length()); u++ )
        {
            SetParmVal( wing_id, "SectTess_U", "XSec_1", m_Tess_U[u] );

            Update();

            //==== Setup export filenames for U Tess Study ====//
            string fname = "Hershey_U" + int(m_Tess_U[u]) + ".vsp3";

            //==== Save Vehicle to File ====//
            string message = "-->Saving vehicle file to: " + fname + "\n";
            Print( message );
            WriteVSPFile( fname, SET_ALL );
            Print( "COMPLETE\n" );
        }
        
        ClearVSPModel();
    }
    
    private void GenerateHersheyBarWTessWings()
    {
        //==== Add Wing Geometry ====//
        string wing_id = AddGeom( "WING", "" );
        
        //==== Set Wing Section ====//
        SetDriverGroup( wing_id, 1, AR_WSECT_DRIVER, ROOTC_WSECT_DRIVER, TIPC_WSECT_DRIVER );
        
        Update();
        
        //==== Set NACA 0012 Airfoil and Common Parms 
        SetParmVal( wing_id, "ThickChord", "XSecCurve_0", 0.12 );
        SetParmVal( wing_id, "ThickChord", "XSecCurve_1", 0.12 );
        SetParmVal( wing_id, "Sweep", "XSec_1", 0 );
        SetParmVal( wing_id, "Root_Chord", "XSec_1", 1.0 );
        SetParmVal( wing_id, "Tip_Chord", "XSec_1", 1.0 );
        SetParmVal( wing_id, "TECluster", "WingGeom", 1.0 );
        SetParmVal( wing_id, "LECluster", "WingGeom", 0.2 );
        
        const uint8 x = 1; // AR
        const uint8 u = 1; // UTess
        const uint8 t = 2; // Tip Clustering
        
        SetParmVal( wing_id, "Aspect", "XSec_1", m_halfAR[x] ); // Constant AR
        SetParmVal( wing_id, "SectTess_U", "XSec_1", m_Tess_U[u] ); // Constant U Tess
        SetParmVal( wing_id, "OutCluster", "XSec_1", m_Tip_Clus[t] ); // Constant Tip Clustering
        
        Update();
        
        for ( uint8 w = 0; w < uint8(m_Tess_W.length()); w++ )
        {
            SetParmVal( wing_id, "Tess_W", "Shape", m_Tess_W[w] );

            Update();

            //==== Setup export filenames for W Tess Study ====//
            string fname = "Hershey_W" + int(m_Tess_W[w]) + ".vsp3";

            //==== Save Vehicle to File ====//
            string message = "-->Saving vehicle file to: " + fname + "\n";
            Print( message );
            WriteVSPFile( fname, SET_ALL );
            Print( "COMPLETE\n" );
        }
        
        ClearVSPModel();
    }
    
    private void GenerateHersheyBarUWTessWings()
    {
        //==== Add Wing Geometry ====//
        string wing_id = AddGeom( "WING", "" );
        
        //==== Set Wing Section ====//
        SetDriverGroup( wing_id, 1, AR_WSECT_DRIVER, ROOTC_WSECT_DRIVER, TIPC_WSECT_DRIVER );
        
        Update();
        
        //==== Set NACA 0012 Airfoil and Common Parms 
        SetParmVal( wing_id, "ThickChord", "XSecCurve_0", 0.12 );
        SetParmVal( wing_id, "ThickChord", "XSecCurve_1", 0.12 );
        SetParmVal( wing_id, "Sweep", "XSec_1", 0 );
        SetParmVal( wing_id, "Root_Chord", "XSec_1", 1.0 );
        SetParmVal( wing_id, "Tip_Chord", "XSec_1", 1.0 );
        SetParmVal( wing_id, "TECluster", "WingGeom", 1.0 );
        SetParmVal( wing_id, "LECluster", "WingGeom", 0.2 );
        
        const uint8 x = 1; // AR
        const uint8 t = 2; // Tip Clustering
        
        SetParmVal( wing_id, "Aspect", "XSec_1", m_halfAR[x] ); // Constant AR
        SetParmVal( wing_id, "OutCluster", "XSec_1", m_Tip_Clus[t] ); // Constant Tip Clustering
        
        Update();
        
        for ( uint8 u = 0; u < uint8(m_Tess_U.length()); u++ )
        {
            for ( uint8 w = 0; w < uint8(m_Tess_W.length()); w++ )
            {
                SetParmVal( wing_id, "SectTess_U", "XSec_1", m_Tess_U[u] );
                SetParmVal( wing_id, "Tess_W", "Shape", m_Tess_W[w] );

                Update();

                //==== Setup export filenames for UW Tess Study ====//
                string fname = "Hershey_U" + int(m_Tess_U[u]) + "_W" + int(m_Tess_W[w]) + ".vsp3";

                //==== Save Vehicle to File ====//
                string message = "-->Saving vehicle file to: " + fname + "\n";
                Print( message );
                WriteVSPFile( fname, SET_ALL );
                Print( "COMPLETE\n" );
            }
        }
        
        ClearVSPModel();
    }
    
    private void GenerateHersheyBarTCWings()
    {
        //==== Add Wing Geometry ====//
        string wing_id = AddGeom( "WING", "" );
        
        //==== Set Wing Section ====//
        SetDriverGroup( wing_id, 1, AR_WSECT_DRIVER, ROOTC_WSECT_DRIVER, TIPC_WSECT_DRIVER );
        
        Update();
        
        //==== Set NACA 0012 Airfoil and Common Parms 
        SetParmVal( wing_id, "ThickChord", "XSecCurve_0", 0.12 );
        SetParmVal( wing_id, "ThickChord", "XSecCurve_1", 0.12 );
        SetParmVal( wing_id, "Sweep", "XSec_1", 0 );
        SetParmVal( wing_id, "Root_Chord", "XSec_1", 1.0 );
        SetParmVal( wing_id, "Tip_Chord", "XSec_1", 1.0 );
        SetParmVal( wing_id, "TECluster", "WingGeom", 1.0 );
        SetParmVal( wing_id, "LECluster", "WingGeom", 0.2 );
        
        const uint8 x = 1; // AR
        const uint8 u = 1; // UTess
        const uint8 w = 1; // WTess
        
        SetParmVal( wing_id, "Aspect", "XSec_1", m_halfAR[x] ); // Constant AR
        SetParmVal( wing_id, "SectTess_U", "XSec_1", m_Tess_U[u] ); // Constant U Tess
        SetParmVal( wing_id, "Tess_W", "Shape", m_Tess_W[w] ); // Constant W Tess
        
        Update();
        
        for ( uint8 t = 0; t < uint8(m_Tip_Clus.length()); t++ )
        {
            SetParmVal( wing_id, "OutCluster", "XSec_1", m_Tip_Clus[t] );

            Update();

            //==== Setup export filenames for Tip Clustering Study ====//
            string fname = "Hershey_TC" + double(m_Tip_Clus[t]) + ".vsp3";

            //==== Save Vehicle to File ====//
            string message = "-->Saving vehicle file to: " + fname + "\n";
            Print( message );
            WriteVSPFile( fname, SET_ALL );
            Print( "COMPLETE\n" );
        }
        
        ClearVSPModel();
    }
    
    private void GenerateHersheyBarWakeWings()
    {
        //==== Add Wing Geometry ====//
        string wing_id = AddGeom( "WING", "" );
        
        //==== Set Wing Section ====//
        SetDriverGroup( wing_id, 1, AR_WSECT_DRIVER, ROOTC_WSECT_DRIVER, TIPC_WSECT_DRIVER );
        
        Update();
        
        //==== Set NACA 0012 Airfoil and Common Parms 
        SetParmVal( wing_id, "ThickChord", "XSecCurve_0", 0.12 );
        SetParmVal( wing_id, "ThickChord", "XSecCurve_1", 0.12 );
        SetParmVal( wing_id, "Sweep", "XSec_1", 0 );
        SetParmVal( wing_id, "Root_Chord", "XSec_1", 1.0 );
        SetParmVal( wing_id, "Tip_Chord", "XSec_1", 1.0 );
        SetParmVal( wing_id, "TECluster", "WingGeom", 1.0 );
        SetParmVal( wing_id, "LECluster", "WingGeom", 0.2 );
        
        const uint8 x = 1; // AR
        const uint8 u = 1; // UTess
        const uint8 w = 1; // WTess
        const uint8 t = 2; // Tip Clustering
        
        SetParmVal( wing_id, "Aspect", "XSec_1", m_halfAR[x] ); // Constant AR
        SetParmVal( wing_id, "SectTess_U", "XSec_1", m_Tess_U[u] ); // Constant U Tess
        SetParmVal( wing_id, "Tess_W", "Shape", m_Tess_W[w] ); // Constant W Tess
        SetParmVal( wing_id, "OutCluster", "XSec_1", m_Tip_Clus[t] ); // Constant Tip Clustering
        
        Update();
        
        for ( uint8 i = 0; i < uint8(m_WakeIter.length()); i++ )
        {
            //==== Setup export filenames for Wake Iteration Study ====//
            string fname = "Hershey_Wake" + int(m_WakeIter[i]) + ".vsp3";

            //==== Save Vehicle to File ====//
            string message = "-->Saving vehicle file to: " + fname + "\n";
            Print( message );
            WriteVSPFile( fname, SET_ALL );
            Print( "COMPLETE\n" );
        }
        
        ClearVSPModel();
    }
    
    private void GenerateHersheyBarAdvancedWings()
    {
        //==== Add Wing Geometry ====//
        string wing_id = AddGeom( "WING", "" );
        
        //==== Set Wing Section ====//
        SetDriverGroup( wing_id, 1, AR_WSECT_DRIVER, ROOTC_WSECT_DRIVER, TIPC_WSECT_DRIVER );
        
        Update();
        
        //==== Set NACA 0012 Airfoil and Common Parms 
        SetParmVal( wing_id, "ThickChord", "XSecCurve_0", 0.12 );
        SetParmVal( wing_id, "ThickChord", "XSecCurve_1", 0.12 );
        SetParmVal( wing_id, "Sweep", "XSec_1", 0 );
        SetParmVal( wing_id, "Root_Chord", "XSec_1", 1.0 );
        SetParmVal( wing_id, "Tip_Chord", "XSec_1", 1.0 );
        SetParmVal( wing_id, "TECluster", "WingGeom", 1.0 );
        SetParmVal( wing_id, "LECluster", "WingGeom", 0.2 );
        
        const uint8 x = 2; // AR
        const uint8 u = 1; // UTess
        const uint8 w = 1; // WTess
        const uint8 t = 2; // Tip Clustering
        
        SetParmVal( wing_id, "Aspect", "XSec_1", m_halfAR[x] ); // Constant AR
        SetParmVal( wing_id, "SectTess_U", "XSec_1", m_Tess_U[u] ); // Constant U Tess
        SetParmVal( wing_id, "Tess_W", "Shape", m_Tess_W[w] ); // Constant W Tess
        SetParmVal( wing_id, "OutCluster", "XSec_1", m_Tip_Clus[t] ); // Constant Tip Clustering
        
        Update();
        
        const uint8 num_case = 4;
        
        for ( uint8 i = 0; i < num_case; i++ )
        {
            //==== Setup export filenames for Wake Iteration Study ====//
            string fname = "Hershey_Advanced_" + int(i) + ".vsp3";

            //==== Save Vehicle to File ====//
            string message = "-->Saving vehicle file to: " + fname + "\n";
            Print( message );
            WriteVSPFile( fname, SET_ALL );
            Print( "COMPLETE\n" );
        }
        
        ClearVSPModel();
    }
    
    //============= Hershey Bar Wing Testing Functions ==============//
    
    private void TestHersheyBarARWings()
    {
        Print( string( "-> Begin HersheyBar AR Study:\n" ) );

        const uint8 num_AR = uint8(m_halfAR.length());

        const double C_bot_two = 1 + (pow(tan(0.0),2)/pow(b,2));
        
        const double alpha_0 = -20.0;
        const double alpha_f = 20.0;
        const double d_alpha = alpha_f - alpha_0;
        const double Vinf = 100;
        const double alpha_step = d_alpha/(m_AlphaNpts - 1);
        const uint8 alpha_mid_index = uint8((m_AlphaNpts - 1)/2.0);

        array<array<double>> alpha_vlm(num_AR), Cl_vlm(num_AR), Cl_approx(num_AR);
        array<double> Cl_alpha_vlm(num_AR), Lift_angle_vlm(num_AR), Lift_angle_theo(num_AR), AR(num_AR), C_ratio(num_AR), 
            Cl_alpha_theo( num_AR ), Lift_angle_pm(num_AR), Cl_alpha_pm(num_AR), Error_Cl_alpha_vlm(m_AlphaNpts);
        
        for ( uint8 x = 0; x < uint8(m_halfAR.length()); x++ )
        {
            //==== Open and test generated wings ====//
            string fname = "Hershey_AR" + int(2*m_halfAR[x]) + ".vsp3";
            string fname_res_vlm = "Hershey_AR" + int(2*m_halfAR[x]) + "_vlm_res.csv";
            string fname_res_pm = "Hershey_AR" + int(2*m_halfAR[x]) + "_pm_res.csv";

            Print( string( "Reading in file: " ), false );
            Print( fname );
            ReadVSPFile( fname ); // Sets VSP3 file name
            
            //==== Analysis: VSPAero VLM Sweep ====//
            Print( m_VSPSweepAnalysis );

            //==== Analysis: VSPAero Compute Geometry to Create Vortex Lattice DegenGeom File ====//
            Print( m_CompGeomAnalysis );

            // Set defaults
            SetAnalysisInputDefaults( m_CompGeomAnalysis );
            
            SetIntAnalysisInput(m_CompGeomAnalysis, "Symmetry", m_SymFlagVec, 0);

            // list inputs, type, and current values
            PrintAnalysisInputs( m_CompGeomAnalysis );

            // Execute
            Print( "\tExecuting..." );
            string compgeom_resid = ExecAnalysis( m_CompGeomAnalysis );
            Print( "COMPLETE" );

            // Get & Display Results
            PrintResults( compgeom_resid );

            //==== Analysis: VSPAero VLM Sweep ====//
            // Set defaults
            SetAnalysisInputDefaults(m_VSPSweepAnalysis);

            // Reference geometry set
            SetIntAnalysisInput(m_VSPSweepAnalysis, "GeomSet", m_GeomVec, 0);
            SetIntAnalysisInput(m_VSPSweepAnalysis, "RefFlag", m_RefFlagVec, 0);

            array< string > wid = FindGeomsWithName( "WingGeom" );
            SetStringAnalysisInput(m_VSPSweepAnalysis, "WingID", wid, 0);
            SetIntAnalysisInput(m_VSPSweepAnalysis, "Symmetry", m_SymFlagVec, 0);
            SetIntAnalysisInput(m_VSPSweepAnalysis, "WakeNumIter", m_WakeIterVec, 0);

            // Freestream Parameters
            array<double> AlphaStart(1, alpha_0);
            array<double> AlphaEnd(1, alpha_f);
            array<int> AlphaNpts(1, m_AlphaNpts);
            SetDoubleAnalysisInput(m_VSPSweepAnalysis, "AlphaStart", AlphaStart, 0);
            SetDoubleAnalysisInput(m_VSPSweepAnalysis, "AlphaEnd", AlphaEnd, 0);
            SetIntAnalysisInput(m_VSPSweepAnalysis, "AlphaNpts", AlphaNpts, 0);
            array<int> MachNpts(1, 1); // Start and end at 0.1
            SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachStart", m_MachVec, 0);
            SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachEnd", m_MachVec, 0);
            SetIntAnalysisInput(m_VSPSweepAnalysis, "MachNpts", MachNpts, 0);

            Update();

            // list inputs, type, and current values
            PrintAnalysisInputs( m_VSPSweepAnalysis );
            Print( "" );

            // Execute
            Print( "\tExecuting..." );
            string rid = ExecAnalysis( m_VSPSweepAnalysis );
            Print( "COMPLETE" );

            // Get & Display Results
            PrintResults( rid );
            WriteResultsCSVFile( rid, fname_res_vlm );
            
            // Get Result ID Vec (History and Load ResultIDs)
            array<string> rid_vec = GetStringResults( rid, "ResultsVec" );
            
            // Calculate Experimental and Theoretical Values
            // Fluid -Dynamic Lift pg 3-2
            // Method 1 of USAF DATCOM Section 1, page 1-7, and also NACA TN-3911)
            AR[x] = 2*m_halfAR[x];
            double C_top = 2*pi*AR[x];
            double C_bot_one_theo = (pow(AR[x],2)*pow(b,2))/pow(k_theo,2);
            double C_bot_theo = (2 + (sqrt((C_bot_one_theo*C_bot_two)+4)));
            Cl_alpha_theo[x] = (C_top/C_bot_theo)*(pi/180); // deg
            Lift_angle_theo[x] = 1/(Cl_alpha_theo[x]); // Cl to lift angle (deg)
            C_ratio[x] = 1/AR[x]; // AR to chord ratio
            
            if ( rid_vec.length() >= 1 )
            {
                array<double> alpha_res( m_AlphaNpts ), Cl_res( m_AlphaNpts ), Cl_approx_vec( m_AlphaNpts );
                
                // Get Result from Final Wake Iteration
                for ( uint8 i = 0; i < uint8(m_AlphaNpts); i++ )
                {
                    array<double> alpha_vec = GetDoubleResults( rid_vec[i], "Alpha" );
                    alpha_res[i] = alpha_vec[int(alpha_vec.length()) - 1];
                    
                    array<double> cl_vec = GetDoubleResults( rid_vec[i], "CL" );
                    Cl_res[i] = cl_vec[int(cl_vec.length()) - 1];
                    
                    Cl_approx_vec[i] = 2 * pi * sin( Deg2Rad( alpha_res[i] ) );
                }
                
                if ( x == 1 )
                {
                    for ( uint8 i = 0; i < uint8(m_AlphaNpts); i++ )
                    {
                        double Cl_alpha_res = 0;
                        
                        if ( i == 0 )
                        {
                            Cl_alpha_res = ((Cl_res[i+1] - Cl_res[i])/(alpha_res[i+1] - alpha_res[i]));
                        }
                        else if ( i == uint8(m_AlphaNpts) - 1 )
                        {
                            Cl_alpha_res = ((Cl_res[i] - Cl_res[i-1])/(alpha_res[i] - alpha_res[i-1]));
                        }
                        else // Central differencing
                        {
                            Cl_alpha_res = ((Cl_res[i+1] - Cl_res[i-1])/(alpha_res[i+1] - alpha_res[i-1]));
                        }
                        
                        Error_Cl_alpha_vlm[i] = (abs((Cl_alpha_res - Cl_alpha_theo[x])/Cl_alpha_theo[x]))*100;
                    }
                }
                
                // Evaluate Cl_alpha near alpha = 0 to avoid errors due to unmodeled stall characteristics
                Cl_alpha_vlm[x] = ((Cl_res[alpha_mid_index + 1] - Cl_res[alpha_mid_index])/alpha_step); 
                
                alpha_vlm[x] = alpha_res;
                Cl_vlm[x] = Cl_res;
                Cl_approx[x] = Cl_approx_vec;
                
                Lift_angle_vlm[x] = 1/(Cl_alpha_vlm[x]); // deg
            }
            
            //==== Analysis: VSPAero Panel Single ====//
            Print( m_VSPSweepAnalysis );
            
            //==== Analysis: VSPAero Compute Geometry to Create Vortex Lattice DegenGeom File ====//
            Print( m_CompGeomAnalysis );

            // Set defaults
            SetAnalysisInputDefaults( m_CompGeomAnalysis );
            
            array<int> panel_analysis(1, VSPAERO_ANALYSIS_METHOD::PANEL);
            SetIntAnalysisInput( m_CompGeomAnalysis, "AnalysisMethod", panel_analysis );
            
            SetIntAnalysisInput(m_CompGeomAnalysis, "Symmetry", m_SymFlagVec, 0);

            // list inputs, type, and current values
            PrintAnalysisInputs( m_CompGeomAnalysis );

            // Execute
            Print( "\tExecuting..." );
            compgeom_resid = ExecAnalysis( m_CompGeomAnalysis );
            Print( "COMPLETE" );

            // Get & Display Results
            PrintResults( compgeom_resid );
            
            //==== Analysis: VSPAero Panel Single ====//
            // Set defaults
            SetAnalysisInputDefaults(m_VSPSweepAnalysis);
            Print(m_VSPSweepAnalysis);

            // Reference geometry set
            SetIntAnalysisInput(m_VSPSweepAnalysis, "GeomSet", m_GeomVec, 0);
            SetIntAnalysisInput(m_VSPSweepAnalysis, "RefFlag", m_RefFlagVec, 0);
            SetStringAnalysisInput(m_VSPSweepAnalysis, "WingID", wid, 0);
            SetIntAnalysisInput(m_VSPSweepAnalysis, "WakeNumIter", m_WakeIterVec, 0);
            SetIntAnalysisInput( m_VSPSweepAnalysis, "AnalysisMethod", panel_analysis );
            SetIntAnalysisInput(m_VSPSweepAnalysis, "Symmetry", m_SymFlagVec, 0);
            
            // Freestream Parameters
            array<double> Alpha(1, 1.0);
            SetDoubleAnalysisInput(m_VSPSweepAnalysis, "AlphaStart", Alpha, 0);
            SetIntAnalysisInput(m_VSPSweepAnalysis, "AlphaNpts", AlphaNpts, 0);
            SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachStart", m_MachVec, 0);
            SetIntAnalysisInput(m_VSPSweepAnalysis, "MachNpts", MachNpts, 0);
            
            Update();

            // list inputs, type, and current values
            PrintAnalysisInputs( m_VSPSweepAnalysis );
            Print( "" );

            // Execute
            Print( "\tExecuting..." );
            rid = ExecAnalysis( m_VSPSweepAnalysis );
            Print( "COMPLETE" );

            // Get & Display Results
            PrintResults( rid );
            WriteResultsCSVFile( rid, fname_res_pm );
            
            // Get Result ID Vec (History and Load ResultIDs)
            rid_vec = GetStringResults( rid, "ResultsVec" );
            
            if ( rid_vec.length() > 0 )
            {
                // Get Result from Final Wake Iteration
                array<double> cl_vec = GetDoubleResults( rid_vec[0], "CL" );
                
                double Cl_pm = cl_vec[int(cl_vec.length()) - 1];
                Cl_alpha_pm[x] = Cl_pm; // deg (alpha = 1.0°)
                Lift_angle_pm[x] = 1/(Cl_alpha_pm[x]); // deg
            }

            ClearVSPModel();
        }

        // Hershey Bar -- C_L vs Alpha  for Various AR
        HTMLChart c1 = HTMLChart(); // Initialize Chart
        c1.m_ChartName = string("CLvA");
        c1.m_Series = string("{ 6: { pointSize: 0, color: 'gray', lineWidth: 3 } }");
        c1.m_Legend = string("'Alpha','AR: 5','AR: 10','AR: 15','AR: 25','AR: 40','AR: 60','2*pi'");
        c1.m_Title = string("Figure ") + m_FigCount + string(". Hershey Bar VLM: Cl vs Alpha for Various AR");
        m_FigCount++;
        c1.m_HAxisString = string("hAxis: { title: 'Alpha (°)', titleTextStyle: { bold: true } }");
        c1.m_VAxisString = string("vAxis: { title: 'Cl', titleTextStyle: { bold: true } }");
        
        array<string> data_str_vec1;
        for( uint8 i = 0; i < uint8(m_AlphaNpts); i++ )
        {
            string row = string("\t\t  [") + double( alpha_vlm[0][i] ) + string(",");

            for ( uint j = 0; j < num_AR; j++ )
            {
                row += Cl_vlm[j][i];
                row += string(",");
            }

            row += Cl_approx[0][i];

            if ( i == uint8(m_AlphaNpts) - 1 )
            {
                row += string("]\n");
            }
            else
            {
                row += string("],\n");
            }
            
            data_str_vec1.push_back( row );
        }
        
        c1.WriteChartHead( data_str_vec1 );
        m_HersheyARIDVec.push_back( c1.m_ChartName );

        // Hershey Bar: C_Lalpha vs AR for Various AR
        HTMLChart c2 = HTMLChart(); // Initialize Chart
        c2.m_ChartName = string("CLavAR");
        c2.m_Series = string("{ 2: { pointSize: 0,  color: 'gray', lineWidth: 3 } }");
        c2.m_Legend = string("'AR','VSPAERO VLM','VSPAERO Panel','LLT'");
        c2.m_Title = string("Figure ") + m_FigCount + string(". Hershey Bar: Cl_alpha vs AR");
        m_FigCount++;
        c2.m_HAxisString = string("hAxis: { title: 'AR', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }");
        c2.m_VAxisString = string("vAxis: { title: 'Cl_alpha (°)', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }");
        
        array<string> data_str_vec2;
        for( uint8 i = 0; i < num_AR; i++ )
        {
            string row = string("\t\t  [") + double( AR[i] ) + string(",") + double( Cl_alpha_vlm[i] ) + string(",") + double( Cl_alpha_pm[i] ) + 
                string(",") + double( Cl_alpha_theo[i] );

            if ( i == num_AR - 1 )
            {
                row += string("]\n");
            }
            else
            {
                row += string("],\n");
            }
            
            data_str_vec2.push_back( row );
        }
        
        c2.WriteChartHead( data_str_vec2 );
        m_HersheyARIDVec.push_back( c2.m_ChartName );
        
        // // Hershey Bar:  Lift Angle vs. Chord Ratio
        // HTMLChart c3 = HTMLChart(); // Initialize Chart
        // c3.m_ChartName = string("LAvCR");
        // c3.m_Legend = string("'1/AR','VSPAERO VLM','VSPAERO Panel','3D Theoretical Approximation'");
        // c3.m_Series = string("{ 2: { pointSize: 0, color: 'gray', lineWidth: 3 } }");
        // c3.m_Title = string("Hershey Bar: Lift Angle vs. Chord Ratio");
        // c3.m_HAxisString = string("hAxis: { title: '1/AR', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }");
        // c3.m_VAxisString = string("vAxis: { title: 'd_alpha/d_Cl (°)', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }");

        // array<string> data_str_vec3;
        // for( uint i = 0; i < num_AR; i++ )
        // {
            // string row = string("\t\t  [") + double( C_ratio[i] ) + string(",") + double( Lift_angle_vlm[i] ) + string(",") + double( Lift_angle_pm[i] ) + 
                // string(",")  + double( Lift_angle_theo[i] );

            // if ( i == num_AR - 1 )
            // {
                // row += string("]\n");
            // }
            // else
            // {
                // row += string("],\n");
            // }
            
            // data_str_vec3.push_back( row );
        // }
        
        // c3.WriteChartHead( data_str_vec3 );
        // m_HersheyARIDVec.push_back( c3.m_ChartName );
        
        // VLM: Cl_alpha % Error of VSPAERO/Theoretical Results vs. Alpha at a single AR
        HTMLChart c4 = HTMLChart(); // Initialize Chart
        c4.m_ChartName = string("HB_ClaErrorvAlpha");
        c4.m_Legend = string("'Alpha','% Error'");
        c4.m_Title = string("Figure ") + m_FigCount + string(". Hershey Bar VLM Cl_alpha Alpha Sensitivity: AR = ") + int(2*m_halfAR[1]); 
        m_FigCount++;
        c4.m_HAxisString = string("hAxis: { title: 'Alpha (°)', titleTextStyle: { bold: true } }");
        c4.m_VAxisString = string("vAxis: { title: 'Cl_alpha % Error', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }");
        
        array<string> data_str_vec4;
        for( uint8 i = 0; i < uint8(m_AlphaNpts); i++ )
        {
            string row = string("\t\t  [") + double( alpha_vlm[1][i] ) + string(",") + double( Error_Cl_alpha_vlm[i] );

            if ( i == uint8(m_AlphaNpts) - 1 )
            {
                row += string("]\n");
            }
            else
            {
                row += string("],\n");
            }
            
            data_str_vec4.push_back( row );
        }
        
        c4.WriteChartHead( data_str_vec4 );
        m_HersheyAlphaErrorIDVec.push_back( c4.m_ChartName );
    }
    
    private void TestHersheyBarUTessWings()
    {
        Print( string( "-> Begin Hershey Bar U Tesselation Study:\n" ) );
        
        const uint8 x = 1; // AR

        const uint8 numUTess = uint8(m_Tess_U.length());
        
        array<array<double>> span_loc_data(numUTess), cl_dist_data(numUTess), cd_dist_data(numUTess);
        
        for ( uint8 u = 0; u < numUTess; u++ )
        {
            string fname = "Hershey_U" + int(m_Tess_U[u]) + ".vsp3";
            string fname_res = "Hershey_U" + int(m_Tess_U[u]) + "_res.csv";
            
            //==== Open and test generated wings ====//
            Print( string( "Reading in file: " ), false );
            Print( fname );
            ReadVSPFile( fname ); // Sets VSP3 file name

            //==== Analysis: m_VSPSweepAnalysis ====//
            Print( m_VSPSweepAnalysis );

            //==== Analysis: VSPAero Compute Geometry to Create Vortex Lattice DegenGeom File ====//
            Print( m_CompGeomAnalysis );

            // Set defaults
            SetAnalysisInputDefaults( m_CompGeomAnalysis );
            
            SetIntAnalysisInput(m_CompGeomAnalysis, "Symmetry", m_SymFlagVec, 0);

            // list inputs, type, and current values
            PrintAnalysisInputs( m_CompGeomAnalysis );

            // Execute
            Print( "\tExecuting..." );
            string compgeom_resid = ExecAnalysis( m_CompGeomAnalysis );
            Print( "COMPLETE" );

            // Get & Display Results
            PrintResults( compgeom_resid );

            //==== Analysis: VSPAero Single Point ====//
            // Set defaults
            SetAnalysisInputDefaults(m_VSPSweepAnalysis);
            Print(m_VSPSweepAnalysis);

            // Reference geometry set
            SetIntAnalysisInput(m_VSPSweepAnalysis, "GeomSet", m_GeomVec, 0);
            SetIntAnalysisInput(m_VSPSweepAnalysis, "RefFlag", m_RefFlagVec, 0);
            SetIntAnalysisInput(m_VSPSweepAnalysis, "Symmetry", m_SymFlagVec, 0);

            array<string> wid = FindGeomsWithName( "WingGeom" );
            SetStringAnalysisInput(m_VSPSweepAnalysis, "WingID", wid, 0);

            // Freestream Parameters
            SetDoubleAnalysisInput(m_VSPSweepAnalysis, "AlphaStart", m_AlphaVec, 0);
            array<int> AlphaNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "AlphaNpts", AlphaNpts, 0);
            SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachStart", m_MachVec, 0);
            array<int> MachNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "MachNpts", MachNpts, 0);
            SetIntAnalysisInput(m_VSPSweepAnalysis, "WakeNumIter", m_WakeIterVec, 0);

            Update();

            // list inputs, type, and current values
            PrintAnalysisInputs( m_VSPSweepAnalysis );
            Print( "" );

            // Execute
            Print( "\tExecuting..." );
            string rid = ExecAnalysis( m_VSPSweepAnalysis );
            Print( "COMPLETE" );

            // Get & Display Results
            PrintResults( rid );
            WriteResultsCSVFile( rid, fname_res );

            // Get Load Result ID
            string load_rid = FindLatestResultsID( "VSPAERO_Load" );
            if ( load_rid != "" )
            {
                // Lift Distribution:
                span_loc_data[u] = GetDoubleResults( load_rid, "Yavg" );
                cl_dist_data[u] = GetDoubleResults( load_rid, "cl" );
                cd_dist_data[u] = GetDoubleResults( load_rid, "cd" );
            }
            
            ClearVSPModel();
        }
        
        // Compute theoretical lift and drag distributions using 100 points
        array<vec3d> cl_dist_theo = GetHersheyBarLiftDist( int(100), Deg2Rad(m_AlphaVec[0]), Vinf, (2*m_halfAR[x]), false );
        array<vec3d> cd_dist_theo = GetHersheyBarDragDist( int(100), Deg2Rad(m_AlphaVec[0]), Vinf, (2*m_halfAR[x]), false );
        
        // Const AR, WTess, and TC, vary UTess Lift Distribution Study
        HTMLChart c1 = HTMLChart(); // Initialize Chart
        c1.m_ChartName = string("UTess_LD");
        c1.m_Legend = string("'Span Location','U Tess: 5','U Tess: 12','U Tess: 20','U Tess: 41','LLT','AVL'");
        c1.m_Series = string("{ 4: { pointSize: 0, color: 'gray', lineWidth: 3 }, 5: { color: 'purple' } }");
        c1.m_Title = string("Figure ") + m_FigCount + string(". Hershey Bar Lift Distribution Span Tesselation (U Tess) Sensitivity");
        m_FigCount++;
        c1.m_HAxisString = string("hAxis: { title: 'Span Location (Y)', titleTextStyle: { bold: true } }");
        c1.m_VAxisString = string("vAxis: { title: 'Cl', titleTextStyle: { bold: true }, viewWindow: { min: 0.0, max: 0.15 } }");
        
        // Const AR, WTess, and TC, vary UTess Drag Distribution Study
        HTMLChart c2 = HTMLChart(); // Initialize Chart
        c2.m_ChartName = string("UTess_DD") ;
        c2.m_Legend = string("'Span Location','U Tess: 5','U Tess: 12','U Tess: 20','U Tess: 41','LLT','AVL'");
        c2.m_Series = string("{ 4: { pointSize: 0, color: 'gray', lineWidth: 3 }, 5: { color: 'purple' } }");
        c2.m_Title = string("Figure ") + m_FigCount + string(". Hershey Bar Drag Distribution Span Tesselation (U Tess) Sensitivity");
        m_FigCount++;
        c2.m_HAxisString = string("hAxis: { title: 'Span Location (Y)', titleTextStyle: { bold: true } }");
        c2.m_VAxisString = string("vAxis: { title: 'Cd', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }"); // TODO: set max
        
        // All Cl and Cd distributions must be plotted against the x-vector that matches the appropriate data
        array<string> data_str_vec1;
        array<string> data_str_vec2;
        for( uint i = 0; i < uint(span_loc_data[0].length()); i++ )
        {
            string row1 = string("\t\t  [") + double( span_loc_data[0][i] ) + string(",") + double( cl_dist_data[0][i] ) + 
                string(",null,null,null,null,null],\n");

            string row2 = string("\t\t  [") + double( span_loc_data[0][i] ) + string(",") + double( cd_dist_data[0][i] ) + 
                string(",null,null,null,null,null],\n");
            
            data_str_vec1.push_back( row1 );
            data_str_vec2.push_back( row2 );
        }
        
        for( uint i = 0; i < uint(span_loc_data[1].length()); i++ )
        {
            string row1 = string("\t\t  [") + double( span_loc_data[1][i] ) + string(",null,") + double( cl_dist_data[1][i] ) + 
                string(",null,null,null,null],\n");
            
            string row2 = string("\t\t  [") + double( span_loc_data[1][i] ) + string(",null,") + double( cd_dist_data[1][i] ) + 
                string(",null,null,null,null],\n");
            
            data_str_vec1.push_back( row1 );
            data_str_vec2.push_back( row2 );
        }
        
        for( uint i = 0; i < uint(span_loc_data[2].length()); i++ )
        {
            string row1 = string("\t\t  [") + double( span_loc_data[2][i] ) + string(",null,null,") + 
            double( cl_dist_data[2][i] ) + string(",null,null,null],\n");
            
            string row2 = string("\t\t  [") + double( span_loc_data[2][i] ) + string(",null,null,") + 
            double( cd_dist_data[2][i] ) + string(",null,null,null],\n");

            data_str_vec1.push_back( row1 );
            data_str_vec2.push_back( row2 );
        }
        
        for( uint i = 0; i < uint(span_loc_data[3].length()); i++ )
        {
            string row1 = string("\t\t  [") + double( span_loc_data[3][i] ) + string(",null,null,null,") + 
            double( cl_dist_data[3][i] ) + string(",null,null],\n");
            
            string row2 = string("\t\t  [") + double( span_loc_data[3][i] ) + string(",null,null,null,") + 
            double( cd_dist_data[3][i] ) + string(",null,null],\n");
            
            data_str_vec1.push_back( row1 );
            data_str_vec2.push_back( row2 );
        }
        
        for( uint i = 0; i < uint(cl_dist_theo.length()); i++ )
        {
            string row1 = string("\t\t  [") + double( cl_dist_theo[i][0] ) + string(",null,null,null,null,") + 
                double( cl_dist_theo[i][1] ) + string(",null],\n");
            
            string row2 = string("\t\t  [") + double( cd_dist_theo[i][0] ) + string(",null,null,null,null,") + 
                double( cd_dist_theo[i][1] ) + string(",null],\n");
            
            data_str_vec1.push_back( row1 );
            data_str_vec2.push_back( row2 );
        }
        
        for( uint i = 0; i < uint(m_AR10_Y_Cl_Cd_vec.length()); i++ )
        {
            string row1 = string("\t\t  [") + double( m_AR10_Y_Cl_Cd_vec[i][0] ) + string(",null,null,null,null,null,") + 
                double( m_AR10_Y_Cl_Cd_vec[i][1] );
            
            string row2 = string("\t\t  [") + double( m_AR10_Y_Cl_Cd_vec[i][0] ) + string(",null,null,null,null,null,") + 
                double( m_AR10_Y_Cl_Cd_vec[i][2] );
            
            if ( i == m_AR10_Y_Cl_Cd_vec.length() - 1 )
            {
                row1 += string("]\n");
                row2 += string("]\n");
            }
            else
            {
                row1 += string("],\n");
                row2 += string("],\n");
            }
            
            data_str_vec1.push_back( row1 );
            data_str_vec2.push_back( row2 );
        }

        c1.WriteChartHead( data_str_vec1 );
        m_HersheyLDUTessIDVec.push_back( c1.m_ChartName );
        
        c2.WriteChartHead( data_str_vec2 );
        m_HersheyDDUTessIDVec.push_back( c2.m_ChartName );
    }
    
    private void TestHersheyBarWTessWings()
    {
        Print( string( "-> Begin Hershey Bar W Tesselation Study:\n" ) );
        
        const uint8 x = 1; // AR

        const uint8 numWTess = uint8(m_Tess_W.length());
        
        array<array<double>> span_loc_data(numWTess), cl_dist_data(numWTess), cd_dist_data(numWTess);
        
        for ( uint8 w = 0; w < numWTess; w++ )
        {
            string fname = "Hershey_W" + int(m_Tess_W[w]) + ".vsp3";
            string fname_res = "Hershey_W" + int(m_Tess_W[w]) + "_res.csv";
            
            //==== Open and test generated wings ====//
            Print( string( "Reading in file: " ), false );
            Print( fname );
            ReadVSPFile( fname ); // Sets VSP3 file name

            //==== Analysis: m_VSPSweepAnalysis ====//
            Print( m_VSPSweepAnalysis );

            //==== Analysis: VSPAero Compute Geometry to Create Vortex Lattice DegenGeom File ====//
            Print( m_CompGeomAnalysis );

            // Set defaults
            SetAnalysisInputDefaults( m_CompGeomAnalysis );
            
            SetIntAnalysisInput(m_CompGeomAnalysis, "Symmetry", m_SymFlagVec, 0);

            // list inputs, type, and current values
            PrintAnalysisInputs( m_CompGeomAnalysis );

            // Execute
            Print( "\tExecuting..." );
            string compgeom_resid = ExecAnalysis( m_CompGeomAnalysis );
            Print( "COMPLETE" );

            // Get & Display Results
            PrintResults( compgeom_resid );

            //==== Analysis: VSPAero Single Point ====//
            // Set defaults
            SetAnalysisInputDefaults(m_VSPSweepAnalysis);
            Print(m_VSPSweepAnalysis);

            // Reference geometry set
            SetIntAnalysisInput(m_VSPSweepAnalysis, "GeomSet", m_GeomVec, 0);
            SetIntAnalysisInput(m_VSPSweepAnalysis, "RefFlag", m_RefFlagVec, 0);
            SetIntAnalysisInput(m_VSPSweepAnalysis, "Symmetry", m_SymFlagVec, 0);

            array<string> wid = FindGeomsWithName( "WingGeom" );
            SetStringAnalysisInput(m_VSPSweepAnalysis, "WingID", wid, 0);

            // Freestream Parameters
            SetDoubleAnalysisInput(m_VSPSweepAnalysis, "AlphaStart", m_AlphaVec, 0);
            array<int> AlphaNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "AlphaNpts", AlphaNpts, 0);
            SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachStart", m_MachVec, 0);
            array<int> MachNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "MachNpts", MachNpts, 0);
            SetIntAnalysisInput(m_VSPSweepAnalysis, "WakeNumIter", m_WakeIterVec, 0);

            Update();

            // list inputs, type, and current values
            PrintAnalysisInputs( m_VSPSweepAnalysis );
            Print( "" );

            // Execute
            Print( "\tExecuting..." );
            string rid = ExecAnalysis( m_VSPSweepAnalysis );
            Print( "COMPLETE" );

            // Get & Display Results
            PrintResults( rid );
            WriteResultsCSVFile( rid, fname_res );

            // Get Load Result ID
            string load_rid = FindLatestResultsID( "VSPAERO_Load" );
            if ( load_rid != "" )
            {
                // Lift Distribution:
                span_loc_data[w] = GetDoubleResults( load_rid, "Yavg" );
                cl_dist_data[w] = GetDoubleResults( load_rid, "cl" );
                cd_dist_data[w] = GetDoubleResults( load_rid, "cd" );
            }
            
            ClearVSPModel();
        }

        // Compute theoretical lift and drag distributions using 100 points
        array<vec3d> cl_dist_theo = GetHersheyBarLiftDist( int(100), Deg2Rad(m_AlphaVec[0]), Vinf, (2*m_halfAR[x]), false );
        array<vec3d> cd_dist_theo = GetHersheyBarDragDist( int(100), Deg2Rad(m_AlphaVec[0]), Vinf, (2*m_halfAR[x]), false );
        
        // Const AR, UTess, and TC, vary WTess Lift Distribution Study
        HTMLChart c1 = HTMLChart(); // Initialize Chart
        c1.m_ChartName = string("WTess_LD");
        c1.m_Legend = string("'Span Location','W Tess: 9','W Tess: 17','W Tess: 29','W Tess: 51','LLT','AVL'");
        c1.m_Series = string("{ 4: { pointSize: 0, color: 'gray', lineWidth: 3 }, 5: { color: 'purple' } }");
        c1.m_Title = string("Figure ") + m_FigCount + string(". Hershey Bar Lift Distribution Chord Tesselation (W Tess) Sensitivity");
        m_FigCount++;
        c1.m_HAxisString = string("hAxis: { title: 'Span Location (Y)', titleTextStyle: { bold: true } }");
        c1.m_VAxisString = string("vAxis: { title: 'Cl', titleTextStyle: { bold: true }, viewWindow: { min: 0.0, max: 0.15 } }");
        
        // Const AR, UTess, and TC, vary WTess Lift Distribution Study
        HTMLChart c2 = HTMLChart(); // Initialize Chart
        c2.m_ChartName = string("WTess_DD");
        c2.m_Legend = string("'Span Location','W Tess: 9','W Tess: 17','W Tess: 29','W Tess: 51','LLT','AVL'");
        c2.m_Series = string("{ 4: { pointSize: 0, color: 'gray', lineWidth: 3 }, 5: { color: 'purple' } }");
        c2.m_Title = string("Figure ") + m_FigCount + string(". Hershey Bar Drag Distribution Chord Tesselation (W Tess) Sensitivity");
        m_FigCount++;
        c2.m_HAxisString = string("hAxis: { title: 'Span Location (Y)', titleTextStyle: { bold: true } }");
        c2.m_VAxisString = string("vAxis: { title: 'Cd', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }"); // TODO: set max
        
        // All Cl and Cd distributions must be plotted against the x-vector that matches the appropriate data
        array<string> data_str_vec1, data_str_vec2;
        for( uint i = 0; i < uint(span_loc_data[0].length()); i++ )
        {
            string row1 = string("\t\t  [") + double( span_loc_data[0][i] ) + string(",") + double( cl_dist_data[0][i] ) + 
                string(",null,null,null,null,null],\n");
                
            string row2 = string("\t\t  [") + double( span_loc_data[0][i] ) + string(",") + double( cd_dist_data[0][i] ) + 
                string(",null,null,null,null,null],\n");

            data_str_vec1.push_back( row1 );
            data_str_vec2.push_back( row2 );
        }
        
        for( uint i = 0; i < uint(span_loc_data[1].length()); i++ )
        {
            string row1 = string("\t\t  [") + double( span_loc_data[1][i] ) + string(",null,") + double( cl_dist_data[1][i] ) + 
                string(",null,null,null,null],\n");
                
            string row2 = string("\t\t  [") + double( span_loc_data[1][i] ) + string(",null,") + double( cd_dist_data[1][i] ) + 
                string(",null,null,null,null],\n");
            
            data_str_vec1.push_back( row1 );
            data_str_vec2.push_back( row2 );
        }
        
        for( uint i = 0; i < uint(span_loc_data[2].length()); i++ )
        {
            string row1 = string("\t\t  [") + double( span_loc_data[2][i] ) + string(",null,null,") + 
                double( cl_dist_data[2][i] )+ string(",null,null,null],\n");
                
            string row2 = string("\t\t  [") + double( span_loc_data[2][i] ) + string(",null,null,") + 
                double( cd_dist_data[2][i] )+ string(",null,null,null],\n");

            data_str_vec1.push_back( row1 );
            data_str_vec2.push_back( row2 );
        }
        
        for( uint i = 0; i < uint(span_loc_data[3].length()); i++ )
        {
            string row1 = string("\t\t  [") + double( span_loc_data[3][i] ) + string(",null,null,null,") + 
                double( cl_dist_data[3][i] ) + string(",null,null],\n");
                
            string row2 = string("\t\t  [") + double( span_loc_data[3][i] ) + string(",null,null,null,") + 
                double( cd_dist_data[3][i] ) + string(",null,null],\n");
            
            data_str_vec1.push_back( row1 );
            data_str_vec2.push_back( row2 );
        }
        
        for( uint i = 0; i < uint(cl_dist_theo.length()); i++ )
        {
            string row1 = string("\t\t  [") + double( cl_dist_theo[i][0] ) + string(",null,null,null,null,") + 
                double( cl_dist_theo[i][1] ) + string(",null],\n");
            
            string row2 = string("\t\t  [") + double( cd_dist_theo[i][0] ) + string(",null,null,null,null,") + 
                double( cd_dist_theo[i][1] ) + string(",null],\n");
            
            data_str_vec1.push_back( row1 );
            data_str_vec2.push_back( row2 );
        }
        
        for( uint i = 0; i < uint(m_AR10_Y_Cl_Cd_vec.length()); i++ )
        {
            string row1 = string("\t\t  [") + double( m_AR10_Y_Cl_Cd_vec[i][0] ) + string(",null,null,null,null,null,") + 
                double( m_AR10_Y_Cl_Cd_vec[i][1] );
            
            string row2 = string("\t\t  [") + double( m_AR10_Y_Cl_Cd_vec[i][0] ) + string(",null,null,null,null,null,") + 
                double( m_AR10_Y_Cl_Cd_vec[i][2] );
            
            if ( i == m_AR10_Y_Cl_Cd_vec.length() - 1 )
            {
                row1 += string("]\n");
                row2 += string("]\n");
            }
            else
            {
                row1 += string("],\n");
                row2 += string("],\n");
            }
            
            data_str_vec1.push_back( row1 );
            data_str_vec2.push_back( row2 );
        }
        
        c1.WriteChartHead( data_str_vec1 );
        m_HersheyLDWTessIDVec.push_back( c1.m_ChartName );
        
        c2.WriteChartHead( data_str_vec2 );
        m_HersheyDDWTessIDVec.push_back( c2.m_ChartName );
    }
    
    private void TestHersheyBarUWTessWings()
    {
        Print( string( "-> Begin Hershey Bar U and W Tesselation Study:\n" ) );
        
        const uint8 x = 1; // AR
        
        const uint8 numUTess = uint8(m_Tess_U.length());
        const uint8 numWTess = uint8(m_Tess_W.length());
        
        array<array<double>> Error_Cla(numUTess), Error_Cl(numUTess), Exe_Time(numUTess); // index 0: UTess, index 0: WTess
        
        // Calculate Experimental and Theoretical Values
        // Fluid -Dynamic Lift pg 3-2
        // Method 1 of USAF DATCOM Section 1, page 1-7, and also NACA TN-3911)
        const double C_bot_two = 1 + (pow(tan(0.0),2)/pow(b,2));
        const double AR = 2*m_halfAR[x];
        const double C_top = 2*pi*AR;
        const double C_bot_one_theo = (pow(AR,2)*pow(b,2))/pow(k_theo,2);
        const double C_bot_theo = (2 + (sqrt((C_bot_one_theo*C_bot_two)+4)));
        const double Cl_alpha_theo = (C_top/C_bot_theo)*(pi/180); // deg
        const double Lift_angle_theo = 1/(Cl_alpha_theo); // Cl to lift angle (deg)
        
        for ( uint8 u = 0; u < numUTess; u++ )
        {
            Error_Cla[u].resize(numWTess);
            Error_Cl[u].resize(numWTess);
            Exe_Time[u].resize(numWTess);
            
            for ( uint8 w = 0; w < numWTess; w++ )
            {
                string fname = "Hershey_U" + int(m_Tess_U[u]) + "_W" + int(m_Tess_W[w]) + ".vsp3";
                string fname_res = "Hershey_U" + int(m_Tess_U[u]) + "_W" + int(m_Tess_W[w])+ "_res.csv";
                
                //==== Open and test generated wings ====//
                Print( string( "Reading in file: " ), false );
                Print( fname );
                ReadVSPFile( fname ); // Sets VSP3 file name

                //==== Analysis: m_VSPSweepAnalysis ====//
                Print( m_VSPSweepAnalysis );

                //==== Analysis: VSPAero Compute Geometry to Create Vortex Lattice DegenGeom File ====//
                Print( m_CompGeomAnalysis );

                // Set defaults
                SetAnalysisInputDefaults( m_CompGeomAnalysis );
                
                SetIntAnalysisInput(m_CompGeomAnalysis, "Symmetry", m_SymFlagVec, 0);

                // list inputs, type, and current values
                PrintAnalysisInputs( m_CompGeomAnalysis );

                // Execute
                Print( "\tExecuting..." );
                string compgeom_resid = ExecAnalysis( m_CompGeomAnalysis );
                Print( "COMPLETE" );

                // Get & Display Results
                PrintResults( compgeom_resid );

                //==== Analysis: VSPAero Single Point ====//
                // Set defaults
                SetAnalysisInputDefaults(m_VSPSweepAnalysis);
                Print(m_VSPSweepAnalysis);

                // Reference geometry set
                SetIntAnalysisInput(m_VSPSweepAnalysis, "GeomSet", m_GeomVec, 0);
                SetIntAnalysisInput(m_VSPSweepAnalysis, "RefFlag", m_RefFlagVec, 0);
                SetIntAnalysisInput(m_VSPSweepAnalysis, "Symmetry", m_SymFlagVec, 0);

                array<string> wid = FindGeomsWithName( "WingGeom" );
                SetStringAnalysisInput(m_VSPSweepAnalysis, "WingID", wid, 0);

                // Freestream Parameters
                SetDoubleAnalysisInput(m_VSPSweepAnalysis, "AlphaStart", m_AlphaVec, 0);
                array<int> AlphaNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "AlphaNpts", AlphaNpts, 0);
                SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachStart", m_MachVec, 0);
                array<int> MachNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "MachNpts", MachNpts, 0);
                SetIntAnalysisInput(m_VSPSweepAnalysis, "WakeNumIter", m_WakeIterVec, 0);

                Update();

                // list inputs, type, and current values
                PrintAnalysisInputs( m_VSPSweepAnalysis );
                Print( "" );

                // Execute
                Print( "\tExecuting..." );
                string rid = ExecAnalysis( m_VSPSweepAnalysis );
                Print( "COMPLETE" );

                // Get & Display Results
                PrintResults( rid );
                WriteResultsCSVFile( rid, fname_res );
                
                // Get Result ID Vec (History and Load ResultIDs)
                array<string> rid_vec = GetStringResults( rid, "ResultsVec" );
                if ( rid_vec.length() > 0 )
                {
                    // Get History Results (rid_vec[0]) from Final Wake Iteration in History Result
                    array<double> cl_vec = GetDoubleResults( rid_vec[0], "CL" );
                    double Cl_alpha_vsp = cl_vec[int(cl_vec.length()) - 1]; // alpha = 1.0 deg
                    
                    Error_Cla[u][w] = (abs((Cl_alpha_vsp - Cl_alpha_theo)/Cl_alpha_theo))*100;
                }
                
                array<double> time_vec = GetDoubleResults( rid, "Analysis_Duration_Sec" );
                
                if ( time_vec.size() > 0 )
                {
                    Exe_Time[u][w] = time_vec[0];
                }
                
                ClearVSPModel();
            }
        }
        
        // Hershey Bar VLM: % Dif of C_l\alpha using Lifting Line Theory 
        
        // Chord Tess
        HTMLChart c1 = HTMLChart(); // Initialize Chart
        c1.m_ChartName = string("ChordError");
        c1.m_Legend = string("'Chord Tesselation (W Tess)','U Tess: 5','U Tess: 12','U Tess: 20','U Tess: 41'");
        c1.m_Title = string("Figure ") + m_FigCount + string(". Hershey Bar VLM Cl_alpha Span Tesselation (U Tess) Sensitivity");
        m_FigCount++;
        c1.m_HAxisString = string("hAxis: { title: 'Chord Tesselation (W Tess)', titleTextStyle: { bold: true } }");
        c1.m_VAxisString = string("vAxis: { title: 'Cl_alpha % Error', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }");
        
        array<string> data_str_vec1;
        for( uint8 w = 0; w < numWTess; w++ )
        {
            string row = string("\t\t  [") + double( m_Tess_W[w] ) + string(",") + double( Error_Cla[0][w] ) + 
                string(",") + double( Error_Cla[1][w] ) + string(",") + double( Error_Cla[2][w] ) + string(",") + 
                double( Error_Cla[3][w] );

            if ( w == numWTess - 1 )
            {
                row += string("]\n");
            }
            else
            {
                row += string("],\n");
            }
            
            data_str_vec1.push_back( row );
        }
        
        c1.WriteChartHead( data_str_vec1 );
        m_HersheyTessIDVec.push_back( c1.m_ChartName );
        
        // Span Tess
        HTMLChart c2 = HTMLChart(); // Initialize Chart
        c2.m_ChartName = string("SpanError");
        c2.m_Legend = string("'Span Tesselation (U Tess)','W Tess: 9','W Tess: 17','W Tess: 29','W Tess: 51'");
        c2.m_Title = string("Figure ") + m_FigCount + string(". Hershey Bar VLM Cl_alpha Chord Tesselation (W Tess) Sensitivity");
        m_FigCount++;
        c2.m_HAxisString = string("hAxis: { title: 'Span Tesselation (U Tess)', titleTextStyle: { bold: true } }");
        c2.m_VAxisString = string("vAxis: { title: 'Cl_alpha % Error', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }");
        
        array<string> data_str_vec2;
        for( uint8 u = 0; u < numUTess; u++ )
        {
            string row = string("\t\t  [") + double( m_Tess_U[u] ) + string(",") + double( Error_Cla[u][0] ) + 
                string(",") + double( Error_Cla[u][1] ) + string(",") + double( Error_Cla[u][2] ) + string(",") + 
                double( Error_Cla[u][3] );

            if ( u == numUTess - 1 )
            {
                row += string("]\n");
            }
            else
            {
                row += string("],\n");
            }
            
            data_str_vec2.push_back( row );
        }
        
        c2.WriteChartHead( data_str_vec2 );
        m_HersheyTessIDVec.push_back( c2.m_ChartName );
        
        // Execution Time

        // Chord Tess
        HTMLChart c3 = HTMLChart(); // Initialize Chart
        c3.m_ChartName = string("ChordTime");
        c3.m_Legend = string("'Chord Tesselation (W Tess)','U Tess: 5','U Tess: 12','U Tess: 20','U Tess: 41'");
        c3.m_Title = string("Figure ") + m_FigCount + string(". Hershey Bar VLM Execution Time Span Tesselation (U Tess) Sensitivity");
        m_FigCount++;
        c3.m_HAxisString = string("hAxis: { title: 'Chord Tesselation (W Tess)', titleTextStyle: { bold: true } }");
        c3.m_VAxisString = string("vAxis: { title: 'Time (sec)', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }");
        
        array<string> data_str_vec3;
        for( uint8 w = 0; w < numWTess; w++ )
        {
            string row = string("\t\t  [") + double( m_Tess_W[w] ) + string(",") + double( Exe_Time[0][w] ) + 
                string(",") + double( Exe_Time[1][w] ) + string(",") + double( Exe_Time[2][w] ) + string(",") + 
                double( Exe_Time[3][w] );

            if ( w == numWTess - 1 )
            {
                row += string("]\n");
            }
            else
            {
                row += string("],\n");
            }
            
            data_str_vec3.push_back( row );
        }
        
        c3.WriteChartHead( data_str_vec3 );
        m_HersheyTimeIDVec.push_back( c3.m_ChartName );

        // Span Tess
        HTMLChart c4 = HTMLChart(); // Initialize Chart
        c4.m_ChartName = string("SpanTime");
        c4.m_Legend = string("'Span Tesselation (U Tess)','W Tess: 9','W Tess: 17','W Tess: 29','W Tess: 51'");
        c4.m_Title = string("Figure ") + m_FigCount + string(". Hershey Bar VLM Execution Time Chord Tesselation (W Tess) Sensitivity");
        m_FigCount++;
        c4.m_HAxisString = string("hAxis: { title: 'Span Tesselation (U Tess)', titleTextStyle: { bold: true } }");
        c4.m_VAxisString = string("vAxis: { title: 'Time (sec)', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }");
        
        array<string> data_str_vec4;
        for( uint8 u = 0; u < numUTess; u++ )
        {
            string row = string("\t\t  [") + double( m_Tess_U[u] ) + string(",") + double( Exe_Time[u][0] ) + 
                string(",") + double( Exe_Time[u][1] ) + string(",") + double( Exe_Time[u][2] ) + string(",") + 
                double( Exe_Time[u][3] );

            if ( u == numUTess - 1 )
            {
                row += string("]\n");
            }
            else
            {
                row += string("],\n");
            }
            
            data_str_vec4.push_back( row );
        }
        
        c4.WriteChartHead( data_str_vec4 );
        m_HersheyTimeIDVec.push_back( c4.m_ChartName );
    }
    
    private void TestHersheyBarTCWings()
    {
        Print( string( "-> Begin Hershey Bar Tip Clustering Study:\n" ) );
        
        const uint8 num_TC = uint8(m_Tip_Clus.length());

        const uint8 x = 1; // AR
        
        array<array<double>> span_loc_data(num_TC), cl_dist_data(num_TC), cd_dist_data(num_TC);

        for ( uint8 t = 0; t < num_TC; t++ )
        {
            string fname = "Hershey_TC" + double(m_Tip_Clus[t]) + ".vsp3";
            string fname_res = "Hershey_TC" + double(m_Tip_Clus[t]) + "_res.csv";
            
            //==== Open and test generated wings ====//
            Print( string( "Reading in file: " ), false );
            Print( fname );
            ReadVSPFile( fname ); // Sets VSP3 file name

            //==== Analysis: m_VSPSweepAnalysis ====//
            Print( m_VSPSweepAnalysis );

            //==== Analysis: VSPAero Compute Geometry to Create Vortex Lattice DegenGeom File ====//
            Print( m_CompGeomAnalysis );

            // Set defaults
            SetAnalysisInputDefaults( m_CompGeomAnalysis );
            
            SetIntAnalysisInput(m_CompGeomAnalysis, "Symmetry", m_SymFlagVec, 0);

            // list inputs, type, and current values
            PrintAnalysisInputs( m_CompGeomAnalysis );

            // Execute
            Print( "\tExecuting..." );
            string compgeom_resid = ExecAnalysis( m_CompGeomAnalysis );
            Print( "COMPLETE" );

            // Get & Display Results
            PrintResults( compgeom_resid );

            //==== Analysis: VSPAero Single Point ====//
            // Set defaults
            SetAnalysisInputDefaults(m_VSPSweepAnalysis);
            Print(m_VSPSweepAnalysis);

            // Reference geometry set
            SetIntAnalysisInput(m_VSPSweepAnalysis, "GeomSet", m_GeomVec, 0);
            SetIntAnalysisInput(m_VSPSweepAnalysis, "RefFlag", m_RefFlagVec, 0);
            SetIntAnalysisInput(m_VSPSweepAnalysis, "Symmetry", m_SymFlagVec, 0);

            array<string> wid = FindGeomsWithName( "WingGeom" );
            SetStringAnalysisInput(m_VSPSweepAnalysis, "WingID", wid, 0);

            // Freestream Parameters
            SetDoubleAnalysisInput(m_VSPSweepAnalysis, "AlphaStart", m_AlphaVec, 0);
            array<int> AlphaNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "AlphaNpts", AlphaNpts, 0);
            SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachStart", m_MachVec, 0);
            array<int> MachNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "MachNpts", MachNpts, 0);
            SetIntAnalysisInput(m_VSPSweepAnalysis, "WakeNumIter", m_WakeIterVec, 0);

            Update();

            // list inputs, type, and current values
            PrintAnalysisInputs( m_VSPSweepAnalysis );
            Print( "" );

            // Execute
            Print( "\tExecuting..." );
            string rid = ExecAnalysis( m_VSPSweepAnalysis );
            Print( "COMPLETE" );

            // Get & Display Results
            PrintResults( rid );
            WriteResultsCSVFile( rid, fname_res );

            // Get Load Result ID
            string load_rid = FindLatestResultsID( "VSPAERO_Load" );
            if ( load_rid != "" )
            {
                // Lift Distribution:
                span_loc_data[t] = GetDoubleResults( load_rid, "Yavg" );
                cl_dist_data[t] = GetDoubleResults( load_rid, "cl" );
                cd_dist_data[t] = GetDoubleResults( load_rid, "cd" );
            }
            
            ClearVSPModel();
        }

        // Compute theoretical lift and drag distributions
        // array<vec3d> cl_dist_theo = GetHersheyBarLiftDist( int(span_loc_data[0].length()), Deg2Rad(m_AlphaVec[0]), Vinf, (2*m_halfAR[x]), false );
        array<vec3d> cl_dist_theo = GetHersheyBarLiftDist( int(100), Deg2Rad(m_AlphaVec[0]), Vinf, (2*m_halfAR[x]), false );
        
        HTMLChart c1 = HTMLChart(); // Initialize Chart
        c1.m_ChartName = string("TC_LD") ;
        c1.m_Legend = string("'Span Location','TC: 0.1','TC: 0.5','TC: 1.0','LLT','AVL'");
        c1.m_Series = string("{ 3: { pointSize: 0, color: 'gray', lineWidth: 3 }, 4: { color: 'purple' } }");
        c1.m_Title = string("Figure ") + m_FigCount + string(". Hershey Bar Lift Distribution Tip Clustering Sensitivity");
        m_FigCount++;
        c1.m_HAxisString = string("hAxis: { title: 'Span Location (Y)', titleTextStyle: { bold: true } }");
        c1.m_VAxisString = string("vAxis: { title: 'Cl', titleTextStyle: { bold: true }, viewWindow: { min: 0.0, max: 0.15 } }");
        
        // All load and Cl distributions must be plotted against the x-vector that matches the appropriate data
        array<string> data_str_vec1;
        for( uint i = 0; i < uint(span_loc_data[0].length()); i++ )
        {
            string row = string("\t\t  [") + double( span_loc_data[0][i] ) + string(",") + double( cl_dist_data[0][i] ) + 
                string(",null,null,null,null],\n");

            data_str_vec1.push_back( row );
        }
        
        for( uint i = 0; i < uint(span_loc_data[1].length()); i++ )
        {
            string row = string("\t\t  [") + double( span_loc_data[1][i] ) + string(",null,") + double( cl_dist_data[1][i] ) + 
                string(",null,null,null],\n");
            
            data_str_vec1.push_back( row );
        }
        
        for( uint i = 0; i < uint(span_loc_data[2].length()); i++ )
        {
            string row = string("\t\t  [") + double( span_loc_data[2][i] ) + string(",null,null,") + 
                double( cl_dist_data[2][i] ) + string(",null,null],\n");

            data_str_vec1.push_back( row );
        }
        
        for( uint i = 0; i < uint(cl_dist_theo.length()); i++ )
        {
            string row = string("\t\t  [") + double( cl_dist_theo[i][0] ) + string(",null,null,null,") + 
                double( cl_dist_theo[i][1] ) + string(",null],\n");

            data_str_vec1.push_back( row );
        }
        
        for( uint i = 0; i < uint(m_AR10_Y_Cl_Cd_vec.length()); i++ )
        {
            string row = string("\t\t  [") + double( m_AR10_Y_Cl_Cd_vec[i][0] ) + string(",null,null,null,null,") + 
                double( m_AR10_Y_Cl_Cd_vec[i][1] );
            
            if ( i == m_AR10_Y_Cl_Cd_vec.length() - 1 )
            {
                row += string("]\n");
            }
            else
            {
                row += string("],\n");
            }
            
            data_str_vec1.push_back( row );
        }
        
        c1.WriteChartHead( data_str_vec1 );
        m_HersheyLDTCIDVec.push_back( c1.m_ChartName );
    }
    
    private void TestHersheyBarWakeWings()
    {
        Print( string( "-> Begin Hershey Bar Wake Study:\n" ) );
        
        const uint8 num_Wake = uint8(m_WakeIter.length());

        const uint8 x = 1; // AR
        
        array<array<double>> wake_span_loc_data(num_Wake), wake_cl_dist_data(num_Wake);
        array<double> computation_time(num_Wake);
        
        // Wake Iteration Study
        for( uint8 i = 0; i < num_Wake; i++ )
        {
            string fname = "Hershey_Wake" + int(m_WakeIter[i]) + ".vsp3";
            string fname_res = "Hershey_Wake" + int(m_WakeIter[i]) + "_res.csv";
    
            //==== Open and test generated wings ====//
            Print( string( "Reading in file: " ), false );
            Print( fname );
            ReadVSPFile( fname ); // Sets VSP3 file name
            
            //==== Analysis: m_VSPSweepAnalysis ====//
            Print( m_VSPSweepAnalysis );

            //==== Analysis: VSPAero Compute Geometry to Create Vortex Lattice DegenGeom File ====//
            Print( m_CompGeomAnalysis );

            // Set defaults
            SetAnalysisInputDefaults( m_CompGeomAnalysis );

            SetIntAnalysisInput(m_CompGeomAnalysis, "Symmetry", m_SymFlagVec, 0);
            
            // list inputs, type, and current values
            PrintAnalysisInputs( m_CompGeomAnalysis );

            // Execute
            Print( "\tExecuting..." );
            string compgeom_resid = ExecAnalysis( m_CompGeomAnalysis );
            Print( "COMPLETE" );

            // Get & Display Results
            PrintResults( compgeom_resid );

            //==== Analysis: VSPAero Single Point ====//
            // Set defaults
            SetAnalysisInputDefaults(m_VSPSweepAnalysis);
            Print(m_VSPSweepAnalysis);

            // Reference geometry set
            SetIntAnalysisInput(m_VSPSweepAnalysis, "GeomSet", m_GeomVec, 0);
            SetIntAnalysisInput(m_VSPSweepAnalysis, "RefFlag", m_RefFlagVec, 0);

            array<string> wid = FindGeomsWithName( "WingGeom" );
            SetStringAnalysisInput(m_VSPSweepAnalysis, "WingID", wid, 0);

            // Freestream Parameters
            array<double> Alpha(1, 1.0);
            SetDoubleAnalysisInput(m_VSPSweepAnalysis, "AlphaStart", Alpha, 0);
            array<int> AlphaNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "AlphaNpts", AlphaNpts, 0);
            SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachStart", m_MachVec, 0);
            array<int> MachNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "MachNpts", MachNpts, 0);
            array<int> wake_vec(1, m_WakeIter[i]);
            SetIntAnalysisInput(m_VSPSweepAnalysis, "WakeNumIter", wake_vec, 0);
            SetIntAnalysisInput(m_VSPSweepAnalysis, "Symmetry", m_SymFlagVec, 0);

            Update();

            // list inputs, type, and current values
            PrintAnalysisInputs( m_VSPSweepAnalysis );
            Print( "" );

            // Execute
            Print( "\tExecuting..." );
            string rid = ExecAnalysis( m_VSPSweepAnalysis );
            Print( "COMPLETE" );

            // Get & Display Results
            PrintResults( rid );
            WriteResultsCSVFile( rid, fname_res );

            // Get Load Result ID
            string load_rid = FindLatestResultsID( "VSPAERO_Load" );
            if ( load_rid != "" )
            {
                // Lift Distribution:
                wake_span_loc_data[i] = GetDoubleResults( load_rid, "Yavg" );
                wake_cl_dist_data[i] = GetDoubleResults( load_rid, "cl" );
            }
            
            array<double> time_vec = GetDoubleResults( rid, "Analysis_Duration_Sec" );
            
            if ( time_vec.size() > 0 )
            {
                computation_time[i] = time_vec[0];
            }
            
            ClearVSPModel();
        }
        
        // Get Approximate LLT Cl distribution (number of points should be the same across all wake iteration cases)
        array<vec3d> wake_cl_dist_theo = GetHersheyBarLiftDist( int(100), Deg2Rad(m_AlphaVec[0]), Vinf, (2*m_halfAR[x]), false );
        
        // Const AR, WTess, TC, and UTess, vary wake iterations Lift Distribution Study
        HTMLChart c1 = HTMLChart(); // Initialize Chart
        c1.m_ChartName = string("Wake_LD");
        c1.m_Legend = string("'Span Location','Wake Iter: 1','Wake Iter: 2','Wake Iter: 3','Wake Iter: 4','Wake Iter: 5','LLT'");
        c1.m_Series = string("{ 5: { pointSize: 0, color: 'gray', lineWidth: 3 } }");
        c1.m_Title = string("Figure ") + m_FigCount + string(". Hershey Bar Lift Distribution Wake Iteration Sensitivity");
        m_FigCount++;
        c1.m_HAxisString = string("hAxis: { title: 'Span Location (Y)', titleTextStyle: { bold: true } }");
        c1.m_VAxisString = string("vAxis: { title: 'Cl', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }"); // add max

        array<string> data_str_vec1;
        for( uint i = 0; i < uint8(wake_span_loc_data[0].length()); i++ )
        {
            string row = string("\t\t  [") + wake_span_loc_data[0][i] + string(",");
            
            for ( uint8 w = 0; w < num_Wake; w++ )
            {
                row += double( wake_cl_dist_data[w][i] );
                
                if ( w == num_Wake - 1 )
                {
                    row += string(",null],\n");
                }
                else
                {
                    row += string(",");
                }
            }
            
            data_str_vec1.push_back( row );
        }

        for( uint i = 0; i < uint8(wake_cl_dist_theo.length()); i++ )
        {
            string row = string("\t\t  [") + double( wake_cl_dist_theo[i][0] ) + string(",");
            
            for ( uint8 w = 0; w < num_Wake; w++ )
            {
                row += string("null,");
            }
            
            row += double( wake_cl_dist_theo[i][1] );

            if ( i == wake_cl_dist_theo.length() - 1 )
            {
                row += string("]\n");
            }
            else
            {
                row += string("],\n");
            }
            
            data_str_vec1.push_back( row );
        }
        
        c1.WriteChartHead( data_str_vec1 );
        m_HersheyLDWakeIDVec.push_back( c1.m_ChartName );
        
        // Computational time plot
        HTMLChart c2 = HTMLChart(); // Initialize Chart
        c2.m_ChartName = string("Wake_Time");
        c2.m_Legend = string("'Wake Iterations','Total Computation Time'");
        c2.m_Title = string("Figure ") + m_FigCount + string(". Hershey Bar VSPAERO Total Computation Time vs. Wake Iterations");
        m_FigCount++;
        c2.m_HAxisString = string("hAxis: { title: 'Wake Iterations', titleTextStyle: { bold: true } }");
        c2.m_VAxisString = string("vAxis: { title: 'Time (sec)', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }"); // add max
        
        array<string> data_str_vec2;
        for( uint i = 0; i < num_Wake; i++ )
        {
            string row = string("\t\t  [") + m_WakeIter[i] + string(",") + computation_time[i];
            
            if ( i == num_Wake - 1 )
            {
                row += string("]\n");
            }
            else
            {
                row += string("],\n");
            }
            
            data_str_vec2.push_back( row );
        }
        
        c2.WriteChartHead( data_str_vec2 );
        m_HersheyLDWakeIDVec.push_back( c2.m_ChartName );
    }
    
    private void TestHersheyBarAdvancedWings()
    {
        Print( string( "-> Begin Hershey Bar Advanced Settings Study:\n" ) );
        
        const uint8 x = 2; // AR
        const uint8 t = 2; // Tip Clustering
        
        const uint8 num_case = 4; // Number of advanced VSPAERO settings to test
        const uint8 num_wake = uint8(m_AdvancedWakeVec.length());

        m_AdvancedTimeVec.resize(num_wake);
        m_HersheyLDAdvancedIDVec.resize(num_wake);
        
        for ( uint8 w = 0; w < num_wake; w++ )
        {
            array<array<double>> span_loc_data(num_case), cl_dist_data(num_case);
            m_AdvancedTimeVec[w].resize(num_case);
            
            for ( uint8 i = 0; i < num_case; i++ )
            {
                string fname = "Hershey_Advanced_" + int(i) + ".vsp3";
                string fname_res = "Hershey_Advanced_" + int(i) + "_res.csv";
        
                //==== Open and test generated wings ====//
                Print( string( "Reading in file: " ), false );
                Print( fname );
                ReadVSPFile( fname ); // Sets VSP3 file name
                
                //==== Analysis: m_VSPSweepAnalysis ====//
                Print( m_VSPSweepAnalysis );

                //==== Analysis: VSPAero Compute Geometry to Create Vortex Lattice DegenGeom File ====//
                Print( m_CompGeomAnalysis );

                // Set defaults
                SetAnalysisInputDefaults( m_CompGeomAnalysis );
                
                SetIntAnalysisInput(m_CompGeomAnalysis, "Symmetry", m_SymFlagVec, 0);

                // list inputs, type, and current values
                PrintAnalysisInputs( m_CompGeomAnalysis );

                // Execute
                Print( "\tExecuting..." );
                string compgeom_resid = ExecAnalysis( m_CompGeomAnalysis );
                Print( "COMPLETE" );

                // Get & Display Results
                PrintResults( compgeom_resid );

                //==== Analysis: VSPAero Single Point ====//
                // Set defaults
                SetAnalysisInputDefaults(m_VSPSweepAnalysis);
                Print(m_VSPSweepAnalysis);

                // Reference geometry set
                SetIntAnalysisInput(m_VSPSweepAnalysis, "GeomSet", m_GeomVec, 0);
                SetIntAnalysisInput(m_VSPSweepAnalysis, "RefFlag", m_RefFlagVec, 0);

                array<string> wid = FindGeomsWithName( "WingGeom" );
                SetStringAnalysisInput(m_VSPSweepAnalysis, "WingID", wid, 0);

                // Freestream Parameters
                array<double> Alpha(1, 1.0);
                SetDoubleAnalysisInput(m_VSPSweepAnalysis, "AlphaStart", Alpha, 0);
                array<int> AlphaNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "AlphaNpts", AlphaNpts, 0);
                SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachStart", m_MachVec, 0);
                array<int> MachNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "MachNpts", MachNpts, 0);
                array<int> wake_vec(1, m_WakeIter[w]);
                SetIntAnalysisInput(m_VSPSweepAnalysis, "WakeNumIter", wake_vec, 0);
                SetIntAnalysisInput(m_VSPSweepAnalysis, "Symmetry", m_SymFlagVec, 0);

                // if i == 0 -> use default advanced settings
                
                if ( i == 1 )
                {
                    array<int> precon_vec(1, PRECON_JACOBI); // Jacobi Preconditioner
                    SetIntAnalysisInput(m_VSPSweepAnalysis, "Precondition", precon_vec, 0);
                }
                else if ( i == 2 )
                {
                    array<int> precon_vec(1, PRECON_SSOR); // SSOR Preconditioner
                    SetIntAnalysisInput(m_VSPSweepAnalysis, "Precondition", precon_vec, 0);
                }
                else if ( i == 3 )
                {
                    array<int> KTCorrect_vec(1, 0); // 2nd Orrder Mach Correction On
                    SetIntAnalysisInput(m_VSPSweepAnalysis, "KTCorrection", KTCorrect_vec, 0);
                }
                
                Update();

                // list inputs, type, and current values
                PrintAnalysisInputs( m_VSPSweepAnalysis );
                Print( "" );

                // Execute
                Print( "\tExecuting..." );
                string rid = ExecAnalysis( m_VSPSweepAnalysis );
                Print( "COMPLETE" );

                // Get & Display Results
                PrintResults( rid );
                WriteResultsCSVFile( rid, fname_res );

                // Get Load Result ID
                string load_rid = FindLatestResultsID( "VSPAERO_Load" );
                if ( load_rid != "" )
                {
                    // Lift Distribution:
                    span_loc_data[i] = GetDoubleResults( load_rid, "Yavg" );
                    cl_dist_data[i] = GetDoubleResults( load_rid, "cl" );
                }
                
                array<double> time_vec = GetDoubleResults( rid, "Analysis_Duration_Sec" );
                
                if ( time_vec.size() > 0 )
                {
                    m_AdvancedTimeVec[w][i] = time_vec[0];
                }
                
                ClearVSPModel();
            }
            
            // Get Approximate LLT Cl distribution (number of points should be the same across all advanced settings cases)
            array<vec3d> cl_dist_theo = GetHersheyBarLiftDist( int(100), Deg2Rad(m_AlphaVec[0]), Vinf, (2*m_halfAR[x]), false );
            
            // Const AR, WTess, TC, and UTess, vary advanced settings Lift Distribution Study
            HTMLChart c1 = HTMLChart(); // Initialize Chart
            c1.m_ChartName = string("Advanced_LD_W") + int(w + 1);
            c1.m_Legend = string("'Span Location','Default','Case #1','Case #2','Case #3','LLT'");
            c1.m_Series = string("{ 6: { pointSize: 0, color: 'gray', lineWidth: 3 } }");
            c1.m_Title = string("Figure ") + m_FigCount + string(". Hershey Bar Lift Distribution Advanced Settings Sensitivity: Wake Iterations = ") + int(w + 1);
            m_FigCount++;
            c1.m_HAxisString = string("hAxis: { title: 'Span Location (Y)', titleTextStyle: { bold: true } }");
            c1.m_VAxisString = string("vAxis: { title: 'Cl', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }"); // add max

            array<string> data_str_vec1;
            for( uint i = 0; i < uint8(span_loc_data[0].length()); i++ )
            {
                string row = string("\t\t  [") + double( span_loc_data[0][i] ) + string(",") + double( cl_dist_data[0][i] ) + string(",") + 
                    double( cl_dist_data[1][i] ) + string(",") + double( cl_dist_data[2][i] ) + string(",") + double( cl_dist_data[3][i] ) + 
                    string(",") + string(",null],\n");
                    
                data_str_vec1.push_back( row );
            }

            for( uint i = 0; i < uint8(cl_dist_theo.length()); i++ )
            {
                string row = string("\t\t  [") + double( cl_dist_theo[i][0] ) + string(",null,null,null,null,") + 
                    double( cl_dist_theo[i][1] );

                if ( i == cl_dist_theo.length() - 1 )
                {
                    row += string("]\n");
                }
                else
                {
                    row += string("],\n");
                }
                
                data_str_vec1.push_back( row );
            }
            
            c1.WriteChartHead( data_str_vec1 );
            m_HersheyLDAdvancedIDVec[w].push_back( c1.m_ChartName );
        }
        
        // Computation Time vs Wake Iterations
        HTMLChart c2 = HTMLChart(); // Initialize Chart
        c2.m_ChartName = string("Advanced_Time_W");
        c2.m_Legend = string("'Wake Iter','Default','Case #1','Case #2','Case #3'");
        c2.m_Title = string("Figure ") + m_FigCount + string(". Hershey Bar Total Computation Time vs. Wake Iterations Advanced Settings Sensitivity");
        m_FigCount++;
        c2.m_HAxisString = string("hAxis: { title: 'Wake Iter', titleTextStyle: { bold: true } }");
        c2.m_VAxisString = string("vAxis: { title: 'Time (Sec)', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }"); 
        
        array<string> data_str_vec2;
        for ( uint8 i = 0; i < uint8(m_AdvancedTimeVec.length()); i++ )
        {
            string row = string("\t\t  [") + double(i + 1) + string(","); // wake iter
            
            for ( uint8 j = 0; j < uint8(m_AdvancedTimeVec[i].length()); j++ )
            {
                row += double( m_AdvancedTimeVec[i][j] );
                
                if ( j != uint8(m_AdvancedTimeVec[i].length()) - 1 )
                {
                    row += string(",");
                }
            }
            
            if ( i == uint8(m_AdvancedTimeVec.length()) - 1 )
            {
                row += string("]\n");
            }
            else
            {
                row += string("],\n");
            }
            
            data_str_vec2.push_back( row );
        }
        
        c2.WriteChartHead( data_str_vec2 );
        m_HersheyAdvancedTimeIDVec.push_back( c2.m_ChartName );
        
    }
    
    // Hershey Bar Class Properties
    private array<double> m_halfAR(6);
    private array<double> m_Tip_Clus(3);
    private array<int> m_Tess_U(4);
    private array<int> m_Tess_W(4);
    private array<int> m_WakeIter(5);
    
    array<int> m_AdvancedWakeVec(3);
    
    array<string> m_HersheyARIDVec;
    array<string> m_HersheyAlphaErrorIDVec;
    array<string> m_HersheyTessIDVec;
    array<string> m_HersheyTimeIDVec;
    array<string> m_HersheyLDTCIDVec;
    array<string> m_HersheyLDUTessIDVec;
    array<string> m_HersheyLDWTessIDVec;
    array<string> m_HersheyDDUTessIDVec;
    array<string> m_HersheyDDWTessIDVec;
    array<string> m_HersheyLDWakeIDVec;
    array<array<string>> m_HersheyLDAdvancedIDVec;
    array<string> m_HersheyAdvancedTimeIDVec;
    
    array<vec3d> m_AR10_Y_Cl_Cd_vec;
    
    int m_AlphaNpts;
    
    double m_Hershey_AR60_Cl_alpha_Err;
    double m_Hershey_LA_Err;
    
    array<array<double>> m_AdvancedTimeVec;
}

//============= Swept Wing Study Functions ==============//
class SweepVV : VV_Base
{
    SweepVV()
    {
        m_halfAR[0] = 2.5;
        m_halfAR[1] = 5.006975;
        m_halfAR[2] = 7.5;
        m_halfAR[3] = 12.5;
        m_halfAR[4] = 20.000;
        m_halfAR[5] = 30;
        
        m_Tess_U[0] = 5;
        m_Tess_U[1] = 12;
        m_Tess_U[2] = 20;
        m_Tess_U[3] = 41;

        m_Tess_W[0] = 9;
        m_Tess_W[1] = 18;
        m_Tess_W[2] = 29;
        m_Tess_W[3] = 51;
        
        m_Sweep[0] = 0;
        m_Sweep[1] = 10.005;
        m_Sweep[2] = 20;
        m_Sweep[3] = 30.005;
        m_Sweep[4] = 40;
    }
    
    void SweptWingStudy()
    {
        m_runflag = true;
        
        //====Generate Wings====//
        GenerateSweepStudyWings();
        
        //====Test Wings====//
        TestSweepStudyWings();
    }
    
    private void GenerateSweepStudyWings()
    {
        GenerateSweptUWTessWings();
        GenerateSweptARSweepWings();
    }
    
    private void TestSweepStudyWings()
    {
        TestSweptUWTessWings();
        TestSweptARSweepWings();
    }
    
    private void GenerateSweptARSweepWings()
    {
        //==== Add Wing Geometry ====//
        string wing_id = AddGeom( "WING", "" );
        
        //==== Set Wing Section ====//
        SetDriverGroup( wing_id, 1, AR_WSECT_DRIVER, ROOTC_WSECT_DRIVER, TIPC_WSECT_DRIVER );
        
        Update();
        
        //==== Set NACA 0012 Airfoil and Common Parms 
        SetParmVal( wing_id, "ThickChord", "XSecCurve_0", 0.12 );
        SetParmVal( wing_id, "ThickChord", "XSecCurve_1", 0.12 );
        SetParmVal( wing_id, "Root_Chord", "XSec_1", 1.0 );
        SetParmVal( wing_id, "Tip_Chord", "XSec_1", 1.0 );
        
        SetParmVal( wing_id, "TECluster", "WingGeom", 1.0 );
        SetParmVal( wing_id, "LECluster", "WingGeom", 0.2 );
        
        const uint8 u = 3; // UTess
        const uint8 w = 3; // WTess
        
        SetParmVal( wing_id, "SectTess_U", "XSec_1", m_Tess_U[u] ); // Constant U Tess
        SetParmVal( wing_id, "Tess_W", "Shape", m_Tess_W[w] ); // Constant W Tess
        SetParmVal( wing_id, "OutCluster", "XSec_1", 1.0 ); // Constant Tip Clustering
        
        Update();
        
        for ( uint8 x = 0; x < uint8(m_halfAR.length()); x++ )
        {
            for ( uint8 s = 0; s < uint8(m_Sweep.length()); s++ )
            {
                SetParmVal( wing_id, "Aspect", "XSec_1", m_halfAR[x] );
                SetParmVal( wing_id, "Sweep", "XSec_1", m_Sweep[s] );

                Update();

                //==== Setup export filenames for AR Study ====//
                string fname = "Swept_AR" + int(2*m_halfAR[x]) + "_Sweep" + int(m_Sweep[s]) + ".vsp3";

                //==== Save Vehicle to File ====//
                string message = "-->Saving vehicle file to: " + fname + "\n";
                Print( message );
                WriteVSPFile( fname, SET_ALL );
                Print( "COMPLETE\n" );
            }
        }
        
        ClearVSPModel();
    }
    
    private void GenerateSweptUWTessWings()
    {
        //==== Add Wing Geometry ====//
        string wing_id = AddGeom( "WING", "" );
        
        //==== Set Wing Section ====//
        SetDriverGroup( wing_id, 1, AR_WSECT_DRIVER, ROOTC_WSECT_DRIVER, TIPC_WSECT_DRIVER );
        
        Update();
        
        //==== Set NACA 0012 Airfoil and Common Parms 
        SetParmVal( wing_id, "ThickChord", "XSecCurve_0", 0.12 );
        SetParmVal( wing_id, "ThickChord", "XSecCurve_1", 0.12 );
        SetParmVal( wing_id, "Root_Chord", "XSec_1", 1.0 );
        SetParmVal( wing_id, "Tip_Chord", "XSec_1", 1.0 );
        
        SetParmVal( wing_id, "TECluster", "WingGeom", 1.0 );
        SetParmVal( wing_id, "LECluster", "WingGeom", 0.2 );
        
        const uint8 x = 1; // AR
        const uint8 s = 3; // Sweep
        
        SetParmVal( wing_id, "Aspect", "XSec_1", m_halfAR[x] ); // Constant AR
        SetParmVal( wing_id, "Sweep", "XSec_1", m_Sweep[s] ); // Constant Sweep
        SetParmVal( wing_id, "OutCluster", "XSec_1", 1.0 ); // Constant Tip Clustering
        
        Update();
        
        for ( uint8 u = 0; u < uint8(m_Tess_U.length()); u++ )
        {
            for ( uint8 w = 0; w < uint8(m_Tess_W.length()); w++ )
            {
                SetParmVal( wing_id, "SectTess_U", "XSec_1", m_Tess_U[u] ); // Constant U Tess
                SetParmVal( wing_id, "Tess_W", "Shape", m_Tess_W[w] ); // Constant W Tess
                
                Update();

                //==== Setup export filenames for AR Study ====//
                string fname = "Swept_U" + int(m_Tess_U[u]) + "_W" + int(m_Tess_W[w]) + ".vsp3";

                //==== Save Vehicle to File ====//
                string message = "-->Saving vehicle file to: " + fname + "\n";
                Print( message );
                WriteVSPFile( fname, SET_ALL );
                Print( "COMPLETE\n" );
            }
        }
        
        ClearVSPModel();
    }
    
    private void TestSweptARSweepWings()
    {
        Print( string( "-> Begin Swept Wing AR Sweep Test:\n" ) );
        
        const uint8 num_AR = uint8(m_halfAR.length());
        const uint8 num_Sweep = uint8(m_Sweep.length());
        
        array<array<double>> Lift_angle_vlm(num_AR), Lift_angle_theo(num_AR), Cl_alpha_vlm(num_AR), Cl_alpha_theo(num_AR), Lift_angle_pm(num_AR), Cl_alpha_pm(num_AR);
        array<double> Avg_Cla_Error_VLM(num_Sweep), C_ratio(num_AR), Avg_Cla_Error_PM(num_Sweep);
        
        for ( uint8 x = 0; x < num_AR; x++ )
        {
            Lift_angle_vlm[x].resize(num_Sweep);
            Lift_angle_pm[x].resize(num_Sweep);
            Lift_angle_theo[x].resize(num_Sweep);

            Cl_alpha_vlm[x].resize(num_Sweep);
            Cl_alpha_pm[x].resize(num_Sweep);
            Cl_alpha_theo[x].resize(num_Sweep);
            
            C_ratio[x] = 1/(2*m_halfAR[x]); // AR to chord ratio
            
            for ( uint8 s = 0; s < num_Sweep; s++ )
            {
                // Open the file
                string fname = "Swept_AR" + int(2*m_halfAR[x]) + "_Sweep" + int(m_Sweep[s]) + ".vsp3";
                string fname_res_vlm = "Swept_AR" + int(2*m_halfAR[x]) + "_Sweep" + int(m_Sweep[s]) + "_vlm_res.csv";
                string fname_res_pm = "Swept_AR" + int(2*m_halfAR[x]) + "_Sweep" + int(m_Sweep[s]) + "_pm_res.csv";

                Print( string( "Reading in file: " ), false );
                Print( fname );
                ReadVSPFile( fname ); // Sets VSP3 file name
                
                //==== Analysis: m_VSPSweepAnalysis ====//
                Print( m_VSPSweepAnalysis );

                //==== Analysis: VSPAero Compute Geometry to Create Vortex Lattice DegenGeom File ====//
                Print( m_CompGeomAnalysis );

                // Set defaults
                SetAnalysisInputDefaults( m_CompGeomAnalysis );
                
                SetIntAnalysisInput(m_CompGeomAnalysis, "Symmetry", m_SymFlagVec, 0);

                // list inputs, type, and current values
                PrintAnalysisInputs( m_CompGeomAnalysis );
                
                // Execute
                Print( "\tExecuting..." );
                string compgeom_resid = ExecAnalysis( m_CompGeomAnalysis );
                Print( "COMPLETE" );

                // Get & Display Results
                PrintResults( compgeom_resid );

                //==== Analysis: VSPAero Single Point ====//
                // Set defaults
                SetAnalysisInputDefaults(m_VSPSweepAnalysis);
                Print(m_VSPSweepAnalysis);

                // Reference geometry set
                SetIntAnalysisInput( m_VSPSweepAnalysis, "GeomSet", m_GeomVec, 0 );
                SetIntAnalysisInput(m_VSPSweepAnalysis, "RefFlag", m_RefFlagVec, 0);
                SetIntAnalysisInput(m_VSPSweepAnalysis, "Symmetry", m_SymFlagVec, 0);

                array< string > wid = FindGeomsWithName( "WingGeom" );
                SetStringAnalysisInput(m_VSPSweepAnalysis, "WingID", wid, 0);

                // Freestream Parameters
                array<double> Alpha(1, 1.0);
                SetDoubleAnalysisInput(m_VSPSweepAnalysis, "AlphaStart", Alpha, 0);
                array<int> AlphaNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "AlphaNpts", AlphaNpts, 0);
                SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachStart", m_MachVec, 0);
                array<int> MachNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "MachNpts", MachNpts, 0);
                SetIntAnalysisInput(m_VSPSweepAnalysis, "WakeNumIter", m_WakeIterVec, 0);

                Update();

                // list inputs, type, and current values
                PrintAnalysisInputs( m_VSPSweepAnalysis );
                Print( "" );

                // Execute
                Print( "\tExecuting..." );
                string rid = ExecAnalysis( m_VSPSweepAnalysis );
                Print( "COMPLETE" );

                // Get & Display Results
                PrintResults( rid );
                WriteResultsCSVFile( rid, fname_res_vlm );

                // Calculate Experimental and Theoretical Values
                double C_bot_two = 1 + (pow((tan(Deg2Rad(m_Sweep[s]))),2)/pow(b,2));
                double C_top = 2*pi*2*m_halfAR[x];
                double C_bot_one_theo = (pow((2*m_halfAR[x]),2)*pow(b,2))/pow(k_theo,2);
                double C_bot_theo = (2 + (sqrt((C_bot_one_theo*C_bot_two)+4)));
                double C_l_alpha_exper_theo = C_top/C_bot_theo;
                Cl_alpha_theo[x][s] = C_l_alpha_exper_theo*(pi/180); // rad --> deg
                Lift_angle_theo[x][s] = 1/Cl_alpha_theo[x][s]; // Cl to lift angle
                
                // Get Result ID Vec (History and Load ResultIDs)
                array<string> rid_vec = GetStringResults( rid, "ResultsVec" );
                if ( rid_vec.length() > 0 )
                {
                    // Get History Results (rid_vec[0]) from Final Wake Iteration in History Result
                    array<double> cl_vec = GetDoubleResults( rid_vec[0], "CL" );
                    double Cl_res = cl_vec[int(cl_vec.length()) - 1];
                    Cl_alpha_vlm[x][s] = Cl_res; // alpha = 1.0 (deg)
                    Lift_angle_vlm[x][s] = 1/(Cl_alpha_vlm[x][s]); // deg
                    
                    // Add error
                    Avg_Cla_Error_VLM[s] += abs((Cl_alpha_vlm[x][s] - Cl_alpha_theo[x][s])/Cl_alpha_theo[x][s]);
                }
                
                //==== Analysis: VSPAero Panel Single ====//
                Print( m_VSPSweepAnalysis );
                
                //==== Analysis: VSPAero Compute Geometry to Create Vortex Lattice DegenGeom File ====//
                Print( m_CompGeomAnalysis );

                // Set defaults
                SetAnalysisInputDefaults( m_CompGeomAnalysis );
                
                array<int> panel_analysis(1, VSPAERO_ANALYSIS_METHOD::PANEL);
                SetIntAnalysisInput( m_CompGeomAnalysis, "AnalysisMethod", panel_analysis );
                
                SetIntAnalysisInput(m_CompGeomAnalysis, "Symmetry", m_SymFlagVec, 0);

                // list inputs, type, and current values
                PrintAnalysisInputs( m_CompGeomAnalysis );

                // Execute
                Print( "\tExecuting..." );
                compgeom_resid = ExecAnalysis( m_CompGeomAnalysis );
                Print( "COMPLETE" );

                // Get & Display Results
                PrintResults( compgeom_resid );
                
                //==== Analysis: VSPAero Panel Single ====//
               // Set defaults
                SetAnalysisInputDefaults(m_VSPSweepAnalysis);
                Print(m_VSPSweepAnalysis);

                // Reference geometry set
                SetIntAnalysisInput(m_VSPSweepAnalysis, "GeomSet", m_GeomVec, 0);
                SetIntAnalysisInput(m_VSPSweepAnalysis, "RefFlag", m_RefFlagVec, 0);
                SetStringAnalysisInput(m_VSPSweepAnalysis, "WingID", wid, 0);
                SetIntAnalysisInput(m_VSPSweepAnalysis, "WakeNumIter", m_WakeIterVec, 0);
                SetIntAnalysisInput(m_VSPSweepAnalysis, "AnalysisMethod", panel_analysis);
                SetIntAnalysisInput(m_VSPSweepAnalysis, "Symmetry", m_SymFlagVec, 0);
                
                // Freestream Parameters
                array<double> alpha(1, 1);
                SetDoubleAnalysisInput(m_VSPSweepAnalysis, "AlphaStart", alpha, 0);
                SetIntAnalysisInput(m_VSPSweepAnalysis, "AlphaNpts", AlphaNpts, 0);
                SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachStart", m_MachVec, 0);
                SetIntAnalysisInput(m_VSPSweepAnalysis, "MachNpts", MachNpts, 0);

                Update();

                // list inputs, type, and current values
                PrintAnalysisInputs( m_VSPSweepAnalysis );
                Print( "" );

                // Execute
                Print( "\tExecuting..." );
                rid = ExecAnalysis( m_VSPSweepAnalysis );
                Print( "COMPLETE" );

                // Get & Display Results
                PrintResults( rid );
                WriteResultsCSVFile( rid, fname_res_pm );
                
                // Get Result ID Vec (History and Load ResultIDs)
                rid_vec = GetStringResults( rid, "ResultsVec" );
                if ( rid_vec.length() > 0 )
                {
                    // Get Result from Final Wake Iteration
                    array<double> cl_vec = GetDoubleResults( rid_vec[0], "CL" );
                    
                    double Cl_pm = cl_vec[int(cl_vec.length()) - 1];
                    Cl_alpha_pm[x][s] = Cl_pm; // deg (alpha = 1.0°)
                    Lift_angle_pm[x][s] = 1/(Cl_alpha_pm[x][s]); // deg
                    
                    // Add error
                    Avg_Cla_Error_PM[s] += abs((Cl_alpha_pm[x][s] - Cl_alpha_theo[x][s])/Cl_alpha_theo[x][s]);
                }
                
                ClearVSPModel();
            }
        }
        
        // Compute average error across all AR for each Sweep case
        for ( uint8 s = 0; s < num_Sweep; s++ )
        {
            Avg_Cla_Error_VLM[s] = (Avg_Cla_Error_VLM[s] / num_AR ) * 100;
            Avg_Cla_Error_PM[s] = (Avg_Cla_Error_PM[s] / num_AR ) * 100;
        }
        
        for ( uint8 s = 0; s < num_Sweep; s++ )
        {
            // // Sweep Sweep -- Chord Ratio vs. Lift Angle
            // HTMLChart c1 = HTMLChart(); // Initialize Chart
            // c1.m_ChartName = string("Sweep") + int(m_Sweep[s]) + string("_CRvLA");
            // c1.m_Legend = string("'1/AR','VSPAERO VLM','VSPAERO Panel','3D Theoretical Approx'");
            // c1.m_Series = string("{ 2: { pointSize: 0, color: 'gray', lineWidth: 3 } }");
            // c1.m_Title = int(m_Sweep[s]) + string("° Sweep: Lift Angle vs Chord Ratio"); 
            // c1.m_HAxisString = string("hAxis: { title: '1/AR', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }");
            // c1.m_VAxisString = string("vAxis: { title: 'd_a/d_C_l (°)', titleTextStyle: { bold: true }, viewWindow: { min: 0.0, max: 20.0 } }");
            // c1.m_Width = 0.38;

            // array<string> data_str_vec1;
            // for( uint8 x = 0; x < num_AR; x++ )
            // {
                // string row = string("\t\t  [") + double( C_ratio[x] ) + string(",") + double( Lift_angle_vlm[x][s] ) + string(",") + 
                    // double( Lift_angle_pm[x][s] ) + string(",") + double( Lift_angle_theo[x][s] );

                // if ( x == num_AR - 1 )
                // {
                    // row += string("]\n");
                // }
                // else
                // {
                    // row += string("],\n");
                // }
                
                // data_str_vec1.push_back( row );
            // }
            
            // c1.WriteChartHead( data_str_vec1 );
            // m_SweepSweepLAvCRIDVec.push_back( c1.m_ChartName );
            
            // Sweep Sweep -- Cl_alpha vs. AR
            HTMLChart c3 = HTMLChart(); // Initialize Chart
            c3.m_ChartName = string("Sweep") + int(m_Sweep[s]) + string("_CLavAR");
            c3.m_Legend = string("'AR','VSPAERO VLM','VSPAERO Panel','LLT'");
            c3.m_Series = string("{ 2: { pointSize: 0, color: 'gray', lineWidth: 3 } }");
            c3.m_Title = string("Figure ") + m_FigCount + string(". ") + int(m_Sweep[s]) + string("° Sweep: Cl_alpha vs. Aspect Ratio"); 
            m_FigCount++;
            c3.m_HAxisString = string("hAxis: { title: 'AR', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }");
            c3.m_VAxisString = string("vAxis: { title: 'Cl_alpha (°)', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }");
            
            array<string> data_str_vec3;
            for( uint8 x = 0; x < num_AR; x++ )
            {
                string row = string("\t\t  [") + double( 2*m_halfAR[x] ) + string(",") + double( Cl_alpha_vlm[x][s] ) + string(",") + double( Cl_alpha_pm[x][s] ) + 
                    string(",") + double( Cl_alpha_theo[x][s] );

                if ( x == num_AR - 1 )
                {
                    row += string("]\n");
                }
                else
                {
                    row += string("],\n");
                }
                
                data_str_vec3.push_back( row );
            }
            
            c3.WriteChartHead( data_str_vec3 );
            m_SweepSweepClavARIDVec.push_back( c3.m_ChartName );
        }
        
        // Average % Dif of VSPAERO/Theoretical Results vs. Sweep
        HTMLChart c2 = HTMLChart(); // Initialize Chart
        c2.m_ChartName = string("ErrorvSweep");
        c2.m_Legend = string("'Sweep','VLM', 'Panel Method'");
        c2.m_Title = string("Figure ") + m_FigCount + string(". Average % Error in Cl_alpha Across All Aspect Ratios Sweep Sensitivity"); 
        m_FigCount++;
        c2.m_HAxisString = string("hAxis: { title: 'Sweep (°)', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }");
        c2.m_VAxisString = string("vAxis: { title: 'Cl_alpha % Error', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }");
        
        array<string> data_str_vec2;
        for( uint8 s = 0; s < num_Sweep; s++ )
        {
            string row = string("\t\t  [") + double( m_Sweep[s] ) + string(",") + double( Avg_Cla_Error_VLM[s] )+ string(",") + double( Avg_Cla_Error_PM[s] );

            if ( s == num_Sweep - 1 )
            {
                row += string("]\n");
            }
            else
            {
                row += string("],\n");
            }
            
            data_str_vec2.push_back( row );
        }
        
        c2.WriteChartHead( data_str_vec2 );
        m_SweepSweepErrorIDVec.push_back( c2.m_ChartName );
        
    }
    
    private void TestSweptUWTessWings()
    {
        Print( string( "-> Begin Swept Wing UW Tesselation Test:\n" ) );
        
        const uint8 x = 1; // AR
        const uint8 s = 2; // Sweep
        
        const uint8 num_UTess = uint8(m_Tess_U.length());
        const uint8 num_WTess = uint8(m_Tess_W.length());
        
        // Calculate Experimental and Theoretical Values
        const double C_bot_two = 1 + (pow((tan(Deg2Rad(m_Sweep[s]))),2)/pow(b,2));
        const double C_top = 2*pi*2*m_halfAR[x];
        const double C_bot_one_theo = (pow((2*m_halfAR[x]),2)*pow(b,2))/pow(k_theo,2);
        const double C_bot_theo = (2 + (sqrt((C_bot_one_theo*C_bot_two)+4)));
        const double C_l_alpha_exper_theo = C_top/C_bot_theo;
        const double Cl_alpha_theo = C_l_alpha_exper_theo*(pi/180); // rad --> deg
        const double Lift_angle_theo = 1/Cl_alpha_theo; // Cl to lift angle
        
        array<array<double>> Error_Cla(num_UTess); // index 0: UTess, index 1: WTess
        
        for (uint8 u = 0; u < num_UTess; u++ )
        {
            Error_Cla[u].resize(num_WTess);
            
            for( uint8 w = 0; w < num_WTess; w++)
            {
                // Open the file
                string fname = "Swept_U" + int(m_Tess_U[u]) + "_W" + int(m_Tess_W[w]) + ".vsp3";
                string fname_res = "Swept_U" + int(m_Tess_U[u]) + "_W" + int(m_Tess_W[w]) + "_res.csv";

                Print( string( "Reading in file: " ), false );
                Print( fname );
                ReadVSPFile( fname ); // Sets VSP3 file name
                
                //==== Analysis: m_VSPSweepAnalysis ====//
                Print( m_VSPSweepAnalysis );

                //==== Analysis: VSPAero Compute Geometry to Create Vortex Lattice DegenGeom File ====//
                Print( m_CompGeomAnalysis );

                // Set defaults
                SetAnalysisInputDefaults( m_CompGeomAnalysis );

                // list inputs, type, and current values
                PrintAnalysisInputs( m_CompGeomAnalysis );

                // Execute
                Print( "\tExecuting..." );
                string compgeom_resid = ExecAnalysis( m_CompGeomAnalysis );
                Print( "COMPLETE" );

                // Get & Display Results
                PrintResults( compgeom_resid );

                //==== Analysis: VSPAero Single Point ====//
                // Set defaults
                SetAnalysisInputDefaults(m_VSPSweepAnalysis);
                Print(m_VSPSweepAnalysis);

                // Reference geometry set
                SetIntAnalysisInput( m_VSPSweepAnalysis, "GeomSet", m_GeomVec, 0 );
                SetIntAnalysisInput(m_VSPSweepAnalysis, "RefFlag", m_RefFlagVec, 0);
                SetIntAnalysisInput(m_VSPSweepAnalysis, "Symmetry", m_SymFlagVec, 0);

                array< string > wid = FindGeomsWithName( "WingGeom" );
                SetStringAnalysisInput(m_VSPSweepAnalysis, "WingID", wid, 0);

                // Freestream Parameters
                SetDoubleAnalysisInput(m_VSPSweepAnalysis, "AlphaStart", m_AlphaVec, 0);
                array<int> AlphaNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "AlphaNpts", AlphaNpts, 0);
                SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachStart", m_MachVec, 0);
                array<int> MachNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "MachNpts", MachNpts, 0);
                SetIntAnalysisInput(m_VSPSweepAnalysis, "WakeNumIter", m_WakeIterVec, 0);

                Update();

                // list inputs, type, and current values
                PrintAnalysisInputs( m_VSPSweepAnalysis );
                Print( "" );

                // Execute
                Print( "\tExecuting..." );
                string rid = ExecAnalysis( m_VSPSweepAnalysis );
                Print( "COMPLETE" );

                // Get & Display Results
                PrintResults( rid );
                WriteResultsCSVFile( rid, fname_res );

                // Get Result ID Vec (History and Load ResultIDs)
                array<string> rid_vec = GetStringResults( rid, "ResultsVec" );
                if ( rid_vec.length() > 0 )
                {
                    // Get History Results (rid_vec[0]) from Final Wake Iteration in History Result
                    array<double> cl_vec = GetDoubleResults( rid_vec[0], "CL" );
                    double Cl_res = cl_vec[int(cl_vec.length()) - 1];

                    // Calculate Error
                    double C_l_alpha_vsp = Cl_res; // alpha = 1.0 (deg)
                    Error_Cla[u][w] = (abs((C_l_alpha_vsp - Cl_alpha_theo)/Cl_alpha_theo))*100;
                }
                
                ClearVSPModel();
            }
        }
        
        // 30° Swept Wing VLM: % Dif of C_l\alpha using Theoretical Methods for Various AR
        
        // Chord Tess
        HTMLChart c1 = HTMLChart(); // Initialize Chart
        c1.m_ChartName = string("ChordAR") + int(2*m_halfAR[x]) + string("_Sweep") + int( m_Sweep[s] );
        c1.m_Legend = string("'Chord Tesselation (W Tess)','U Tess: 5','U Tess: 12','U Tess: 20','U Tess: 41'");
        c1.m_Title = string("Figure ") + m_FigCount + string(". ") + int( m_Sweep[s] ) + string("° Swept Wing VLM Cl_alpha Span Tesselation (U Tess) Sensitivity");
        m_FigCount++;
        c1.m_HAxisString = string("hAxis: { title: 'Chord Tesselation (W Tess)', titleTextStyle: { bold: true } }");
        c1.m_VAxisString = string("vAxis: { title: 'Cl_alpha % Error', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }");

        array<string> data_str_vec1;
        for( uint8 w = 0; w < num_WTess; w++ )
        {
            string row = string("\t\t  [") + double( m_Tess_W[w] ) + string(",") + double( Error_Cla[0][w] ) + 
                string(",") + double( Error_Cla[1][w] ) + string(",") + double( Error_Cla[2][w] ) + string(",") + 
                double( Error_Cla[3][w] );

            if ( w == num_WTess - 1 )
            {
                row += string("]\n");
            }
            else
            {
                row += string("],\n");
            }
            
            data_str_vec1.push_back( row );
        }
        
        c1.WriteChartHead( data_str_vec1 );
        m_SweepTessIDVec.push_back( c1.m_ChartName );
        
        // Span Tess
        HTMLChart c2 = HTMLChart(); // Initialize Chart
        c2.m_ChartName = string("SpanAR") + int(2*m_halfAR[x]) + string("_Sweep") + int( m_Sweep[s] );
        c2.m_Legend = string("'Span Tesselation (U Tess)','W Tess: 9','W Tess: 17','W Tess: 29','W Tess: 51'");
        c2.m_Title = string("Figure ") + m_FigCount + string(". ") + int( m_Sweep[s] ) + string("° Swept Wing VLM Cl_alpha Chord Tesselation (W Tess) Sensitivity");
        m_FigCount++;
        c2.m_HAxisString = string("hAxis: { title: 'Span Tesselation (U Tess)', titleTextStyle: { bold: true } }");
        c2.m_VAxisString = string("vAxis: { title: 'Cl_alpha % Error', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }");
        
        array<string> data_str_vec2;
        for( uint8 u = 0; u < num_UTess; u++ )
        {
            string row = string("\t\t  [") + double( m_Tess_U[u] ) + string(",") + double( Error_Cla[u][0] ) + 
                string(",") + double( Error_Cla[u][1] ) + string(",") + double( Error_Cla[u][2] ) + string(",") + 
                double( Error_Cla[u][3] );

            if ( u == num_UTess - 1 )
            {
                row += string("]\n");
            }
            else
            {
                row += string("],\n");
            }
            
            data_str_vec2.push_back( row );
        }
        
        c2.WriteChartHead( data_str_vec2 );
        m_SweepTessIDVec.push_back( c2.m_ChartName );
    }
    
    // Swept Wing Class Properties
    private array<double> m_halfAR(6);
    private array<int> m_Tess_U(4);
    private array<int> m_Tess_W(4);
    private array<double> m_Sweep(5);

    array<string> m_SweepTessIDVec;
    //array<string> m_SweepSweepLAvCRIDVec;
    array<string> m_SweepSweepClavARIDVec;
    array<string> m_SweepSweepErrorIDVec;
    
    double m_35Sweep_LA_Err;
}

//============= Bertin-Smith Study Functions ==============//
class BertinVV : VV_Base
{
    BertinVV()
    {
        m_AlphaNpts = 9;
        m_Cl_alpha_expected = 3.433; // rad
    }
    
    void BertinSmithStudy()
    {
        m_runflag = true;
        
        //====Generate Bertin-Smith Wings====//
        GenerateBertinSmithWings();

        //====Test Bertin-Smith Sweep Wings====//
        TestBertinSmithWings();
    }
    
    private void GenerateBertinSmithWings()
    {
        //==== Add Wing Geometry ====//
        string wing_id = AddGeom( "WING", "" );

        //==== Set Wing Section Controls to AR, Root Chord, and Tail Chord ====//
        SetDriverGroup( wing_id, 1, AR_WSECT_DRIVER, ROOTC_WSECT_DRIVER, TIPC_WSECT_DRIVER );
        
        Update();

        //==== Set Airfoil to NACA0012 and Bertin-Smith Wing Parms ====//
        SetParmVal( wing_id, "ThickChord", "XSecCurve_0", 0.12 );
        SetParmVal( wing_id, "ThickChord", "XSecCurve_1", 0.12 );
        SetParmVal( wing_id, "Sweep_Location", "XSec_1", 0 );
        SetParmVal( wing_id, "Sweep", "XSec_1", 45 );
        SetParmVal( wing_id, "Root_Chord", "XSec_1", 0.2 );
        SetParmVal( wing_id, "Tip_Chord", "XSec_1", 0.2 );
        SetParmVal( wing_id, "Aspect", "XSec_1", 2.5 );

        Update();

        //==== Setup export filenames for VSPAERO sweep ====//
        string fname = "Bertin_Smith.vsp3";

        //==== Save Vehicle to File ====//
        Print("-->Saving vehicle file to: ", false );
        Print( fname, true );
        Print( "" );
        WriteVSPFile( fname, SET_ALL );
        Print( "COMPLETE\n" );

        ClearVSPModel();
    }

    private void TestBertinSmithWings()
    {
        Print( string( "-> Begin Bertin-Smith Sweep Test:\n" ) );
        
        const double alpha_0 = -20.0; // deg
        const double alpha_f = 20.0; // deg
        const double d_alpha = alpha_f - alpha_0; // deg
        
        array<double> Cl_res( m_AlphaNpts ), Cl_approx_vec( m_AlphaNpts );
        m_Cl_alpha_error.resize( m_AlphaNpts );
        m_Cl_alpha_res.resize( m_AlphaNpts );
        m_AlphaSweepVec.resize( m_AlphaNpts );
        
        //==== Open and test generated wing ====//
        string fname = "Bertin_Smith.vsp3";
        string fname_res = "Bertin_Smith_res.csv";

        Print( string( "Reading in file: " ), false );
        Print( fname );
        ReadVSPFile( fname ); // Sets VSP3 file name

        //==== Analysis: VSPAero Sweep ====//
        Print( m_VSPSweepAnalysis );

        //==== Analysis: VSPAero Compute Geometry to Create Vortex Lattice DegenGeom File ====//
        Print( m_CompGeomAnalysis );

        // Set defaults
        SetAnalysisInputDefaults( m_CompGeomAnalysis );
        
        SetIntAnalysisInput(m_CompGeomAnalysis, "Symmetry", m_SymFlagVec, 0);

        // list inputs, type, and current values
        PrintAnalysisInputs( m_CompGeomAnalysis );

        // Execute
        Print( "\tExecuting..." );
        string compgeom_resid = ExecAnalysis( m_CompGeomAnalysis );
        Print( "COMPLETE" );

        // Get & Display Results
        PrintResults( compgeom_resid );

        //==== Analysis: VSPAero Sweep ====//
        // Set defaults
        SetAnalysisInputDefaults(m_VSPSweepAnalysis);
        Print(m_VSPSweepAnalysis);

        // Reference geometry set
        SetIntAnalysisInput(m_VSPSweepAnalysis, "GeomSet", m_GeomVec, 0);
        SetIntAnalysisInput(m_VSPSweepAnalysis, "RefFlag", m_RefFlagVec, 0);
        SetIntAnalysisInput(m_VSPSweepAnalysis, "Symmetry", m_SymFlagVec, 0);

        array< string > wid = FindGeomsWithName( "WingGeom" );
        SetStringAnalysisInput(m_VSPSweepAnalysis, "WingID", wid, 0);
        
        SetIntAnalysisInput(m_VSPSweepAnalysis, "WakeNumIter", m_WakeIterVec, 0);

        // Freestream Parameters
        array<double> AlphaStart( 1, alpha_0 );
        array<double> AlphaEnd( 1, alpha_f );
        array<int> AlphaNpts( 1, m_AlphaNpts );
        SetDoubleAnalysisInput(m_VSPSweepAnalysis, "AlphaStart", AlphaStart, 0);
        SetDoubleAnalysisInput(m_VSPSweepAnalysis, "AlphaEnd", AlphaEnd, 0);
        SetIntAnalysisInput(m_VSPSweepAnalysis, "AlphaNpts", AlphaNpts, 0);
        array<double> MachNpts(1, 1); // Start and end at 0.1
        SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachStart", m_MachVec, 0);
        SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachEnd", m_MachVec, 0);
        SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachNpts", MachNpts, 0);

        Update();

        // list inputs, type, and current values
        PrintAnalysisInputs( m_VSPSweepAnalysis );
        Print( "" );

        // Execute
        Print( "\tExecuting..." );
        string rid = ExecAnalysis( m_VSPSweepAnalysis );
        Print( "COMPLETE" );

        // Get & Display Results
        PrintResults( rid );
        WriteResultsCSVFile( rid, fname_res );

        // Get Result ID Vec (History and Load ResultIDs)
        array<string> rid_vec = GetStringResults( rid, "ResultsVec" );
        if ( rid_vec.length() <= 1 )
        {
            ClearVSPModel();
            return;
        }
            
        // Get Result from Final Wake Iteration
        for ( uint8 i = 0; i < uint8(m_AlphaNpts); i++ )
        {
            array<double> alpha_vec = GetDoubleResults( rid_vec[i], "Alpha" );
            m_AlphaSweepVec[i] = alpha_vec[int(alpha_vec.length()) - 1];
            
            array<double> cl_vec = GetDoubleResults( rid_vec[i], "CL" );
            Cl_res[i] = cl_vec[int(cl_vec.length()) - 1];
            
            Cl_approx_vec[i] = m_Cl_alpha_expected * sin( Deg2Rad( m_AlphaSweepVec[i] ) );
        }
        
        for ( uint8 i = 0; i < uint8(m_AlphaNpts); i++ )
        {
            if ( i == 0 )
            {
                m_Cl_alpha_res[i] = (((Cl_res[i+1] - Cl_res[i])/(m_AlphaSweepVec[i+1] - m_AlphaSweepVec[i]))*(180/pi));
            }
            else if ( i == uint8(m_AlphaNpts) - 1 )
            {
                m_Cl_alpha_res[i] = (((Cl_res[i] - Cl_res[i-1])/(m_AlphaSweepVec[i] - m_AlphaSweepVec[i-1]))*(180/pi));
            }
            else // Central differencing
            {
                m_Cl_alpha_res[i] = (((Cl_res[i+1] - Cl_res[i-1])/(m_AlphaSweepVec[i+1] - m_AlphaSweepVec[i-1]))*(180/pi));
            }
            
            m_Cl_alpha_error[i] = (abs((m_Cl_alpha_res[i] - m_Cl_alpha_expected)/m_Cl_alpha_expected))*100;
        }

        double Cl_alpha_res_avg = ((Cl_res[m_AlphaNpts - 1] - Cl_res[0])/d_alpha)*(180/pi); //rad

        m_Bertin_Sweep_Cl_alpha_Err = (abs((Cl_alpha_res_avg - m_Cl_alpha_expected)/m_Cl_alpha_expected))*100;
        
        ClearVSPModel();
        
        // VLM: Cl vs alpha
        HTMLChart c2 = HTMLChart(); // Initialize Chart
        c2.m_ChartName = string("BS_ClvA");
        c2.m_Series = string("{ 1: { pointSize: 0, color: 'gray', lineWidth: 3 } }");
        c2.m_Legend = string("'Alpha','VSPAERO','Expected'");
        c2.m_Title = string("Figure ") + m_FigCount + string(". Bertin-Smith VLM: Cl vs Alpha");
        m_FigCount++;
        c2.m_HAxisString = string("hAxis: { title: 'Alpha (°)', titleTextStyle: { bold: true } }");
        c2.m_VAxisString = string("vAxis: { title: 'Cl', titleTextStyle: { bold: true } }");
        
        array<string> data_str_vec2;
        for( uint8 i = 0; i < uint8(m_AlphaNpts); i++ )
        {
            string row = string("\t\t  [") + double( m_AlphaSweepVec[i] ) + string(",") + Cl_res[i] + string(",") + Cl_approx_vec[i];

            if ( i == uint8(m_AlphaNpts) - 1 )
            {
                row += string("]\n");
            }
            else
            {
                row += string("],\n");
            }
            
            data_str_vec2.push_back( row );
        }
        
        c2.WriteChartHead( data_str_vec2 );
        m_BSClAIDVec.push_back( c2.m_ChartName );
        
        // VLM: Cl_alpha % Difference of VSPAERO/Theoretical Results vs. Alpha
        HTMLChart c1 = HTMLChart(); // Initialize Chart
        c1.m_ChartName = string("BS_ClaErrorvAlpha");
        c1.m_Legend = string("'Alpha','% Difference'");
        c1.m_Title = string("Figure ") + m_FigCount + string(". Bertin-Smith VLM Cl_alpha Alpha Sensitivity"); 
        m_FigCount++;
        c1.m_HAxisString = string("hAxis: { title: 'Alpha (°)', titleTextStyle: { bold: true } }");
        c1.m_VAxisString = string("vAxis: { title: 'Cl_alpha % Difference', titleTextStyle: { bold: true } }");
        
        array<string> data_str_vec1;
        for( uint8 i = 0; i < uint8(m_AlphaNpts); i++ )
        {
            string row = string("\t\t  [") + double( m_AlphaSweepVec[i] ) + string(",") + double( m_Cl_alpha_error[i] );

            if ( i == uint8(m_AlphaNpts) - 1 )
            {
                row += string("]\n");
            }
            else
            {
                row += string("],\n");
            }
            
            data_str_vec1.push_back( row );
        }
        
        c1.WriteChartHead( data_str_vec1 );
        m_BSErrorIDVec.push_back( c1.m_ChartName );
    }
    
    // Bertin-Smith Class Properties
    int m_AlphaNpts;
    array<double> m_AlphaSweepVec;
    double m_Bertin_Sweep_Cl_alpha_Err;
    array<double> m_Cl_alpha_error;
    array<double> m_Cl_alpha_res;
    double m_Cl_alpha_expected;
    
    array<string> m_BSErrorIDVec;
    array<string> m_BSClAIDVec;
}

//============= Warren-12 Study Functions ==============//
class WarrenVV : VV_Base
{
    WarrenVV()
    {
        m_AlphaNpts = 9;
        m_Cl_alpha_expected = 2.743; // rad
        m_Cm_alpha_expected = -3.10; // rad
    }
    
    void WarrrenStudy()
    {
        m_runflag = true;
        
        //====Generate Warren-12 Wings====//
        GenerateWarrenWings();

        //====Test Warren-12 Sweep Wing====//
        TestWarrenWings();
    }
    
    private void GenerateWarrenWings()
    {
        //==== Add Wing Geometry ====//
        string wing_id = AddGeom( "WING", "" );

        //==== Set Wing Section Controls to AR, Root Chord, and Tail Chord ====//
        SetDriverGroup( wing_id, 1, AR_WSECT_DRIVER, ROOTC_WSECT_DRIVER, TIPC_WSECT_DRIVER );
        
        Update();

        //==== Set Airfoil to NACA 0012 and Specify Warren-12 Wing Parms ====//
        SetParmVal( wing_id, "ThickChord", "XSecCurve_0", 0.12 );
        SetParmVal( wing_id, "ThickChord", "XSecCurve_1", 0.12 );
        SetParmVal( wing_id, "Sweep_Location", "XSec_1", 0 );
        SetParmVal( wing_id, "Sweep", "XSec_1", 53.54 );
        SetParmVal( wing_id, "Root_Chord", "XSec_1", 1.5 );
        SetParmVal( wing_id, "Tip_Chord", "XSec_1", 0.5 );
        SetParmVal( wing_id, "Aspect", "XSec_1", sqrt(2) );
        
        Update();

        //==== Setup export filenames for VSPAERO sweep ====//
        string fname = "Warren_12.vsp3";

        //==== Save Vehicle to File ====//
        Print("-->Saving vehicle file to: ", false );
        Print( fname, true );
        Print( "" );
        WriteVSPFile( fname, SET_ALL );
        Print( "COMPLETE\n" );

        ClearVSPModel();
    }

    private void TestWarrenWings()
    {
        Print( string( "-> Begin Warren-12 Sweep Test:\n" ) );
        
        const double alpha_0 = -20.0; // deg
        const double alpha_f = 20.0; // deg
        const double d_alpha = alpha_f - alpha_0; // deg
        
        array<double> Cl_res(m_AlphaNpts), Cm_res(m_AlphaNpts), Cl_approx_vec(m_AlphaNpts), Cm_approx_vec(m_AlphaNpts);
        m_AlphaSweepVec.resize(m_AlphaNpts);
        m_Cl_alpha_error.resize(m_AlphaNpts);
        m_Cl_alpha_res.resize(m_AlphaNpts);
        m_Cm_alpha_error.resize(m_AlphaNpts);
        m_Cm_alpha_res.resize(m_AlphaNpts);
        
        //==== Open and test generated wings ====//
        string fname = "Warren_12.vsp3";
        string fname_res = "Warren_12_res.csv";

        Print( string( "Reading in file: " ), false );
        Print( fname );
        ReadVSPFile( fname ); // Sets VSP3 file name
        
        //==== Analysis: VSPAero Sweep ====//
        Print( m_VSPSweepAnalysis );

        //==== Analysis: VSPAero Compute Geometry to Create Vortex Lattice DegenGeom File ====//
        Print( m_CompGeomAnalysis );

        // Set defaults
        SetAnalysisInputDefaults( m_CompGeomAnalysis );
        
        SetIntAnalysisInput(m_CompGeomAnalysis, "Symmetry", m_SymFlagVec, 0);

        // list inputs, type, and current values
        PrintAnalysisInputs( m_CompGeomAnalysis );

        // Execute
        Print( "\tExecuting..." );
        string compgeom_resid = ExecAnalysis( m_CompGeomAnalysis );
        Print( "COMPLETE" );

        // Get & Display Results
        PrintResults( compgeom_resid );

        //==== Analysis: VSPAero Sweep ====//
        // Set defaults
        SetAnalysisInputDefaults(m_VSPSweepAnalysis);
        Print(m_VSPSweepAnalysis);

        // Reference geometry set
        SetIntAnalysisInput(m_VSPSweepAnalysis, "GeomSet", m_GeomVec, 0);
        SetIntAnalysisInput(m_VSPSweepAnalysis, "RefFlag", m_RefFlagVec, 0);
        SetIntAnalysisInput(m_VSPSweepAnalysis, "Symmetry", m_SymFlagVec, 0);

        array< string > wid = FindGeomsWithName( "WingGeom" );
        SetStringAnalysisInput(m_VSPSweepAnalysis, "WingID", wid, 0);
        
        SetIntAnalysisInput(m_VSPSweepAnalysis, "WakeNumIter", m_WakeIterVec, 0);

        // Freestream Parameters
        array<double> AlphaStart( 1, alpha_0 );
        array<double> AlphaEnd( 1, alpha_f );
        array<int> AlphaNpts( 1, m_AlphaNpts );
        SetDoubleAnalysisInput(m_VSPSweepAnalysis, "AlphaStart", AlphaStart, 0);
        SetDoubleAnalysisInput(m_VSPSweepAnalysis, "AlphaEnd", AlphaEnd, 0);
        SetIntAnalysisInput(m_VSPSweepAnalysis, "AlphaNpts", AlphaNpts, 0);
        array<double> MachNpts(1, 1); // Start and end at 0.1
        SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachStart", m_MachVec, 0);
        SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachEnd", m_MachVec, 0);
        SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachNpts", MachNpts, 0);

        Update();

        // list inputs, type, and current values
        PrintAnalysisInputs( m_VSPSweepAnalysis );
        Print( "" );

        // Execute
        Print( "\tExecuting..." );
        string rid = ExecAnalysis( m_VSPSweepAnalysis );
        Print( "COMPLETE" );

        // Get & Display Results
        PrintResults( rid );
        WriteResultsCSVFile( rid, fname_res );

        // Get Result ID Vec (History and Load ResultIDs)
        array<string> rid_vec = GetStringResults( rid, "ResultsVec" );
        if ( rid_vec.length() <= 1 )
        {
            ClearVSPModel();
            return;
        }

        // Get Result from Final Wake Iteration
        for ( uint8 i = 0; i < uint8(m_AlphaNpts); i++ )
        {
            array<double> alpha_vec = GetDoubleResults( rid_vec[i], "Alpha" );
            m_AlphaSweepVec[i] = alpha_vec[int(alpha_vec.length()) - 1];
            
            array<double> cl_vec = GetDoubleResults( rid_vec[i], "CL" );
            Cl_res[i] = cl_vec[int(cl_vec.length()) - 1];

            array<double> cmy_vec = GetDoubleResults( rid_vec[i], "CMy" );
            Cm_res[i] = cmy_vec[int(cmy_vec.length()) - 1];
            
            Cl_approx_vec[i] = m_Cl_alpha_expected * sin( Deg2Rad( m_AlphaSweepVec[i] ) );
            Cm_approx_vec[i] = m_Cm_alpha_expected * sin( Deg2Rad( m_AlphaSweepVec[i] ) );
        }
        
        for ( uint8 i = 0; i < uint8(m_AlphaNpts); i++ )
        {
            if ( i == 0 )
            {
                m_Cl_alpha_res[i] = (((Cl_res[i+1] - Cl_res[i])/(m_AlphaSweepVec[i+1] - m_AlphaSweepVec[i]))*(180/pi));
                m_Cm_alpha_res[i] = (((Cm_res[i+1] - Cm_res[i])/(m_AlphaSweepVec[i+1] - m_AlphaSweepVec[i]))*(180/pi));
            }
            else if ( i == uint8(m_AlphaNpts) - 1 )
            {
                m_Cl_alpha_res[i] = (((Cl_res[i] - Cl_res[i-1])/(m_AlphaSweepVec[i] - m_AlphaSweepVec[i-1]))*(180/pi));
                m_Cm_alpha_res[i] = (((Cm_res[i] - Cm_res[i-1])/(m_AlphaSweepVec[i] - m_AlphaSweepVec[i-1]))*(180/pi));
            }
            else // Central differencing
            {
                m_Cl_alpha_res[i] = (((Cl_res[i+1] - Cl_res[i-1])/(m_AlphaSweepVec[i+1] - m_AlphaSweepVec[i-1]))*(180/pi));
                m_Cm_alpha_res[i] = (((Cm_res[i+1] - Cm_res[i-1])/(m_AlphaSweepVec[i+1] - m_AlphaSweepVec[i-1]))*(180/pi));
            }
            
            m_Cl_alpha_error[i] = (abs((m_Cl_alpha_res[i] - m_Cl_alpha_expected)/m_Cl_alpha_expected))*100;
            m_Cm_alpha_error[i] = (abs((m_Cm_alpha_res[i] - m_Cm_alpha_expected)/m_Cm_alpha_expected))*100;
        }

        double Cl_alpha_res_avg = ((Cl_res[m_AlphaNpts - 1] - Cl_res[0])/d_alpha)*(180/pi); //rad
        double Cm_alpha_res_avg = ((Cm_res[m_AlphaNpts - 1] - Cm_res[0])/d_alpha)*(180/pi); //rad

        m_Warren_Sweep_Cl_alpha_Err = (abs((Cl_alpha_res_avg - m_Cl_alpha_expected)/m_Cl_alpha_expected))*100;
        m_Warren_Sweep_Cm_alpha_Err = (abs((Cm_alpha_res_avg - m_Cm_alpha_expected)/m_Cm_alpha_expected))*100;
        
        ClearVSPModel();
        
        // VLM: Cl vs alpha
        HTMLChart c3 = HTMLChart(); // Initialize Chart
        c3.m_ChartName = string("W12_ClvA");
        c3.m_Series = string("{ 1: { pointSize: 0, color: 'gray', lineWidth: 3 } }");
        c3.m_Legend = string("'Alpha','VSPAERO','Expected'");
        c3.m_Title = string("Figure ") + m_FigCount + string(". Warren-12 VLM: Cl vs Alpha");
        m_FigCount++;
        c3.m_HAxisString = string("hAxis: { title: 'Alpha (°)', titleTextStyle: { bold: true } }");
        c3.m_VAxisString = string("vAxis: { title: 'Cl', titleTextStyle: { bold: true } }");
        
        array<string> data_str_vec3;
        for( uint8 i = 0; i < uint8(m_AlphaNpts); i++ )
        {
            string row = string("\t\t  [") + double( m_AlphaSweepVec[i] ) + string(",") + Cl_res[i] + string(",") + Cl_approx_vec[i];

            if ( i == uint8(m_AlphaNpts) - 1 )
            {
                row += string("]\n");
            }
            else
            {
                row += string("],\n");
            }
            
            data_str_vec3.push_back( row );
        }
        
        c3.WriteChartHead( data_str_vec3 );
        m_W12ClAIDVec.push_back( c3.m_ChartName );
        
        // VLM: Cl_alpha % Difference of VSPAERO/Theoretical Results vs. Alpha
        HTMLChart c1 = HTMLChart(); // Initialize Chart
        c1.m_ChartName = string("W12_ClaErrorvAlpha");
        c1.m_Legend = string("'Alpha','% Difference'");
        c1.m_Title = string("Figure ") + m_FigCount + string(". Warren-12 VLM Cl_alpha Alpha Sensitivity"); 
        m_FigCount++;
        c1.m_HAxisString = string("hAxis: { title: 'Alpha (°)', titleTextStyle: { bold: true } }");
        c1.m_VAxisString = string("vAxis: { title: 'Cl_alpha % Difference', titleTextStyle: { bold: true } }");
        
        array<string> data_str_vec1;
        for( uint8 i = 0; i < uint8(m_AlphaNpts); i++ )
        {
            string row = string("\t\t  [") + double( m_AlphaSweepVec[i] ) + string(",") + double( m_Cl_alpha_error[i] );

            if ( i == uint8(m_AlphaNpts) - 1 )
            {
                row += string("]\n");
            }
            else
            {
                row += string("],\n");
            }
            
            data_str_vec1.push_back( row );
        }
        
        c1.WriteChartHead( data_str_vec1 );
        m_W12ClaErrorIDVec.push_back( c1.m_ChartName );
        
        // VLM: Cm vs alpha
        HTMLChart c4 = HTMLChart(); // Initialize Chart
        c4.m_ChartName = string("W12_CmvA");
        c4.m_Series = string("{ 1: { pointSize: 0, color: 'gray', lineWidth: 3 } }");
        c4.m_Legend = string("'Alpha','VSPAERO','Expected'");
        c4.m_Title = string("Figure ") + m_FigCount + string(". Warren-12 VLM: Cm vs Alpha");
        m_FigCount++;
        c4.m_HAxisString = string("hAxis: { title: 'Alpha (°)', titleTextStyle: { bold: true } }");
        c4.m_VAxisString = string("vAxis: { title: 'Cm', titleTextStyle: { bold: true } }");
        
        array<string> data_str_vec4;
        for( uint8 i = 0; i < uint8(m_AlphaNpts); i++ )
        {
            string row = string("\t\t  [") + double( m_AlphaSweepVec[i] ) + string(",") + Cm_res[i] + string(",") + Cm_approx_vec[i];

            if ( i == uint8(m_AlphaNpts) - 1 )
            {
                row += string("]\n");
            }
            else
            {
                row += string("],\n");
            }
            
            data_str_vec4.push_back( row );
        }
        
        c4.WriteChartHead( data_str_vec4 );
        m_W12CmAIDVec.push_back( c4.m_ChartName );
        
        // VLM: Cm_alpha % Difference of VSPAERO/Theoretical Results vs. Alpha
        HTMLChart c2 = HTMLChart(); // Initialize Chart
        c2.m_ChartName = string("W12_CmaErrorvAlpha");
        c2.m_Legend = string("'Alpha','% Difference'");
        c2.m_Title = string("Figure ") + m_FigCount + string(". Warren-12 VLM Cm_alpha Alpha Sensitivity"); 
        m_FigCount++;
        c2.m_HAxisString = string("hAxis: { title: 'Alpha (°)', titleTextStyle: { bold: true } }");
        c2.m_VAxisString = string("vAxis: { title: 'Cm_alpha % Difference', titleTextStyle: { bold: true } }");
        
        array<string> data_str_vec2;
        for( uint8 i = 0; i < uint8(m_AlphaNpts); i++ )
        {
            string row = string("\t\t  [") + double( m_AlphaSweepVec[i] ) + string(",") + double( m_Cm_alpha_error[i] );

            if ( i == uint8(m_AlphaNpts) - 1 )
            {
                row += string("]\n");
            }
            else
            {
                row += string("],\n");
            }
            
            data_str_vec2.push_back( row );
        }
        
        c2.WriteChartHead( data_str_vec2 );
        m_W12CmaErrorIDVec.push_back( c2.m_ChartName );
    }
    
    // Warren-12 Class Properties
    double m_Warren_Sweep_Cl_alpha_Err;
    double m_Warren_Sweep_Cm_alpha_Err;
    array<double> m_AlphaSweepVec;
    array<double> m_Cl_alpha_error;
    array<double> m_Cl_alpha_res;
    double m_Cl_alpha_expected;
    array<double> m_Cm_alpha_error;
    array<double> m_Cm_alpha_res;
    double m_Cm_alpha_expected;
    int m_AlphaNpts;
    
    array<string> m_W12ClaErrorIDVec;
    array<string> m_W12CmaErrorIDVec;
    array<string> m_W12ClAIDVec;
    array<string> m_W12CmAIDVec;
}

//============= VKT Study Functions ==============//
// Note: These studies can cause VSPAERO to crash, but typically not on the same run case every time. When VSPAERO crashes,
//  click "close" to skip the particular run case and continue the study. 
class VKTVV : VV_Base
{
    VKTVV()
    {
        m_epsilon[0] = 0.1;
        m_epsilon[1] = 0.2;
        
        m_kappa[0] = 0;
        m_kappa[1] = 0.1;
        
        m_tau[0] = 0;
        m_tau[1] = 10;
        
        // Add another intermediary chord and span tesselation value?
        m_Tess_U[0] = 12;
        m_Tess_U[1] = 20;
        m_Tess_U[2] = 41;
        
        m_Tess_W[0] = 17;
        m_Tess_W[1] = 29;
        m_Tess_W[2] = 51;
    }
    
    void VKTStudy()
    {
        m_runflag = true;
        
        //====Generate VKT Wings====//
        GenerateVKTWings();

        //====Test VKT Wings====//
        TestVKTWings();
    }
    
    private void GenerateVKTWings()
    {
        GenerateVKTEKTWings();
        GenerateVKTUWTessWings();
    }
    
    private void TestVKTWings()
    {
        TestVKTEKTWings();
        TestVKTUWTessWings();
    }
    
    private void GenerateVKTEKTWings()
    {
        //==== Add Wing Geometry ====//
        string wing_id = AddGeom( "WING", "" );
        
        // Set VKT airfoil
        string xsec_surf = GetXSecSurf( wing_id, 0 );
        ChangeXSecShape( xsec_surf, 0, XS_VKT_AIRFOIL );
        string xsec_surf1 = GetXSecSurf(wing_id, 1);
        ChangeXSecShape( xsec_surf1, 1, XS_VKT_AIRFOIL );
        
        //==== Set Wing Section Controls ====//
        SetDriverGroup( wing_id, 1, AR_WSECT_DRIVER, ROOTC_WSECT_DRIVER, TIPC_WSECT_DRIVER );
        
        Update();
        
        //==== Set Common Parms ====//
        SetParmVal( wing_id, "Sweep", "XSec_1", 0 );
        SetParmVal( wing_id, "Root_Chord", "XSec_1", 1 );
        SetParmVal( wing_id, "Tip_Chord", "XSec_1", 1 );

        //TODO: Make consistent with other VKT wing geoms
        SetParmVal( wing_id, "Aspect", "XSec_1", 15.0001 );
        SetParmVal( wing_id, "TECluster", "WingGeom", 1.0 );
        SetParmVal( wing_id, "LECluster", "WingGeom", 0.2 );
        
        const uint8 u = 2; // UTess
        const uint8 w = 2; // WTess
        
        SetParmVal( wing_id, "SectTess_U", "XSec_1", m_Tess_U[u] ); // Constant U Tess
        SetParmVal( wing_id, "Tess_W", "Shape", m_Tess_W[w] ); // Constant W Tess
        
        Update();
        
        for ( uint8 e = 0; e < uint8(m_epsilon.length()); e++ )
        {
            for ( uint8 k = 0; k < uint8(m_kappa.length()); k++ )
            {
                for ( uint8 t = 0; t < uint8(m_tau.length()); t++ )
                {
                    SetParmVal( wing_id, "Epsilon", "XSecCurve_0", m_epsilon[e] );
                    SetParmVal( wing_id, "Epsilon", "XSecCurve_1", m_epsilon[e] );
                    SetParmVal( wing_id, "Kappa", "XSecCurve_0", m_kappa[k] );
                    SetParmVal( wing_id, "Kappa", "XSecCurve_1", m_kappa[k] );
                    SetParmVal( wing_id, "Tau", "XSecCurve_0", m_tau[t] );
                    SetParmVal( wing_id, "Tau", "XSecCurve_1", m_tau[t] );

                    Update();

                    //==== Setup export filenames for EKT Study ====//
                    string fname = "VKT" + "_e" + double(m_epsilon[e]) + "_k" + double(m_kappa[k]) + "_t" + double(m_tau[t]) + ".vsp3";

                    //==== Save Vehicle to File ====//
                    string message = string("-->Saving vehicle file to: ") + fname + string("\n");
                    Print( message );
                    WriteVSPFile( fname, SET_ALL );
                    Print( "COMPLETE\n" );
                }
            }
        }
        
        ClearVSPModel();
    }
    
    private void GenerateVKTUWTessWings()
    {
        //==== Add Wing Geometry ====//
        string wing_id = AddGeom( "WING", "" );
        
        // Set VKT airfoil
        string xsec_surf = GetXSecSurf( wing_id, 0 );
        ChangeXSecShape( xsec_surf, 0, XS_VKT_AIRFOIL );
        string xsec_surf1 = GetXSecSurf(wing_id, 1);
        ChangeXSecShape( xsec_surf1, 1, XS_VKT_AIRFOIL );
        
        //==== Set Wing Section Controls ====//
        SetDriverGroup( wing_id, 1, AR_WSECT_DRIVER, ROOTC_WSECT_DRIVER, TIPC_WSECT_DRIVER );
        
        Update();
        
        //==== Set Common Parms ====//
        SetParmVal( wing_id, "Sweep", "XSec_1", 0 );
        SetParmVal( wing_id, "Root_Chord", "XSec_1", 1 );
        SetParmVal( wing_id, "Tip_Chord", "XSec_1", 1 );
        
        //TODO: Make consistent with other VKT wing geoms
        SetParmVal( wing_id, "Aspect", "XSec_1", 7.5005 );
        SetParmVal( wing_id, "TECluster", "WingGeom", 1.0 );
        SetParmVal( wing_id, "LECluster", "WingGeom", 0.2 );
        
        const uint8 e = 0; // epsilon
        const uint8 k = 1; // kappa
        const uint8 t = 1; // tau
        
        SetParmVal( wing_id, "Epsilon", "XSecCurve_0", m_epsilon[e] );
        SetParmVal( wing_id, "Epsilon", "XSecCurve_1", m_epsilon[e] );
        SetParmVal( wing_id, "Kappa", "XSecCurve_0", m_kappa[k] );
        SetParmVal( wing_id, "Kappa", "XSecCurve_1", m_kappa[k] );
        SetParmVal( wing_id, "Tau", "XSecCurve_0", m_tau[t] );
        SetParmVal( wing_id, "Tau", "XSecCurve_1", m_tau[t] );
        
        for ( uint8 u = 0; u < uint8(m_Tess_U.length()); u++ )
        {
            for ( uint8 w = 0; w < uint8(m_Tess_W.length()); w++ )
            {
                SetParmVal( wing_id, "Tess_W", "Shape", m_Tess_W[w] );
                SetParmVal( wing_id, "SectTess_U", "XSec_1", m_Tess_U[u] );

                Update();

                //==== Setup export filenames ====//
                string fname = "VKT_U" + int(m_Tess_U[u]) + "_W" + int(m_Tess_W[w]) + ".vsp3";

                //==== Save Vehicle to File ====//
                string message = string("-->Saving vehicle file to: ") + fname + string("\n");
                Print(message );
                WriteVSPFile( fname, SET_ALL );
                Print( "COMPLETE\n" );
            }
        }
        
        ClearVSPModel();
    }
    
    private void TestVKTEKTWings()
    {
        Print( string( "-> Begin VKT Epsilon Kappa Tau Test:\n" ) );
        
        uint8 num_epsilon = uint8(m_epsilon.length());
        uint8 num_kappa = uint8(m_kappa.length());
        uint8 num_tau = uint8(m_tau.length());
        
        array<double> cut(1, 0.0); // cut at root
        const int npts_l = 122; // low point density
        
        array<array<array<array<vec3d>>>> xyz_airfoil_mat_ekt(num_epsilon); // index 0: epsilon, index 1: kappa, index 3: tau
        array<array<array<array<double>>>> cp_airfoil_mat_ekt(num_epsilon), x_slicer_mat_ekt(num_epsilon), 
            cp_slicer_mat_ekt(num_epsilon); // index 0: epsilon, index 1: kappa, index 3: tau
        
        array<double> Alpha(1,0.0);
        
        for ( uint8 e = 0; e < num_epsilon; e++ )
        {
            xyz_airfoil_mat_ekt[e].resize(num_kappa);
            cp_airfoil_mat_ekt[e].resize(num_kappa);
            x_slicer_mat_ekt[e].resize(num_kappa);
            cp_slicer_mat_ekt[e].resize(num_kappa);
            
            for ( uint8 k = 0; k < num_kappa; k++ )
            {
                xyz_airfoil_mat_ekt[e][k].resize(num_tau);
                cp_airfoil_mat_ekt[e][k].resize(num_tau);
                x_slicer_mat_ekt[e][k].resize(num_tau);
                cp_slicer_mat_ekt[e][k].resize(num_tau);
                
                for ( uint8 t = 0; t < num_tau; t++ )
                {
                    //==== Epsilon Kappa Tau Study ====//
                    
                    //==== Open and test generated wings ====//
                    string fname = "VKT" + "_e" + double(m_epsilon[e]) + "_k" + double(m_kappa[k]) + "_t" + double(m_tau[t]) + ".vsp3";
                    string fname_res = "VKT" + "_e" + double(m_epsilon[e]) + "_k" + double(m_kappa[k]) + "_t" + double(m_tau[t]) + "_res.csv";

                    Print( string( "Reading in file: " ), false );
                    Print( fname );
                    ReadVSPFile( fname ); // Sets VSP3 file name

                    //==== Analysis: m_VSPSweepAnalysis ====//
                    Print( m_VSPSweepAnalysis );

                    //==== Analysis: VSPAero Compute Geometry to Create Vortex Lattice DegenGeom File ====//
                    Print( m_CompGeomAnalysis );

                    // Set defaults
                    SetAnalysisInputDefaults( m_CompGeomAnalysis );

                    // list inputs, type, and current values
                    PrintAnalysisInputs( m_CompGeomAnalysis );

                    array<int> panel_analysis(1, VSPAERO_ANALYSIS_METHOD::PANEL);
                    SetIntAnalysisInput( m_CompGeomAnalysis, "AnalysisMethod", panel_analysis );

                    // Execute
                    Print( "\tExecuting..." );
                    string compgeom_resid = ExecAnalysis( m_CompGeomAnalysis );
                    Print( "COMPLETE" );

                    // Get & Display Results
                    PrintResults( compgeom_resid );

                    //==== Analysis: VSPAero Single Point ====//
                    // Set defaults
                    SetAnalysisInputDefaults(m_VSPSweepAnalysis);
                    Print(m_VSPSweepAnalysis);

                    SetIntAnalysisInput( m_VSPSweepAnalysis, "GeomSet", m_GeomVec, 0 );
                    SetIntAnalysisInput(m_VSPSweepAnalysis, "RefFlag", m_RefFlagVec, 0);
                    //SetIntAnalysisInput(m_VSPSweepAnalysis, "Symmetry", m_SymFlagVec, 0); // TODO: Add symmetry if VSPAERO doesn't crash

                    array< string > wid = FindGeomsWithName( "WingGeom" );
                    SetStringAnalysisInput(m_VSPSweepAnalysis, "WingID", wid, 0);

                    SetIntAnalysisInput( m_VSPSweepAnalysis, "AnalysisMethod", panel_analysis );

                    // Freestream Parameters
                    SetDoubleAnalysisInput(m_VSPSweepAnalysis, "AlphaStart", Alpha, 0);
                    array<int> AlphaNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "AlphaNpts", AlphaNpts, 0);
                    SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachStart", m_MachVec, 0);
                    array<int> MachNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "MachNpts", MachNpts, 0);
                    SetIntAnalysisInput(m_VSPSweepAnalysis, "WakeNumIter", m_WakeIterVec, 0);

                    Update();

                    // list inputs, type, and current values
                    PrintAnalysisInputs(m_VSPSweepAnalysis);
                    Print( "" );

                    // Execute
                    Print( "\tExecuting..." );
                    string rid = ExecAnalysis( m_VSPSweepAnalysis );
                    Print( "COMPLETE" );

                    // Get & Display Results
                    PrintResults( rid );
                    WriteResultsCSVFile( rid, fname_res );
                    
                    // Calculate Analytical Solution
                    xyz_airfoil_mat_ekt[e][k][t] = GetVKTAirfoilPnts( npts_l, Alpha[0], m_epsilon[e], m_kappa[k], m_tau[t]*(pi/180) );
                    cp_airfoil_mat_ekt[e][k][t] = GetVKTAirfoilCpDist( Alpha[0], m_epsilon[e], m_kappa[k], m_tau[t]*(pi/180), xyz_airfoil_mat_ekt[e][k][t] );
                    
                    // Setup and Execute CpSlicer
                    Print( string( "\tGenerating Cp Slices...\n" ) );
                    
                    //==== Analysis: CpSlicer ====//
                    Print( m_CpSliceAnalysis );

                    // Set defaults
                    SetAnalysisInputDefaults( m_CpSliceAnalysis );

                    // Indicate VSPAERO analysis type for CpSlicer (dCp vs. Cp Results)
                    SetIntAnalysisInput( m_CpSliceAnalysis, "AnalysisMethod", panel_analysis );
                    
                    // Setup cuts
                    SetDoubleAnalysisInput( m_CpSliceAnalysis, "YSlicePosVec", cut, 0 );

                    // list inputs, type, and current values
                    PrintAnalysisInputs( m_CpSliceAnalysis );
                    Print( "" );

                    // Execute
                    Print( "\tExecuting..." );
                    string sid = ExecAnalysis( m_CpSliceAnalysis );
                    Print( "COMPLETE" );

                    // Get & Display Results
                    PrintResults( sid );
                    Print( string( "" ) );
                    
                    array<string> rid_vec = GetStringResults( sid, "CpSlice_Case_ID_Vec" );
                    if ( rid_vec.length() >= 1 )
                    {
                        x_slicer_mat_ekt[e][k][t] = GetDoubleResults( rid_vec[0], "X_Loc" );
                        cp_slicer_mat_ekt[e][k][t] = GetDoubleResults( rid_vec[0], "Cp" );
                    }
                    
                    ClearVSPModel();
                }
            }
        }

        for ( uint8 e = 0; e < num_epsilon; e++ )
        {
            for ( uint8 k = 0; k < num_kappa; k++ )
            {
                for ( uint8 t = 0; t < num_tau; t++ )
                {
                    // Panel: C_P distribution of Karman-Trefftz Airfoil for Epsilon Kappa Tau Study:
                    if ( cp_slicer_mat_ekt[e][k][t].length() != 0 && cp_airfoil_mat_ekt[e][k][t].length() != 0 )
                    {
                        HTMLChart c1 = HTMLChart();
                        c1.m_ChartName = string("Cp_Epsilon") + int(e) + string("_Kappa") + int(k) + string("_Tau") + double(m_tau[t]);
                        c1.m_Title = string("Figure ") + m_FigCount + string(". VKT Cp Distribution at Y = 0: Epsilon = ") + double(m_epsilon[e]) + string(", Kappa = ") + 
                            double(m_kappa[k]) + string(", Tau = ") + double(m_tau[t]) + string("°");
                        m_FigCount++;
                        c1.m_Legend = string("'X','Airfoil','Exact','VSPAERO'");
                        c1.m_Series = string("{ 0: { targetAxisIndex: 1, lineWidth: 0 }, 1: { targetAxisIndex: 0, pointSize: 0, color: 'gray', lineWidth: 5 }, 2: { targetAxisIndex: 0} }");
                        c1.m_LegendPos = string("bottom");
                        c1.m_HAxisString = string("hAxis: { title: 'Chord Location (X)', titleTextStyle: { bold: true } }");
                        c1.m_VAxisString = string("vAxes: { 0: { title: 'Cp', titleTextStyle: { bold: true } }, 1: { title: 'Z', titleTextStyle: { bold: true } } }");
                        
                        // Note: int k used for kappa and epsilon in chart name, since decimal values not supported in names
                        array<string> data_str_vec1;
                        
                        // Google Charts API only supports a single X data vector. The x data from GetVKTAirfoilData and the CpSlicer are different, so must be offset
                        for( uint x = 0; x < xyz_airfoil_mat_ekt[e][k][t].length(); x++ )
                        {
                            string row = string("\t\t  [") + double( xyz_airfoil_mat_ekt[e][k][t][x][0] ) + string(",") + double( xyz_airfoil_mat_ekt[e][k][t][x][1] ) +
                                string(",") + double( cp_airfoil_mat_ekt[e][k][t][x] ) + string(",null],\n");
                            
                            data_str_vec1.push_back( row );
                        }
                        
                        for( uint x = 0; x < x_slicer_mat_ekt[e][k][t].length(); x++ )
                        {
                            string row = string("\t\t  [") + double( x_slicer_mat_ekt[e][k][t][x] ) + string(",null,null,") + double( cp_slicer_mat_ekt[e][k][t][x] );
                            
                            if ( x == x_slicer_mat_ekt[e][k][t].length() - 1 )
                            {
                                row += string("]\n");
                            }
                            else
                            {
                                row += string("],\n");
                            }
                            
                            data_str_vec1.push_back( row );
                        }
                        
                        c1.WriteChartHead( data_str_vec1 );
                        m_VKTEpsKapTauIDVec.push_back( c1.m_ChartName );
                    }
                }
            }
        }
    }
    
    private void TestVKTUWTessWings()
    {
        Print( string( "-> Begin VKT Tesselation Test:\n" ) );
        
        const uint8 num_TessU = uint8(m_Tess_U.length());
        const uint8 num_TessW = uint8(m_Tess_W.length());
        
        array<array<array<double>>> x_slicer_mat_tess(num_TessU), z_slicer_mat_tess(num_TessU), cp_slicer_mat_tess(num_TessU); // index 0: U Tess, index 1: W Tess
        
        array<double> cut(1, 0.0); // cut at root
        const int npts_l = 122; // low point density
        
        const uint8 e = 0; // epsilon
        const uint8 k = 1; // kappa
        const uint8 t = 1; // tau
        
        array<double> Alpha(1, 0.0);
        
        // Load XFoil's Cp Distribution for VKT with epsilon = 0.1, kappa = 0.1, && tau = 10°
        string xfoil_file_name = GetVSPExePath() + string("/airfoil/XFoil_VKT_CpDist.txt");
        array<vec3d> Xfoil_CpDist = ReadCpDistFile( xfoil_file_name );
        
        // Calculate Analytical Solution
        array<vec3d> xyz_airfoil_mat_tess = GetVKTAirfoilPnts(npts_l, Alpha[0], m_epsilon[e], m_kappa[k], m_tau[t]*(pi/180) );
        array<double> cp_airfoil_mat_tess = GetVKTAirfoilCpDist( Alpha[0], m_epsilon[e], m_kappa[k], m_tau[t]*(pi/180), xyz_airfoil_mat_tess );
        
        //==== Tesselation Study ====//
        for ( uint8 u = 0; u < num_TessU; u++ )
        {
            x_slicer_mat_tess[u].resize(num_TessW);
            z_slicer_mat_tess[u].resize(num_TessW);
            cp_slicer_mat_tess[u].resize(num_TessW);
            
            for ( uint8 w = 0; w < num_TessW; w++ )
            {
                //==== Open and test generated wings ====//
                // Note: *.csv result file name can not be the same as *.vsp3 file name
                string fname = "VKT_U" + int(m_Tess_U[u]) + "_W" + int(m_Tess_W[w]) + ".vsp3";
                string fname_res = "VKT_U" + int(m_Tess_U[u]) + "_W" + int(m_Tess_W[w]) + "_res.csv";

                Print( string( "Reading in file: " ), false );
                Print( fname );
                ReadVSPFile( fname ); // Sets VSP3 file name
                
                //==== Analysis: m_VSPSweepAnalysis ====//
                Print( m_VSPSweepAnalysis );

                //==== Analysis: VSPAero Compute Geometry to Create Vortex Lattice DegenGeom File ====//
                Print( m_CompGeomAnalysis );

                // Set defaults
                SetAnalysisInputDefaults( m_CompGeomAnalysis );

                // list inputs, type, and current values
                PrintAnalysisInputs( m_CompGeomAnalysis );

                array<int> panel_analysis(1, VSPAERO_ANALYSIS_METHOD::PANEL);
                SetIntAnalysisInput( m_CompGeomAnalysis, "AnalysisMethod", panel_analysis );

                // Execute
                Print( "\tExecuting..." );
                string compgeom_resid = ExecAnalysis( m_CompGeomAnalysis );
                Print( "COMPLETE" );

                // Get & Display Results
                PrintResults( compgeom_resid );

                //==== Analysis: VSPAero Single Point ====//
                // Set defaults
                SetAnalysisInputDefaults(m_VSPSweepAnalysis);
                Print(m_VSPSweepAnalysis);

                SetIntAnalysisInput( m_VSPSweepAnalysis, "GeomSet", m_GeomVec, 0 );

                SetIntAnalysisInput( m_VSPSweepAnalysis, "AnalysisMethod", panel_analysis );

                // Freestream Parameters
                SetDoubleAnalysisInput(m_VSPSweepAnalysis, "AlphaStart", Alpha, 0);
                array<int> AlphaNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "AlphaNpts", AlphaNpts, 0);
                SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachStart", m_MachVec, 0);
                array<int> MachNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "MachNpts", MachNpts, 0);
                SetIntAnalysisInput(m_VSPSweepAnalysis, "WakeNumIter", m_WakeIterVec, 0);

                Update();

                // list inputs, type, and current values
                PrintAnalysisInputs( m_VSPSweepAnalysis );
                Print( "" );

                // Execute
                Print( "\tExecuting..." );
                string rid = ExecAnalysis( m_VSPSweepAnalysis );
                Print( "COMPLETE" );

                // Get & Display Results
                PrintResults( rid );
                WriteResultsCSVFile( rid, fname_res );

                // Setup and Execute CpSlicer
                Print( string( "\tGenerating Cp Slices...\n" ) );
                
                //==== Analysis: CpSlicer ====//
                Print( m_CpSliceAnalysis );

                // Set defaults
                SetAnalysisInputDefaults( m_CpSliceAnalysis );
                
                // Indicate VSPAERO analysis type for CpSlicer (dCp vs. Cp Results)
                SetIntAnalysisInput( m_CpSliceAnalysis, "AnalysisMethod", panel_analysis );
                
                // Setup cuts
                SetDoubleAnalysisInput( m_CpSliceAnalysis, "YSlicePosVec", cut, 0 );

                // list inputs, type, and current values
                PrintAnalysisInputs( m_CpSliceAnalysis );
                Print( "" );

                // Execute
                Print( "\tExecuting..." );
                string sid = ExecAnalysis( m_CpSliceAnalysis );
                Print( "COMPLETE" );

                // Get & Display Results
                PrintResults( sid );
                Print( string( "" ) );
                
                array<string> rid_vec = GetStringResults( sid, "CpSlice_Case_ID_Vec" );
                if ( rid_vec.size() > 0 )
                {
                    x_slicer_mat_tess[u][w] = GetDoubleResults( rid_vec[0], "X_Loc" );
                    z_slicer_mat_tess[u][w] = GetDoubleResults( rid_vec[0], "Z_Loc" );
                    cp_slicer_mat_tess[u][w] = GetDoubleResults( rid_vec[0], "Cp" );
                }

                ClearVSPModel();
            }
        }
        
        // Chord Tesselation Sensitivity Study:
        for ( uint8 u = 0; u < num_TessU; u++ )
        {
            HTMLChart c1 = HTMLChart();
            c1.m_ChartName = string("VKT_Cp_U") + int(m_Tess_U[u]);
            c1.m_Title = string("Figure ") + m_FigCount + string(". VKT Cp distribution at Y = 0 Chord Tesselation (W Tess) Sensitivity: ") + string("Span Tess = ") + int(m_Tess_U[u]);
            m_FigCount++;
            c1.m_Legend = string("'X','Airfoil','Exact','W Tess: 17','W Tess: 29','W Tess: 51','XFoil'");
            //c1.m_PointSize = 3.0;
            c1.m_Series = string("{ 0: { targetAxisIndex: 1, lineWidth: 0 }, 1: { targetAxisIndex: 0, pointSize: 0, color: 'gray', lineWidth: 5 }, 2: { targetAxisIndex: 0 }, 3: { targetAxisIndex: 0 }, 4: { targetAxisIndex: 0, color: 'red' }, 5: { targetAxisIndex: 0, color: 'purple' } }");
            c1.m_LegendPos = string("bottom");
            c1.m_HAxisString = string("hAxis: { title: 'Chord Location (X)', titleTextStyle: { bold: true } }");
            c1.m_VAxisString = string("vAxes: { 0: { title: 'Cp', titleTextStyle: { bold: true } }, 1: { title: 'Z', titleTextStyle: { bold: true } } }");
            
            array<string> data_str_vec1;
            
            // Google Charts API only supports a single X data vector. The x data from GetVKTAirfoilData and the CpSlicer are different, so must be offset
            for( uint x = 0; x < xyz_airfoil_mat_tess.length(); x++ ) // airfoil and exact solution
            {
                string row = string("\t\t  [") + double( xyz_airfoil_mat_tess[x][0] ) + string(",") + double( xyz_airfoil_mat_tess[x][1] ) +
                string(",") + double( cp_airfoil_mat_tess[x] ) + string(",null,null,null,null],\n");
                
                data_str_vec1.push_back( row );
            }
            
            for( uint x = 0; x < Xfoil_CpDist.length(); x++ ) // XFoil
            {
                string row = string("\t\t  [") + double( Xfoil_CpDist[x][0] ) + string(",null,null,null,null,null,") + 
                    double(Xfoil_CpDist[x][2]) + string("],\n");
                
                data_str_vec1.push_back( row );
            }
            
            for( uint x = 0; x < x_slicer_mat_tess[u][0].length(); x++ ) // Chord Tess = 20
            {
                string row = string("\t\t  [") + double( x_slicer_mat_tess[u][0][x] ) + string(",null,null,") + 
                    double( cp_slicer_mat_tess[u][0][x] )+ string(",null,null,null],\n") ;
                
                data_str_vec1.push_back( row );
            }
            
            for( uint x = 0; x < x_slicer_mat_tess[u][1].length(); x++ ) // Chord Tess = 50
            {
                string row = string("\t\t  [") + double( x_slicer_mat_tess[u][1][x] ) + string(",null,null,null,") + 
                    double( cp_slicer_mat_tess[u][1][x] )+ string(",null,null],\n") ;
                
                data_str_vec1.push_back( row );
            }
            
            for( uint x = 0; x < x_slicer_mat_tess[u][2].length(); x++ ) // Chord Tess = 80
            {
                string row = string("\t\t  [") + double( x_slicer_mat_tess[u][2][x] ) + string(",null,null,null,null,") + 
                double( cp_slicer_mat_tess[u][2][x] )+ string(",null") ;
                
                if ( x == x_slicer_mat_tess[u][2].length() - 1 )
                {
                    row += string("]\n");
                }
                else
                {
                    row += string("],\n");
                }
                
                data_str_vec1.push_back( row );
            }
            
            c1.WriteChartHead( data_str_vec1 );
            m_VKTWTessIDVec.push_back( c1.m_ChartName );
        }
        
        // Span Teselation Sensitivity Study:
        for ( uint8 w = 0; w < num_TessW; w++ )
        {
            HTMLChart c1 = HTMLChart();
            c1.m_ChartName = string("VKT_Cp_W") + int(m_Tess_W[w]);
            c1.m_Title = string("Figure ") + m_FigCount + string(". VKT Cp distribution at Y = 0 Span Tesselation (U Tess) Sensitivity: ") + string("Chord Tess = ") + int(m_Tess_W[w]);
            m_FigCount++;
            c1.m_Legend = string("'X','Airfoil','Exact','U Tess: 12','U Tess: 20','U Tess: 41','XFoil'");
            //c1.m_PointSize = 3.0;
            c1.m_Series = string("{ 0: { targetAxisIndex: 1, lineWidth: 0 }, 1: { targetAxisIndex: 0, pointSize: 0, color: 'gray', lineWidth: 5 }, 2: { targetAxisIndex: 0 }, 3: { targetAxisIndex: 0 }, 4: { targetAxisIndex: 0, color: 'red' }, 5: { targetAxisIndex: 0, color: 'purple' } }");
            c1.m_LegendPos = string("bottom");
            c1.m_HAxisString = string("hAxis: { title: 'Chord Location (X)', titleTextStyle: { bold: true } }");
            c1.m_VAxisString = string("vAxes: { 0: { title: 'Cp', titleTextStyle: { bold: true } }, 1: { title: 'Z', titleTextStyle: { bold: true } } }");
            
            array<string> data_str_vec1;
            
            // Google Charts API only supports a single X data vector. The x data from GetVKTAirfoilData and the CpSlicer are different, so must be offset
            for( uint x = 0; x < xyz_airfoil_mat_tess.length(); x++ ) // airfoil and exact solution
            {
                string row = string("\t\t  [") + double( xyz_airfoil_mat_tess[x][0] ) + string(",") + double( xyz_airfoil_mat_tess[x][1] ) +
                string(",") + double( cp_airfoil_mat_tess[x] ) + string(",null,null,null,null],\n");
                
                data_str_vec1.push_back( row );
            }
            
            for( uint x = 0; x < Xfoil_CpDist.length(); x++ ) // XFoil
            {
                string row = string("\t\t  [") + double( Xfoil_CpDist[x][0] ) + string(",null,null,null,null,null,") + 
                    double(Xfoil_CpDist[x][2]) + string("],\n");
                
                data_str_vec1.push_back( row );
            }
            
            for( uint x = 0; x < x_slicer_mat_tess[0][w].length(); x++ ) // Span Tess = 20
            {
                string row = string("\t\t  [") + double( x_slicer_mat_tess[0][w][x] ) + string(",null,null,") + 
                    double( cp_slicer_mat_tess[0][w][x] )+ string(",null,null,null],\n") ;
                
                data_str_vec1.push_back( row );
            }
            
            for( uint x = 0; x < x_slicer_mat_tess[1][w].length(); x++ ) // Span Tess = 60
            {
                string row = string("\t\t  [") + double( x_slicer_mat_tess[1][w][x] ) + string(",null,null,null,") + 
                    double( cp_slicer_mat_tess[1][w][x] )+ string(",null,null],\n") ;
                
                data_str_vec1.push_back( row );
            }
            
            for( uint x = 0; x < x_slicer_mat_tess[2][w].length(); x++ ) // Span Tess = 100
            {
                string row = string("\t\t  [") + double( x_slicer_mat_tess[2][w][x] ) + string(",null,null,null,null,") + 
                double( cp_slicer_mat_tess[2][w][x] )+ string(",null") ;
                
                if ( x == x_slicer_mat_tess[2][w].length() - 1 )
                {
                    row += string("]\n");
                }
                else
                {
                    row += string("],\n");
                }
                
                data_str_vec1.push_back( row );
            }
            
            c1.WriteChartHead( data_str_vec1 );
            m_VKTUTessIDVec.push_back( c1.m_ChartName );
        }
    }
    
    // VTK Epsilon Kappa Tau Class Properties
    //=== Wing Parameters ===//
    private array<double> m_epsilon(2); // thickness
    private array<double> m_kappa(2); 
    private array<double> m_tau(2); // deg 
    private array<double> m_Tess_U(3);
    private array<double> m_Tess_W(3);
    
    array<string> m_VKTEpsKapTauIDVec;
    array<string> m_VKTUTessIDVec;
    array<string> m_VKTWTessIDVec;
}

//============= Ellipsoid Study Functions ==============//
class EllipsoidVV : VV_Base
{
    EllipsoidVV()
    {
        m_alpha_vec[0] = 0.0;
        m_alpha_vec[1] = 20.0;
        
        m_beta_vec[0] = 0.0;
        m_beta_vec[1] = 20.0;
    }
    
    void EllipsoidStudy()
    {
        m_runflag = true;
        
        //====Generate Ellipsoids====//
        // Generate ellipsoids of various orientation to test //
        GenerateEllipsoids();

        //====Test Ellipsoids====//
        TestEllipsoids();
    }

    private void GenerateEllipsoids()
    {
        //==== One Ellipsoid Geom, Different File Names for Flow Conditions ====//
        string geom_id = AddGeom( "ELLIPSOID", "" );

        SetParmVal( geom_id, "X_Rel_Location", "XForm", -1.0 );
        SetParmVal( geom_id, "A_Radius", "Design", 1.0 );
        SetParmVal( geom_id, "B_Radius", "Design", 2.0 );
        SetParmVal( geom_id, "C_Radius", "Design", 3.0 );
        SetParmVal( geom_id, "Tess_U", "Shape", 40 );
        SetParmVal( geom_id, "Tess_W", "Shape", 41 );

        Update();

        for ( uint8 a = 0; a < uint8(m_alpha_vec.length()); a++ )
        {
            for ( uint8 b = 0; b < uint8(m_beta_vec.length()); b++ )
            {
                //==== Setup export filenames ====//
                string fname = "Ellipsoid_alpha" + double(m_alpha_vec[a]) + "_beta" + double(m_beta_vec[b]) + ".vsp3";

                //==== Save Vehicle to File ====//
                string message = string("-->Saving vehicle file to: ") + fname + string("\n");
                Print(message );
                WriteVSPFile( fname, SET_ALL );
                Print( "COMPLETE\n" );
            }
        }
        
        ClearVSPModel();
    }

    private void TestEllipsoids()
    {
        Print( string( "-> Begin Ellipsoid Test:\n" ) );
        
        const uint8 num_alpha = uint8(m_alpha_vec.length());
        const uint8 num_beta = uint8(m_beta_vec.length());
        
        const vec3d center = vec3d(0.0, 0.0, 0.0);
        const vec3d abc_rad = vec3d(1.0, 2.0, 3.0);
        const double V_inf = 100.0;
        
        const int npts = 101;
        
        // Setup cuts
        array<double> cut(1, 0.0);
        
        array<array<array<array<double>>>> cp_slicer_mat(num_alpha), xyz_slicer_mat(num_alpha), ellipsoid_cp_mat(num_alpha);
        
        array<vec3d> x_slice_pnt_vec(npts), y_slice_pnt_vec(npts), z_slice_pnt_vec(npts);

        array<double> theta_vec(npts);
        theta_vec[0] = 0;
        
        for ( uint i = 1; i < npts; i++ )
        {
            theta_vec[i] = theta_vec[i-1] + (2*pi/(npts-1));
        }
        
        for ( uint i = 0; i < npts; i++ )
        {
            x_slice_pnt_vec[i] = vec3d(0,abc_rad[1]*cos(theta_vec[i]),abc_rad[2]*sin(theta_vec[i]));
            y_slice_pnt_vec[i] = vec3d(abc_rad[0]*cos(theta_vec[i]),0,abc_rad[2]*sin(theta_vec[i]));
            z_slice_pnt_vec[i] = vec3d(abc_rad[0]*cos(theta_vec[i]),abc_rad[1]*sin(theta_vec[i]),0);
        }
        
        for ( uint8 a = 0; a < num_alpha; a++ )
        {
            cp_slicer_mat[a].resize(num_beta);
            xyz_slicer_mat[a].resize(num_beta);
            ellipsoid_cp_mat[a].resize(num_beta);
            
            for ( uint8 b = 0; b < num_beta; b++ )
            {
                xyz_slicer_mat[a][b].resize(3);
                cp_slicer_mat[a][b].resize(3);
                ellipsoid_cp_mat[a][b].resize(3);
                
                //==== Open and test generated ellipsoids ====//
                string fname = "Ellipsoid_alpha" + double(m_alpha_vec[a]) + "_beta" + double(m_beta_vec[b]) + ".vsp3";
                string fname_res = "Ellipsoid_alpha" + double(m_alpha_vec[a]) + "_beta" + double(m_beta_vec[b]) + "_res.csv";
                
                //==== Open and test generated wings ====//
                Print( string( "Reading in file: " ), false );
                Print( fname );
                ReadVSPFile( fname ); // Sets VSP3 file name

                //==== Analysis: m_VSPSweepAnalysis ====//
                Print( m_VSPSweepAnalysis );

                //==== Analysis: VSPAero Compute Geometry to Create Vortex Lattice DegenGeom File ====//
                Print( m_CompGeomAnalysis );

                // Set defaults
                SetAnalysisInputDefaults( m_CompGeomAnalysis );

                // list inputs, type, and current values
                PrintAnalysisInputs( m_CompGeomAnalysis );

                array<int> panel_analysis(1,VSPAERO_ANALYSIS_METHOD::PANEL );
                SetIntAnalysisInput( m_CompGeomAnalysis, "AnalysisMethod", panel_analysis );

                // Execute
                Print( "\tExecuting..." );
                string compgeom_resid = ExecAnalysis( m_CompGeomAnalysis );
                Print( "COMPLETE" );

                // Get & Display Results
                PrintResults( compgeom_resid );

                //==== Analysis: VSPAero Single Point ====//
                // Set defaults
                SetAnalysisInputDefaults(m_VSPSweepAnalysis);
                Print(m_VSPSweepAnalysis);

                // Note: Symmetry will cause VSPAERO to crash

                // Freestream Parameters
                array<double> Alpha(1, m_alpha_vec[a]);
                array<double> Beta(1, m_beta_vec[b]);
                SetDoubleAnalysisInput(m_VSPSweepAnalysis, "AlphaStart", Alpha, 0);
                array<int> AlphaNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "AlphaNpts", AlphaNpts, 0);
                SetDoubleAnalysisInput(m_VSPSweepAnalysis, "BetaStart", Beta, 0);
                
                array<double> Mach(1, 0.0);
                SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachStart", Mach, 0);
                array<int> MachNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "MachNpts", MachNpts, 0);
                
                SetIntAnalysisInput( m_VSPSweepAnalysis, "GeomSet", m_GeomVec, 0 ); // Reference geometry set
                SetIntAnalysisInput( m_VSPSweepAnalysis, "AnalysisMethod", panel_analysis );
                SetIntAnalysisInput(m_VSPSweepAnalysis, "WakeNumIter", m_WakeIterVec, 0);

                Update();

                // list inputs, type, and current values
                PrintAnalysisInputs( m_VSPSweepAnalysis );
                Print( "" );

                // Execute
                Print( "\tExecuting..." );
                string rid = ExecAnalysis( m_VSPSweepAnalysis );
                Print( "COMPLETE" );

                // Get & Display Results
                PrintResults( rid );
                WriteResultsCSVFile( rid, fname_res );
                
                // Setup and Execute CpSlicer
                Print( string( "\tGenerating Cp Slices...\n" ) );
                
                //==== Analysis: CpSlicer ====//
                Print( m_CpSliceAnalysis );

                // Set defaults
                SetAnalysisInputDefaults( m_CpSliceAnalysis );
                
                // Indicate VSPAERO analysis type for CpSlicer (dCp vs. Cp Results)
                SetIntAnalysisInput( m_CpSliceAnalysis, "AnalysisMethod", panel_analysis );
                
                // Set cuts
                SetDoubleAnalysisInput( m_CpSliceAnalysis, "XSlicePosVec", cut, 0 );
                SetDoubleAnalysisInput( m_CpSliceAnalysis, "YSlicePosVec", cut, 0 );
                SetDoubleAnalysisInput( m_CpSliceAnalysis, "ZSlicePosVec", cut, 0 );

                // list inputs, type, and current values
                PrintAnalysisInputs( m_CpSliceAnalysis );
                Print( "" );

                // Execute
                Print( "\tExecuting..." );
                string sid = ExecAnalysis( m_CpSliceAnalysis );
                Print( "COMPLETE" );

                // Get & Display Results
                PrintResults( sid );
                Print( string( "" ) );

                array<string> rid_vec = GetStringResults( sid, "CpSlice_Case_ID_Vec" );

                if ( rid_vec.length() > 0 )
                {
                    // X Cut
                    xyz_slicer_mat[a][b][0] = GetDoubleResults( rid_vec[0], "Y_Loc" );
                    cp_slicer_mat[a][b][0] =  GetDoubleResults( rid_vec[0], "Cp" );
                    
                    // Y Cut
                    xyz_slicer_mat[a][b][1] = GetDoubleResults( rid_vec[1], "X_Loc" );
                    cp_slicer_mat[a][b][1] =  GetDoubleResults( rid_vec[1], "Cp" );

                    // Z Cut
                    xyz_slicer_mat[a][b][2] = GetDoubleResults( rid_vec[2], "X_Loc" );
                    cp_slicer_mat[a][b][2] =  GetDoubleResults( rid_vec[2], "Cp" );
                }
                
                // Calculate theoretical distribution
                vec3d V_vec = vec3d((V_inf*cos(Deg2Rad(m_alpha_vec[a]))*cos(Deg2Rad(m_beta_vec[b]))), (V_inf*sin(Deg2Rad(m_beta_vec[b]))), (V_inf*sin(Deg2Rad(m_alpha_vec[a]))*cos(Deg2Rad(m_beta_vec[b]))));
                ellipsoid_cp_mat[a][b][0] = GetEllipsoidCpDist(x_slice_pnt_vec, abc_rad, V_vec);
                ellipsoid_cp_mat[a][b][1] = GetEllipsoidCpDist(y_slice_pnt_vec, abc_rad, V_vec);
                ellipsoid_cp_mat[a][b][2] = GetEllipsoidCpDist(z_slice_pnt_vec, abc_rad, V_vec);
                
                ClearVSPModel();
            }
        }
        
        uint8 run_cnt = 1;

        for ( uint8 a = 0; a < num_alpha; a++ )
        {
            for ( uint8 b = 0; b < num_beta; b++ )
            {
                if ( ellipsoid_cp_mat[a][b][0].length() != 0 && cp_slicer_mat[a][b][0].length() != 0 )
                {
                    // X Cut
                    array<string> data_str_vec1;

                    //Google Charts API only supports a single X data vector. The x data from GetFeatureLinePnts and the CpSlicer are different, so must be offset
                    for( uint x = 0; x < uint(x_slice_pnt_vec.length()); x++ )
                    {
                        string row = string("\t\t  [") + double( x_slice_pnt_vec[x][1] ) + string(",") + double( x_slice_pnt_vec[x][2] ) +
                            string(",") + double( ellipsoid_cp_mat[a][b][0][x] ) + string(",null],\n");
                        
                        data_str_vec1.push_back( row );
                    }
                    
                    for( uint x = 0; x < uint(xyz_slicer_mat[a][b][0].length()); x++ )
                    {
                        string row = string("\t\t  [") + double( xyz_slicer_mat[a][b][0][x] ) + string(",null") +
                            string(",null,") + double( cp_slicer_mat[a][b][0][x] );
                        
                        if ( x == xyz_slicer_mat[a][b][0].length() - 1 )
                        {
                            row += string("]\n");
                        }
                        else
                        {
                            row += string("],\n");
                        }
                        
                        data_str_vec1.push_back( row );
                    }
                    
                    HTMLChart c1 = HTMLChart();
                    c1.m_ChartName = string("Cp_ellipse_alpha") + double(m_alpha_vec[a]) + string("_beta") + double(m_beta_vec[b]) + string("_X");
                    c1.m_Title = string("Figure ") + m_FigCount + string(". Run #") + run_cnt + string(" Ellipsoid Cp Distribution at X = 0: Alpha = ") + double(m_alpha_vec[a]) + string("°, Beta = ") + 
                        double(m_beta_vec[b]) + string("°");
                    m_FigCount++;
                    c1.m_Legend = string("'X','Ellipsoid','Exact Solution','VSPAERO'");
                    c1.m_PointSize = 3.0;
                    c1.m_Series = string("{ 0: { targetAxisIndex: 1, lineWidth: 2, pointSize: 0 }, 1: { targetAxisIndex: 0, lineWidth: 3, pointSize: 0, color: 'gray' }, 2: { targetAxisIndex: 0, lineWidth: 0 } }");
                    c1.m_LegendPos = string("bottom");
                    c1.m_HAxisString = string("hAxis: { title: 'Y', titleTextStyle: { bold: true }, viewWindow: { min: -3.0, max: 3.0 } }");
                    c1.m_VAxisString = string("vAxes: { 0: { title: 'Cp', titleTextStyle: { bold: true }, viewWindow: { min: -5.0, max: 1.5 } }, 1: { title: 'Z', titleTextStyle: { bold: true }, viewWindow: { min: -3.0, max: 3.0 } } }");
                    
                    c1.WriteChartHead( data_str_vec1 );
                    m_EllipsoidIDVec.push_back( c1.m_ChartName );
                    
                    // Y Cut
                    array<string> data_str_vec2;
                    
                    //Google Charts API only supports a single X data vector. The x data from GetFeatureLinePnts and the CpSlicer are different, so must be offset
                    for( uint x = 0; x < uint(y_slice_pnt_vec.length()); x++ )
                    {
                        string row = string("\t\t  [") + double( y_slice_pnt_vec[x][0] ) + string(",") + + double( y_slice_pnt_vec[x][2] ) +
                            string(",") + double( ellipsoid_cp_mat[a][b][1][x] ) + string(",null],\n");
                        
                        data_str_vec2.push_back( row );
                    }
                    
                    for( uint x = 0; x < uint(xyz_slicer_mat[a][b][1].length()); x++ )
                    {
                        string row = string("\t\t  [") + double( xyz_slicer_mat[a][b][1][x] ) + string(",null") +
                            string(",null,") + double( cp_slicer_mat[a][b][1][x] );
                        
                        if ( x == xyz_slicer_mat[a][b][1].length() - 1 )
                        {
                            row += string("]\n");
                        }
                        else
                        {
                            row += string("],\n");
                        }
                        
                        data_str_vec2.push_back( row );
                    }
                    
                    HTMLChart c2 = HTMLChart();
                    c2.m_ChartName = string("Cp_ellipse_alpha") + double(m_alpha_vec[a]) + string("_beta") + double(m_beta_vec[b]) + string("_Y");
                    c2.m_Title = string("Figure ") + m_FigCount + string(". Run #") + run_cnt + string(" Ellipsoid Cp Distribution at Y = 0: Alpha = ") + double(m_alpha_vec[a]) + string("°, Beta = ") + 
                        double(m_beta_vec[b]) + string("°");
                    m_FigCount++;
                    c2.m_Legend = string("'X','Ellipsoid','Exact Solution','VSPAERO'");
                    c2.m_PointSize = 3.0;
                    c2.m_Series = string("{ 0: { targetAxisIndex: 1, lineWidth: 2, pointSize: 0 }, 1: { targetAxisIndex: 0, lineWidth: 3, pointSize: 0, color: 'gray' }, 2: { targetAxisIndex: 0, lineWidth: 0 } }");
                    c2.m_LegendPos = string("bottom");
                    c2.m_HAxisString = string("hAxis: { title: 'X', titleTextStyle: { bold: true }, viewWindow: { min: -3.0, max: 3.0 } }");
                    c2.m_VAxisString = string("vAxes: { 0: { title: 'Cp', titleTextStyle: { bold: true }, viewWindow: { min: -5.0, max: 1.5 } }, 1: { title: 'Z', titleTextStyle: { bold: true }, viewWindow: { min: -3.0, max: 3.0 } } }");
                    
                    c2.WriteChartHead( data_str_vec2 );
                    m_EllipsoidIDVec.push_back( c2.m_ChartName );

                    // Z Cut
                    array<string> data_str_vec3;
                    
                    for( uint x = 0; x < uint(z_slice_pnt_vec.length()); x++ )
                    {
                        string row = string("\t\t  [") + double( z_slice_pnt_vec[x][0] ) + string(",") + double( z_slice_pnt_vec[x][1] ) +
                            string(",") + double( ellipsoid_cp_mat[a][b][2][x] ) + string(",null],\n");
                        
                        data_str_vec3.push_back(row);
                    }
                    
                    for( uint x = 0; x < uint(xyz_slicer_mat[a][b][2].length()); x++ )
                    {
                        string row = string("\t\t  [") + double( xyz_slicer_mat[a][b][2][x] ) + string(",null") +
                            string(",null,") + double( cp_slicer_mat[a][b][2][x] );
                        
                        if ( x == xyz_slicer_mat[a][b][2].length() - 1 )
                        {
                            row += string("]\n");
                        }
                        else
                        {
                            row += string("],\n");
                        }
                        
                        data_str_vec3.push_back(row);
                    }
                    
                    HTMLChart c3 = HTMLChart();
                    c3.m_ChartName = string("Cp_ellipse_alpha") + double(m_alpha_vec[a]) + string("_beta") + double(m_beta_vec[b]) + string("_Z");
                    c3.m_Title = string("Figure ") + m_FigCount + string(". Run #") + run_cnt + string(" Ellipsoid Cp Distribution at Z = 0: Alpha = ") + double(m_alpha_vec[a]) + string("°, Beta = ") + 
                        double(m_beta_vec[b]) + string("°");
                    m_FigCount++;
                    c3.m_Legend = string("'X','Ellipsoid','Exact Solution','VSPAERO'");
                    c3.m_PointSize = 3.0;
                    c3.m_Series = string("{ 0: { targetAxisIndex: 1, lineWidth: 2, pointSize: 0 }, 1: { targetAxisIndex: 0, lineWidth: 3, pointSize: 0, color: 'gray' }, 2: { targetAxisIndex: 0, lineWidth: 0 } }");
                    c3.m_LegendPos = string("bottom");
                    c3.m_HAxisString = string("hAxis: { title: 'X', titleTextStyle: { bold: true }, viewWindow: { min: -3.0, max: 3.0 } }");
                    c3.m_VAxisString = string("vAxes: { 0: { title: 'Cp', titleTextStyle: { bold: true }, viewWindow: { min: -5.0, max: 1.5 } }, 1: { title: 'Y', titleTextStyle: { bold: true }, viewWindow: { min: -3.0, max: 3.0 } } }");
                    
                    c3.WriteChartHead( data_str_vec3 );
                    m_EllipsoidIDVec.push_back( c3.m_ChartName );
                    
                    run_cnt++;
                }
            }
        }
    }
    
    // Ellipsoid Class Properties

    // Flow Condition Parameters
    private array<double> m_alpha_vec(2);
    private array<double> m_beta_vec(2);
    
    array<string> m_EllipsoidIDVec;
}

//============= Supersonic Delta Wing Study Functions ==============//
class SDVV : VV_Base
{
    SDVV()
    {
        m_Sweep[0] = 45;
        m_Sweep[1] = 65;
        
        m_SuperMachVec[0] = 1.1347;
        m_SuperMachVec[1] = 1.4662;
        m_SuperMachVec[2] = 1.8939;
        m_SuperMachVec[3] = 2.3662; // Note m_SuperMachVec[3] = 2.366 will cause VSPAERO to crash for the 65° swept delta wing;
        m_SuperMachVec[4] = 2.8611;
        m_SuperMachVec[5] = 3.3686;
        m_SuperMachVec[6] = 3.8838;
        m_SuperMachVec[7] = 4.4040;
    }
    
    void TestSupersonicDeltaWing()
    {
        m_runflag = true;
        
        //====Generate Supersonic Delta Wings====//
        GenerateSupersonicDeltaWings();
        
        //====Test Supersonic Delta Wings====//
        TestSupersonicDeltaWings();
    }

    private void GenerateSupersonicDeltaWings()
    {
        //==== Add Wing ====//
        string wing_id = AddGeom( "WING", "" );

        //==== Set Wing Section ====//
        SetDriverGroup( wing_id, 1, SPAN_WSECT_DRIVER, ROOTC_WSECT_DRIVER, TIPC_WSECT_DRIVER );
        
        Update();

        //==== Set Airfoil to NACA 0012 Airfoil and Set Common Parms====//
        SetParmVal( wing_id, "ThickChord", "XSecCurve_0", 0.04 );
        SetParmVal( wing_id, "ThickChord", "XSecCurve_1", 0.06 );
        SetParmVal( wing_id, "Sweep_Location", "XSec_1", 0 );
        SetParmVal( wing_id, "Sec_Sweep_Location", "XSec_1", 1 );
        SetParmVal( wing_id, "Tip_Chord", "XSec_1", 1 );
        SetParmVal( wing_id, "Span", "XSec_1", 10.00004 );
        SetParmVal( wing_id, "SectTess_U", "XSec_1", 30 );
        SetParmVal( wing_id, "TECluster", "WingGeom", 1.0 );
        SetParmVal( wing_id, "LECluster", "WingGeom", 1.0 );
        
        Update();
        
        for ( uint8 s = 0; s < uint8(m_Sweep.length()); s++ )
        {
            SetParmVal( wing_id, "Sweep", "XSec_1", m_Sweep[s] );
            
            if (m_Sweep[s] == 45)
            {
                SetParmVal( wing_id, "Root_Chord", "XSec_1", 11 );
            }
            else if (m_Sweep[s] == 65)
            {
                SetParmVal( wing_id, "Root_Chord", "XSec_1", 22.39583 );
            }

            Update();
            
            for ( uint8 m = 0; m < uint8(m_SuperMachVec.length()); m++ )
            {
                //==== Setup export filenames ====//
                string fname = "Supersonic_Delta_Wing_Sweep" + int(m_Sweep[s]) + string("_Mach") + int(m) + ".vsp3";

                //==== Save Vehicle to File ====//
                string message = string("-->Saving vehicle file to: ") + fname + string("\n");
                Print(message );
                WriteVSPFile( fname, SET_ALL );
                Print( "COMPLETE\n" );
            }
        }
        
        ClearVSPModel();
    }

    private void TestSupersonicDeltaWings()
    {
        Print( string( "-> Begin Supersonic Delta Wing Test:\n" ) );

        uint8 num_sweep = uint8(m_Sweep.length());
        uint8 num_mach = uint8(m_SuperMachVec.length());
        
        array<array<double>> Cl_alpha_tan_sweep(num_sweep), M_sweep_fun(num_sweep);
        
        for ( uint8 s = 0; s < num_sweep; s++ )
        {
            Cl_alpha_tan_sweep[s].resize(num_mach);
            M_sweep_fun[s].resize(num_mach);
            
            for ( uint8 m = 0; m < num_mach; m++ )
            {
                //==== Open and test generated wings ====//
                string fname = "Supersonic_Delta_Wing_Sweep" + int(m_Sweep[s]) + string("_Mach") + int(m) + ".vsp3";
                string fname_res = "Supersonic_Delta_Wing_Sweep" + int(m_Sweep[s]) + string("_Mach") + int(m) + "_res.csv";

                Print( string( "Reading in file: " ), false );
                Print( fname );
                ReadVSPFile( fname ); // Sets VSP3 file name
                
                //==== Analysis: VSPAero SinglePoint ====//
                Print( m_VSPSweepAnalysis );

                //==== Analysis: VSPAero Compute Geometry to Create Vortex Lattice DegenGeom File ====//
                Print( m_CompGeomAnalysis );

                // Set defaults
                SetAnalysisInputDefaults( m_CompGeomAnalysis );
                
                SetIntAnalysisInput(m_CompGeomAnalysis, "Symmetry", m_SymFlagVec, 0);

                // list inputs, type, and current values
                PrintAnalysisInputs( m_CompGeomAnalysis );

                // Execute
                Print( "\tExecuting..." );
                string compgeom_resid = ExecAnalysis( m_CompGeomAnalysis );
                Print( "COMPLETE" );

                // Get & Display Results
                PrintResults( compgeom_resid );

                //==== Analysis: VSPAero Single Point ====//
                // Set defaults
                SetAnalysisInputDefaults(m_VSPSweepAnalysis);
                Print(m_VSPSweepAnalysis);

                // Reference geometry set
                SetIntAnalysisInput( m_VSPSweepAnalysis, "GeomSet", m_GeomVec, 0 );
                SetIntAnalysisInput(m_VSPSweepAnalysis, "RefFlag", m_RefFlagVec, 0);
                SetIntAnalysisInput(m_VSPSweepAnalysis, "Symmetry", m_SymFlagVec, 0);

                array< string > wid = FindGeomsWithName( "WingGeom" );
                SetStringAnalysisInput(m_VSPSweepAnalysis, "WingID", wid, 0);

                // Freestream Parameters
                array<double> Alpha( 1, 5.0 );
                array<double> Mach( 1, m_SuperMachVec[m] );
                SetDoubleAnalysisInput(m_VSPSweepAnalysis, "AlphaStart", Alpha, 0);
                array<int> AlphaNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "AlphaNpts", AlphaNpts, 0);
                SetDoubleAnalysisInput(m_VSPSweepAnalysis, "MachStart", Mach, 0);
                array<int> MachNpts(1, 1); SetIntAnalysisInput(m_VSPSweepAnalysis, "MachNpts", MachNpts, 0);
                SetIntAnalysisInput(m_VSPSweepAnalysis, "WakeNumIter", m_WakeIterVec, 0);

                Update();

                // list inputs, type, and current values
                PrintAnalysisInputs( m_VSPSweepAnalysis );
                Print( "" );

                // Execute
                Print( "\tExecuting..." );
                string rid = ExecAnalysis( m_VSPSweepAnalysis );
                Print( "COMPLETE" );

                // Get & Display Results
                PrintResults( rid );
                WriteResultsCSVFile( rid, fname_res );
                
                // Get Result ID Vec (History and Load ResultIDs)
                array<string> rid_vec = GetStringResults( rid, "ResultsVec" );
                if ( rid_vec.length() > 0 )
                {
                    // Get History Results (rid_vec[0]) from Final Wake Iteration in History Result
                    array<double> cl_vec = GetDoubleResults( rid_vec[0], "CL" );
                    double Cl_res = cl_vec[int(cl_vec.length()) - 1];
                    double Cl_alpha_vsp = Cl_res / 5.0; // alpha = 5.0 (deg)
                    Cl_alpha_vsp = Cl_alpha_vsp*(180/pi); // deg -> rad 
                    
                    Cl_alpha_tan_sweep[s][m] = Cl_alpha_vsp * tan( Deg2Rad(m_Sweep[s]) );
                    M_sweep_fun[s][m] = sqrt( pow( m_SuperMachVec[m], 2.0 ) - 1 ) / tan( Deg2Rad(m_Sweep[s]) );
                }

                ClearVSPModel();
            }
        }
        
        // Experimental data from Aerodynamics Of Wings And Bodies [Ashley & Landahl, pg. 169] using Plot Digitizer
        array<double> m_exp(21), Cl_alpha_tan_sweep_exp(21);
        m_exp[0] = 0.192469;
        m_exp[1] = 0.312413;
        m_exp[2] = 0.253835;
        m_exp[3] = 0.298466;
        m_exp[4] = 0.354254;
        m_exp[5] = 0.538354;
        m_exp[6] = 0.496513;
        m_exp[7] = 0.415621;
        m_exp[8] = 0.468619;
        m_exp[9] = 0.64993;
        m_exp[10] = 0.714086;
        m_exp[11] = 0.725244;
        m_exp[12] = 0.803347;
        m_exp[13] = 0.88424;
        m_exp[14] = 0.956764;
        m_exp[15] = 1.0265;
        m_exp[16] = 1.07392;
        m_exp[17] = 1.18271;
        m_exp[18] = 1.38633;
        m_exp[19] = 1.43654;
        m_exp[20] = 1.91632;
        
        Cl_alpha_tan_sweep_exp[0] = 5.81731;
        Cl_alpha_tan_sweep_exp[1] = 6.45192;
        Cl_alpha_tan_sweep_exp[2] = 5.71635;
        Cl_alpha_tan_sweep_exp[3] = 5.64423;
        Cl_alpha_tan_sweep_exp[4] = 5.47115;
        Cl_alpha_tan_sweep_exp[5] = 5.65865;
        Cl_alpha_tan_sweep_exp[6] = 5.45673;
        Cl_alpha_tan_sweep_exp[7] = 5.25481;
        Cl_alpha_tan_sweep_exp[8] = 5.06731;
        Cl_alpha_tan_sweep_exp[9] = 5.02404;
        Cl_alpha_tan_sweep_exp[10] = 4.99519;
        Cl_alpha_tan_sweep_exp[11] = 4.57692;
        Cl_alpha_tan_sweep_exp[12] = 4.17308;
        Cl_alpha_tan_sweep_exp[13] = 4.38942;
        Cl_alpha_tan_sweep_exp[14] = 3.71154;
        Cl_alpha_tan_sweep_exp[15] = 3.61058;
        Cl_alpha_tan_sweep_exp[16] = 3.35096;
        Cl_alpha_tan_sweep_exp[17] = 3.13462;
        Cl_alpha_tan_sweep_exp[18] = 2.37019;
        Cl_alpha_tan_sweep_exp[19] = 2.61538;
        Cl_alpha_tan_sweep_exp[20] = 2.02404;

        // Supersonic Delta Wing Plot: Cl_alpha*tan(sweep) = f(((M^2 - 1)^0.5)/tan(sweep)) 
        HTMLChart c1 = HTMLChart(); // Initialize Chart
        c1.m_ChartName = string("SuperDelta");
        c1.m_Legend = string("'((M^2 - 1)^0.5)/tan(sweep)','VSPAERO 45° Sweep','VSPAERO 65° Sweep','Experimental Data'");
        c1.m_Title = string("Figure ") + m_FigCount + string(". Supersonic Delta Wing: Cl_alpha*tan(sweep) = f(m)");
        m_FigCount++;
        c1.m_Series = string("{ 2: { pointSize: 3, color: 'gray', lineWidth: 0 } }");
        c1.m_HAxisString = string("hAxis: { title: 'm', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }");
        c1.m_VAxisString = string("vAxis: { title: 'Cl_alpha*tan(sweep)', titleTextStyle: { bold: true }, viewWindow: { min: 0.0 } }");
        
        array<string> data_str_vec1;
        for( uint i = 0; i < num_mach; i++ )
        {
            string row = string("\t\t  [") + double( M_sweep_fun[0][i] ) + string(",") + double( Cl_alpha_tan_sweep[0][i] ) + string(",null,null],\n");
            
            data_str_vec1.push_back( row );
        }

        for( uint i = 0; i < num_mach; i++ )
        {
            string row = string("\t\t  [") + double( M_sweep_fun[1][i] ) + string(",null,") + double( Cl_alpha_tan_sweep[1][i] ) + string(",null],\n");
            
            data_str_vec1.push_back( row );
        }
        
        for( uint i = 0; i < uint(m_exp.length()); i++ )
        {
            string row = string("\t\t  [") + double( m_exp[i] ) + string(",null,null,") + double( Cl_alpha_tan_sweep_exp[i] );
            
            if ( i == uint(m_exp.length()) - 1 )
            {
                row += string("]\n");
            }
            else
            {
                row += string("],\n");
            }
            
            data_str_vec1.push_back( row );
        }
        
        c1.WriteChartHead( data_str_vec1 );
        m_SuperDeltaIDVec.push_back( c1.m_ChartName );
    }
    
    array<int> m_Sweep(2);
    private array<double> m_SuperMachVec(8);
    
    array<string> m_SuperDeltaIDVec;
}
