From d446f85c1d7ad547d824d91076099a3f952b41cc Mon Sep 17 00:00:00 2001 From: Emmanuel Branlard Date: Thu, 3 Feb 2022 16:29:22 -0700 Subject: [PATCH] IO: adding excel file, hawcstab2 cmb, and misc updates from weio/welib --- .../{Main_Onshore_OF.fst => Main_Onshore.fst} | 13 +- data/NREL5MW/onshore/NREL5MW_AD.dat | 35 ++- data/NREL5MW/onshore/NREL5MW_ED_Onshore.dat | 8 +- pyFAST/case_generation/case_gen.py | 6 +- .../examples/Example_CPLambdaPitch.py | 8 +- .../examples/Example_ExcelFile.py | 4 +- .../examples/Example_Parametric.py | 4 +- .../examples/Example_PowerCurve_Parametric.py | 8 +- pyFAST/case_generation/runner.py | 32 +-- .../tests/test_run_Examples.py | 2 +- pyFAST/input_output/__init__.py | 4 +- pyFAST/input_output/bmodes_out_file.py | 152 +++++++++++++ pyFAST/input_output/csv_file.py | 2 +- .../examples/Example_RadialInterp.py | 8 +- .../examples/Example_RadialPostPro.py | 2 +- pyFAST/input_output/excel_file.py | 6 + pyFAST/input_output/fast_input_deck.py | 187 +++++++++++++++- pyFAST/input_output/fast_input_file.py | 5 +- pyFAST/input_output/fast_output_file.py | 6 +- pyFAST/input_output/fast_wind_file.py | 80 +++++++ pyFAST/input_output/file.py | 8 +- pyFAST/input_output/hawc2_st_file.py | 17 +- pyFAST/input_output/hawcstab2_cmb_file.py | 36 ++++ pyFAST/input_output/mannbox_file.py | 78 ++++++- pyFAST/input_output/mini_yaml.py | 2 +- pyFAST/input_output/postpro.py | 101 ++++++++- pyFAST/input_output/test.py | 63 ++++++ .../tests/example_files/CSVxIsString.csv | 8 + .../example_files/ExcelFile_OneSheet.xlsx | Bin 0 -> 11708 bytes .../tests/example_files/FASTIn_BD_bld.dat | 48 ++--- .../tests/example_files/FASTIn_BD_bld_2.dat | 39 ++++ .../tests/example_files/FASTIn_SbD.dat | 2 +- .../example_files/FASTIn_SbD_comments.dat | 165 +++++++++++++++ .../tests/example_files/FASTWnd.wnd | 8 + .../tests/example_files/HAWC2_st.dat | 38 ++++ .../input_decks/Elliptic_AD15_40.dat | 124 +++++++++++ .../Elliptic_AD15_blade_40_cos.dat | 46 ++++ .../example_files/input_decks/Elliptic_IW.dat | 57 +++++ .../input_decks/Elliptic_OLAF.dat | 45 ++++ .../input_decks/Elliptic_Wind.wnd | 5 + .../Main_EllipticalWingInf_OLAF.dvr | 55 +++++ .../input_decks/Polar2PiAlpha_AD15.dat | 152 +++++++++++++ pyFAST/input_output/tests/test_csv.py | 6 + pyFAST/input_output/tests/test_fast_input.py | 18 ++ .../tests/test_fast_input_deck.py | 26 +++ pyFAST/input_output/tests/test_hawc.py | 2 +- .../input_output/tests/test_run_Examples.py | 4 +- pyFAST/input_output/turbsim_file.py | 199 +++++++++++++++++- 48 files changed, 1800 insertions(+), 124 deletions(-) rename data/NREL5MW/{Main_Onshore_OF.fst => Main_Onshore.fst} (84%) create mode 100644 pyFAST/input_output/bmodes_out_file.py create mode 100644 pyFAST/input_output/fast_wind_file.py create mode 100644 pyFAST/input_output/hawcstab2_cmb_file.py create mode 100644 pyFAST/input_output/test.py create mode 100644 pyFAST/input_output/tests/example_files/CSVxIsString.csv create mode 100644 pyFAST/input_output/tests/example_files/ExcelFile_OneSheet.xlsx create mode 100644 pyFAST/input_output/tests/example_files/FASTIn_BD_bld_2.dat create mode 100644 pyFAST/input_output/tests/example_files/FASTIn_SbD_comments.dat create mode 100644 pyFAST/input_output/tests/example_files/FASTWnd.wnd create mode 100644 pyFAST/input_output/tests/example_files/HAWC2_st.dat create mode 100644 pyFAST/input_output/tests/example_files/input_decks/Elliptic_AD15_40.dat create mode 100644 pyFAST/input_output/tests/example_files/input_decks/Elliptic_AD15_blade_40_cos.dat create mode 100644 pyFAST/input_output/tests/example_files/input_decks/Elliptic_IW.dat create mode 100644 pyFAST/input_output/tests/example_files/input_decks/Elliptic_OLAF.dat create mode 100644 pyFAST/input_output/tests/example_files/input_decks/Elliptic_Wind.wnd create mode 100644 pyFAST/input_output/tests/example_files/input_decks/Main_EllipticalWingInf_OLAF.dvr create mode 100644 pyFAST/input_output/tests/example_files/input_decks/Polar2PiAlpha_AD15.dat create mode 100644 pyFAST/input_output/tests/test_fast_input_deck.py diff --git a/data/NREL5MW/Main_Onshore_OF.fst b/data/NREL5MW/Main_Onshore.fst similarity index 84% rename from data/NREL5MW/Main_Onshore_OF.fst rename to data/NREL5MW/Main_Onshore.fst index 99f93de..a1d3c86 100644 --- a/data/NREL5MW/Main_Onshore_OF.fst +++ b/data/NREL5MW/Main_Onshore.fst @@ -18,9 +18,20 @@ False Echo - Echo input data to .ech (flag) 0 CompSub - Compute sub-structural dynamics (switch) {0=None; 1=SubDyn} 0 CompMooring - Compute mooring system (switch) {0=None; 1=MAP++; 2=FEAMooring; 3=MoorDyn; 4=OrcaFlex} 0 CompIce - Compute ice loads (switch) {0=None; 1=IceFloe; 2=IceDyn} +0 MHK - MHK turbine type (switch) {0=Not an MHK turbine; 1=Fixed MHK turbine; 2=Floating MHK turbine} +---------------------- ENVIRONMENTAL CONDITIONS -------------------------------- +9.80665 Gravity - Gravitational acceleration (m/s^2) +1.225 AirDens - Air density (kg/m^3) +0 WtrDens - Water density (kg/m^3) +1.464e-05 KinVisc - Kinematic viscosity of working fluid (m^2/s) +335 SpdSound - Speed of sound in working fluid (m/s) +103500 Patm - Atmospheric pressure (Pa) [used only for an MHK turbine cavitation check] +1700 Pvap - Vapour pressure of working fluid (Pa) [used only for an MHK turbine cavitation check] +0 WtrDpth - Water depth (m) +0 MSL2SWL - Offset between still-water level and mean sea level (m) [positive upward] ---------------------- INPUT FILES --------------------------------------------- "onshore/NREL5MW_ED_Onshore.dat" EDFile - Name of file containing ElastoDyn input parameters (quoted string) -"NA" BDBldFile(1) - Name of file containing BeamDyn input parameters for blade 1 (quoted string) +"5MW_Baseline/NRELOffshrBsline5MW_BeamDyn.dat" BDBldFile(1) - Name of file containing BeamDyn input parameters for blade 1 (quoted string) "NA" BDBldFile(2) - Name of file containing BeamDyn input parameters for blade 2 (quoted string) "NA" BDBldFile(3) - Name of file containing BeamDyn input parameters for blade 3 (quoted string) "onshore/NREL5MW_IW.dat" InflowFile - Name of file containing inflow wind input parameters (quoted string) diff --git a/data/NREL5MW/onshore/NREL5MW_AD.dat b/data/NREL5MW/onshore/NREL5MW_AD.dat index 7cf4f84..7b2cc10 100644 --- a/data/NREL5MW/onshore/NREL5MW_AD.dat +++ b/data/NREL5MW/onshore/NREL5MW_AD.dat @@ -13,29 +13,28 @@ False CavitCheck - Perform cavitation check? (flag) [AFAeroMod m False CompAA - Flag to compute AeroAcoustics calculation [used only when WakeMod = 1 or 2] "unused" AA_InputFile - AeroAcoustics input file [used only when CompAA=true] ====== Environmental Conditions =================================================================== - 1.225 AirDens - Air density (kg/m^3) - 1.464E-05 KinVisc - Kinematic air viscosity (m^2/s) - 335 SpdSound - Speed of sound (m/s) - 103500 Patm - Atmospheric pressure (Pa) [used only when CavitCheck=True] - 1700 Pvap - Vapour pressure of fluid (Pa) [used only when CavitCheck=True] - 0.5 FluidDepth - Water depth above mid-hub height (m) [used only when CavitCheck=True] -====== Blade-Element/Momentum Theory Options ====================================================== [unused when WakeMod=0 or 3] - 2 SkewMod - Type of skewed-wake correction model (switch) {1=uncoupled, 2=Pitt/Peters, 3=coupled} [unused when WakeMod=0 or 3] -"default" SkewModFactor - Constant used in Pitt/Peters skewed wake model {or "default" is 15/32*pi} (-) [used only when SkewMod=2; unused when WakeMod=0 or 3] -True TipLoss - Use the Prandtl tip-loss model? (flag) [unused when WakeMod=0 or 3] -True HubLoss - Use the Prandtl hub-loss model? (flag) [unused when WakeMod=0 or 3] -True TanInd - Include tangential induction in BEMT calculations? (flag) [unused when WakeMod=0 or 3] -False AIDrag - Include the drag term in the axial-induction calculation? (flag) [unused when WakeMod=0 or 3] -False TIDrag - Include the drag term in the tangential-induction calculation? (flag) [unused when WakeMod=0,3 or TanInd=FALSE] -"Default" IndToler - Convergence tolerance for BEMT nonlinear solve residual equation {or "default"} (-) [unused when WakeMod=0 or 3] - 100 MaxIter - Maximum number of iteration steps (-) [unused when WakeMod=0] + default AirDens - Air density (kg/m^3) + default KinVisc - Kinematic air viscosity (m^2/s) + default SpdSound - Speed of sound (m/s) + default Patm - Atmospheric pressure (Pa) [used only when CavitCheck=True] + default Pvap - Vapour pressure of fluid (Pa) [used only when CavitCheck=True] +====== Blade-Element/Momentum Theory Options ====================================================== [used only when WakeMod=1] + 2 SkewMod - Type of skewed-wake correction model (switch) {1=uncoupled, 2=Pitt/Peters, 3=coupled} [used only when WakeMod=1] +"default" SkewModFactor - Constant used in Pitt/Peters skewed wake model {or "default" is 15/32*pi} (-) [used only when SkewMod=2; unused when WakeMod=0] +True TipLoss - Use the Prandtl tip-loss model? (flag) [used only when WakeMod=1] +False HubLoss - Use the Prandtl hub-loss model? (flag) [used only when WakeMod=1] +True TanInd - Include tangential induction in BEMT calculations? (flag) [used only when WakeMod=1] +False AIDrag - Include the drag term in the axial-induction calculation? (flag) [used only when WakeMod=1] +False TIDrag - Include the drag term in the tangential-induction calculation? (flag) [used only when WakeMod=1 and TanInd=TRUE] +"default" IndToler - Convergence tolerance for BEMT nonlinear solve residual equation {or "default"} (-) [used only when WakeMod=1] + 100 MaxIter - Maximum number of iteration steps (-) [used only when WakeMod=1] ====== Dynamic Blade-Element/Momentum Theory Options ============================================== [used only when WakeMod=2] 2 DBEMT_Mod - Type of dynamic BEMT (DBEMT) model {1=constant tau1, 2=time-dependent tau1} (-) [used only when WakeMod=2] 4 tau1_const - Time constant for DBEMT (s) [used only when WakeMod=2 and DBEMT_Mod=1] ====== OLAF -- cOnvecting LAgrangian Filaments (Free Vortex Wake) Theory Options ================== [used only when WakeMod=3] -"unused" OLAFInputFileName - Input file for OLAF [used only when WakeMod=3] +"unused" OLAFInputFileName - Input file for OLAF [used only when WakeMod=3] ====== Beddoes-Leishman Unsteady Airfoil Aerodynamics Options ===================================== [used only when AFAeroMod=2] - 3 UAMod - Unsteady Aero Model Switch (switch) {1=Baseline model (Original), 2=Gonzalez's variant (changes in Cn,Cc,Cm), 3=Minnema/Pierce variant (changes in Cc and Cm)} [used only when AFAeroMod=2] + 3 UAMod - Unsteady Aero Model Switch (switch) {1=Baseline model (Original), 2=Gonzalez’s variant (changes in Cn,Cc,Cm), 3=Minemma/Pierce variant (changes in Cc and Cm)} [used only when AFAeroMod=2] True FLookup - Flag to indicate whether a lookup for f' will be calculated (TRUE) or whether best-fit exponential equations will be used (FALSE); if FALSE S1-S4 must be provided in airfoil input files (flag) [used only when AFAeroMod=2] ====== Airfoil Information ========================================================================= 1 AFTabMod - Interpolation method for multiple airfoil tables {1=1D interpolation on AoA (first table only); 2=2D interpolation on AoA and Re; 3=2D interpolation on AoA and UserProp} (-) diff --git a/data/NREL5MW/onshore/NREL5MW_ED_Onshore.dat b/data/NREL5MW/onshore/NREL5MW_ED_Onshore.dat index 9f134b2..b32ee02 100644 --- a/data/NREL5MW/onshore/NREL5MW_ED_Onshore.dat +++ b/data/NREL5MW/onshore/NREL5MW_ED_Onshore.dat @@ -4,8 +4,6 @@ NREL 5.0 MW Baseline Wind Turbine for Use in Offshore Analysis. Properties from False Echo - Echo input data to ".ech" (flag) 3 Method - Integration method: {1: RK4, 2: AB4, or 3: ABM4} (-) "default" DT - Integration time step (s) ----------------------- ENVIRONMENTAL CONDITION --------------------------------- - 9.80665 Gravity - Gravitational acceleration (m/s^2) ---------------------- DEGREES OF FREEDOM -------------------------------------- True FlapDOF1 - First flapwise blade mode DOF (flag) True FlapDOF2 - Second flapwise blade mode DOF (flag) @@ -32,7 +30,7 @@ False PtfmYDOF - Platform yaw rotation DOF (flag) 0 BlPitch(3) - Blade 3 initial pitch (degrees) [unused for 2 blades] 0 TeetDefl - Initial or fixed teeter angle (degrees) [unused for 3 blades] 0 Azimuth - Initial azimuth angle for blade 1 (degrees) - 0.0 RotSpeed - Initial or fixed rotor speed (rpm) + 10.0 RotSpeed - Initial or fixed rotor speed (rpm) 0 NacYaw - Initial or fixed nacelle-yaw angle (degrees) 0 TTDspFA - Initial fore-aft tower-top displacement (meters) 0 TTDspSS - Initial side-to-side tower-top displacement (meters) @@ -210,6 +208,10 @@ True TabDelim - Use tab delimiters in text tabular output file? (fla "TwrBsMzt" - Side-to-side bending, fore-aft bending, and yaw moments at the base of the tower (platform) "RootMyb1" "NcIMUTVxs" +"Q_DrTr" Displacement of drivetrain rotational-flexibility DOF +"Q_GeAz" Displacement of variable speed generator DOF +"QD_DrTr" Velocity of drivetrain rotational-flexibility DOF +"QD_GeAz" Velocity of variable speed generator DOF END of input file (the word "END" must appear in the first 3 columns of this last OutList line) ---------------------- NODE OUTPUTS -------------------------------------------- 1 BldNd_BladesOut - Blades to output diff --git a/pyFAST/case_generation/case_gen.py b/pyFAST/case_generation/case_gen.py index 23ca57b..f4613ec 100644 --- a/pyFAST/case_generation/case_gen.py +++ b/pyFAST/case_generation/case_gen.py @@ -345,7 +345,7 @@ def paramsStiff(p=None): p['EDFile|PtfmYDOF'] = 'False' return p -def paramsWS_RPM_Pitch(WS, RPM, Pitch, baseDict=None, FlatInputs=False): +def paramsWS_RPM_Pitch(WS, RPM, Pitch, baseDict=None, flatInputs=False): """ Generate OpenFAST "parameters" (list of dictionaries with "address") chaing the inputs in ElastoDyn, InflowWind for different wind speed, RPM and Pitch @@ -470,8 +470,8 @@ def createStepWind(filename,WSstep=1,WSmin=3,WSmax=25,tstep=100,dt=0.5,tmin=0,tm #print(f.toDataFrame()) #pass -#createStepWind('tests.wnd',tstep=200,WSmax=28) -# createStepWind('tests.wnd',tstep=200,WSmin=5,WSmax=7,WSstep=2) +#createStepWind('test.wnd',tstep=200,WSmax=28) +# createStepWind('test.wnd',tstep=200,WSmin=5,WSmax=7,WSstep=2) diff --git a/pyFAST/case_generation/examples/Example_CPLambdaPitch.py b/pyFAST/case_generation/examples/Example_CPLambdaPitch.py index db6c74a..a7b98b5 100644 --- a/pyFAST/case_generation/examples/Example_CPLambdaPitch.py +++ b/pyFAST/case_generation/examples/Example_CPLambdaPitch.py @@ -12,13 +12,13 @@ def CPLambdaExample(): """ Example to determine the CP-CT Lambda Pitch matrices of a turbine. This script uses the function CPCT_LambdaPitch which basically does the same as Parametric Examples given in this folder """ - FAST_EXE = os.path.join(MyDir, "../../../../openfast/build/bin/openfast_x64.exe") # Location of a FAST exe (and dll) + FAST_EXE = os.path.join(MyDir, '../../../data/openfast.exe') # Location of a FAST exe (and dll) ref_dir = os.path.join(MyDir, '../../../data/NREL5MW/') # Folder where the fast input files are located (will be copied) - main_file = 'Main_Onshore_OF.fst' # Main file in ref_dir, used as a template + main_file = 'Main_Onshore.fst' # Main file in ref_dir, used as a template # --- Computing CP and CT matrices for range of lambda and pitches - Lambda = np.linspace(0.1,10,20) - Pitch = np.linspace(-2,10,20) + Lambda = np.linspace(0.1,10,3) + Pitch = np.linspace(-10,10,4) CP,CT,Lambda,Pitch,MaxVal,result = case_gen.CPCT_LambdaPitch(ref_dir,main_file,Lambda,Pitch,fastExe=FAST_EXE,showOutputs=False,nCores=4,TMax=10) diff --git a/pyFAST/case_generation/examples/Example_ExcelFile.py b/pyFAST/case_generation/examples/Example_ExcelFile.py index 9cd3877..2a99e25 100644 --- a/pyFAST/case_generation/examples/Example_ExcelFile.py +++ b/pyFAST/case_generation/examples/Example_ExcelFile.py @@ -16,8 +16,8 @@ def main(): # --- Main Parameters ref_dir = os.path.join(MyDir, '../../../data/NREL5MW/') # Folder where the fast input files are located (will be copied) - FAST_EXE = FAST_EXE = os.path.join(MyDir, '../../../../openfast/build/bin/openfast_x64.exe') # Location of a FAST exe (and dll) - main_file = 'Main_Onshore_OF.fst' # Main file in ref_dir, used as a template + FAST_EXE = os.path.join(MyDir, '../../../data/openfast.exe') # Location of a FAST exe (and dll) + main_file = 'Main_Onshore.fst' # Main file in ref_dir, used as a template work_dir = os.path.join(MyDir, '_NREL5MW_ParametricExcel/') # Output folder (will be created) parametricFile = 'ParametricExcel.xlsx' # Excel file containing set of parameters diff --git a/pyFAST/case_generation/examples/Example_Parametric.py b/pyFAST/case_generation/examples/Example_Parametric.py index 05a613f..9118de8 100644 --- a/pyFAST/case_generation/examples/Example_Parametric.py +++ b/pyFAST/case_generation/examples/Example_Parametric.py @@ -28,9 +28,9 @@ def ParametricExample(): """ # --- Parameters for this script - FAST_EXE = FAST_EXE = os.path.join(MyDir, '../../../../openfast/build/bin/openfast_x64.exe') # Location of a FAST exe (and dll) + FAST_EXE = os.path.join(MyDir, '../../../data/openfast.exe') # Location of a FAST exe (and dll) ref_dir = os.path.join(MyDir, '../../../data/NREL5MW/') # Folder where the fast input files are located (will be copied) - main_file = 'Main_Onshore_OF.fst' # Main file in ref_dir, used as a template + main_file = 'Main_Onshore.fst' # Main file in ref_dir, used as a template work_dir = '_NREL5MW_Parametric/' # Output folder (will be created) # --- Defining the parametric study (list of dictionnaries with keys as FAST parameters) diff --git a/pyFAST/case_generation/examples/Example_PowerCurve_Parametric.py b/pyFAST/case_generation/examples/Example_PowerCurve_Parametric.py index bf1327e..8282cfc 100644 --- a/pyFAST/case_generation/examples/Example_PowerCurve_Parametric.py +++ b/pyFAST/case_generation/examples/Example_PowerCurve_Parametric.py @@ -23,9 +23,9 @@ def PowerCurveParametricExample1(): - they values are the values corresponding to this parameter """ # --- Parameters for this script - FAST_EXE = os.path.join(MyDir, '../../../../openfast/build/bin/openfast_x64.exe') # Location of a FAST exe (and dll) + FAST_EXE = os.path.join(MyDir, '../../../data/openfast.exe') # Location of a FAST exe (and dll) ref_dir = os.path.join(MyDir, '../../../data/NREL5MW/') # Folder where the fast input files are located (will be copied) - main_file = 'Main_Onshore_OF.fst' # Main file in ref_dir, used as a template + main_file = 'Main_Onshore.fst' # Main file in ref_dir, used as a template work_dir = '_NREL5MW_PowerCurveParametric/' # Output folder (will be created) # --- Defining the parametric study (list of dictionnaries with keys as FAST parameters) @@ -82,9 +82,9 @@ def PowerCurveParametricExample2(): - they values are the values corresponding to this parameter """ # --- Parameters for this script - FAST_EXE = FAST_EXE = os.path.join(MyDir, '../../../../openfast/build/bin/openfast_x64.exe') # Location of a FAST exe (and dll) + FAST_EXE = os.path.join(MyDir, '../../../data/openfast.exe') # Location of a FAST exe (and dll) ref_dir = os.path.join(MyDir, '../../../data/NREL5MW/') # Folder where the fast input files are located (will be copied) - main_file = 'Main_Onshore_OF.fst' # Main file in ref_dir, used as a template + main_file = 'Main_Onshore.fst' # Main file in ref_dir, used as a template work_dir = '_NREL5MW_PowerCurveParametric2/' # Output folder (will be created) out_Ext = '.outb' # Output extension diff --git a/pyFAST/case_generation/runner.py b/pyFAST/case_generation/runner.py index fb4a3e5..c98fbbc 100644 --- a/pyFAST/case_generation/runner.py +++ b/pyFAST/case_generation/runner.py @@ -22,7 +22,7 @@ # --- Tools for executing FAST # --------------------------------------------------------------------------------{ # --- START cmd.py -def run_cmds(inputfiles, exe, parallel=True, showOutputs=True, nCores=None, showCommand=True): +def run_cmds(inputfiles, exe, parallel=True, showOutputs=True, nCores=None, showCommand=True, verbose=True): """ Run a set of simple commands of the form `exe input_file` By default, the commands are run in "parallel" (though the method needs to be improved) The stdout and stderr may be displayed on screen (`showOutputs`) or hidden. @@ -31,13 +31,15 @@ def run_cmds(inputfiles, exe, parallel=True, showOutputs=True, nCores=None, show Failed=[] def _report(p): if p.returncode==0: - print('[ OK ] Input : ',p.input_file) + if verbose: + print('[ OK ] Input : ',p.input_file) else: Failed.append(p) - print('[FAIL] Input : ',p.input_file) - print(' Directory: '+os.getcwd()) - print(' Command : '+p.cmd) - print(' Use `showOutputs=True` to debug, or run the command above.') + if verbose: + print('[FAIL] Input : ',p.input_file) + print(' Directory: '+os.getcwd()) + print(' Command : '+p.cmd) + print(' Use `showOutputs=True` to debug, or run the command above.') #out, err = p.communicate() #print('StdOut:\n'+out) #print('StdErr:\n'+err) @@ -68,13 +70,14 @@ def _report(p): _report(p) # --- Giving a summary if len(Failed)==0: - print('[ OK ] All simulations run successfully.') - return True + if verbose: + print('[ OK ] All simulations run successfully.') + return True, Failed else: print('[FAIL] {}/{} simulations failed:'.format(len(Failed),len(inputfiles))) for p in Failed: print(' ',p.input_file) - return False + return False, Failed def run_cmd(input_file_or_arglist, exe, wait=True, showOutputs=False, showCommand=True): """ Run a simple command of the form `exe input_file` or `exe arg1 arg2` """ @@ -116,7 +119,7 @@ class Dummy(): return p # --- END cmd.py -def run_fastfiles(fastfiles, fastExe=None, parallel=True, showOutputs=True, nCores=None, showCommand=True, reRun=True): +def run_fastfiles(fastfiles, fastExe=None, parallel=True, showOutputs=True, nCores=None, showCommand=True, reRun=True, verbose=True): if fastExe is None: fastExe=FAST_EXE if not reRun: @@ -131,7 +134,7 @@ def run_fastfiles(fastfiles, fastExe=None, parallel=True, showOutputs=True, nCor newfiles.append(f) fastfiles=newfiles - return run_cmds(fastfiles, fastExe, parallel=parallel, showOutputs=showOutputs, nCores=nCores, showCommand=showCommand) + return run_cmds(fastfiles, fastExe, parallel=parallel, showOutputs=showOutputs, nCores=nCores, showCommand=showCommand, verbose=verbose) def run_fast(input_file, fastExe=None, wait=True, showOutputs=False, showCommand=True): if fastExe is None: @@ -139,7 +142,7 @@ def run_fast(input_file, fastExe=None, wait=True, showOutputs=False, showCommand return run_cmd(input_file, fastExe, wait=wait, showOutputs=showOutputs, showCommand=showCommand) -def writeBatch(batchfile, fastfiles, fastExe=None, nBatches=1): +def writeBatch(batchfile, fastfiles, fastExe=None, nBatches=1, pause=False): """ Write batch file, everything is written relative to the batch file""" if fastExe is None: fastExe=FAST_EXE @@ -154,6 +157,9 @@ def writeb(batchfile, fastfiles): ff_rel = os.path.relpath(ff_abs, batchdir) l = fastExe_rel + ' '+ ff_rel f.write("%s\n" % l) + if pause: + f.write("pause\n") # windows only.. + if nBatches==1: writeb(batchfile, fastfiles) else: @@ -167,7 +173,7 @@ def writeb(batchfile, fastfiles): -def removeFASTOutputs(workDir): +def removeFASTOuputs(workDir): # Cleaning folder for f in glob.glob(os.path.join(workDir,'*.out')): os.remove(f) diff --git a/pyFAST/case_generation/tests/test_run_Examples.py b/pyFAST/case_generation/tests/test_run_Examples.py index c4c22ba..c705186 100644 --- a/pyFAST/case_generation/tests/test_run_Examples.py +++ b/pyFAST/case_generation/tests/test_run_Examples.py @@ -20,13 +20,13 @@ def test_run_examples(self): MyDir=os.path.dirname(__file__) files = glob.glob(os.path.join(MyDir,'../examples/[a-zA-Z]*.py')) import matplotlib.pyplot as plt - plt.close('all') print('\n--------------------------------------------------------------') for f in files: print('Running example script: {}'.format(f)) if hasattr(self,'subTest'): with self.subTest(filename=os.path.basename(f)): execfile(f, {'__name__': '__test__', 'print': lambda *_:None}) + plt.close('all') if __name__ == '__main__': diff --git a/pyFAST/input_output/__init__.py b/pyFAST/input_output/__init__.py index 1ccbb3f..fb2a909 100644 --- a/pyFAST/input_output/__init__.py +++ b/pyFAST/input_output/__init__.py @@ -1,7 +1,9 @@ from .csv_file import CSVFile +from .excel_file import ExcelFile from .fast_input_file import FASTInputFile +from .fast_input_deck import FASTInputDeck from .fast_linearization_file import FASTLinearizationFile from .fast_output_file import FASTOutputFile from .fast_summary_file import FASTSummaryFile from .turbsim_file import TurbSimFile -from .raawmat_file import RAAWMatFile +from .raawmat_file import RAAWMatFile diff --git a/pyFAST/input_output/bmodes_out_file.py b/pyFAST/input_output/bmodes_out_file.py new file mode 100644 index 0000000..337d284 --- /dev/null +++ b/pyFAST/input_output/bmodes_out_file.py @@ -0,0 +1,152 @@ +""" +Input/output class for the BModes output files +""" +import numpy as np +import pandas as pd +import os + +try: + from .file import File, WrongFormatError, BrokenFormatError +except: + EmptyFileError = type('EmptyFileError', (Exception,),{}) + WrongFormatError = type('WrongFormatError', (Exception,),{}) + BrokenFormatError = type('BrokenFormatError', (Exception,),{}) + File=dict + +class BModesOutFile(File): + """ + Read/write a BModes output file. The object behaves as a dictionary. + + Main methods + ------------ + - read, write, toDataFrame, keys + + Examples + -------- + f = BModesOutFile('file.out') + print(f.keys()) + print(f.toDataFrame().columns) + + """ + + @staticmethod + def defaultExtensions(): + """ List of file extensions expected for this fileformat""" + return ['.out'] + + @staticmethod + def formatName(): + """ Short string (~100 char) identifying the file format""" + return 'BModes output file' + + def __init__(self, filename=None, **kwargs): + """ Class constructor. If a `filename` is given, the file is read. """ + self.filename = filename + if filename: + self.read(**kwargs) + + def read(self, filename=None, **kwargs): + """ Reads the file self.filename, or `filename` if provided """ + + # --- Standard tests and exceptions (generic code) + if filename: + self.filename = filename + if not self.filename: + raise Exception('No filename provided') + if not os.path.isfile(self.filename): + raise OSError(2,'File not found:',self.filename) + if os.stat(self.filename).st_size == 0: + raise EmptyFileError('File is empty:',self.filename) + # --- Calling (children) function to read + self._read(**kwargs) + + def write(self, filename=None): + """ Rewrite object to file, or write object to `filename` if provided """ + if filename: + self.filename = filename + if not self.filename: + raise Exception('No filename provided') + # Calling (children) function to write + self._write() + + def _read(self): + """ Reads self.filename and stores data into self. Self is (or behaves like) a dictionary""" + # --- Example: + #self['data']=[] + #with open(self.filename, 'r', errors="surrogateescape") as f: + # for i, line in enumerate(f): + # self['data'].append(line) + + with open(self.filename) as f: + self['frequencies'] = [] + self['mode_shapes'] = [] + row_string = f.readline() + if row_string.find('BModes')<0: + raise WrongFormatError('This file was not generated by BModes, "BModes" is not found on first line') + while row_string: + row_string = f.readline() + freq_id = row_string.find('freq = ') + if freq_id > 0: + self['frequencies'].append(float(row_string[freq_id+7:freq_id+19])) + f.readline() + f.readline() + f.readline() + data=[] + while True: + row_data = f.readline() + if len(row_data.strip())==0 or row_data.find('===')==0: + break + else: + data.append(row_data.split()) + self['mode_shapes'].append(np.asarray(data).astype(float)) + + def _write(self): + """ Writes to self.filename""" + # --- Example: + #with open(self.filename,'w') as f: + # f.write(self.toString) + raise NotImplementedError() + + def toDataFrame(self): + """ Returns object into one DataFrame, or a dictionary of DataFrames""" + dfs={} + cols =['span_loc','s-s disp','s-s slope','f-a disp','f-a slope','twist'] + for iMode,mode in enumerate(self['mode_shapes']): + dfs['Mode_{}'.format(iMode+1)]= pd.DataFrame(data=mode,columns=cols) + return dfs + + # --- Optional functions + def __repr__(self): + """ String that is written to screen when the user calls `print()` on the object. + Provide short and relevant information to save time for the user. + """ + s='<{} object>:\n'.format(type(self).__name__) + s+='|Main attributes:\n' + s+='| - filename: {}\n'.format(self.filename) + # --- Example printing some relevant information for user + s+='|Main keys:\n' + s+='| - frequencies: {}\n'.format(self['frequencies']) + s+='| - mode_shapes : {} shapes of shape {}x{}\n'.format(len(self['mode_shapes']), *self['mode_shapes'][0].shape) + s+='|Main methods:\n' + s+='| - read, write, toDataFrame, keys' + return s + + def _get_modal_coefficients(x, y, deg=[2, 3, 4, 5, 6]): + # Normalize x input + xn = (x - x.min()) / (x.max() - x.min()) + # Get coefficients to 6th order polynomial + p6 = np.polynomial.polynomial.polyfit(xn, y, deg) + return p6 + + def _identify(self): + """ identify modes""" + pass + + + +if __name__ == '__main__': + f = BModesOutFile('tests/example_files/BModesOut.out') + df = f.toDataFrame() + print(f) + print(df) + diff --git a/pyFAST/input_output/csv_file.py b/pyFAST/input_output/csv_file.py index b7f7904..582f9b1 100644 --- a/pyFAST/input_output/csv_file.py +++ b/pyFAST/input_output/csv_file.py @@ -20,7 +20,7 @@ class CSVFile(File): -------- # Read a csv file and convert it to a pandas dataframe - f = CSVFile('tests.csv') + f = CSVFile('test.csv') df = f.toDataFrame() """ diff --git a/pyFAST/input_output/examples/Example_RadialInterp.py b/pyFAST/input_output/examples/Example_RadialInterp.py index d11d944..4c1b095 100644 --- a/pyFAST/input_output/examples/Example_RadialInterp.py +++ b/pyFAST/input_output/examples/Example_RadialInterp.py @@ -21,7 +21,7 @@ def main(): # --- Define output radial stations # Option 1 - Get all these locations automatically (recommended) - fstFile = os.path.join(MyDir,'../../../data/NREL5MW/Main_Onshore_OF.fst') # must correspond to the one used to generate outputs + fstFile = os.path.join(MyDir,'../../../data/NREL5MW/Main_Onshore.fst') # must correspond to the one used to generate outputs r_AD, r_ED, r_BD, IR_AD, IR_ED, IR_BD, R, r_hub, fst = postpro.FASTRadialOutputs(fstFile, df.columns.values) # Option 2 - Get ouputs locations for each module @@ -42,9 +42,9 @@ def main(): # --- Plot fig,ax = plt.subplots(1, 1, sharey=False, figsize=(6.4,4.8)) # (6.4,4.8) fig.subplots_adjust(left=0.12, right=0.95, top=0.95, bottom=0.11, hspace=0.20, wspace=0.20) - ax.plot(df['Time_[s]'], df['AB1N017Cl_[-]'], label='Section before (r={}m)'.format(r_AD[16])) - ax.plot(df['Time_[s]'], Cl_interp , label='Interpolated (r={}m)'.format(r)) - ax.plot(df['Time_[s]'], df['AB1N018Cl_[-]'], label='Section after (r={}m)'.format(r_AD[17])) + ax.plot(df['Time_[s]'].values, df['AB1N017Cl_[-]'].values, label='Section before (r={}m)'.format(r_AD[16])) + ax.plot(df['Time_[s]'].values, Cl_interp.values , label='Interpolated (r={}m)'.format(r)) + ax.plot(df['Time_[s]'].values, df['AB1N018Cl_[-]'].values, label='Section after (r={}m)'.format(r_AD[17])) ax.set_xlabel('Time [s]') ax.set_ylabel('Cl [-]') ax.set_xlim([7,10]) diff --git a/pyFAST/input_output/examples/Example_RadialPostPro.py b/pyFAST/input_output/examples/Example_RadialPostPro.py index 42f5e61..12af82d 100644 --- a/pyFAST/input_output/examples/Example_RadialPostPro.py +++ b/pyFAST/input_output/examples/Example_RadialPostPro.py @@ -22,7 +22,7 @@ def main(): # --- Step2 : Average data and extrat the radial stations # Averaging here is done over 1 period (avgParam=1, avgMethod='periods') # To get the output radial stations, a .fst file is needed - fstFile = os.path.join(MyDir,'../../../data/NREL5MW/Main_Onshore_OF.fst') + fstFile = os.path.join(MyDir,'../../../data/NREL5MW/Main_Onshore.fst') dfRad_ED, dfRad_AD, dfRad_BD = postpro.spanwisePostPro(FST_In=fstFile, avgMethod='periods', avgParam=1, df=df) # --- Step1&2 at once (when .outb and .fst are next to each other in same folder, with same name) diff --git a/pyFAST/input_output/excel_file.py b/pyFAST/input_output/excel_file.py index 0b9c9ab..df28931 100644 --- a/pyFAST/input_output/excel_file.py +++ b/pyFAST/input_output/excel_file.py @@ -1,3 +1,9 @@ +from __future__ import division,unicode_literals,print_function,absolute_import +from builtins import map, range, chr, str +from io import open +from future import standard_library +standard_library.install_aliases() + from .file import File, WrongFormatError, BrokenFormatError import numpy as np import pandas as pd diff --git a/pyFAST/input_output/fast_input_deck.py b/pyFAST/input_output/fast_input_deck.py index 9fa4eef..9dcf601 100644 --- a/pyFAST/input_output/fast_input_deck.py +++ b/pyFAST/input_output/fast_input_deck.py @@ -1,3 +1,12 @@ +from __future__ import division +from __future__ import unicode_literals +from __future__ import print_function +from __future__ import absolute_import +from io import open +from builtins import range +from builtins import str +from future import standard_library +standard_library.install_aliases() import os import numpy as np import re @@ -24,9 +33,15 @@ def __init__(self, fullFstPath='', readlist=['all'], verbose=False): AC: airfoil coordinates (if present) """ + # Sanity + if type(verbose) is not bool: + raise Exception('`verbose` arguments needs to be a boolean') + self.filename = fullFstPath self.verbose = verbose self.readlist = readlist + + if not type(self.readlist) is list: self.readlist=[readlist] if 'all' in self.readlist: @@ -64,6 +79,8 @@ def __init__(self, fullFstPath='', readlist=['all'], verbose=False): self.fst_vt['ac_data'] = [] # TODO, how is it stored in WEIS? + self.ADversion='' + # Read all inputs files if len(fullFstPath)>0: self.read() @@ -133,6 +150,75 @@ def FAST_InputFile(self): def FAST_directory(self): return os.path.dirname(self.filename) # Path to fst directory files + + @property + def inputFiles(self): + files=[] + files+=[self.ED_path, self.ED_twr_path, self.ED_bld_path] + files+=[self.BD_path, self.BD_bld_path] + return [f for f in files if f not in self.unusedNames] + + + @property + def ED_relpath(self): + try: + return self.fst_vt['Fst']['EDFile'].replace('"','') + except: + return 'none' + + @property + def ED_twr_relpath(self): + try: + return os.path.join(os.path.dirname(self.fst_vt['Fst']['EDFile']).replace('"',''), self.fst_vt['ElastoDyn']['TwrFile'].replace('"','')) + except: + return 'none' + + @property + def ED_bld_relpath(self): + try: + if 'BldFile(1)' in self.fst_vt['ElastoDyn'].keys(): + return os.path.join(os.path.dirname(self.fst_vt['Fst']['EDFile'].replace('"','')), self.fst_vt['ElastoDyn']['BldFile(1)'].replace('"','')) + else: + return os.path.join(os.path.dirname(self.fst_vt['Fst']['EDFile'].replace('"','')), self.fst_vt['ElastoDyn']['BldFile1'].replace('"','')) + except: + return 'none' + + @property + def BD_relpath(self): + try: + return self.fst_vt['Fst']['BDBldFile(1)'].replace('"','') + except: + return 'none' + + @property + def BD_bld_relpath(self): + try: + return os.path.join(os.path.dirname(self.fst_vt['Fst']['BDBldFile(1)'].replace('"','')), self.fst_vt['BeamDyn']['BldFile'].replace('"','')) + except: + return 'none' + + @property + def ED_path(self): return self._fullpath(self.ED_relpath) + @property + def BD_path(self): return self._fullpath(self.BD_relpath) + @property + def BD_bld_path(self): return self._fullpath(self.BD_bld_relpath) + @property + def ED_twr_path(self): return self._fullpath(self.ED_twr_relpath) + @property + def ED_bld_path(self): return self._fullpath(self.ED_bld_relpath) + + + + def _fullpath(self, relfilepath): + relfilepath = relfilepath.replace('"','') + basename = os.path.basename(relfilepath) + if basename.lower() in self.unusedNames: + return 'none' + else: + return os.path.join(self.FAST_directory, relfilepath) + + def read(self, filename=None): if filename is not None: self.filename = filename @@ -166,11 +252,8 @@ def read(self, filename=None): if 'EDFile' in self.fst_vt['Fst'].keys(): self.fst_vt['ElastoDyn'] = self._read(self.fst_vt['Fst']['EDFile'],'ED') if self.fst_vt['ElastoDyn'] is not None: - twr_file = os.path.join(os.path.dirname(self.fst_vt['Fst']['EDFile']), self.fst_vt['ElastoDyn']['TwrFile']) - try: - bld_file = os.path.join(os.path.dirname(self.fst_vt['Fst']['EDFile']), self.fst_vt['ElastoDyn']['BldFile(1)']) - except: - bld_file = os.path.join(os.path.dirname(self.fst_vt['Fst']['EDFile']), self.fst_vt['ElastoDyn']['BldFile1']) + twr_file = self.ED_twr_relpath + bld_file = self.ED_bld_relpath self.fst_vt['ElastoDynTower'] = self._read(twr_file,'EDtwr') self.fst_vt['ElastoDynBlade'] = self._read(bld_file,'EDbld') @@ -255,12 +338,104 @@ def _read(self, relfilepath, shortkey): print('[WARN] File not found '+fullpath) return None + + + def write(self, filename=None, prefix='', suffix='', directory=None): + """ Write a standardized input file deck""" + if filename is None: + filename=self.filename # Overwritting + self.filename=filename + if directory is None: + directory = os.path.dirname(filename) + basename = os.path.splitext(os.path.basename(filename))[0] + + + fst = self.fst_vt['Fst'] + + # Filenames + filename_ED = os.path.join(directory,prefix+'ED'+suffix+'.dat') if fst['CompElast']>0 else 'none' + filename_IW = os.path.join(directory,prefix+'IW'+suffix+'.dat') if fst['CompInflow']>0 else 'none' + filename_BD = os.path.join(directory,prefix+'BD'+suffix+'.dat') if fst['CompElast']==2 else 'none' + filename_AD = os.path.join(directory,prefix+'AD'+suffix+'.dat') if fst['CompAero']>0 else 'none' + filename_HD = os.path.join(directory,prefix+'HD'+suffix+'.dat') if fst['CompHydro']>0 else 'none' + filename_SD = os.path.join(directory,prefix+'SD'+suffix+'.dat') if fst['CompSub']>0 else 'none' + filename_MD = os.path.join(directory,prefix+'MD'+suffix+'.dat') if fst['CompMooring']>0 else 'none' + filename_SvD = os.path.join(directory,prefix+'SvD'+suffix+'.dat') if fst['CompServo']>0 else 'none' + filename_Ice = os.path.join(directory,prefix+'Ice'+suffix+'.dat') if fst['CompIce']>0 else 'none' + filename_ED_bld = os.path.join(directory,prefix+'ED_bld'+suffix+'.dat') if fst['CompElast']>0 else 'none' + filename_ED_twr = os.path.join(directory,prefix+'ED_twr'+suffix+'.dat') if fst['CompElast']>0 else 'none' + filename_BD_bld = os.path.join(directory,prefix+'BD_bld'+suffix+'.dat') if fst['CompElast']>0 else 'none' + # TODO AD Profiles and OLAF + + fst['EDFile'] = '"' + os.path.basename(filename_ED) + '"' + fst['BDBldFile(1)'] = '"' + os.path.basename(filename_BD) + '"' + fst['BDBldFile(2)'] = '"' + os.path.basename(filename_BD) + '"' + fst['BDBldFile(3)'] = '"' + os.path.basename(filename_BD) + '"' + fst['InflowFile'] = '"' + os.path.basename(filename_IW) + '"' + fst['AeroFile'] = '"' + os.path.basename(filename_AD) + '"' + fst['ServoFile'] = '"' + os.path.basename(filename_AD) + '"' + fst['HydroFile'] = '"' + os.path.basename(filename_HD) + '"' + fst['SubFile'] = '"' + os.path.basename(filename_SD) + '"' + fst['MooringFile'] = '"' + os.path.basename(filename_MD) + '"' + fst['IceFile'] = '"' + os.path.basename(filename_Ice)+ '"' + fst.write(filename) + + + ED = self.fst_vt['ElastoDyn'] + if fst['CompElast']>0: + ED['TwrFile'] = '"' + os.path.basename(filename_ED_twr)+ '"' + self.fst_vt['ElastoDynTower'].write(filename_ED_twr) + if fst['CompElast']==1: + if 'BldFile1' in ED.keys(): + ED['BldFile1'] = '"' + os.path.basename(filename_ED_bld)+ '"' + ED['BldFile2'] = '"' + os.path.basename(filename_ED_bld)+ '"' + ED['BldFile3'] = '"' + os.path.basename(filename_ED_bld)+ '"' + else: + ED['BldFile(1)'] = '"' + os.path.basename(filename_ED_bld)+ '"' + ED['BldFile(2)'] = '"' + os.path.basename(filename_ED_bld)+ '"' + ED['BldFile(3)'] = '"' + os.path.basename(filename_ED_bld)+ '"' + self.fst_vt['ElastoDynBlade'].write(filename_ED_bld) + + elif fst['CompElast']==2: + BD = self.fst_vt['BeamDyn'] + BD['BldFile'] = '"'+os.path.basename(filename_BD_bld)+'"' + self.fst_vt['BeamDynBlade'].write(filename_BD_bld) # TODO TODO pick up the proper blade file! + BD.write(filename_BD) + ED.write(filename_ED) + + + if fst['CompInflow']>0: + self.fst_vt['InflowWind'].write(filename_IW) + + if fst['CompAero']>0: + self.fst_vt['AeroDyn15'].write(filename_AD) + # TODO other files + + if fst['CompServo']>0: + self.fst_vt['ServoDyn'].write(filename_SvD) + + if fst['CompHydro']==1: + self.fst_vt['HydroDyn'].write(filename_HD) + + if fst['CompSub']==1: + self.fst_vt['SubDyn'].write(filename_SD) + elif fst['CompSub']==2: + raise NotImplementedError() + + if fst['CompMooring']==1: + self.fst_vt['MAP'].write(filename_MD) + if self.fst_vt['Fst']['CompMooring']==2: + self.fst_vt['MoorDyn'].write(filename_MD) + + + def __repr__(self): s=''+'\n' s+='filename : '+self.filename+'\n' s+='version : '+self.version+'\n' s+='AD version : '+self.ADversion+'\n' - s+='fst_vt : dict{'+','.join([k for k,v in self.fst_vt.items() if v is not None])+'}' + s+='fst_vt : dict{'+','.join([k for k,v in self.fst_vt.items() if v is not None])+'}\n' + s+='inputFiles : {}\n'.format(self.inputFiles) s+='\n' return s diff --git a/pyFAST/input_output/fast_input_file.py b/pyFAST/input_output/fast_input_file.py index 5e9e47a..59acfc7 100644 --- a/pyFAST/input_output/fast_input_file.py +++ b/pyFAST/input_output/fast_input_file.py @@ -517,8 +517,8 @@ def toStringVLD(val,lab,descr): return val+' '+lab+' - '+descr.strip().strip('-').strip()+'\n' def beamdyn_section_mat_tostring(x,K,M): - def mat_tostring(M,fmt='.5e'): - return '\n'.join([' '+' '.join(['{:.6E}'.format(m) for m in M[i,:]]) for i in range(np.size(M,1))]) + def mat_tostring(M,fmt='24.16e'): + return '\n'.join([' '+' '.join(['{:24.16E}'.format(m) for m in M[i,:]]) for i in range(np.size(M,1))]) s='' s+='{:.6f}\n'.format(x) s+=mat_tostring(K) @@ -1053,7 +1053,6 @@ def parseFASTInputLine(line_raw,i,allowSpaceSeparatedList=False): _merge_value(splits) s=splits[0] - if strIsInt(s): d['value']=int(s) if allowSpaceSeparatedList and len(splits)>1: diff --git a/pyFAST/input_output/fast_output_file.py b/pyFAST/input_output/fast_output_file.py index f125304..b71a133 100644 --- a/pyFAST/input_output/fast_output_file.py +++ b/pyFAST/input_output/fast_output_file.py @@ -12,11 +12,11 @@ from itertools import takewhile -from .file import File, WrongFormatError, BrokenReaderError +from .file import File, WrongFormatError, BrokenReaderError, EmptyFileError from .csv_file import CSVFile import numpy as np import pandas as pd -import struct +import struct import os import re @@ -82,6 +82,8 @@ def readline(iLine): raise BrokenReaderError('FAST Out File {}: Memory error encountered\n{}'.format(self.filename,e)) except Exception as e: raise WrongFormatError('FAST Out File {}: {}'.format(self.filename,e.args)) + if self.data.shape[0]==0: + raise EmptyFileError('This FAST output file contains no data: {}'.format(self.filename)) if self.info['attribute_units'] is not None: self.info['attribute_units'] = [re.sub(r'[()\[\]]','',u) for u in self.info['attribute_units']] diff --git a/pyFAST/input_output/fast_wind_file.py b/pyFAST/input_output/fast_wind_file.py new file mode 100644 index 0000000..102f1e7 --- /dev/null +++ b/pyFAST/input_output/fast_wind_file.py @@ -0,0 +1,80 @@ +from __future__ import division +from __future__ import unicode_literals +from __future__ import print_function +from __future__ import absolute_import +from io import open +from builtins import map +from builtins import range +from builtins import chr +from builtins import str +from future import standard_library +standard_library.install_aliases() + +from .csv_file import CSVFile +from .file import isBinary, WrongFormatError +import numpy as np +import pandas as pd + +class FASTWndFile(CSVFile): + + @staticmethod + def defaultExtensions(): + return ['.wnd'] + + @staticmethod + def formatName(): + return 'FAST determ. wind file' + + def __init__(self, *args, **kwargs): + self.colNames=['Time','WindSpeed','WindDir','VertSpeed','HorizShear','VertShear','LinVShear','GustSpeed'] + self.units=['[s]','[m/s]','[deg]','[m/s]','[-]','[-]','[-]','[m/s]'] + Cols=['{}_{}'.format(c,u) for c,u in zip(self.colNames,self.units)] + + header=[] + header+=['!Wind file with step changes in wind speed.'] + header+=['!Time Wind Wind Vert. Horiz. Vert. LinV Gust'] + header+=['! Speed Dir Speed Shear Shear Shear Speed'] + + super(FASTWndFile, self).__init__(sep=' ',commentChar='!',colNames=Cols, header=header, *args, **kwargs) + + def _read(self, *args, **kwargs): + if isBinary(self.filename): + raise WrongFormatError('This is a binary file (turbulence file?) not a FAST ascii determinisctic wind file') + super(FASTWndFile, self)._read(*args, **kwargs) + + def _toDataFrame(self): + return self.data + + +# --------------------------------------------------------------------------------} +# --- Functions specific to file type +# --------------------------------------------------------------------------------{ + def stepWind(self,WSstep=1,WSmin=3,WSmax=25,tstep=100,dt=0.5,tmin=0,tmax=999): + """ Set the wind file to a step wind + tstep: can be an array of size 2 [tstepmax tstepmin] + + + """ + + Steps= np.arange(WSmin,WSmax+WSstep,WSstep) + if hasattr(tstep,'__len__'): + tstep = np.around(np.linspace(tstep[0], tstep[1], len(Steps)),0) + else: + tstep = len(Steps)*[tstep] + nCol = len(self.colNames) + nRow = len(Steps)*2 + M = np.zeros((nRow,nCol)); + M[0,0] = tmin + M[0,1] = WSmin + for i,s in enumerate(Steps[:-1]): + M[2*i+1,0] = tmin + tstep[i]-dt + M[2*i+2,0] = tmin + tstep[i] + tmin +=tstep[i] + M[2*i+1,1] = Steps[i] + if i -Ly/2 but we flip this to -Ly/2 -> Ly/2 @@ -55,6 +56,16 @@ def read(self, filename=None, N=(1024,32,32)): if os.stat(self.filename).st_size == 0: raise EmptyFileError('File is empty:',self.filename) + if N is None: + # try to infer N's from filename with format 'stringN1xN2xN3' + basename = os.path.splitext(os.path.basename(self.filename))[0] + splits = basename.split('x') + temp = re.findall(r'\d+', basename) + res = list(map(int, temp)) + if len(res)>=3: + N=res[-3:] + else: + raise BrokenFormatError('Reading a Mann box requires the knowledge of the dimensions. The dimensions can be inferred from the filename, for instance: `filebase_1024x32x32.u`. Try renaming your file such that the three last digits are the dimensions in x, y and z.') nx,ny,nz=N def _read_buffered(): data=np.zeros((nx,ny,nz),dtype=np.float32) @@ -75,6 +86,11 @@ def _read_nonbuffered(): # self['field']= _read_nonbuffered() self['field']= _read_buffered() + self['dy']=dy + self['dz']=dz + self['y0']=y0 + self['z0']=z0 + self['zMid']=zMid # print('1',self['field'][:,-1,0]) # print('2',self['field'][0,-1::-1,0]) # print('3',self['field'][0,-1,:]) @@ -96,11 +112,50 @@ def write(self, filename=None): def __repr__(self): s='<{} object> with keys:\n'.format(type(self).__name__) - s+=' - filename: {}\n'.format(self.filename) - s+=' - field: shape {}x{}x{}\n'.format(self['field'].shape[0],self['field'].shape[1],self['field'].shape[2]) - s+=' min: {}, max: {}, mean: {} \n'.format(np.min(self['field']), np.max(self['field']), np.mean(self['field'])) + s+='| - filename: {}\n'.format(self.filename) + s+='| - field: shape {}x{}x{}\n'.format(self['field'].shape[0],self['field'].shape[1],self['field'].shape[2]) + s+='| min: {}, max: {}, mean: {} \n'.format(np.min(self['field']), np.max(self['field']), np.mean(self['field'])) + s+='| - dy, dz: {}, {}\n'.format(self['dy'], self['dz']) + s+='| - y0, z0 zMid: {}, {}, {}\n'.format(self['y0'], self['z0'], self['zMid']) + s+='|useful getters: y, z, _iMid, fromTurbSim\n' + z=self.z + y=self.y + s+='| y: [{} ... {}], dy: {}, n: {} \n'.format(y[0],y[-1],self['dy'],len(y)) + s+='| z: [{} ... {}], dz: {}, n: {} \n'.format(z[0],z[-1],self['dz'],len(z)) return s + def _iMid(self): + _, ny, nz = self['field'].shape + return int(ny/2), int(nz/2) + + @property + def z(self): + zmax = self['z0'] + (self['field'].shape[2]-1+0.1)*self['dz'] + z = np.arange(self['z0'], zmax, self['dz']) + if self['zMid'] is not None: + z+= self['zMid']-np.mean(z) + return z + + @property + def y(self): + if self['y0'] is not None: + ymax = self['y0'] + (self['field'].shape[1]-1+0.1)*self['dy'] + y = np.arange(self['y0'], ymax, self['dy']) + else: + ymax = (self['field'].shape[1]-1+0.1)*self['dy'] + y = np.arange(0, ymax, self['dy']) + y -= np.mean(y) + return y + + + @property + def vertProfile(self): + iy, iz = self._iMid() + m = np.mean(self['field'][:,iy,:], axis=0) + s = np.std (self['field'][:,iy,:], axis=0) + return self.z,m,s + + def toDataFrame(self): pass @@ -122,10 +177,11 @@ def fromTurbSim(self,u,icomp=0, removeConstant=None, removeAllMean=False): self['field'] = u[icomp, :, : ,: ] if __name__=='__main__': - mb = MannBoxFile('mann_bin/mini-u.bin', N=(2,4,8)) - F1=mb['field'].ravel() - mb.write('mann_bin/mini-u-out.bin') - - mb2= MannBoxFile('mann_bin/mini-u-out.bin', N=(2,4,8)) - F2=mb2['field'].ravel() + mb = MannBoxFile('mini-u_1024x32x32.bin') +# mb = MannBoxFile('mann_bin/mini-u.bin', N=(2,4,8)) +# F1=mb['field'].ravel() +# mb.write('mann_bin/mini-u-out.bin') +# +# mb2= MannBoxFile('mann_bin/mini-u-out.bin', N=(2,4,8)) +# F2=mb2['field'].ravel() # print(F1-F2) diff --git a/pyFAST/input_output/mini_yaml.py b/pyFAST/input_output/mini_yaml.py index d2b18d1..04269b9 100644 --- a/pyFAST/input_output/mini_yaml.py +++ b/pyFAST/input_output/mini_yaml.py @@ -90,7 +90,7 @@ def readDashList(iStart): if __name__=='__main__': - d=read('tests.yaml') + d=read('test.yaml') #d=yaml_read('TetraSpar_outputs_DOUBLE_PRECISION.SD.sum.yaml') print(d.keys()) print(d) diff --git a/pyFAST/input_output/postpro.py b/pyFAST/input_output/postpro.py index 5febce5..ff68e55 100644 --- a/pyFAST/input_output/postpro.py +++ b/pyFAST/input_output/postpro.py @@ -648,7 +648,7 @@ def insert_extra_columns_AD(dfRad, tsAvg, vr=None, rho=None, R=None, nB=None, ch def spanwisePostPro(FST_In=None,avgMethod='constantwindow',avgParam=5,out_ext='.outb',df=None): """ - Postprocess FAST radial data + Postprocess FAST radial data. Average the time series, return a dataframe nr x nColumns INPUTS: - FST_IN: Fast .fst input file @@ -718,7 +718,7 @@ def spanwisePostPro(FST_In=None,avgMethod='constantwindow',avgParam=5,out_ext='. def spanwisePostProRows(df, FST_In=None): """ - Returns a 3D matrix: n x nSpan x nColumn where df is of size n x nColumn + Returns a 3D matrix: n x nSpan x nColumn where df is of size n x mColumn NOTE: this is really not optimal. Spanwise columns should be extracted only once.. """ @@ -743,6 +743,7 @@ def spanwisePostProRows(df, FST_In=None): try: rho = fst.AD['AirDens'] except: + print('[WARN] Using default air density (1.225)') pass # --- Extract radial data for each azimuthal average M_AD=None @@ -1031,7 +1032,7 @@ def _zero_crossings(y,x=None,direction=None): raise Exception('Direction should be either `up` or `down`') return xzc, iBef, sign -def find_matching_pattern(List, pattern): +def find_matching_pattern(List, pattern, sort=False): """ Return elements of a list of strings that match a pattern and return the first matching group """ @@ -1046,11 +1047,47 @@ def find_matching_pattern(List, pattern): MatchedStrings.append(match.groups(1)[0]) else: MatchedStrings.append('') + + if sort: + # Sorting by Matched string, NOTE: assumes that MatchedStrings are int. + # that's probably not necessary since alphabetical/integer sorting should be the same + # but it might be useful if number of leading zero differs, which would skew the sorting.. + MatchedElements = np.asarray(MatchedElements) + MatchedStrings = np.asarray(MatchedStrings) + Idx = np.array([int(s) for s in MatchedStrings]) + Isort = np.argsort(Idx) + Idx = Idx[Isort] + MatchedElements = MatchedElements[Isort] + MatchedStrings = MatchedStrings[Isort] + return MatchedElements, MatchedStrings +def extractSpanTS(df, pattern): + r""" + Extract spanwise time series of a given "type" (e.g. Cl for each radial node) + Return a dataframe of size nt x nr + + NOTE: time is not inserted in the output dataframe + + To find "r" use FASTRadialOutputs, it is different for AeroDyn/ElastoDyn/BeamDyn/ + There is no guarantee that the number of columns matching pattern will exactly + corresponds to the number of radial stations. That's the responsability of the + OpenFAST user. + + INPUTS: + - df : a dataframe of size nt x nColumns + - pattern: Pattern used to find "radial" columns amongst the dataframe columns + r'B1N(\d*)Cl_\[-\]' + r'^AB1N(\d*)Cl_\[-\]' -> to match AB1N001Cl_[-], AB1N002Cl_[-], etc. + OUTPUTS: + - dfOut : a dataframe of size nt x nr where nr is the number of radial stations matching the pattern. The radial stations are sorted. + """ + cols, sIdx = find_matching_pattern(df.columns, pattern, sort=True) + return df[cols] + -def extractSpanTSReg(ts, col_pattern, colname, IR=None): +def _extractSpanTSReg_Legacy(ts, col_pattern, colname, IR=None): r""" Helper function to extract spanwise results, like B1N1Cl B1N2Cl etc. Example @@ -1058,7 +1095,7 @@ def extractSpanTSReg(ts, col_pattern, colname, IR=None): colname : r'B1Cl_[-]' """ # Extracting columns matching pattern - cols, sIdx = find_matching_pattern(ts.keys(), col_pattern) + cols, sIdx = find_matching_pattern(ts.keys(), col_pattern, sort=True) if len(cols) ==0: return (None,None) @@ -1088,7 +1125,7 @@ def extractSpanTSReg(ts, col_pattern, colname, IR=None): print('[WARN] More values found for {}, found {}/{}'.format(colname,len(cols),nrMax)) return (colname,Values) -def extractSpanTS(ts, nr, col_pattern, colname, IR=None): +def _extractSpanTS_Legacy(ts, nr, col_pattern, colname, IR=None): """ Helper function to extract spanwise results, like B1N1Cl B1N2Cl etc. Example @@ -1360,5 +1397,57 @@ def averagePostPro(outFiles,avgMethod='periods',avgParam=None,ColMap=None,ColKee return result +def integrateMoment(r, F): + """ + Integrate moment from force and radial station + M_j = \int_{r_j}^(r_n) f(r) * (r-r_j) dr for j=1,nr + TODO: integrate analytically the "r" part + """ + M = np.zeros(len(r)-1) + for ir,_ in enumerate(r[:-1]): + M[ir] = np.trapz(F[ir:]*(r[ir:]-r[ir]), r[ir:]-r[ir]) + return M + +def integrateMomentTS(r, F): + """ + Integrate moment from time series of forces at nr radial stations + + Compute + M_j = \int_{r_j}^(r_n) f(r) * (r-r_j) dr for j=1,nr + M_j = \int_{r_j}^(r_n) f(r) *r*dr - r_j * \int_(r_j}^{r_n} f(r) dr + j are the columns of M + + NOTE: simply trapezoidal integration is used. + The "r" term is not integrated analytically. This can be improved! + + INPUTS: + - r: array of size nr, of radial stations (ordered) + - F: array nt x nr of time series of forces at each radial stations + OUTPUTS: + - M: array nt x nr of integrated moment at each radial station + + """ + import scipy.integrate as si + # Compute \int_{r_j}^{r_n} f(r) dr, with "j" each column + IF = np.fliplr(-si.cumtrapz(np.fliplr(F), r[-1::-1])) + # Compute \int_{r_j}^{r_n} f(r)*r dr, with "j" each column + FR = F * r + IFR = np.fliplr(-si.cumtrapz(np.fliplr(FR), r[-1::-1])) + # Compute x_j * \int_{r_j}^(r_n) f(r) * r dr + R_IF = IF * r[:-1] + # \int_{r_j}^(r_n) f(r) * (r-r_j) dr = IF + IFR + M = IFR - R_IF + + + # --- Sanity checks + M0 = integrateMoment(r, F[0,:]) + Mm1 = integrateMoment(r, F[-1,:]) + if np.max(np.abs(M0-M[0,:]))>1e-8: + raise Exception('>>> Inaccuracies in integrateMomentTS') + if np.max(np.abs(Mm1-M[-1,:]))>1e-8: + raise Exception('>>> Inaccuracies in integrateMomentTS') + + return M + if __name__ == '__main__': main() diff --git a/pyFAST/input_output/test.py b/pyFAST/input_output/test.py new file mode 100644 index 0000000..52025f3 --- /dev/null +++ b/pyFAST/input_output/test.py @@ -0,0 +1,63 @@ +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt +# Local +import pyFAST +from pyFAST.input_output.fast_output_file import FASTOutputFile, writeBinary + + +outb='C:/Work/DigiTwin-Stiesdal/code/F5T1RNA/Main_Spar_ED.outb' +outb='./Main_Spar_ED.outb' +outb2='./Main_Spar_ED2.outb' +outb3='./Main_Spar_ED3.outb' +outb22='./Main_Spar_ED22.outb' +outb33='./Main_Spar_ED33.outb' + +# f = FASTOutputFile(outb) +# print(f.info) +# print(f.data.shape) + +# info = {'name': os.path.splitext(os.path.basename(filename))[0], +# 'description': DescStr, +# 'attribute_names': ChanName, +# 'attribute_units': ChanUnit} +# + + +time = np.linspace(0,10,100) +C1 = np.cos(time) +C2 = time*0 + +colNames = ['Time','cos','cst'] +colUnits = ['(s)','(deg)','(-)'] + +datalist = [time, C1, C2] + +data = np.column_stack([time, C1, C2]) + +print(np.min(data, axis=0)) + +# +# nT, numOutWithTime = np.shape(f.data) +# +# datalist = [[None]*nT for i in range(numOutWithTime)] +# +# for j in range(numOutWithTime): +# datalist[j] = f.data[:,j] +# +# + +# writeBinary2(outb2, datalist, f.info['attribute_names'], f.info['attribute_units'], fileID=2, descStr='') +# writeBinary2(outb2, datalist, colNames, colUnits, fileID=2, descStr='') +writeBinary(outb3, data, colNames, colUnits, fileID=2, descStr='') +# writeBinary(outb2, f.data, f.info['attribute_names'], f.info['attribute_units'], fileID=2, descStr='') + + +f3= FASTOutputFile(outb3) +f3.write(outb33) + +# print(f2.toDataFrame()) + + +if __name__ == '__main__': + pass diff --git a/pyFAST/input_output/tests/example_files/CSVxIsString.csv b/pyFAST/input_output/tests/example_files/CSVxIsString.csv new file mode 100644 index 0000000..1d026d3 --- /dev/null +++ b/pyFAST/input_output/tests/example_files/CSVxIsString.csv @@ -0,0 +1,8 @@ +#Label_[-], Motivation_[%] +Monday , 30.0 +Tuesday , 90.0 +Wednesday, 80.0 +Thursday , 50.0 +Friday , 10.0 +Saturday , 2.0 +Sunday , 1.0 diff --git a/pyFAST/input_output/tests/example_files/ExcelFile_OneSheet.xlsx b/pyFAST/input_output/tests/example_files/ExcelFile_OneSheet.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..6beadf0f580ed18b705bcdab797ffd8f234c0a8c GIT binary patch literal 11708 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.SD.ech" (flag) +"DEFAULT" SDdeltaT - Local Integration Step. If "default", the glue-code integration step will be used. + 3 IntMethod - Integration Method [1/2/3/4 = RK4/AB4/ABM4/AM2]. +True SttcSolve - Solve dynamics about static equilibrium point +True GuyanLoadCorrection - Include extra moment from lever arm at interface and rotate FEM for floating. +-------------------- FEA and CRAIG-BAMPTON PARAMETERS--------------------------- + 1 FEMMod - FEM switch: element model in the FEM. [1= Euler-Bernoulli(E-B); 2=Tapered E-B (unavailable); 3= 2-node Timoshenko; 4= 2-node tapered Timoshenko (unavailable)] + 1 NDiv - Number of sub-elements per member +True CBMod - [T/F] If True perform C-B reduction, else full FEM dofs will be retained. If True, select Nmodes to retain in C-B reduced system. + 8 Nmodes - Number of internal modes to retain (ignored if CBMod=False). If Nmodes=0 --> Guyan Reduction. + 1 JDampings - Damping Ratios for each retained mode (% of critical) If Nmodes>0, list Nmodes structural damping ratios for each retained mode (% of critical), or a single damping ratio to be applied to all retained modes. (last entered value will be used for all remaining modes). + 0 GuyanDampMod - Guyan damping {0=none, 1=Rayleigh Damping, 2=user specified 6x6 matrix} + 0.000, 0.000 RayleighDamp - Mass and stiffness proportional damping coefficients (Rayleigh Damping) [only if GuyanDampMod=1] + 6 GuyanDampSize - Guyan damping matrix (6x6) [only if GuyanDampMod=2] + 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 + 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 + 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 + 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 + 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 + 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 0.0000e+00 +---- STRUCTURE JOINTS: joints connect structure members (~Hydrodyn Input File)--- + 19 NJoints - Number of joints (-) +JointID JointXss JointYss JointZss JointType JointDirX JointDirY JointDirZ JointStiff ! Comment Columns + (-) (m) (m) (m) (-) (-) (-) (-) (Nm/rad) ! Comment unit + 43 0.00000 0.00000 23.20000 1 0.0 0.0 0.0 0.0 ! First tower node + 44 0.00000 0.00000 28.50000 1 0.0 0.0 0.0 0.0 + 45 0.00000 0.00000 30.30000 1 0.0 0.0 0.0 0.0 + 46 0.00000 0.00000 35.30000 1 0.0 0.0 0.0 0.0 + 47 0.00000 0.00000 40.30000 1 0.0 0.0 0.0 0.0 + 48 0.00000 0.00000 45.30000 1 0.0 0.0 0.0 0.0 + 49 0.00000 0.00000 49.90000 1 0.0 0.0 0.0 0.0 + 50 0.00000 0.00000 52.75000 1 0.0 0.0 0.0 0.0 + 51 0.00000 0.00000 60.82000 1 0.0 0.0 0.0 0.0 + 52 0.00000 0.00000 74.40000 1 0.0 0.0 0.0 0.0 + 53 0.00000 0.00000 77.73000 1 0.0 0.0 0.0 0.0 + 54 0.00000 0.00000 81.06000 1 0.0 0.0 0.0 0.0 + 55 0.00000 0.00000 84.39000 1 0.0 0.0 0.0 0.0 + 56 0.00000 0.00000 87.72000 1 0.0 0.0 0.0 0.0 + 57 0.00000 0.00000 91.05000 1 0.0 0.0 0.0 0.0 + 58 0.00000 0.00000 94.38000 1 0.0 0.0 0.0 0.0 + 59 0.00000 0.00000 97.71000 1 0.0 0.0 0.0 0.0 + 60 0.00000 0.00000 100.0930 1 0.0 0.0 0.0 0.0 + 61 0.00000 0.00000 102.7930 1 0.0 0.0 0.0 0.0 ! Tower top +------------------- BASE REACTION JOINTS: 1/0 for Locked/Free DOF @ each Reaction Node --------------------- + 1 NReact - Number of Joints with reaction forces; be sure to remove all rigid motion DOFs of the structure (else det([K])=[0]) +RJointID RctTDXss RctTDYss RctTDZss RctRDXss RctRDYss RctRDZss SSIfile [Global Coordinate System] ! Comment colnames + (-) (flag) (flag) (flag) (flag) (flag) (flag) (string) ! Comment units + 43 1 1 1 1 1 1 "" ! Tower top +------- INTERFACE JOINTS: 1/0 for Locked (to the TP)/Free DOF @each Interface Joint (only Locked-to-TP implemented thus far (=rigid TP)) --------- + 1 NInterf - Number of interface joints locked to the Transition Piece (TP): be sure to remove all rigid motion dofs +IJointID ItfTDXss ItfTDYss ItfTDZss ItfRDXss ItfRDYss ItfRDZss [Global Coordinate System] ! Comment + (-) (flag) (flag) (flag) (flag) (flag) (flag) ! Comment + 61 1 1 1 1 1 1 ! Tower top +----------------------------------- MEMBERS -------------------------------------- + 18 NMembers - Number of frame members +MemberID MJointID1 MJointID2 MPropSetID1 MPropSetID2 MType COSMID ! Column headers + (-) (-) (-) (-) (-) (-) (-) + 77 61 60 11 11 1 ! Tower top + 78 60 59 12 12 1 ! Tower top + 79 59 58 13 13 1 + 80 58 57 14 14 1 + 81 57 56 15 15 1 + 82 56 55 16 16 1 + 83 55 54 17 17 1 + 84 54 53 18 18 1 + 85 53 52 19 19 1 + 86 52 51 20 20 1 + 87 51 50 21 21 1 + 88 50 49 22 22 1 + 89 49 48 23 23 1 + 90 48 47 24 24 1 + 91 47 46 25 25 1 + 92 46 45 26 26 1 + 93 45 44 27 27 1 + 94 44 43 28 28 1 +------------------ MEMBER X-SECTION PROPERTY data 1/2 [isotropic material for now: use this table for circular-tubular elements] ------------------------ + 26 NPropSets - Number of structurally unique x-sections (i.e. how many groups of X-sectional properties are utilized throughout all of the members) +PropSetID YoungE ShearG MatDens XsecD XsecT + (-) (N/m2) (N/m2) (kg/m3) (m) (m) + 1 2.00000e+11 8.07690e+10 8000.00 0.60960 0.015875 + 2 2.00000e+11 8.07690e+10 8000.00 0.76200 0.019050 + 3 2.00000e+11 8.07690e+10 8000.00 0.91440 0.019050 + 4 2.00000e+11 8.07690e+10 8000.00 1.52400 0.063500 + 5 2.00000e+11 8.07690e+10 8000.00 1.62560 0.012700 + 6 2.00000e+11 8.07690e+10 8000.00 1.72720 0.025400 + 11 2.00000e+11 8.07690e+10 8000.00 4.09400 0.03800 ! tower + 12 2.00000e+11 8.07690e+10 8000.00 4.27000 0.02800 ! tower + 13 2.00000e+11 8.07690e+10 8000.00 4.46800 0.02400 ! tower + 14 2.00000e+11 8.07690e+10 8000.00 4.70000 0.02400 ! tower + 15 2.00000e+11 8.07690e+10 8000.00 4.93100 0.02400 ! tower + 16 2.00000e+11 8.07690e+10 8000.00 5.16200 0.02400 ! tower + 17 2.00000e+11 8.07690e+10 8000.00 5.39300 0.02400 ! tower + 18 2.00000e+11 8.07690e+10 8000.00 5.62400 0.02400 ! tower + 19 2.00000e+11 8.07690e+10 8000.00 5.85500 0.02400 ! tower + 20 2.00000e+11 8.07690e+10 8000.00 5.97100 0.02400 ! tower + 21 2.00000e+11 8.07690e+10 8000.00 5.97300 0.02500 ! tower + 22 2.00000e+11 8.07690e+10 8000.00 5.97300 0.02600 ! tower + 23 2.00000e+11 8.07690e+10 8000.00 5.98100 0.02600 ! tower + 24 2.00000e+11 8.07690e+10 8000.00 5.98200 0.02700 ! tower + 25 2.00000e+11 8.07690e+10 8000.00 5.98400 0.02800 ! tower + 26 2.00000e+11 8.07690e+10 8000.00 5.98500 0.02900 ! tower + 27 2.00000e+11 8.07690e+10 8000.00 5.98700 0.03600 ! tower + 28 2.00000e+11 8.07690e+10 8000.00 5.98700 0.03800 ! tower + 29 2.00000e+11 8.07690e+10 8000.00 5.98900 0.04200 ! tower + 30 2.00000e+12 8.07690e+10 0.00 1.72720 0.02540 ! "rigid dummy beam" +------------------ MEMBER X-SECTION PROPERTY data 2/2 [isotropic material for now: use this table if any section other than circular, however provide COSM(i,j) below] ------------------------ + 0 NXPropSets - Number of structurally unique non-circular x-sections (if 0 the following table is ignored) +PropSetID YoungE ShearG MatDens XsecA XsecAsx XsecAsy XsecJxx XsecJyy XsecJ0 + (-) (N/m2) (N/m2) (kg/m3) (m2) (m2) (m2) (m4) (m4) (m4) +-------------------------- CABLE PROPERTIES ------------------------------------- + 0 NCablePropSets - Number of cable cable properties +PropSetID EA MatDens T0 + (-) (N) (kg/m) (N) +----------------------- RIGID LINK PROPERTIES ------------------------------------ + 0 NRigidPropSets - Number of rigid link properties +PropSetID MatDens + (-) (kg/m) +---------------------- MEMBER COSINE MATRICES COSM(i,j) ------------------------ + 0 NCOSMs - Number of unique cosine matrices (i.e., of unique member alignments including principal axis rotations); ignored if NXPropSets=0 or 9999 in any element below +COSMID COSM11 COSM12 COSM13 COSM21 COSM22 COSM23 COSM31 COSM32 COSM33 + (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) +------------------------ JOINT ADDITIONAL CONCENTRATED MASSES-------------------------- + 8 NCmass - Number of joints with concentrated masses; Global Coordinate System +CMJointID JMass JMXX JMYY JMZZ JMXY JMXZ JMYZ MCGX MCGY MCGZ + (-) (kg) (kg*m^2) (kg*m^2) (kg*m^2) (kg*m^2) (kg*m^2) (kg*m^2) (m) (m) (m) + 1 5355 0 0 0 0 0 0 0 0 0 + 2 5355 0 0 0 0 0 0 0 0 0 + 3 5355 0 0 0 0 0 0 0 0 0 + 4 5355 0 0 0 0 0 0 0 0 0 + 36 5355 0 0 0 0 0 0 0 0 0 + 37 5355 0 0 0 0 0 0 0 0 0 + 38 5355 0 0 0 0 0 0 0 0 0 + 39 5355 0 0 0 0 0 0 0 0 0 +---------------------------- OUTPUT: SUMMARY & OUTFILE ------------------------------ +True SumPrint - Output a Summary File (flag).It contains: matrices K,M and C-B reduced M_BB, M-BM, K_BB, K_MM(OMG^2), PHI_R, PHI_L. It can also contain COSMs if requested. +False OutCOSM - Output cosine matrices with the selected output member forces (flag) +False OutAll - [T/F] Output all members' end forces + 2 OutSwtch - [1/2/3] Output requested channels to: 1=.SD.out; 2=.out (generated by FAST); 3=both files. +True TabDelim - Generate a tab-delimited output in the .SD.out file + 1 OutDec - Decimation of output in the .SD.out file +"ES11.4e2" OutFmt - Output format for numerical results in the .SD.out file +"A11" OutSFmt - Output format for header strings in the .SD.out file +------------------------- MEMBER OUTPUT LIST ------------------------------------------ + 8 NMOutputs - Number of members whose forces/displacements/velocities/accelerations will be output (-) [Must be <= 9]. +MemberID NOutCnt NodeCnt [NOutCnt=how many nodes to get output for [< 10]; NodeCnt are local ordinal numbers from the start of the member, and must be >=1 and <= NDiv+1] If NMOutputs=0 leave blank as well. + (-) (-) (-) + 30 2 1 2 ! M1 + 31 2 1 2 ! M2 + 32 2 1 2 ! M3 + 58 2 1 2 ! M4 + 26 2 1 2 ! M5 + 27 2 1 2 ! M6 + 28 2 1 2 ! M7 + 29 2 1 2 ! M8 +------------------------- SDOutList: The next line(s) contains a list of output parameters that will be output in .SD.out or .out. ------ +"M1N1FKZe, M2N1FKZe" - Axial force in leg 2 at K1L2 and in leg 4 at K1L4 +"M3N1TDXss, M3N1TDYss, M3N1TDZss, M4N1TDXss, M4N1TDYss, M4N1TDZss" - Deflections at X2S2, X2S3: use cosdir matrix to get Out-of-plane (OOP) deflection +"M5N2TDXss, M5N2TDYss, M5N2TDZss, M6N2TDXss, M6N2TDYss, M6N2TDZss" - Deflections at X4S2, X4S3: use cosdir matrix to get OOP deflection +"M5N1FKXe,M5N1FKYe,M5N1FKZe,M6N1FKXe,M6N1FKYe,M6N1FKZe" - Forces OOP and Axial at mid brace points x,y, z >> *we will need to do some post-processing using the direction cosine matrices to get OOP forces +"M7N1FKZe, M8N1FKZe" - Axial force in leg 2 and leg 4 at mudbrace level: MudbraceL2, MudbraceL4 +"-ReactFXss, -ReactFYss, -ReactMXss, -ReactMYss, -ReactMZss, -ReactFZss" - Base reactions: fore-aft shear, side-to-side shear, side-to-side moment, fore-aft moment, yaw moment, vertical force +END of output channels and end of file. (the word "END" must appear in the first 3 columns of this line) diff --git a/pyFAST/input_output/tests/example_files/FASTWnd.wnd b/pyFAST/input_output/tests/example_files/FASTWnd.wnd new file mode 100644 index 0000000..bc00931 --- /dev/null +++ b/pyFAST/input_output/tests/example_files/FASTWnd.wnd @@ -0,0 +1,8 @@ +! Deterministic Wind file +! Time Wind Wind Vert. Horiz. Vert. LinV Gust +! Speed Dir Speed Shear Shear Shear Speed + 00.00 06.0 0.0 0.0 0.0 0.0 0.0 0.0 + 20.05 06.0 0.0 0.0 0.0 0.0 0.0 0.0 + 20.10 08.0 0.0 0.0 0.0 0.0 0.0 0.0 + 40.05 08.0 0.0 0.0 0.0 0.0 0.0 0.0 +999.99 08.0 0.0 0.0 0.0 0.0 0.0 0.0 diff --git a/pyFAST/input_output/tests/example_files/HAWC2_st.dat b/pyFAST/input_output/tests/example_files/HAWC2_st.dat new file mode 100644 index 0000000..9014a0f --- /dev/null +++ b/pyFAST/input_output/tests/example_files/HAWC2_st.dat @@ -0,0 +1,38 @@ +a3 number of sets Nset +Nrel 5MW wind turbine data +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +#1 Tower +$1 11 90m tower structure. flexible +0 5590.87 0 0 2.11E+00 2.11E+00 0 0 2.10E+11 8.08E+10 2.93E+00 2.93E+00 5.85E+00 0.5 0.5 6.58E-01 0 0 0 +8.76 5232.43 0 0 2.03E+00 2.03E+00 0 0 2.10E+11 8.08E+10 2.55E+00 2.55E+00 5.09E+00 0.5 0.5 6.16E-01 0 0 0 +17.52 4885.76 0 0 1.96E+00 1.96E+00 0 0 2.10E+11 8.08E+10 2.21E+00 2.21E+00 4.41E+00 0.5 0.5 5.75E-01 0 0 0 +26.28 4550.87 0 0 1.88E+00 1.88E+00 0 0 2.10E+11 8.08E+10 1.90E+00 1.90E+00 3.80E+00 0.5 0.5 5.35E-01 0 0 0 +35.04 4227.75 0 0 1.81E+00 1.81E+00 0 0 2.10E+11 8.08E+10 1.63E+00 1.63E+00 3.26E+00 0.5 0.5 4.97E-01 0 0 0 +43.8 3916.41 0 0 1.73E+00 1.73E+00 0 0 2.10E+11 8.08E+10 1.39E+00 1.39E+00 2.77E+00 0.5 0.5 4.61E-01 0 0 0 +52.56 3616.83 0 0 1.66E+00 1.66E+00 0 0 2.10E+11 8.08E+10 1.17E+00 1.17E+00 2.34E+00 0.5 0.5 4.26E-01 0 0 0 +61.32 3329.03 0 0 1.58E+00 1.58E+00 0 0 2.10E+11 8.08E+10 9.83E-01 9.83E-01 1.97E+00 0.5 0.5 3.92E-01 0 0 0 +70.08 3053.01 0 0 1.51E+00 1.51E+00 0 0 2.10E+11 8.08E+10 8.18E-01 8.18E-01 1.64E+00 0.5 0.5 3.59E-01 0 0 0 +78.84 2788.75 0 0 1.43E+00 1.43E+00 0 0 2.10E+11 8.08E+10 6.75E-01 6.75E-01 1.35E+00 0.5 0.5 3.28E-01 0 0 0 +87.6 2536.27 0 0 1.36E+00 1.36E+00 0 0 2.10E+11 8.08E+10 5.52E-01 5.52E-01 1.10E+00 0.5 0.5 2.98E-01 0 0 0 +r m x_cg y_cg ri_x ri_y x_sh y_sh E G I_x I_y I_p k_x k_y A pitch x_e y_e +$2 11 90m tower structure. stiff +0 5590.87 0 0 2.11E+00 2.11E+00 0 0 2.10E+16 8.08E+15 2.93E+00 2.93E+00 5.85E+00 0.5 0.5 6.58E-01 0 0 0 +8.76 5232.43 0 0 2.03E+00 2.03E+00 0 0 2.10E+16 8.08E+15 2.55E+00 2.55E+00 5.09E+00 0.5 0.5 6.16E-01 0 0 0 +17.52 4885.76 0 0 1.96E+00 1.96E+00 0 0 2.10E+16 8.08E+15 2.21E+00 2.21E+00 4.41E+00 0.5 0.5 5.75E-01 0 0 0 +26.28 4550.87 0 0 1.88E+00 1.88E+00 0 0 2.10E+16 8.08E+15 1.90E+00 1.90E+00 3.80E+00 0.5 0.5 5.35E-01 0 0 0 +35.04 4227.75 0 0 1.81E+00 1.81E+00 0 0 2.10E+16 8.08E+15 1.63E+00 1.63E+00 3.26E+00 0.5 0.5 4.97E-01 0 0 0 +43.8 3916.41 0 0 1.73E+00 1.73E+00 0 0 2.10E+16 8.08E+15 1.39E+00 1.39E+00 2.77E+00 0.5 0.5 4.61E-01 0 0 0 +52.56 3616.83 0 0 1.66E+00 1.66E+00 0 0 2.10E+16 8.08E+15 1.17E+00 1.17E+00 2.34E+00 0.5 0.5 4.26E-01 0 0 0 +61.32 3329.03 0 0 1.58E+00 1.58E+00 0 0 2.10E+16 8.08E+15 9.83E-01 9.83E-01 1.97E+00 0.5 0.5 3.92E-01 0 0 0 +70.08 3053.01 0 0 1.51E+00 1.51E+00 0 0 2.10E+16 8.08E+15 8.18E-01 8.18E-01 1.64E+00 0.5 0.5 3.59E-01 0 0 0 +78.84 2788.75 0 0 1.43E+00 1.43E+00 0 0 2.10E+16 8.08E+15 6.75E-01 6.75E-01 1.35E+00 0.5 0.5 3.28E-01 0 0 0 +87.6 2536.27 0 0 1.36E+00 1.36E+00 0 0 2.10E+16 8.08E+15 5.52E-01 5.52E-01 1.10E+00 0.5 0.5 2.98E-01 0 0 0 +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +#2 Tower top +r m x_cg y_cg ri_x ri_y x_sh y_sh E G I_x I_y I_p k_x k_y A pitch x_e y_e +$1 2 towertop with nacelle mass on bottom element. flexible +0 1.00E-04 0 0 1.36E+00 1.36E+00 0 0 2.10E+11 8.08E+10 5.52E-01 5.52E-01 1.10E+00 0.5 0.5 2.98E-01 0 0 0 +1.96256 1.00E-04 0 0 1.36E+00 1.36E+00 0 0 2.10E+11 8.08E+10 5.52E-01 5.52E-01 1.10E+00 0.5 0.5 2.98E-01 0 0 0 +$2 2 towertop with nacelle mass on bottom element. Stiff +0 1.00E-04 0 0 1.36E+00 1.36E+00 0 0 2.10E+16 8.08E+15 5.52E-01 5.52E-01 1.10E+00 0.5 0.5 2.98E-01 0 0 0 +1.96256 1.00E-04 0 0 1.36E+00 1.36E+00 0 0 2.10E+16 8.08E+15 5.52E-01 5.52E-01 1.10E+00 0.5 0.5 2.98E-01 0 0 0 diff --git a/pyFAST/input_output/tests/example_files/input_decks/Elliptic_AD15_40.dat b/pyFAST/input_output/tests/example_files/input_decks/Elliptic_AD15_40.dat new file mode 100644 index 0000000..26fd59c --- /dev/null +++ b/pyFAST/input_output/tests/example_files/input_decks/Elliptic_AD15_40.dat @@ -0,0 +1,124 @@ +------- AERODYN v15 for OpenFAST INPUT FILE ----------------------------------------------- +Elliptical wing test case for OLAF free vortex wake mdoule in AD15 +====== General Options ============================================================================ +False Echo - Echo the input to ".AD.ech"? (flag) +"default" DTAero - Time interval for aerodynamic calculations {or "default"} (s) + 3 WakeMod - Type of wake/induction model (switch) {0=none, 1=BEMT, 2=DBEMT, 3=OLAF} [WakeMod cannot be 2 or 3 when linearizing] + 1 AFAeroMod - Type of blade airfoil aerodynamics model (switch) {1=steady model, 2=Beddoes-Leishman unsteady model} [AFAeroMod must be 1 when linearizing] + 0 TwrPotent - Type tower influence on wind based on potential flow around the tower (switch) {0=none, 1=baseline potential flow, 2=potential flow with Bak correction} + 0 TwrShadow - Calculate tower influence on wind based on downstream tower shadow (switch) {0=none, 1=Powles model, 2=Eames model} +False TwrAero - Calculate tower aerodynamic loads? (flag) +False FrozenWake - Assume frozen wake during linearization? (flag) [used only when WakeMod=1 and when linearizing] +False CavitCheck - Perform cavitation check? (flag) [AFAeroMod must be 1 when CavitCheck=true] +False CompAA - Flag to compute AeroAcoustics calculation [used only when WakeMod = 1 or 2] +"unused" AA_InputFile - AeroAcoustics input file [used only when CompAA=true] +====== Environmental Conditions =================================================================== + 1.225 AirDens - Air density (kg/m^3) + 1.4639E-05 KinVisc - Kinematic air viscosity (m^2/s) + 335 SpdSound - Speed of sound (m/s) + 103500 Patm - Atmospheric pressure (Pa) [used only when CavitCheck=True] + 1700 Pvap - Vapour pressure of fluid (Pa) [used only when CavitCheck=True] + 0.5 FluidDepth - Water depth above mid-hub height (m) [used only when CavitCheck=True] +====== Blade-Element/Momentum Theory Options ====================================================== [unused when WakeMod=0 or 3] + 2 SkewMod - Type of skewed-wake correction model (switch) {1=uncoupled, 2=Pitt/Peters, 3=coupled} [unused when WakeMod=0 or 3] +"default" SkewModFactor - Constant used in Pitt/Peters skewed wake model {or "default" is 15/32*pi} (-) [used only when SkewMod=2; unused when WakeMod=0 or 3] +True TipLoss - Use the Prandtl tip-loss model? (flag) [unused when WakeMod=0 or 3] +True HubLoss - Use the Prandtl hub-loss model? (flag) [unused when WakeMod=0 or 3] +True TanInd - Include tangential induction in BEMT calculations? (flag) [unused when WakeMod=0 or 3] +False AIDrag - Include the drag term in the axial-induction calculation? (flag) [unused when WakeMod=0 or 3] +False TIDrag - Include the drag term in the tangential-induction calculation? (flag) [unused when WakeMod=0,3 or TanInd=FALSE] +"Default" IndToler - Convergence tolerance for BEMT nonlinear solve residual equation {or "default"} (-) [unused when WakeMod=0 or 3] + 100 MaxIter - Maximum number of iteration steps (-) [unused when WakeMod=0] +====== Dynamic Blade-Element/Momentum Theory Options ============================================== [used only when WakeMod=2] + 2 DBEMT_Mod - Type of dynamic BEMT (DBEMT) model {1=constant tau1, 2=time-dependent tau1} (-) [used only when WakeMod=2] + 2 tau1_const - Time constant for DBEMT (s) [used only when WakeMod=2 and DBEMT_Mod=1] +====== OLAF -- cOnvecting LAgrangian Filaments (Free Vortex Wake) Theory Options ================== [used only when WakeMod=3] +"Elliptic_OLAF.dat" OLAFInputFileName - Input file for OLAF [used only when WakeMod=3] +====== Beddoes-Leishman Unsteady Airfoil Aerodynamics Options ===================================== [used only when AFAeroMod=2] + 3 UAMod - Unsteady Aero Model Switch (switch) {1=Baseline model (Original), 2=Gonzalez's variant (changes in Cn,Cc,Cm), 3=Minnema/Pierce variant (changes in Cc and Cm)} [used only when AFAeroMod=2] +True FLookup - Flag to indicate whether a lookup for f' will be calculated (TRUE) or whether best-fit exponential equations will be used (FALSE); if FALSE S1-S4 must be provided in airfoil input files (flag) [used only when AFAeroMod=2] +====== Airfoil Information ========================================================================= + 1 AFTabMod - Interpolation method for multiple airfoil tables {1=1D interpolation on AoA (first table only); 2=2D interpolation on AoA and Re; 3=2D interpolation on AoA and UserProp} (-) + 1 InCol_Alfa - The column in the airfoil tables that contains the angle of attack (-) + 2 InCol_Cl - The column in the airfoil tables that contains the lift coefficient (-) + 3 InCol_Cd - The column in the airfoil tables that contains the drag coefficient (-) + 4 InCol_Cm - The column in the airfoil tables that contains the pitching-moment coefficient; use zero if there is no Cm column (-) + 0 InCol_Cpmin - The column in the airfoil tables that contains the Cpmin coefficient; use zero if there is no Cpmin column (-) + 1 NumAFfiles - Number of airfoil files used (-) +"Polar2PiAlpha_AD15.dat" AFNames - Airfoil file names (NumAFfiles lines) (quoted strings) +====== Rotor/Blade Properties ===================================================================== +True UseBlCm - Include aerodynamic pitching moment in calculations? (flag) +"Elliptic_AD15_blade_40_cos.dat" ADBlFile(1) - Name of file containing distributed aerodynamic properties for Blade #1 (-) +"Elliptic_AD15_blade_40_dummy.dat" ADBlFile(2) - Name of file containing distributed aerodynamic properties for Blade #2 (-) [unused if NumBl < 2] +"Elliptic_AD15_blade_40_dummy.dat" ADBlFile(3) - Name of file containing distributed aerodynamic properties for Blade #3 (-) [unused if NumBl < 3] +====== Tower Influence and Aerodynamics ============================================================= [used only when TwrPotent/=0, TwrShadow/=0, or TwrAero=True] + 7 NumTwrNds - Number of tower nodes used in the analysis (-) [used only when TwrPotent/=0, TwrShadow/=0, or TwrAero=True] +TwrElev TwrDiam TwrCd TwrTI (used only with TwrShadow=2) +(m) (m) (-) (-) +0.0000000E+00 1.0000000E+01 1.0000000E+00 1.0000000E-01 +2.3330000E+01 9.3300000E+00 1.0000000E+00 1.0000000E-01 +4.6670000E+01 8.6700000E+00 1.0000000E+00 1.0000000E-01 +7.0000000E+01 8.0000000E+00 1.0000000E+00 1.0000000E-01 +9.3330000E+01 7.3300000E+00 1.0000000E+00 1.0000000E-01 +1.1667000E+02 6.6700000E+00 1.0000000E+00 1.0000000E-01 +1.4000000E+02 6.0000000E+00 1.0000000E+00 1.0000000E-01 +====== Outputs ==================================================================================== +False SumPrint - Generate a summary file listing input options and interpolated properties to ".AD.sum"? (flag) + 0 NBlOuts - Number of blade node outputs [0 - 9] (-) + 1 BlOutNd - Blade nodes whose values will be output (-) + 0 NTwOuts - Number of tower node outputs [0 - 9] (-) + 1 TwOutNd - Tower nodes whose values will be output (-) + OutList - The next line(s) contains a list of output parameters. See OutListParameters.xlsx for a listing of available output channels, (-) +"RtTSR" +"RtVAvgxh" +"RtVAvgyh" +"RtVAvgzh" +"RtSkew" +"RtAeroFxh" +"RtAeroFyh" +"RtAeroFzh" +"RtAeroMxh" +"RtAeroMyh" +"RtAeroMzh" +"RtAeroPwr" +"RtAeroCp" +"RtAeroCq" +"RtAeroCt" +END of input file (the word "END" must appear in the first 3 columns of this last OutList line) +====== Outputs for all blade stations (same ending as above for B1N1.... =========================== [optional section] + 1 BldNd_BladesOut - Number of blades to output all node information at. Up to number of blades on turbine. (-) + "All" BldNd_BlOutNd - Future feature will allow selecting a portion of the nodes to output. Not implemented yet. (-) + OutListAD - The next line(s) contains a list of output parameters. See OutListParameters.xlsx for a listing of available output channels, (-) +Fx +Fy +Vx +Vy +VUndx +VUndy +VUndz +STVx +STVy +STVz +Vrel +TnInd +AxInd +Theta +Phi +Vindx +Vindy +Alpha +Gam +Fn +Ft +Cl +Cd +Cm +Cx +Cy +Cn +Ct +Fl +Fd +Mm +END of input file (the word "END" must appear in the first 3 columns of this last OutList line) +--------------------------------------------------------------------------------------- diff --git a/pyFAST/input_output/tests/example_files/input_decks/Elliptic_AD15_blade_40_cos.dat b/pyFAST/input_output/tests/example_files/input_decks/Elliptic_AD15_blade_40_cos.dat new file mode 100644 index 0000000..b38b9d0 --- /dev/null +++ b/pyFAST/input_output/tests/example_files/input_decks/Elliptic_AD15_blade_40_cos.dat @@ -0,0 +1,46 @@ +AERODYN v15.00.* - BLADE DEFINITION INPUT FILE +Turbine EllipticWing, Generated by script Main_CreateModel.py V112-3.075MW - +--- Blade Properties --- +40 NumBlNds - Number of blade nodes used in the analysis (-) +BlSpn BlCrvAC BlSwpAC BlCrvAng BlTwist BlChord BlAFID +(m) (m) (m) (deg) (deg) (m) (-) + 0 0 0 0 0 1.00000000e-05 1 + 8.10672966e-03 0 0 0 0 8.05000000e-02 1 + 3.23743434e-02 0 0 0 0 1.60400000e-01 1 + 7.26454564e-02 0 0 0 0 2.39300000e-01 1 + 1.28658895e-01 0 0 0 0 3.16700000e-01 1 + 2.00051391e-01 0 0 0 0 3.92000000e-01 1 + 2.86359936e-01 0 0 0 0 4.64700000e-01 1 + 3.87024786e-01 0 0 0 0 5.34500000e-01 1 + 5.01393091e-01 0 0 0 0 6.00700000e-01 1 + 6.28723130e-01 0 0 0 0 6.63100000e-01 1 + 7.68189116e-01 0 0 0 0 7.21200000e-01 1 + 9.18886561e-01 0 0 0 0 7.74600000e-01 1 + 1.07983813e+00 0 0 0 0 8.23000000e-01 1 + 1.25000000e+00 0 0 0 0 8.66000000e-01 1 + 1.42826860e+00 0 0 0 0 9.03500000e-01 1 + 1.61348778e+00 0 0 0 0 9.35000000e-01 1 + 1.80445634e+00 0 0 0 0 9.60500000e-01 1 + 1.99993577e+00 0 0 0 0 9.79800000e-01 1 + 2.19865830e+00 0 0 0 0 9.92700000e-01 1 + 2.39933515e+00 0 0 0 0 9.99200000e-01 1 + 2.60066485e+00 0 0 0 0 9.99200000e-01 1 + 2.80134170e+00 0 0 0 0 9.92700000e-01 1 + 3.00006423e+00 0 0 0 0 9.79800000e-01 1 + 3.19554366e+00 0 0 0 0 9.60500000e-01 1 + 3.38651222e+00 0 0 0 0 9.35000000e-01 1 + 3.57173140e+00 0 0 0 0 9.03500000e-01 1 + 3.75000000e+00 0 0 0 0 8.66000000e-01 1 + 3.92016187e+00 0 0 0 0 8.23000000e-01 1 + 4.08111344e+00 0 0 0 0 7.74600000e-01 1 + 4.23181088e+00 0 0 0 0 7.21200000e-01 1 + 4.37127687e+00 0 0 0 0 6.63100000e-01 1 + 4.49860691e+00 0 0 0 0 6.00700000e-01 1 + 4.61297521e+00 0 0 0 0 5.34500000e-01 1 + 4.71364006e+00 0 0 0 0 4.64700000e-01 1 + 4.79994861e+00 0 0 0 0 3.92000000e-01 1 + 4.87134110e+00 0 0 0 0 3.16700000e-01 1 + 4.92735454e+00 0 0 0 0 2.39300000e-01 1 + 4.96762566e+00 0 0 0 0 1.60400000e-01 1 + 4.99189327e+00 0 0 0 0 8.05000000e-02 1 + 5 0 0 0 0 1.00000000e-05 1 diff --git a/pyFAST/input_output/tests/example_files/input_decks/Elliptic_IW.dat b/pyFAST/input_output/tests/example_files/input_decks/Elliptic_IW.dat new file mode 100644 index 0000000..9e6c6b5 --- /dev/null +++ b/pyFAST/input_output/tests/example_files/input_decks/Elliptic_IW.dat @@ -0,0 +1,57 @@ +------- InflowWind INPUT FILE ------------------------------------------------------------------------- +Input for elliptical wing uniform wind +--------------------------------------------------------------------------------------------------------------- +False Echo - Echo input data to .ech (flag) + 2 WindType - switch for wind file type (1=steady; 2=uniform; 3=binary TurbSim FF; 4=binary Bladed-style FF; 5=HAWC format; 6=User defined; 7=native Bladed FF) + 0 PropagationDir - Direction of wind propagation (meteorological rotation from aligned with X (positive rotates towards -Y) -- degrees) (not used for native Bladed format WindType=7) + 0 VFlowAng - Upflow angle (degrees) (not used for native Bladed format WindType=7) + 1 NWindVel - Number of points to output the wind velocity (0 to 9) + 0 WindVxiList - List of coordinates in the inertial X direction (m) + 0 WindVyiList - List of coordinates in the inertial Y direction (m) + 100 WindVziList - List of coordinates in the inertial Z direction (m) +================== Parameters for Steady Wind Conditions [used only for WindType = 1] ========================= + 1 HWindSpeed - Horizontal wind speed (m/s) + 100 RefHt - Reference height for horizontal wind speed (m) + 0 PLExp - Power law exponent (-) +================== Parameters for Uniform wind file [used only for WindType = 2] ============================ +"Elliptic_Wind.wnd" FileName_Uni - Filename of time series data for uniform wind field. (-) + 100 RefHt_Uni - Reference height for horizontal wind speed (m) + 125.88 RefLength - Reference length for linear horizontal and vertical sheer (-) +================== Parameters for Binary TurbSim Full-Field files [used only for WindType = 3] ============== +"unused" FileName_BTS - Name of the Full field wind file to use (.bts) +================== Parameters for Binary Bladed-style Full-Field files [used only for WindType = 4 or WindType = 7] ========= +"unused" FileNameRoot - WindType=4: Rootname of the full-field wind file to use (.wnd, .sum); WindType=7: name of the intermediate file with wind scaling values +False TowerFile - Have tower file (.twr) (flag) ignored when WindType = 7 +================== Parameters for HAWC-format binary files [Only used with WindType = 5] ===================== +"unused" FileName_u - name of the file containing the u-component fluctuating wind (.bin) +"unused" FileName_v - name of the file containing the v-component fluctuating wind (.bin) +"unused" FileName_w - name of the file containing the w-component fluctuating wind (.bin) + 64 nx - number of grids in the x direction (in the 3 files above) (-) + 32 ny - number of grids in the y direction (in the 3 files above) (-) + 32 nz - number of grids in the z direction (in the 3 files above) (-) + 16 dx - distance (in meters) between points in the x direction (m) + 3 dy - distance (in meters) between points in the y direction (m) + 3 dz - distance (in meters) between points in the z direction (m) + 15000 RefHt_Hawc - reference height; the height (in meters) of the vertical center of the grid (m) + ------------- Scaling parameters for turbulence --------------------------------------------------------- + 2 ScaleMethod - Turbulence scaling method [0 = none, 1 = direct scaling, 2 = calculate scaling factor based on a desired standard deviation] + 1 SFx - Turbulence scaling factor for the x direction (-) [ScaleMethod=1] + 1 SFy - Turbulence scaling factor for the y direction (-) [ScaleMethod=1] + 1 SFz - Turbulence scaling factor for the z direction (-) [ScaleMethod=1] + 1.2 SigmaFx - Turbulence standard deviation to calculate scaling from in x direction (m/s) [ScaleMethod=2] + 0.8 SigmaFy - Turbulence standard deviation to calculate scaling from in y direction (m/s) [ScaleMethod=2] + 0.2 SigmaFz - Turbulence standard deviation to calculate scaling from in z direction (m/s) [ScaleMethod=2] + ------------- Mean wind profile parameters (added to HAWC-format files) --------------------------------- + 12 URef - Mean u-component wind speed at the reference height (m/s) + 2 WindProfile - Wind profile type (0=constant;1=logarithmic,2=power law) + 0.2 PLExp_Hawc - Power law exponent (-) (used for PL wind profile type only) + 0.03 Z0 - Surface roughness length (m) (used for LG wind profile type only) + 0 XOffset - Initial offset in +x direction (shift of wind box) +====================== OUTPUT ================================================== +False SumPrint - Print summary data to .IfW.sum (flag) + OutList - The next line(s) contains a list of output parameters. See OutListParameters.xlsx for a listing of available output channels, (-) +"Wind1VelX" X-direction wind velocity at point WindList(1) +"Wind1VelY" Y-direction wind velocity at point WindList(1) +"Wind1VelZ" Z-direction wind velocity at point WindList(1) +END of input file (the word "END" must appear in the first 3 columns of this last OutList line) +--------------------------------------------------------------------------------------- diff --git a/pyFAST/input_output/tests/example_files/input_decks/Elliptic_OLAF.dat b/pyFAST/input_output/tests/example_files/input_decks/Elliptic_OLAF.dat new file mode 100644 index 0000000..8c35766 --- /dev/null +++ b/pyFAST/input_output/tests/example_files/input_decks/Elliptic_OLAF.dat @@ -0,0 +1,45 @@ +--------------------------- FREE WAKE INPUT FILE ---------------------------------------------- +Free wake input file for the elliptic wing case +--------------------------- GENERAL OPTIONS --------------------------------------------------- +5 IntMethod Integration method {4: 2nd order Predictor/Corrector, 5: Forward Euler 1st order, default: 5} (switch) +default DTfvw Time interval for wake propagation. {default: dtaero} (s) +99999 FreeWakeStart Time when wake is free. (-) value = always free. {default: 0.0} (s) +0.0 FullCircStart Time at which full circulation is reached. {default: 0.0} (s) +--------------------------- CIRCULATION SPECIFICATIONS ---------------------------------------- +1 CircSolvingMethod Circulation solving method {1: Cl-Based, 2: No-Flow Through, 3: Prescribed, default: 1 }(switch) +0.00005 CircSolvConvCrit Convergence criteria {default: 0.001} [only if CircSolvingMethod=1] (-) +0.1 CircSolvRelaxation Relaxation factor {default: 0.1} [only if CircSolvingMethod=1] (-) +200 CircSolvMaxIter Maximum number of iterations for circulation solving {default: 30} (-) +"NA" PrescribedCircFile File containing prescribed circulation [only if CircSolvingMethod=3] (quoted string) +=============================================================================================== +--------------------------- WAKE OPTIONS ------------------------------------------------------ +------------------- WAKE EXTENT AND DISCRETIZATION -------------------------------------------- +100 nNWPanel Number of near-wake panels (-) +10 WakeLength Total wake distance, in number of wake panels (-) +default FreeWakeLength Wake length that is free, in number of wake panels (-) {default: WakeLength} +default FWShedVorticity Include shed vorticity in the far wake {default: false} +------------------- WAKE REGULARIZATIONS AND DIFFUSION ----------------------------------------- +0 DiffusionMethod Diffusion method to account for viscous effects {0: None, 1: Core Spreading, "default": 0} +0 RegDeterMethod Method to determine the regularization parameters {0: Manual, 1: Optimized, default: 0 } +2 RegFunction Viscous diffusion function {0: None, 1: Rankine, 2: LambOseen, 3: Vatistas, 4: Denominator, "default": 3} (switch) +1 WakeRegMethod Wake regularization method {1: Constant, 2: Stretching, 3: Age, default: 1} (switch) +0.0 WakeRegFactor Wake regularization factor (m) +0.0 WingRegFactor Wing regularization factor (m) +100 CoreSpreadEddyVisc Eddy viscosity in core spreading methods, typical values 1-1000 +------------------- WAKE TREATMENT OPTIONS --------------------------------------------------- +False TwrShadowOnWake Include tower flow disturbance effects on wake convection {default:false} [only if TwrPotent or TwrShadow] +0 ShearModel Shear Model {0: No treatment, 1: Mirrored vorticity, default: 0} +------------------- SPEEDUP OPTIONS ----------------------------------------------------------- +1 VelocityMethod Method to determine the velocity {1:Biot-Savart Segment, 2:Particle tree, default: 1} +1.5 TreeBranchFactor Branch radius fraction above which a multipole calculation is used {default: 2.0} [only if VelocityMethod=2] +1 PartPerSegment Number of particles per segment [only if VelocityMethod=2] +=============================================================================================== +--------------------------- OUTPUT OPTIONS --------------------------------------------------- +1 WrVTk Outputs Visualization Toolkit (VTK) (independent of .fst option) {False: NoVTK, True: Write VTK at each time step} (flag) +1 nVTKBlades Number of blades for which VTK files are exported {0: No VTK per blade, n: VTK for blade 1 to n} (-) +1 VTKCoord Coordinate system used for VTK export. {1: Global, 2: Hub, 3: Both, "default": 1} +default VTK_fps Frame rate for VTK output (frames per second) {"all" for all glue code timesteps, "default" for all FVW timesteps} [only if WrVTK=1] +0 nGridOut Number of grid outputs +GridName DTOut XStart XEnd nX YStart YEnd nY ZStart ZEnd nZ +(-) (s) (m) (m) (-) (m) (m) (-) (m) (m) (-) +=============================================================================================== diff --git a/pyFAST/input_output/tests/example_files/input_decks/Elliptic_Wind.wnd b/pyFAST/input_output/tests/example_files/input_decks/Elliptic_Wind.wnd new file mode 100644 index 0000000..fa64f04 --- /dev/null +++ b/pyFAST/input_output/tests/example_files/input_decks/Elliptic_Wind.wnd @@ -0,0 +1,5 @@ +!Wind file with step changes in wind speed. +!Time Wind Wind Vert. Horiz. Vert. LinV Gust +! Speed Dir Speed Shear Shear Shear Speed +0.00 1.00 0.00 0.10 0.00 0.00 0.00 0.00 +1000.0 1.00 0.00 0.10 0.00 0.00 0.00 0.00 diff --git a/pyFAST/input_output/tests/example_files/input_decks/Main_EllipticalWingInf_OLAF.dvr b/pyFAST/input_output/tests/example_files/input_decks/Main_EllipticalWingInf_OLAF.dvr new file mode 100644 index 0000000..df2a42b --- /dev/null +++ b/pyFAST/input_output/tests/example_files/input_decks/Main_EllipticalWingInf_OLAF.dvr @@ -0,0 +1,55 @@ +----- AeroDyn MultiRotor Driver Input File -------------------------------------- +Elliptical wing test case for OLAF free vortex wake +----- Input Configuration ---------------------------------------------------- +False Echo - Echo input parameters to ".ech"? + 1000 TMax - Total run time (s) + 100 DT - Simulation time step (s) +"Elliptic_AD15_40.dat" AeroFile - Name of the AeroDyn input file +----- Inflow Data ----------------------------------------------------------- + 1 CompInflow - Compute inflow wind velocities (switch) {0=Steady Wind; 1=InflowWind} +"Elliptic_IW.dat" InflowFile - Name of the InflowWind input file + 0 HWindSpeed - Horizontal wind speed [used only when CompInflow=0 and NumCase=0] (m/s) + 0 RefHt - Reference height for horizontal wind speed [used only when CompInflow=0 and NumCase=0] (m) + 0 PLExp - Power law exponent [used only when CompInflow=0 and NumCase=0] (-) +----- Turbine Data ----------------------------------------------------------- +1 NumTurbines - Number of turbines +====== Turbine 1 ================================================================ + False BasicHAWTFormat(1) - Flag to switch between basic or generic input format {True: next 7 lines are basic inputs, False: Base/Twr/Nac/Hub/Bld geometry and motion must follow} +0,0,00 BaseOriginInit(1) - x,y,z coordinates of base origin (m) +0,0,0 BaseOrientationInit(1) - successive rotations (theta_x, theta_y, theta_z) defining initial orientation of the base frame from the global frame (e.g. roll, tilt, yaw) (deg) +True HasTower(1) - True if turbine has a tower (flag) +False HAWTprojection(1) - True if turbine is horizontal axis (for AeroDyn projections) (flag) +0,0,0 TwrOrigin_t(1) - Coordinate of tower base in base coordinates [used only when HasTower is True] (m) +0,0,100 NacOrigin_t(1) - x,y,z coordinates of nacelle origin (and tower top) from base, in base coordinates (m) +0,0,0 HubOrigin_n(1) - NOTE: Bar has 7m overhang, tilt of 6deg, and twr2shaft 3.09343 x,y,z coordinates of hub origin from nacelle origin, in nacelle coordinates (m) +0,0,0 HubOrientation_n(1) - successive rotations (theta_x, theta_y, theta_z) defining initial orientation of the hub frame from the nacelle frame (e.g. roll, tilt, yaw). The x axis needs to be aligned with the rotational speed. (deg) +----- Turbine 1 Blades ----------------------------------------------------------------- +1 NumBlades(1) - Number of blades for current rotor (-) +0,0,0 BldOrigin_h(1_1) - Orign of blade 1 wrt. hub origin in hub coordinates (m) +-90,0,-90 BldOrientation_h(1_1) - successive rotations (theta_x, theta_y, theta_z) defining initial orientation of the blade frame from the hub frame such that the "z" is along span, "y" along trailing edge without pitch (azimuth, precone, pitch) (deg) +0.0 BldHubRad_bl(1_1) - z-offset in blade coordinates of blade 1 where radial input data start (m) +----- Turbine 1 Base Motion ----------------------------------------------------------------- +0 BaseMotionType(1) - Type of motion prescribed for this base {0: fixed, 1: Sinusoidal motion, 2: arbitrary motion} (flag) +1 DegreeOfFreedom(1) - {1:xt, 2:yt, 3:zt, 4:theta_xt, 5:theta_yt, 6:theta_zt} [used only when BaseMotionType=1] (flag) +0 Amplitude(1) - Amplitude of sinusoidal motion [used only when BaseMotionType=1] +0 Frequency(1) - Frequency of sinusoidal motion [used only when BaseMotionType=1] +"" BaseMotionFileName(1) - Filename containing rotor motion [used only when BaseMotionType=2] +----- Turbine 1 Nacelle Motion ----------------------------------------------------------------- +0 NacMotionType(1) - Type of motion prescribed for the nacelle {0: fixed yaw, 1: time varying yaw angle} (flag) +0 NacYaw(1) - Yaw angle (about z_t) of the nacelle [user only when NacMotionType=0] (deg) +"NA" NacMotionFileName(1) - Filename containing yaw motion [used only when NacMotionType=1] +----- Turbine 1 Rotor Motion ----------------------------------------------------------------- +0 RotMotionType(1) - Type of motion prescribed for this rotor {0: constant rotation, 1: time varying rotation} (flag) +0. RotSpeed(1) - Rotational speed of rotor in rotor coordinates [used only when RotorMotionType=0] (rpm) +"NA" RotMotionFileName(1) - Filename containing rotor motion [used only when RotorMotionType=1] +----- Turbine 1 Blade Pitch Motion ----------------------------------------------------------------- +0 BldMotionType(1) - Type of pitch motion prescribed for the blades {0: fixed, 1: time varying pitch} (flag) +0 BldPitch(1_1) - Blade 1 pitch [used only when BlMotiontype=1] (deg) +"NA" BldMotionFileName(1_1) - Filename containing blade pitch motion [used only when BlMotionType=1] +----- I/O Settings ----------------------------------------------------------- +"ES15.8E2" OutFmt - Format used for text tabular output, excluding the time channel. Resulting field should be 10 characters. (quoted string) +2 OutFileFmt - Format for tabular (time-marching) output file (switch) {1: text file [.out], 2: binary file [.outb], 3: both} +0 WrVTK - VTK visualization data output: (switch) {0=none; 1=animation} +0.25 VTKHubRad - HubRadius for VTK visualization (m) +-0.1,-0.1,-0.1,0.2,0.2,0.2 VTKNacDim - Nacelle Dimension for VTK visualization x0,y0,z0,Lx,Ly,Lz (m) + diff --git a/pyFAST/input_output/tests/example_files/input_decks/Polar2PiAlpha_AD15.dat b/pyFAST/input_output/tests/example_files/input_decks/Polar2PiAlpha_AD15.dat new file mode 100644 index 0000000..a704f03 --- /dev/null +++ b/pyFAST/input_output/tests/example_files/input_decks/Polar2PiAlpha_AD15.dat @@ -0,0 +1,152 @@ +! ------------ AirfoilInfo v1.00.x Input File ----------------------------------. +! Airfoil properties to be used with AeroDyn v15 +! Generated from ... +! note that this file uses Marshall Buhl's new input file processing; start all comment lines with ! +! ------------------------------------------------------------------------------ +"DEFAULT" InterpOrd ! Interpolation order to use for quasi-steady table lookup {1=linear; 3=cubic spline; "default"} [default=3] +1.0 NonDimArea ! The non-dimensional area of the airfoil (area/chord^2) (set to 1.0 if unsure or unneeded) +0 NumCoords ! The number of coordinates in the airfoil shape file. Set to zero if coordinates not included. +"unused" BL_file ! The file name including the boundary layer characteristics of the profile. Ignored if the aeroacoustic module is not called. +1 NumTabs ! Number of airfoil tables in this file. Each table must have lines for Re and Ctrl. +! ------------------------------------------------------------------------------ +! data for table 1 +! ------------------------------------------------------------------------------ +1.0 Re ! Reynolds number in millions +0 Ctrl ! Control setting (must be 0 for current AirfoilInfo) +True InclUAdata ! Is unsteady aerodynamics data included in this table? If TRUE, then include 30 UA coefficients below this line +!........................................ +0.0 alpha0 ! 0-lift angle of attack, depends on airfoil. +27.8411 alpha1 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA>alpha0. (deg) +-27.8411 alpha2 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA1] +0 S2 ! Constant in the f curve best-fit for AOA>alpha1;by definition it depends on the airfoil. [ignored if UAMod<>1] +0 S3 ! Constant in the f curve best-fit for alpha2<=AOA1] +0 S4 ! Constant in the f curve best-fit for AOA1] +2.598 Cn1 ! Critical value of C0n at leading edge separation. It should be extracted from airfoil data at a given Mach and Reynolds number. It can be calculated from the static value of Cn at either the break in the pitching moment or the loss of chord force at the onset of stall. It is close to the condition of maximum lift of the airfoil at low Mach numbers. +-2.598 Cn2 ! As Cn1 for negative AOAs. +default St_sh ! Strouhal's shedding frequency constant. [default = 0.19] +0.0 Cd0 ! 2D drag coefficient value at 0-lift. +0.0 Cm0 ! 2D pitching moment coefficient about 1/4-chord location, at 0-lift, positive if nose up. [If the aerodynamics coefficients table does not include a column for Cm, this needs to be set to 0.0] +0 k0 ! Constant in the \hat(x)_cp curve best-fit; = (\hat(x)_AC-0.25). [ignored if UAMod<>1] +0 k1 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] +0 k2 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] +0 k3 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] +0 k1_hat ! Constant in the expression of Cc due to leading edge vortex effects. [ignored if UAMod<>1] +default x_cp_bar ! Constant in the expression of \hat(x)_cp^v. [ignored if UAMod<>1, default = 0.2] +default UACutout ! Angle of attack above which unsteady aerodynamics are disabled (deg). [Specifying the string "Default" sets UACutout to 45 degrees] +default filtCutOff ! Reduced frequency cut-off for low-pass filtering the AoA input to UA, as well as the 1st and 2nd derivatives (-) [default = 0.5] +!........................................ +! Table of aerodynamics coefficients +98 NumAlf ! Number of data lines in the following table +! Alpha Cl Cd Cm +! (deg) (-) (-) (-) +-1.80000000e+02 0.00000000e+00 0.00000000e+00 0.00000000e+00 +-1.79000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-1.78000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-1.77000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-1.76000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-1.75000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-1.74000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-1.73000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-1.72000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-1.71000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-1.70000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-1.69000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-1.68000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-1.67000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-1.66000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-1.65000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-1.60000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-1.50000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-1.40000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-1.30000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-1.20000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-1.10000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-1.00000000e+02 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-9.00000000e+01 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-8.00000000e+01 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-7.00000000e+01 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-6.00000000e+01 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-5.00000000e+01 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-4.00000000e+01 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-3.00000000e+01 -2.99990000e+00 0.00000000e+00 0.00000000e+00 +-2.50000000e+01 -2.74156000e+00 0.00000000e+00 0.00000000e+00 +-2.00000000e+01 -2.19325000e+00 0.00000000e+00 0.00000000e+00 +-1.70000000e+01 -1.86426000e+00 0.00000000e+00 0.00000000e+00 +-1.60000000e+01 -1.75460000e+00 0.00000000e+00 0.00000000e+00 +-1.50000000e+01 -1.64493000e+00 0.00000000e+00 0.00000000e+00 +-1.40000000e+01 -1.53527000e+00 0.00000000e+00 0.00000000e+00 +-1.30000000e+01 -1.42561000e+00 0.00000000e+00 0.00000000e+00 +-1.20000000e+01 -1.31595000e+00 0.00000000e+00 0.00000000e+00 +-1.10000000e+01 -1.20628000e+00 0.00000000e+00 0.00000000e+00 +-1.00000000e+01 -1.09662000e+00 0.00000000e+00 0.00000000e+00 +-9.00000000e+00 -9.86960000e-01 0.00000000e+00 0.00000000e+00 +-8.00000000e+00 -8.77300000e-01 0.00000000e+00 0.00000000e+00 +-7.00000000e+00 -7.67640000e-01 0.00000000e+00 0.00000000e+00 +-6.00000000e+00 -6.57970000e-01 0.00000000e+00 0.00000000e+00 +-4.00000000e+00 -4.38650000e-01 0.00000000e+00 0.00000000e+00 +-2.00000000e+00 -2.19320000e-01 0.00000000e+00 0.00000000e+00 + 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 + 1.00000000e+00 1.09660000e-01 0.00000000e+00 0.00000000e+00 + 2.00000000e+00 2.19320000e-01 0.00000000e+00 0.00000000e+00 + 3.00000000e+00 3.28990000e-01 0.00000000e+00 0.00000000e+00 + 4.00000000e+00 4.38650000e-01 0.00000000e+00 0.00000000e+00 + 5.00000000e+00 5.48310000e-01 0.00000000e+00 0.00000000e+00 + 6.00000000e+00 6.57970000e-01 0.00000000e+00 0.00000000e+00 + 7.00000000e+00 7.67640000e-01 0.00000000e+00 0.00000000e+00 + 8.00000000e+00 8.77300000e-01 0.00000000e+00 0.00000000e+00 + 9.00000000e+00 9.86960000e-01 0.00000000e+00 0.00000000e+00 + 1.00000000e+01 1.09662000e+00 0.00000000e+00 0.00000000e+00 + 1.10000000e+01 1.20628000e+00 0.00000000e+00 0.00000000e+00 + 1.20000000e+01 1.31595000e+00 0.00000000e+00 0.00000000e+00 + 1.30000000e+01 1.42561000e+00 0.00000000e+00 0.00000000e+00 + 1.40000000e+01 1.53527000e+00 0.00000000e+00 0.00000000e+00 + 1.50000000e+01 1.64493000e+00 0.00000000e+00 0.00000000e+00 + 1.60000000e+01 1.75460000e+00 0.00000000e+00 0.00000000e+00 + 1.70000000e+01 1.86426000e+00 0.00000000e+00 0.00000000e+00 + 1.80000000e+01 1.97392000e+00 0.00000000e+00 0.00000000e+00 + 2.00000000e+01 2.19325000e+00 0.00000000e+00 0.00000000e+00 + 2.20000000e+01 2.41257000e+00 0.00000000e+00 0.00000000e+00 + 2.50000000e+01 2.74156000e+00 0.00000000e+00 0.00000000e+00 + 3.00000000e+01 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 4.00000000e+01 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 5.00000000e+01 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 6.00000000e+01 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 7.00000000e+01 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 8.00000000e+01 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 9.00000000e+01 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.00000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.10000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.20000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.30000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.40000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.50000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.60000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.65000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.66000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.67000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.68000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.69000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.70000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.71000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.72000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.73000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.74000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.75000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.76000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.77000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.78000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.79000000e+02 2.99990000e+00 0.00000000e+00 0.00000000e+00 + 1.80000000e+02 0.00000000e+00 0.00000000e+00 0.00000000e+00 diff --git a/pyFAST/input_output/tests/test_csv.py b/pyFAST/input_output/tests/test_csv.py index a7ef076..7f0f66d 100644 --- a/pyFAST/input_output/tests/test_csv.py +++ b/pyFAST/input_output/tests/test_csv.py @@ -45,6 +45,12 @@ def test_CSV(self): self.assertEqual(DF.columns.values[-1],'GenTq_(kN m)') self.assertEqual(DF.shape,(9,6)) + def test_CSV_string(self): + DF=self.DF('CSVxIsString.csv') + self.assertEqual(DF.shape,(7,2)) + self.assertEqual(DF.columns.values[0],'Label_[-]') + + if __name__ == '__main__': #Test().test_CSV() unittest.main() diff --git a/pyFAST/input_output/tests/test_fast_input.py b/pyFAST/input_output/tests/test_fast_input.py index d2a93b1..7091eaa 100644 --- a/pyFAST/input_output/tests/test_fast_input.py +++ b/pyFAST/input_output/tests/test_fast_input.py @@ -6,6 +6,7 @@ import pyFAST from pyFAST.input_output import FASTInputFile +from pyFAST.input_output.fast_wind_file import FASTWndFile class Test(unittest.TestCase): @@ -70,6 +71,23 @@ def test_FASTIn(self): F.test_ascii(bCompareWritesOnly=True,bDelete=True) self.assertEqual(float(F['LineTypes'][0,1]),0.02) + def test_FASTWnd(self): + F=FASTWndFile(os.path.join(MyDir,'FASTWnd.wnd')) + F.test_ascii(bCompareWritesOnly=True,bDelete=True) + + def test_FASTInGraph(self): + F=FASTInputFile(os.path.join(MyDir,'FASTIn_HD.dat')) + #graph = F.toGraph() + #print(graph) + #self.assertEqual(len(graph.Nodes), 4) + #self.assertEqual(len(graph.Elements), 3) +# + #F=FASTInputFile(os.path.join(MyDir,'FASTIn_SbD.dat')) + #print(F) + #graph = F.toGraph() +# self.assertEqual(len(graph.Nodes), 2) +# self.assertEqual(len(graph.Elements), 1) + if __name__ == '__main__': #Test().test_FASTIn() unittest.main() diff --git a/pyFAST/input_output/tests/test_fast_input_deck.py b/pyFAST/input_output/tests/test_fast_input_deck.py new file mode 100644 index 0000000..337ed1a --- /dev/null +++ b/pyFAST/input_output/tests/test_fast_input_deck.py @@ -0,0 +1,26 @@ +import unittest +import os +import numpy as np +from .helpers_for_test import MyDir, reading_test + +from pyFAST.input_output.fast_input_deck import FASTInputDeck + +class Test(unittest.TestCase): + + def test_deck_driver(self): + F=FASTInputDeck(os.path.join(MyDir,'input_decks/Main_EllipticalWingInf_OLAF.dvr')) + #F.test_ascii(bCompareWritesOnly=True,bDelete=True) + self.assertEqual(F.fst['NumTurbines'],1) + + self.assertEqual(F.version,'AD_driver') + self.assertEqual(F.ADversion,'AD15') + self.assertTrue(F.fst is not None) + self.assertTrue(F.IW is not None) + self.assertTrue(F.AD is not None) + self.assertTrue(F.AD.Bld1 is not None) + + + +if __name__ == '__main__': + #Test().test_FASTIn() + unittest.main() diff --git a/pyFAST/input_output/tests/test_hawc.py b/pyFAST/input_output/tests/test_hawc.py index fff4b94..6e476c7 100644 --- a/pyFAST/input_output/tests/test_hawc.py +++ b/pyFAST/input_output/tests/test_hawc.py @@ -111,5 +111,5 @@ def test_HAWCStab2(self): self.assertEqual(DF.columns[1], 'Node_[-]') if __name__ == '__main__': - #Test().test_HAWC2_pc() + #Test().test_HAWC2_st() unittest.main() diff --git a/pyFAST/input_output/tests/test_run_Examples.py b/pyFAST/input_output/tests/test_run_Examples.py index 14faa56..c705186 100644 --- a/pyFAST/input_output/tests/test_run_Examples.py +++ b/pyFAST/input_output/tests/test_run_Examples.py @@ -19,12 +19,14 @@ def test_run_examples(self): # Add tests to class MyDir=os.path.dirname(__file__) files = glob.glob(os.path.join(MyDir,'../examples/[a-zA-Z]*.py')) + import matplotlib.pyplot as plt + print('\n--------------------------------------------------------------') for f in files: - print('\n--------------------------------------------------------------') print('Running example script: {}'.format(f)) if hasattr(self,'subTest'): with self.subTest(filename=os.path.basename(f)): execfile(f, {'__name__': '__test__', 'print': lambda *_:None}) + plt.close('all') if __name__ == '__main__': diff --git a/pyFAST/input_output/turbsim_file.py b/pyFAST/input_output/turbsim_file.py index 0f85f32..4dec8d9 100644 --- a/pyFAST/input_output/turbsim_file.py +++ b/pyFAST/input_output/turbsim_file.py @@ -200,6 +200,166 @@ def closestPoint(self, y, z): iz = np.argmin(np.abs(self['z']-z)) return iy,iz + def _longiline(ts, iy0=None, iz0=None, mean=True): + """ return velocity components on a longitudinal line + If no index is provided, computed at mid box + """ + if iy0 is None: + iy0,iz0 = ts._iMid() + u = ts['u'][0,:,iy0,iz0] + v = ts['u'][1,:,iy0,iz0] + w = ts['u'][2,:,iy0,iz0] + if not mean: + u -= np.mean(u) + v -= np.mean(v) + w -= np.mean(w) + return u, v, w + + def _latline(ts, ix0=None, iz0=None, mean=True): + """ return velocity components on a lateral line + If no index is provided, computed at mid box + """ + if ix0 is None: + iy0,iz0 = ts._iMid() + ix0=int(len(ts['t'])/2) + u = ts['u'][0,ix0,:,iz0] + v = ts['u'][1,ix0,:,iz0] + w = ts['u'][2,ix0,:,iz0] + if not mean: + u -= np.mean(u) + v -= np.mean(v) + w -= np.mean(w) + return u, v, w + + def _vertline(ts, ix0=None, iy0=None, mean=True): + """ return velocity components on a vertical line + If no index is provided, computed at mid box + """ + if ix0 is None: + iy0,iz0 = ts._iMid() + ix0=int(len(ts['t'])/2) + u = ts['u'][0,ix0,iy0,:] + v = ts['u'][1,ix0,iy0,:] + w = ts['u'][2,ix0,iy0,:] + if not mean: + u -= np.mean(u) + v -= np.mean(v) + w -= np.mean(w) + return u, v, w + + + def crosscorr_y(ts, iy0=None, iz0=None): + """ Cross correlation along y + If no index is provided, computed at mid box + """ + y = ts['y'] + if iy0 is None: + iy0,iz0 = ts._iMid() + u, v, w = ts._longiline(iy0=iy0, iz0=iz0, mean=False) + rho_uu_y=np.zeros(len(y)) + rho_vv_y=np.zeros(len(y)) + rho_ww_y=np.zeros(len(y)) + for iy,_ in enumerate(y): + ud, vd, wd = ts._longiline(iy0=iy, iz0=iz0, mean=False) + rho_uu_y[iy] = np.mean(u*ud)/(np.std(u)*np.std(ud)) + rho_vv_y[iy] = np.mean(v*vd)/(np.std(v)*np.std(vd)) + rho_ww_y[iy] = np.mean(w*wd)/(np.std(w)*np.std(wd)) + return y, rho_uu_y, rho_vv_y, rho_ww_y + + def crosscorr_z(ts, iy0=None, iz0=None): + """ + Cross correlation along z, mid box + If no index is provided, computed at mid box + """ + z = ts['z'] + if iy0 is None: + iy0,iz0 = ts._iMid() + u, v, w = ts._longiline(iy0=iy0, iz0=iz0, mean=False) + rho_uu_z = np.zeros(len(z)) + rho_vv_z = np.zeros(len(z)) + rho_ww_z = np.zeros(len(z)) + for iz,_ in enumerate(z): + ud, vd, wd = ts._longiline(iy0=iy0, iz0=iz, mean=False) + rho_uu_z[iz] = np.mean(u*ud)/(np.std(u)*np.std(ud)) + rho_vv_z[iz] = np.mean(v*vd)/(np.std(v)*np.std(vd)) + rho_ww_z[iz] = np.mean(w*wd)/(np.std(w)*np.std(wd)) + return z, rho_uu_z, rho_vv_z, rho_ww_z + + + def csd_longi(ts, iy0=None, iz0=None): + """ Compute cross spectral density + If no index is provided, computed at mid box + """ + import scipy.signal as sig + u, v, w = ts._longiline(iy0=iy0, iz0=iz0, mean=False) + t = ts['t'] + dt = t[1]-t[0] + fs = 1/dt + fc, chi_uu = sig.csd(u, u, fs=fs, scaling='density') #nperseg=4096, noverlap=2048, detrend='constant') + fc, chi_vv = sig.csd(v, v, fs=fs, scaling='density') #nperseg=4096, noverlap=2048, detrend='constant') + fc, chi_ww = sig.csd(w, w, fs=fs, scaling='density') #nperseg=4096, noverlap=2048, detrend='constant') + return fc, chi_uu, chi_vv, chi_ww + + def csd_lat(ts, ix0=None, iz0=None): + """ Compute lateral cross spectral density + If no index is provided, computed at mid box + """ + import scipy.signal as sig + u, v, w = ts._latline(ix0=ix0, iz0=iz0, mean=False) + t = ts['t'] + dt = t[1]-t[0] + fs = 1/dt + fc, chi_uu = sig.csd(u, u, fs=fs, scaling='density') #nperseg=4096, noverlap=2048, detrend='constant') + fc, chi_vv = sig.csd(v, v, fs=fs, scaling='density') #nperseg=4096, noverlap=2048, detrend='constant') + fc, chi_ww = sig.csd(w, w, fs=fs, scaling='density') #nperseg=4096, noverlap=2048, detrend='constant') + return fc, chi_uu, chi_vv, chi_ww + + def csd_vert(ts, ix0=None, iy0=None): + """ Compute vertical cross spectral density + If no index is provided, computed at mid box + """ + import scipy.signal as sig + t = ts['t'] + dt = t[1]-t[0] + fs = 1/dt + u, v, w = ts._vertline(ix0=ix0, iy0=iy0, mean=False) + u= u-np.mean(u) + v= v-np.mean(v) + w= w-np.mean(w) + fc, chi_uu = sig.csd(u, u, fs=fs, scaling='density') #nperseg=4096, noverlap=2048, detrend='constant') + fc, chi_vv = sig.csd(v, v, fs=fs, scaling='density') #nperseg=4096, noverlap=2048, detrend='constant') + fc, chi_ww = sig.csd(w, w, fs=fs, scaling='density') #nperseg=4096, noverlap=2048, detrend='constant') + return fc, chi_uu, chi_vv, chi_ww + + + def coherence_longi(ts, iy0=None, iz0=None): + """ Coherence on a longitudinal line for different delta y and delta z + compared to a given point with index iy0,iz0 + """ + import scipy.signal as sig + if iy0 is None: + iy0,iz0 = ts._iMid() + u, v, w = ts._longiline(iy0=iy0, iz0=iz0, mean=False) + y = ts['y'] + z = ts['z'] + diy=1 + dy=y[iy]-y[iy0] + # TODO + iy = iy0+diy + ud, vd, wd = ts._longiline(iy0=iy, iz0=iz0, mean=False) + fc, coh_uu_y1 = sig.coherence(u,ud, fs=fs) + _ , coh_vv_y1 = sig.coherence(v,vd, fs=fs) + _ , coh_ww_y1 = sig.coherence(w,wd, fs=fs) + + iy = iy+diy + ud, vd, wd = ts._longiline(iy0=iy, iz0=iz0, mean=False) + _ , coh_uu_y2 = sig.coherence(u,ud, fs=fs) + _ , coh_vv_y2 = sig.coherence(v,vd, fs=fs) + _ , coh_ww_y2 = sig.coherence(w,wd, fs=fs) + + + + def makePeriodic(self): """ Make the box periodic by mirroring it """ nDim, nt0, ny, nz = self['u'].shape @@ -284,15 +444,46 @@ def toDataFrame(self): m = np.mean(self['u'][:,:,iy,:], axis=1) s = np.std( self['u'][:,:,iy,:], axis=1) ti = s/m*100 - Cols=['z_[m]','u_[m/s]','v_[m/s]','w_[m/s]','sigma_u_[m/s]','sigma_v_[m/s]','sigma_w_[m/s]','TI_[%]'] + cols=['z_[m]','u_[m/s]','v_[m/s]','w_[m/s]','sigma_u_[m/s]','sigma_v_[m/s]','sigma_w_[m/s]','TI_[%]'] data = np.column_stack((self['z'],m[0,:],m[1,:],m[2,:],s[0,:],s[1,:],s[2,:],ti[0,:])) - dfs['VertProfile'] = pd.DataFrame(data = data ,columns = Cols) + dfs['VertProfile'] = pd.DataFrame(data = data ,columns = cols) # Mid time series u = self['u'][:,:,iy,iz] - Cols=['t_[s]','u_[m/s]','v_[m/s]','w_[m/s]'] + cols=['t_[s]','u_[m/s]','v_[m/s]','w_[m/s]'] data = np.column_stack((self['t'],u[0,:],u[1,:],u[2,:])) - dfs['MidLine'] = pd.DataFrame(data = data ,columns = Cols) + dfs['MidLine'] = pd.DataFrame(data = data ,columns = cols) + + # Mid crosscorr y + y, rho_uu_y, rho_vv_y, rho_ww_y = self.crosscorr_y() + cols = ['y_[m]', 'rho_uu_[-]','rho_vv_[-]','rho_ww_[-]'] + data = np.column_stack((y, rho_uu_y, rho_vv_y, rho_ww_y)) + dfs['Mid_xcorr_y'] = pd.DataFrame(data = data ,columns = cols) + + # Mid crosscorr z + z, rho_uu_z, rho_vv_z, rho_ww_z = self.crosscorr_z() + cols = ['z_[m]', 'rho_uu_[-]','rho_vv_[-]','rho_ww_[-]'] + data = np.column_stack((z, rho_uu_z, rho_vv_z, rho_ww_z)) + dfs['Mid_xcorr_z'] = pd.DataFrame(data = data ,columns = cols) + + # Mid csd + fc, chi_uu, chi_vv, chi_ww = self.csd_longi() + cols = ['f_[Hz]','chi_uu_[-]', 'chi_vv_[-]','chi_ww_[-]'] + data = np.column_stack((fc, chi_uu, chi_vv, chi_ww)) + dfs['Mid_csd_longi'] = pd.DataFrame(data = data ,columns = cols) + + # Mid csd + fc, chi_uu, chi_vv, chi_ww = self.csd_lat() + cols = ['f_[Hz]','chi_uu_[-]', 'chi_vv_[-]','chi_ww_[-]'] + data = np.column_stack((fc, chi_uu, chi_vv, chi_ww)) + dfs['Mid_csd_lat'] = pd.DataFrame(data = data ,columns = cols) + + # Mid csd + fc, chi_uu, chi_vv, chi_ww = self.csd_vert() + cols = ['f_[Hz]','chi_uu_[-]', 'chi_vv_[-]','chi_ww_[-]'] + data = np.column_stack((fc, chi_uu, chi_vv, chi_ww)) + dfs['Mid_csd_vert'] = pd.DataFrame(data = data ,columns = cols) + # Hub time series #try: