string PropGeom::BuildBEMResults() { // Calculate prop center and normal vector vec3d cen = m_ModelMatrix.xform( vec3d( 0, 0, 0 ) ); vec3d norm = m_ModelMatrix.xform( vec3d( -1.0, 0, 0 ) ) - cen; int n = m_TessU(); //==== Create Results ====// Results* res = ResultsMgr.CreateResults( "PropBEM" ); res->Add( NameValData( "Num_Sections", n ) ); res->Add( NameValData( "Num_Blade", m_Nblade() ) ); res->Add( NameValData( "Diameter", m_Diameter() ) ); res->Add( NameValData( "Beta34", m_Beta34() ) ); res->Add( NameValData( "Feather", m_Feather() ) ); res->Add( NameValData( "Center", cen ) ); res->Add( NameValData( "Normal", norm ) ); double rfirst = m_ChordCurve.GetRFirst(); double rlast = m_ChordCurve.GetRLast(); // Establish points to evaluate sections at. vector < double > vtess; m_MainSurfVec[0].MakeVTess( m_TessW(), vtess, m_CapUMinTess(), false ); vector < double > r_vec(n); vector < double > chord_vec(n); vector < double > twist_vec(n); vector < double > rake_vec(n); vector < double > skew_vec(n); double rspan = rlast - rfirst; for ( int i = 0; i < n; i++ ) { double t = static_cast < double > ( i ) / ( n - 1 ); double r = rfirst + rspan * Cluster( t, m_RootCluster(), m_TipCluster() ); double u = m_rtou.CompPnt( r ); VspCurve c; m_FoilSurf.GetUConstCurve( c, u ); vec3d v = c.CompPnt( 0 ); c.OffsetZ( -v.z() ); vector < vec3d > pts; c.Tesselate( vtess, pts ); vector < double > xpts( pts.size() ); vector < double > ypts( pts.size() ); for ( int j = 0; j < pts.size(); j++ ) { xpts[j] = pts[j].x(); ypts[j] = pts[j].y(); } char str[255]; sprintf( str, "%03d", i ); res->Add( NameValData( "XSection_" + string( str ), xpts ) ); res->Add( NameValData( "YSection_" + string( str ), ypts ) ); r_vec[i] = r; chord_vec[i] = m_ChordCurve.Comp( r ); twist_vec[i] = m_TwistCurve.Comp( r ); rake_vec[i] = m_RakeCurve.Comp( r ); skew_vec[i] = m_SkewCurve.Comp( r ); } res->Add( NameValData( "Radius", r_vec ) ); res->Add( NameValData( "Chord", chord_vec ) ); res->Add( NameValData( "Twist", twist_vec ) ); res->Add( NameValData( "Rake", rake_vec ) ); res->Add( NameValData( "Skew", skew_vec ) ); return res->GetID(); }
//==== Update Fuselage And Cross Section Placement ====// void PropGeom::UpdateSurf() { int nxsec = m_XSecSurf.NumXSec(); double radius = m_Diameter() / 2.0; double rfirst = 0.0; double rlast = 1.0; vector < double > uvec( nxsec ); vector < double > rvec( nxsec ); double rev = 1.0; if ( m_ReverseFlag() ) { rev = -1.0; } //==== Update XSec Location/Rotation ====// for ( int i = 0 ; i < nxsec ; i++ ) { PropXSec* xs = ( PropXSec* ) m_XSecSurf.FindXSec( i ); if ( xs ) { if ( xs->GetXSecCurve()->GetType() == XS_POINT ) { printf( "Warning: XS_POINT type not valid for propellers\n" ); // Propellers are constructed in two phases. The first phase stacks // all the XSecs with unit chord. Intermediate curves are extracted // from that surface and are scaled to match the required chord. // Since XS_POINT XSecs already have zero chord, they mess up this // process. } //==== Reset Group Names ====// xs->SetGroupDisplaySuffix( i ); //==== Set X Limits ====// EnforceOrder( xs, i ); xs->SetRefLength( radius ); bool first = false; bool last = false; if( i == 0 ) first = true; else if( i == (nxsec-1) ) last = true; if ( first ) { rfirst = xs->m_RadiusFrac(); } if ( last ) { rlast = xs->m_RadiusFrac(); } uvec[i] = i; rvec[i] = xs->m_RadiusFrac(); } } m_rtou = Vsp1DCurve(); m_rtou.InterpolateLinear( uvec, rvec, false ); EnforcePCurveOrder( rfirst, rlast ); // Set lower limit for activity factor integration limit m_AFLimit.SetLowerLimit( rfirst ); // Integrate activity factor. m_AF.Set( m_ChordCurve.IntegrateAF( m_AFLimit() ) ); m_AF.Deactivate(); if ( m_UseBeta34Flag() == 1 ) { if ( rfirst <= 0.75 ) { double theta34 = m_TwistCurve.Comp( 0.75 ); m_Feather = m_Beta34() - theta34; } else { m_Feather = m_Beta34(); } m_Beta34.Activate(); m_Feather.Deactivate(); } else { if ( rfirst <= 0.75 ) { double theta34 = m_TwistCurve.Comp( 0.75 ); m_Beta34 = m_Feather() + theta34; } else { m_Beta34 = m_Feather(); } m_Beta34.Deactivate(); m_Feather.Activate(); } // Set up fold axis & store for visualization. Matrix4d fold, foldrot; fold.loadIdentity(); fold.rotateY( m_AzimuthFoldDir() ); fold.rotateX( m_ElevationFoldDir() ); m_FoldAxDirection = fold.xform( vec3d( 0, 0, 1 ) ); m_FoldAxOrigin = vec3d( m_AxialFoldAxis() * radius, m_RadFoldAxis() * radius, m_OffsetFoldAxis() * radius ); //==== Cross Section Curves & joint info ====// vector< VspCurve > crv_vec; crv_vec.resize( nxsec ); //==== Update XSec Location/Rotation ====// for ( int i = 0 ; i < nxsec ; i++ ) { PropXSec* xs = ( PropXSec* ) m_XSecSurf.FindXSec( i ); if ( xs ) { XSecCurve* xsc = xs->GetXSecCurve(); double r = xs->m_RadiusFrac(); double w = m_ChordCurve.Comp( r ) * radius; if ( xsc ) { //==== Find Width Parm ====// string width_id = xsc->GetWidthParmID(); Parm* width_parm = ParmMgr.FindParm( width_id ); piecewise_curve_type pwc; if ( width_parm ) { width_parm->Deactivate(); Airfoil* af = dynamic_cast < Airfoil* > ( xsc ); if ( af ) { width_parm->Set( 1.0 ); xs->GetXSecCurve()->SetFakeWidth( w ); xs->GetXSecCurve()->SetUseFakeWidth( true ); pwc = xs->GetCurve().GetCurve(); xs->GetXSecCurve()->SetUseFakeWidth( false ); width_parm->Set( w ); } else { CircleXSec* cir = dynamic_cast < CircleXSec* > ( xsc ); if ( cir ) { width_parm->Set( 1.0 ); pwc = xs->GetCurve().GetCurve(); width_parm->Set( w ); } else { double h = xs->GetXSecCurve()->GetHeight(); xsc->SetWidthHeight( 1.0, h/w ); pwc = xs->GetCurve().GetCurve(); xsc->SetWidthHeight( w, h ); } } } else { pwc = xs->GetCurve().GetCurve(); } // Set up prop positioner for highlight curves - not lofting. xs->m_PropPos.m_ParentProp = GetXSecSurf( 0 ); xs->m_PropPos.m_Radius = r * radius; xs->m_PropPos.m_Chord = w; xs->m_PropPos.m_Twist = m_TwistCurve.Comp( r ); xs->m_PropPos.m_XRotate = 0.0; xs->m_PropPos.m_ZRotate = atan( -m_RakeCurve.Compdt( r ) ) * 180.0 / PI; xs->m_PropPos.m_Rake = m_RakeCurve.Comp( r ) * radius; xs->m_PropPos.m_Skew = m_SkewCurve.Comp( r ) * radius; xs->m_PropPos.m_PropRot = m_Rotate(); xs->m_PropPos.m_Feather = m_Feather(); xs->m_PropPos.m_FoldOrigin = m_FoldAxOrigin; xs->m_PropPos.m_FoldDirection = m_FoldAxDirection; xs->m_PropPos.m_FoldAngle = m_FoldAngle(); xs->m_PropPos.m_Reverse = rev; crv_vec[i].SetCurve( pwc ); } } } // This surface linearly interpolates the airfoil sections without // any other transformations. // These sections can be extracted (as u-const curves) and then // transformed to their final position before skinning. m_FoilSurf = VspSurf(); m_FoilSurf.SkinC0( crv_vec, false ); // Find the union of stations required to approximate the blade parameters // with cubic functions. vector < double > tmap = rvec; // Initialize with specified XSecs. vector < double > tdisc; tdisc.push_back( rfirst ); tdisc.push_back( rlast ); for ( int i = 0; i < m_pcurve_vec.size(); i++ ) { vector < double > tm; vector < double > tmout; vector < double > td; m_pcurve_vec[i]->GetTMap( tm, td ); std::set_union( tmap.begin(), tmap.end(), tm.begin(), tm.end(), std::back_inserter(tmout), &aboutcomp ); std::swap( tmout, tmap ); } // Not sure why above set_union leaves duplicate entries, but // sort and force unique just to be sure. std::sort( tmap.begin(), tmap.end() ); auto tmit = std::unique( tmap.begin(), tmap.end(), &abouteq ); tmap.erase( tmit, tmap.end() ); // Treat all control points as possible discontinuities. tdisc = tmap; // Refine by adding two intermediate points to each cubic section // this is needed because the adaptive algorithm above uses derivatives // while our later reconstruction does not. vector < double > tref( ( tmap.size() - 1 ) * 3 + 1 ); for ( int i = 0; i < tmap.size() - 1; i++ ) { int iref = 3*i; double t = tmap[i]; double tnxt = tmap[i+1]; double dt = (tnxt-t)/3.0; tref[iref] = t; tref[iref+1] = t + dt; tref[iref+2] = t + 2 * dt; } tref.back() = tmap.back(); std::swap( tmap, tref ); // Convert tdisc to final parameterization. for ( int i = 0; i < tdisc.size(); i++ ) { tdisc[i] = ( tdisc[i] - rfirst ) / ( rlast - rfirst ); } // Pseudo cross sections // Not directly user-controlled, but an intermediate step in lofting the // surface. int npseudo = tmap.size(); vector < rib_data_type > rib_vec( npseudo ); vector < double > u_pseudo( npseudo ); for ( int i = 0; i < npseudo; i++ ) { // Assume linear interpolation means linear u/r relationship. double r = tmap[i]; double u = m_rtou.CompPnt( r ); VspCurve c; m_FoilSurf.GetUConstCurve( c, u ); vec3d v = c.CompPnt( 0 ); c.OffsetZ( -v.z() ); PropPositioner pp; pp.m_ParentProp = this->GetXSecSurf( 0 ); pp.m_Radius = r * radius; pp.m_Chord = m_ChordCurve.Comp( r ) * radius; pp.m_Twist = m_TwistCurve.Comp( r ); pp.m_XRotate = 0.0; pp.m_ZRotate = atan( -m_RakeCurve.Compdt( r ) ) * 180.0 / PI; pp.m_Rake = m_RakeCurve.Comp( r ) * radius; pp.m_Skew = m_SkewCurve.Comp( r ) * radius; pp.m_PropRot = m_Rotate(); pp.m_Feather = m_Feather(); pp.m_FoldOrigin = m_FoldAxOrigin; pp.m_FoldDirection = m_FoldAxDirection; pp.m_FoldAngle = m_FoldAngle(); pp.m_Reverse = rev; // Set a bunch of other pp variables. pp.SetCurve( c ); pp.Update(); rib_vec[i].set_f( pp.GetCurve().GetCurve() ); u_pseudo[i] = ( r - rfirst ) / ( rlast - rfirst ); } m_MainSurfVec.resize( 1 ); m_MainSurfVec.reserve( m_Nblade() ); m_MainSurfVec[0].SetMagicVParm( false ); m_MainSurfVec[0].SkinCubicSpline( rib_vec, u_pseudo, tdisc, false ); m_MainSurfVec[0].SetMagicVParm( true ); m_MainSurfVec[0].SetSurfType( PROP_SURF ); m_MainSurfVec[0].SetClustering( m_LECluster(), m_TECluster() ); m_MainSurfVec[0].SetFoilSurf( &m_FoilSurf ); if ( m_XSecSurf.GetFlipUD() ) { m_MainSurfVec[0].FlipNormal(); } if ( this->m_ReverseFlag() ) { m_MainSurfVec[0].FlipNormal(); } // UpdateEndCaps here so we only have to cap one blade. UpdateEndCaps(); m_MainSurfVec.resize( m_Nblade(), m_MainSurfVec[0] ); // Duplicate capping variables m_CapUMinSuccess.resize( m_Nblade(), m_CapUMinSuccess[0] ); m_CapUMaxSuccess.resize( m_Nblade(), m_CapUMaxSuccess[0] ); m_CapWMinSuccess.resize( m_Nblade(), m_CapWMinSuccess[0] ); m_CapWMaxSuccess.resize( m_Nblade(), m_CapWMaxSuccess[0] ); Matrix4d rot; for ( int i = 1; i < m_Nblade(); i++ ) { double theta = 360.0 * i / ( double )m_Nblade(); rot.loadIdentity(); rot.rotateX( theta ); m_MainSurfVec[i].Transform( rot ); } CalculateMeshMetrics( u_pseudo ); }
//==== Update Method ===// void SSControlSurf::Update() { // Build Control Surface as a rectangle with the points counter clockwise vec3d c_uws_upper, c_uws_lower, c_uwe_upper, c_uwe_lower; vector< vec3d > pnt_vec; double u, w; Geom* geom = VehicleMgr.GetVehicle()->FindGeom( m_CompID ); if ( !geom ) { return; } VspSurf* surf = geom->GetSurfPtr(); if ( !surf ) { return; } VspCurve startcrv; surf->GetU01ConstCurve( startcrv, m_UStart() ); piecewise_curve_type c = startcrv.GetCurve(); double vmin = c.get_parameter_min(); // Really must be 0.0 double vmax = c.get_parameter_max(); // Really should be 4.0 double vle = ( vmin + vmax ) * 0.5; double vtelow = vmin + TMAGIC; double vteup = vmax - TMAGIC; curve_point_type te, le; te = c.f( vmin ); le = c.f( vle ); double chord, d; chord = dist( le, te ); if ( m_AbsRelFlag() == vsp::REL ) { d = chord * m_StartLenFrac(); m_StartLength.Set( d ); } else { d = m_StartLength(); m_StartLenFrac.Set( d / chord ); } if ( m_ConstFlag.Get() ) { if ( m_AbsRelFlag() == vsp::REL ) { m_EndLenFrac.Set( d / chord ); } else { m_EndLength.Set( d ); } } // Mid-curve points on upper and lower surface. To serve as initial guess. double vlowmid, vupmid; vlowmid = vtelow + m_StartLenFrac() * ( vle - vtelow ); vupmid = vle + ( 1.0 - m_StartLenFrac() ) * ( vteup - vle ); curve_point_type telow, teup; telow = c.f( vtelow ); teup = c.f( vteup ); piecewise_curve_type clow, cup; c.split( clow, cup, vle ); double vlow, vup; if ( m_SurfType() != LOWER_SURF ) { eli::geom::intersect::specified_distance( vup, cup, teup, d, vupmid ); c_uws_upper = vec3d( m_UStart(), vup / vmax, 0 ); } if ( m_SurfType() != UPPER_SURF ) { eli::geom::intersect::specified_distance( vlow, clow, telow, d, vlowmid ); c_uws_lower = vec3d( m_UStart(), vlow / vmax, 0 ); } VspCurve endcrv; surf->GetU01ConstCurve( endcrv, m_UEnd() ); c = endcrv.GetCurve(); te = c.f( vmin ); le = c.f( vle ); chord = dist( le, te ); if ( m_AbsRelFlag() == vsp::REL ) { d = chord * m_EndLenFrac(); m_EndLength.Set( d ); } else { d = m_EndLength(); m_EndLenFrac.Set( d / chord ); } vlowmid = vtelow + m_EndLenFrac() * ( vle - vtelow ); vupmid = vle + ( 1.0 - m_EndLenFrac() ) * ( vteup - vle ); telow = c.f( vtelow ); teup = c.f( vteup ); c.split( clow, cup, vle ); if ( m_SurfType() != LOWER_SURF ) { eli::geom::intersect::specified_distance( vup, cup, teup, d, vupmid ); c_uwe_upper = vec3d( m_UEnd(), vup / vmax, 0 ); } if ( m_SurfType() != UPPER_SURF ) { eli::geom::intersect::specified_distance( vlow, clow, telow, d, vlowmid ); c_uwe_lower = vec3d( m_UEnd(), vlow / vmax, 0 ); } // Build Control Surface if ( m_SurfType() == UPPER_SURF ) { pnt_vec.resize( 4 ); pnt_vec[0] = vec3d( m_UStart(), 1, 0 ); pnt_vec[1] = c_uws_upper; pnt_vec[2] = c_uwe_upper; pnt_vec[3] = vec3d( m_UEnd(), 1, 0 ); } else if ( m_SurfType() == LOWER_SURF ) { pnt_vec.resize( 4 ); pnt_vec[0] = vec3d( m_UStart(), 0, 0 ); pnt_vec[1] = c_uws_lower; pnt_vec[2] = c_uwe_lower; pnt_vec[3] = vec3d( m_UEnd(), 0, 0 ); } else { pnt_vec.resize( 8 ); pnt_vec[0] = vec3d( m_UStart(), 1, 0 ); pnt_vec[1] = c_uws_upper; pnt_vec[2] = c_uwe_upper; pnt_vec[3] = pnt_vec[3] = vec3d( m_UEnd(), 1, 0 ); pnt_vec[4] = vec3d( m_UEnd(), 0, 0 ); pnt_vec[5] = c_uwe_lower; pnt_vec[6] = c_uws_lower; pnt_vec[7] = vec3d( m_UStart(), 0, 0 ); } // pnt_vec[3] = pnt_vec[0]; int pind = 0; int num_segs = pnt_vec.size() - 1; if ( m_SurfType() == BOTH_SURF ) { num_segs--; } m_LVec.resize( num_segs ); for ( int i = 0; i < num_segs; i++ ) { if ( m_SurfType() == BOTH_SURF && i == 3 ) { pind++; } m_LVec[i].SetSP0( pnt_vec[pind] ); pind++; m_LVec[i].SetSP1( pnt_vec[pind] ); m_LVec[i].Update( geom ); } SubSurface::Update(); }