o
    N#iV                     @   s
  d Z ddlZddlZddlZddlZddlZddlZddlZg dZdZ	dZ
dZdZdZd	Zd
ZejejeZdZd,ddZdd Zdd Zdd Zdd Zdd Zd-ddZdd Zdd Zdd  Zd!d" Z d#d$ Z!d%d& Z"d'd( Z#d)d* Z$e%d+kre$  dS dS ).av  
Phase 2 CFD: Control vane parametric sweep.
Creates a 3D duct with a deflected vane at various angles,
runs simpleFoam, and extracts forces.

Duct: cylinder R=20mm, L=137mm (approximated as O-grid in blockMesh)
Vane: span=38mm (diameter, so half-span=19mm from center), chord=18mm, thickness=1.5mm
Vane center at Z=36mm from inlet (midpoint of Z=34..38mm in real geometry)
    N)r      
               g{Gz?gK7?g~jt?g;On?g~jtX?g;On?g      >@z0source /usr/lib/openfoam/openfoam2406/etc/bashrcc                 C   sF   t  d| d|  }|r|d| d7 }tj|dd|du ddd	}|S )
z$Run an OpenFOAM command in case_dir.z && cd z && z > z 2>&1Tz	/bin/bashNiX  )shell
executablecapture_outputtexttimeout)	OF_SOURCE
subprocessrun)cmdcase_dirlog_namefull_cmdresult r   1/home/zenith/canfly/cfd/phase2_vanes/run_sweep.pyrun_of"   s   

r   c           	   
   C   s   t | dO}|d |tdt| |D ]3\}}}}|D ]}|td| q |||fD ]}|D ]}|td| q5q1|tdd qW d   dS 1 sWw   Y  dS )zBWrite binary STL. triangles = list of (normal, v1, v2, v3) tuples.wbsP                                                                                   z<Iz<fz<Hr   N)openwritestructpacklen)	filename	trianglesfnormalv1v2v3coordvr   r   r   write_stl_binary,   s   
"r'   c              
      s<  t | }t | t |td }td }td }| | | f|| | f||| f| || f| | |f|| |f|||f| ||fg} fddfdd|D }g d}dd }	d	d
 }
dd }g }|D ])\}}}|| || || }}}||	|
|||
||}|||||f qmt|| dS )zGenerate STL for a flat vane at given deflection angle.
    Vane is centered at origin in X-Y, centered at VANE_Z_CENTER in Z.
    Deflection rotates around Y-axis (tilts in X-Z plane).
       c                    s.   |   |  }|  |   t  }|||fS )z8Rotate around Y axis, then translate to vane Z position.)VANE_Z_CENTER)xyzxrzr)cos_asin_ar   r   rotate_yT   s   
z#generate_vane_stl.<locals>.rotate_yc                    s   g | ]} | qS r   r   ).0c)r1   r   r   
<listcomp>\   s    z%generate_vane_stl.<locals>.<listcomp>))r      r(   )r   r(      )      r   )r7      r8   )r6   r(   r8   )r6   r8   r9   )r   r   r5   )r   r7   r   )r   r6   r9   )r   r9   r7   )r5   r   r8   )r5   r8   r(   c                 S   s^   | d |d  | d |d   | d |d  | d |d   | d |d  | d |d   fS )Nr5   r(   r   r   abr   r   r   crossn   s   ^z generate_vane_stl.<locals>.crossc                 S   s.   | d |d  | d |d  | d |d  fS )Nr   r5   r(   r   r:   r   r   r   subq   s   .zgenerate_vane_stl.<locals>.subc                    s:   t tdd | D   dk rdS t fdd| D S )Nc                 s   s    | ]}|| V  qd S Nr   r2   r*   r   r   r   	<genexpr>u       z7generate_vane_stl.<locals>.normalize.<locals>.<genexpr>g-q=)r   r   r5   c                 3   s    | ]}|  V  qd S r?   r   r@   magr   r   rA   x   rB   )mathsqrtsumtuple)r&   r   rC   r   	normalizet   s   z$generate_vane_stl.<locals>.normalizeN)	rE   radianscossin
VANE_CHORD	VANE_SPAN
VANE_THICKappendr'   )	angle_degr   	angle_rad
half_chord	half_span
half_thickcorners_localcornersfacesr=   r>   rI   r   i0i1i2v0r"   r#   nr   )r/   r1   r0   r   generate_vane_stl:   s6   





r^   c                 C   sT  t d }td }dg d|  d|  d| d|  d| d| d|  d| d	|  d|  d
| d| d|  d
| d| d| d
| d|  d| d
| d}tjtj| ddd ttj| ddd}|| W d   dS 1 sw   Y  dS )zWrite blockMeshDict for a 3D rectangular duct approximating the cylinder.
    Using a simple box: X=[-R,R], Y=[-R,R], Z=[0,L]
    This is a square duct approximation - good enough for force trends.
    i   zFoamFile
{
    version     2.0;
    format      ascii;
    class       dictionary;
    object      blockMeshDict;
}

scale   0.001;

vertices
(
    (z  z   0)    // 0
    ( z   0)    // 1
    ( z   z   0)    // 2
    (z   0)    // 3
    ( z)    // 4
    ( z)    // 5
    ( z)    // 6
    (a  )    // 7
);

blocks
(
    hex (0 1 2 3 4 5 6 7) (20 20 70) simpleGrading (1 1 1)
);

boundary
(
    inlet
    {
        type patch;
        faces ( (0 1 2 3) );
    }

    outlet
    {
        type patch;
        faces ( (4 5 6 7) );
    }

    ductWall
    {
        type wall;
        faces
        (
            (0 1 5 4)
            (1 2 6 5)
            (2 3 7 6)
            (3 0 4 7)
        );
    }
);
systemTexist_okblockMeshDictwN)DUCT_RDUCT_Ljoinosmakedirspathr   r   )r   RLcontentr    r   r   r   write_blockmesh_dict   s^   
6"ro   c                 C   sV   t j|}d}tt j| ddd}|| W d   dS 1 s$w   Y  dS )z=Write snappyHexMeshDict to refine around vane and snap to it.a  FoamFile
{
    version     2.0;
    format      ascii;
    class       dictionary;
    object      snappyHexMeshDict;
}

castellatedMesh true;
snap            true;
addLayers       false;

geometry
{
    vane.stl
    {
        type triSurfaceMesh;
        name vane;
    }
}

castellatedMeshControls
{
    maxLocalCells   50000;
    maxGlobalCells  200000;
    minRefinementCells 10;
    maxLoadUnbalance 0.10;
    nCellsBetweenLevels 3;
    resolveFeatureAngle 30;

    features
    (
    );

    refinementSurfaces
    {
        vane
        {
            level (2 2);
            patchInfo
            {
                type wall;
            }
        }
    }

    refinementRegions
    {
    }

    locationInMesh (0 0 0.001);  // point inside the duct (not inside vane)
    allowFreeStandingZoneFaces true;
}

snapControls
{
    nSmoothPatch    3;
    tolerance       2.0;
    nSolveIter      100;
    nRelaxIter      5;
    nFeatureSnapIter 10;
    implicitFeatureSnap false;
    explicitFeatureSnap true;
    multiRegionFeatureSnap false;
}

addLayersControls
{
    relativeSizes   true;
    layers          {}
    expansionRatio  1.0;
    finalLayerThickness 0.3;
    minThickness    0.1;
    nGrow           0;
    featureAngle    60;
    nRelaxIter      3;
    nSmoothSurfaceNormals 1;
    nSmoothNormals  3;
    nSmoothThickness 10;
    maxFaceThicknessRatio 0.5;
    maxThicknessToMedialRatio 0.3;
    minMedialAxisAngle 90;
    nBufferCellsNoExtrude 0;
    nLayerIter      50;
}

meshQualityControls
{
    maxNonOrtho     65;
    maxBoundarySkewness 20;
    maxInternalSkewness 4;
    maxConcave      80;
    minVol          1e-13;
    minTetQuality   -1e30;
    minArea         -1;
    minTwist        0.02;
    minDeterminant  0.001;
    minFaceWeight   0.02;
    minVolRatio     0.01;
    minTriangleTwist -1;
    nSmoothScale    4;
    errorReduction  0.75;
    relaxed
    {
        maxNonOrtho     75;
    }
}

debug           0;
mergeTolerance  1e-6;
ra   snappyHexMeshDictre   N)ri   rk   basenamer   rh   r   )r   stl_filestl_namern   r    r   r   r   write_snappy_dict   s
   o"rt   c                 C   sJ   d}t tj| ddd}|| W d   dS 1 sw   Y  dS )z*Write decomposeParDict (single processor).zFoamFile
{
    version     2.0;
    format      ascii;
    class       dictionary;
    object      decomposeParDict;
}

numberOfSubdomains 1;
method          simple;
simpleCoeffs
{
    n           (1 1 1);
    delta       0.001;
}
ra   decomposeParDictre   Nr   ri   rk   rh   r   r   rn   r    r   r   r   write_decompose_dict<  s   "rx     c                 C   sX   d| dt  d}ttj| ddd}|| W d   dS 1 s%w   Y  dS )z<Write controlDict with force function objects on vane patch.zFoamFile
{
    version     2.0;
    format      ascii;
    class       dictionary;
    object      controlDict;
}

application     simpleFoam;
startFrom       startTime;
startTime       0;
stopAt          endTime;
endTime         a  ;
deltaT          1;

writeControl    timeStep;
writeInterval   100;
purgeWrite      2;

writeFormat     ascii;
writePrecision  8;
writeCompression off;
timeFormat      general;
timePrecision   6;
runTimeModifiable true;

functions
{
    vaneForces
    {
        type            forces;
        libs            (forces);
        writeControl    timeStep;
        writeInterval   1;
        log             true;
        patches         (vane);
        rho             rhoInf;
        rhoInf          1.225;
        CofR            (0 0 z);
    }
}
ra   controlDictre   N)r)   r   ri   rk   rh   r   )r   end_timern   r    r   r   r   write_control_dictR  s   &*"r|   c                 C   J   d}t tj| ddd}|| W d    d S 1 sw   Y  d S )Na  FoamFile
{
    version     2.0;
    format      ascii;
    class       dictionary;
    object      fvSchemes;
}

ddtSchemes
{
    default         steadyState;
}

gradSchemes
{
    default         Gauss linear;
    grad(p)         Gauss linear;
    grad(U)         cellLimited Gauss linear 1;
}

divSchemes
{
    default         none;
    div(phi,U)      bounded Gauss linearUpwind grad(U);
    div(phi,k)      bounded Gauss upwind;
    div(phi,omega)  bounded Gauss upwind;
    div((nuEff*dev2(T(grad(U))))) Gauss linear;
}

laplacianSchemes
{
    default         Gauss linear corrected;
}

interpolationSchemes
{
    default         linear;
}

snGradSchemes
{
    default         corrected;
}

wallDist
{
    method          meshWave;
}
ra   	fvSchemesre   rv   rw   r   r   r   write_fv_schemes  s   1"r   c                 C   r}   )Na4  FoamFile
{
    version     2.0;
    format      ascii;
    class       dictionary;
    object      fvSolution;
}

solvers
{
    p
    {
        solver          GAMG;
        smoother        GaussSeidel;
        tolerance       1e-06;
        relTol          0.1;
    }

    "(U|k|omega)"
    {
        solver          smoothSolver;
        smoother        symGaussSeidel;
        tolerance       1e-06;
        relTol          0.1;
    }
}

SIMPLE
{
    nNonOrthogonalCorrectors 1;
    consistent      yes;

    residualControl
    {
        p               1e-4;
        U               1e-4;
        "(k|omega)"     1e-4;
    }
}

relaxationFactors
{
    fields
    {
        p               0.3;
    }
    equations
    {
        U               0.7;
        k               0.7;
        omega           0.7;
    }
}
ra   
fvSolutionre   rv   rw   r   r   r   write_fv_solution  s   6"r   c                 C   s  t j| d}t j|dd tt j|dd}|dt dt d W d	   n1 s/w   Y  tt j|d
d}|d W d	   n1 sNw   Y  dtd d  }tt j|dd}|d| d| d| d| d	 W d	   n1 sw   Y  |d dt  }tt j|dd}|d|dd|dd|dd|dd	 W d	   n1 sw   Y  tt j|dd}|d W d	   d	S 1 sw   Y  d	S )z)Write 0/ boundary conditions for 3D case.0Trb   Ure   zFoamFile
{
    version     2.0;
    format      ascii;
    class       volVectorField;
    object      U;
}

dimensions      [0 1 -1 0 0 0 0];
internalField   uniform (0 0 zm);

boundaryField
{
    inlet
    {
        type            fixedValue;
        value           uniform (0 0 z);
    }
    outlet
    {
        type            zeroGradient;
    }
    ductWall
    {
        type            noSlip;
    }
    vane
    {
        type            noSlip;
    }
}
Npa  FoamFile
{
    version     2.0;
    format      ascii;
    class       volScalarField;
    object      p;
}

dimensions      [0 2 -2 0 0 0 0];
internalField   uniform 0;

boundaryField
{
    inlet
    {
        type            zeroGradient;
    }
    outlet
    {
        type            fixedValue;
        value           uniform 0;
    }
    ductWall
    {
        type            zeroGradient;
    }
    vane
    {
        type            zeroGradient;
    }
}
g      ?g?r(   kzFoamFile
{
    version     2.0;
    format      ascii;
    class       volScalarField;
    object      k;
}

dimensions      [0 2 -2 0 0 0 0];
internalField   uniform zg;

boundaryField
{
    inlet
    {
        type            fixedValue;
        value           uniform z;
    }
    outlet
    {
        type            zeroGradient;
    }
    ductWall
    {
        type            kqRWallFunction;
        value           uniform z`;
    }
    vane
    {
        type            kqRWallFunction;
        value           uniform z
;
    }
}
g      ?gvZ`?omegazFoamFile
{
    version     2.0;
    format      ascii;
    class       volScalarField;
    object      omega;
}

dimensions      [0 0 -1 0 0 0 0];
internalField   uniform z.1fz;
    }
    outlet
    {
        type            zeroGradient;
    }
    ductWall
    {
        type            omegaWallFunction;
        value           uniform zb;
    }
    vane
    {
        type            omegaWallFunction;
        value           uniform nutaB  FoamFile
{
    version     2.0;
    format      ascii;
    class       volScalarField;
    object      nut;
}

dimensions      [0 2 -1 0 0 0 0];
internalField   uniform 0;

boundaryField
{
    inlet
    {
        type            calculated;
        value           uniform 0;
    }
    outlet
    {
        type            calculated;
        value           uniform 0;
    }
    ductWall
    {
        type            nutkWallFunction;
        value           uniform 0;
    }
    vane
    {
        type            nutkWallFunction;
        value           uniform 0;
    }
}
)ri   rk   rh   rj   r   r   INLET_VELOCITYrf   )r   zero_dirr    k_val	omega_valr   r   r   write_initial_conditions  sN   	#$	'	%"r   c                 C   s   t j| d}t j|dd tt j|dd}|d W d    n1 s(w   Y  tt j|dd}|d W d    d S 1 sHw   Y  d S )	NconstantTrb   turbulencePropertiesre   zFoamFile
{
    version     2.0;
    format      ascii;
    class       dictionary;
    object      turbulenceProperties;
}

simulationType  RAS;

RAS
{
    RASModel        kOmegaSST;
    turbulence      on;
    printCoeffs     on;
}
transportPropertieszFoamFile
{
    version     2.0;
    format      ascii;
    class       dictionary;
    object      transportProperties;
}

transportModel  Newtonian;
nu              [0 2 -1 0 0 0 0] 1.5e-05;
)ri   rk   rh   rj   r   r   )r   	const_dirr    r   r   r   write_turbulence_properties  s   "r   c                 C   s   t j|rt| t | t j|dd}t j|dd t j|d}t| | t| t	|| t
| t| t| t| t| t| dS )z7Set up a complete OpenFOAM case for a given vane angle.r   
triSurfaceTrb   zvane.stlN)ri   rk   existsshutilrmtreerj   rh   r^   ro   rt   r|   r   r   rx   r   r   )rQ   r   stl_dirrr   r   r   r   
setup_case  s   



r   c                 C   s   t dd  t d|  d t d  t ddd td|d	}|jd
kr.t d|   dS t ddd td|d}|jd
krHt d|   dS td|d}t ddd td|d}|jd
krgt d|  d dS )z6Run blockMesh + snappyHexMesh + simpleFoam for a case.
<============================================================z  Running angle = z degz  blockMesh...T)flush	blockMeshzlog.blockMeshr   z$  ERROR: blockMesh failed for angle Fz  snappyHexMesh...zsnappyHexMesh -overwritezlog.snappyHexMeshz(  ERROR: snappyHexMesh failed for angle 	checkMeshzlog.checkMeshz  simpleFoam...
simpleFoamzlog.simpleFoamz2  WARNING: simpleFoam returned non-zero for angle z (may have converged early))printr   
returncode)rQ   r   rr   r   r   run_case  s&   



r   c              
   C   s  t j| dd}t j|std|  dS tdd t |D td}|s-td dS |d	 }t j||d
}t j||d}t j|stt j||d}t j|sttd |D ]}t j||}td| dt |  q[dS ddddddd}zNt|d}	dd |		 D }
W d   n1 sw   Y  |
r|
d	 }ddl
}|d|}t|dkrt|d |d< t|d |d< t|d |d< W n ty } ztd|  W Y d}~nd}~ww t j|r`zXt|d}	dd |		 D }
W d   n	1 sw   Y  |
r?|
d	 }ddl
}|d|}t|dkrBt|d |d< t|d |d< t|d |d < W |S W |S W |S  ty_ } ztd!|  W Y d}~|S d}~ww |S )"z<Extract final forces from the forces function object output.postProcessing
vaneForcesz(  WARNING: No forces directory found at Nc                 S   s    g | ]}| d d r|qS ).r_   )replaceisdigit)r2   dr   r   r   r4     s     z"extract_forces.<locals>.<listcomp>)keyz/  WARNING: No time directories in forces outputz	force.datz
moment.datz
forces.datz#  WARNING: No force data file foundz    z/: r   )FxFyFzMxMyMzr   c                 S   *   g | ]}|  r|  d s|  qS #strip
startswithr2   lr   r   r   r4   5     * z[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?r7   r5   r   r(   r   r6   r   z%  WARNING: Error parsing force file: c                 S   r   r   r   r   r   r   r   r4   F  r   r   r   r   z&  WARNING: Error parsing moment file: )ri   rk   rh   r   r   sortedlistdirfloatr   	readlinesrefindallr   	Exception)r   
forces_dirtimeslatest_time
force_filemoment_filettdforcesr    lines	last_liner   numser   r   r   extract_forces  s~   r   c            
      C   sT  g } t D ]}tjtd|d}td| d|  t|| t||}|rzt|}|red|i|}| 	| td|d dd	|d
 dd|d d td|d dd|d dd|d d qtd|  | 	|ddddddd qtd|  | 	|ddddddd qtjtd}t
|ddd}tj|g dd}|  ||  W d    n1 sw   Y  tdd  td|  td  tddd d!dd"d!d
d"d!dd"d!dd"d!dd"d!dd" td# | D ]/}	t|	d d d!|	d d$d!|	d
 d$d!|	d d$d!|	d d$d!|	d d$d!|	d d$ qd S )%Nangle_02dz
>>> Setting up case for angle=z deg at anglez  Forces: Fx=r   z.6fz Fy=r   z Fz=r   z  Moments: Mx=r   z My=r   z Mz=r   z%  Could not extract forces for angle r   )r   r   r   r   r   r   r   z  Simulation failed for angle zvane_forces.csvre   r_   )newline)
fieldnamesr   r   zResults saved to Anglez>6r`   z>12zT------------------------------------------------------------------------------------z>12.6f)ANGLESri   rk   rh   BASE_DIRr   r   r   r   rP   r   csv
DictWriterwriteheader	writerows)
resultsr   r   successr   rowcsv_filer    writerr   r   r   r   mainV  sR   


,.


@2
r   __main__r?   )ry   )&__doc__ri   sysr   r   rE   r   r   r   rf   rg   rN   rM   rO   r)   r   rk   dirnameabspath__file__r   r   r   r'   r^   ro   rt   rx   r|   r   r   r   r   r   r   r   r   __name__r   r   r   r   <module>   sJ   


ICv
06; B% F-
