├── .gitattributes
├── .gitignore
├── .travis.yml
├── CACTUS.sln
├── CACTUS.vfproj
├── CMakeLists.txt
├── DAKOTA
├── Driver
│ └── CACTUS_Driver.py
├── Example1
│ ├── OptTSR.dak
│ ├── OptTSR_CL.out
│ ├── Opt_Data_Out.dat
│ ├── ParamTSR.dak
│ ├── Param_Data_Out.dat
│ └── TestVAWTNom.in
└── Opt_SubLevel_Driver
│ └── Opt_SubLevel_Driver.py
├── LICENSE
├── README.md
├── bin
└── .gitignore
├── doc
├── DAKOTA
│ ├── CACTUS_DAKOTA.docx
│ └── CACTUS_DAKOTA.pdf
├── compile.md
├── paper
│ ├── AIAA-2011-147-194.pdf
│ ├── Michelen_etal_METS2014.pdf
│ └── Wosnik et al CACTUS Model Eval HK Cross Flow Turbine SAND 2016 9787.pdf
└── user-guide
│ ├── User Guide.pdf
│ └── tex
│ ├── .gitignore
│ ├── User Guide.pdf
│ ├── User Guide.tex
│ ├── content
│ ├── appendix.tex
│ ├── details.tex
│ ├── input_description.tex
│ ├── introduction.tex
│ ├── normalization.tex
│ ├── output_description.tex
│ └── titlepage.tex
│ ├── figures
│ └── vortex_lattice.png
│ └── refs.bib
├── media
└── CACTUS.png
├── src
├── AeroCoeffs.f90
├── BGeomSetup.f90
├── BV_DynStall.f90
├── BladeIndVel.f90
├── BladeLoads.f90
├── CACTUS.f90
├── CalcBladeVel.f90
├── CalcFreestream.f90
├── CalcIndVel.f90
├── EndRev.f90
├── EndTS.f90
├── InputGeom.f90
├── LB_DynStall.f90
├── RotateTurbine.f90
├── SGeomSetup.f90
├── SetBoundWake.f90
├── UpdateBladeVel.f90
├── UpdateStrutLoads.f90
├── UpdateTowerVelocity.f90
├── UpdateWakeVel.f90
├── UpdateWall.f90
├── VorIVel.f90
├── WGeomSetup.f90
├── WSolnSetup.f90
├── WriteFieldData.f90
├── WriteFinalOutput.f90
├── WriteRegTOutput.f90
├── WriteWakeElementData.f90
├── WriteWallData.f90
├── bsload.f90
├── conlp.f90
├── input.f90
├── mod
│ ├── airfoil.f90
│ ├── blade.f90
│ ├── compiler.f90
│ ├── configr.f90
│ ├── dystl.f90
│ ├── element.f90
│ ├── fielddata.f90
│ ├── fnames.f90
│ ├── iecgust.f90
│ ├── output.f90
│ ├── parameters.f90
│ ├── pathseparator.f90
│ ├── probesystem.f90
│ ├── quadsourcepanel.f90
│ ├── regtest.f90
│ ├── shear.f90
│ ├── strut.f90
│ ├── time.f90
│ ├── tower.f90
│ ├── util
│ │ ├── geomutils.f90
│ │ ├── pidef.f90
│ │ ├── plot3d.f90
│ │ ├── util.f90
│ │ └── vecutils.f90
│ ├── varscale.f90
│ ├── vortex.f90
│ ├── wake.f90
│ ├── wakedata.f90
│ ├── wallgeom.f90
│ ├── wallsoln.f90
│ └── wallsystem.f90
└── shedvor.f90
└── test
├── Airfoil_Section_Data
├── N63_MCT_rR0p2.dat
├── N63_MCT_rR0p2_3DS.dat
├── N63_MCT_rR0p3.dat
├── N63_MCT_rR0p3_3DS.dat
├── N63_MCT_rR0p4.dat
├── N63_MCT_rR0p4_3DS.dat
├── N63_MCT_rR0p8.dat
├── N63_MCT_rR0p8_3DS.dat
├── NACA_0015.dat
├── NACA_0018.dat
├── NACA_0021.dat
├── Orig Section Data Files
│ ├── alsectr.dat
│ └── stall.dat
├── S809.dat
├── S809_UAE_rR0p3.dat
├── S809_UAE_rR0p45.dat
├── S809_UAE_rR0p6.dat
├── S809_UAE_rR0p8.dat
└── Sandia_001850.dat
├── RegTest
├── .gitignore
├── RegTest1.in
├── RegTest1_RegData_Ex.out
├── RegTest2.in
├── RegTest2_RegData_Ex.out
├── RegTest3.in
├── RegTest3_RegData_Ex.out
└── runreg.py
├── TestCase1
└── TestHAWT.in
├── TestCase2
├── TestVAWT.in
└── output_org
│ ├── TestVAWT_ElementData.csv
│ ├── TestVAWT_Param.csv
│ ├── TestVAWT_RevData.csv
│ └── TestVAWT_TimeData.csv
└── TestGeom
├── TestHAWT.geom
├── TestHAWT.m
├── TestVAWT.geom
└── TestVAWT.m
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | *.pdf binary
3 | *.doc binary
4 | *.docx binary
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | CM/
2 | *.o
3 | *.mod
4 | *~
5 | *.u2d
6 | *.suo
7 | *.DS_Store
8 | Thumbs.db
9 | ~*.docx
10 | test/RegTest/__pycache__
11 | build/
12 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: c
2 |
3 | jobs:
4 | include:
5 | - os: linux
6 | dist: bionic
7 | - os: osx
8 | osx_image: xcode10.2 # nasty hack to get Python 3 (see https://blog.travis-ci.com/2019-08-07-extensive-python-testing-on-travis-ci)
9 |
10 | addons:
11 | apt:
12 | - gfortran
13 | - libblas-dev
14 | - libopenblas-dev
15 | homebrew:
16 | casks:
17 | - gfortran
18 | packages:
19 | - openblas
20 | - lapack
21 |
22 | install:
23 | - sudo pip install pytest
24 |
25 | script:
26 | - mkdir -p build
27 | - cd build
28 | - cmake -DOPENMP=OFF ../
29 | - make
30 | - cd ..
31 | - bin/cactus
32 | - cd test/RegTest
33 | - PATH=$PATH:../../bin/ pytest runreg.py
34 |
--------------------------------------------------------------------------------
/CACTUS.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 11.00
3 | # Visual Studio 2010
4 | Project("{6989167D-11E4-40FE-8C1A-2192A86A7E90}") = "CACTUS", "CACTUS.vfproj", "{2CECD596-E387-4116-85A0-1C3EE0DC9F5A}"
5 | EndProject
6 | Global
7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
8 | Debug|Win32 = Debug|Win32
9 | Release|Win32 = Release|Win32
10 | EndGlobalSection
11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
12 | {2CECD596-E387-4116-85A0-1C3EE0DC9F5A}.Debug|Win32.ActiveCfg = Debug|Win32
13 | {2CECD596-E387-4116-85A0-1C3EE0DC9F5A}.Debug|Win32.Build.0 = Debug|Win32
14 | {2CECD596-E387-4116-85A0-1C3EE0DC9F5A}.Release|Win32.ActiveCfg = Release|Win32
15 | {2CECD596-E387-4116-85A0-1C3EE0DC9F5A}.Release|Win32.Build.0 = Release|Win32
16 | EndGlobalSection
17 | GlobalSection(SolutionProperties) = preSolution
18 | HideSolutionNode = FALSE
19 | EndGlobalSection
20 | EndGlobal
21 |
--------------------------------------------------------------------------------
/CACTUS.vfproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.11)
2 |
3 | project(CACTUS
4 | VERSION 0.0
5 | DESCRIPTION "CACTUS"
6 | HOMEPAGE_URL http://www.github.com/SNL-WaterPower/CACTUS
7 | LANGUAGES Fortran)
8 |
9 | # set compiler-specific options
10 | if(CMAKE_Fortran_COMPILER_ID MATCHES "GNU")
11 | set(dialect "-O2 -fdefault-real-8 -ffree-line-length-0 -cpp")
12 | set(bounds "-fbounds-check")
13 | endif()
14 | if(CMAKE_Fortran_COMPILER_ID MATCHES "Intel")
15 | set(dialect "-O2 -r8 -mkl -fpp")
16 | set(bounds "-check bounds")
17 | set(BLA_VENDOR Intel10_64lp) # force use of MKL BLAS
18 | endif()
19 | if(CMAKE_Fortran_COMPILER_ID MATCHES "PGI")
20 | set(dialect "-O -r8 -Mpreprocess -Mvect=sse -Mcache_align -Mpre -Mnoframe -Mlre -Mflushz -Mautoinline")
21 | set(bounds "-C -Mbounds")
22 | endif()
23 |
24 | # find LAPACK, OpenMP
25 | enable_language(C) # For compatibility, some systems complain about this
26 | find_package(LAPACK)
27 |
28 | option(OPENMP "Compile with OpenMP enabled?" ON)
29 | if(OPENMP MATCHES ON)
30 | find_package(OpenMP)
31 | if (NOT OpenMP_Fortran_FOUND)
32 | message(FATAL_ERROR "OPENMP option was set to true (default), but no OpenMP was found. Install OpenMP, or try again with -DOPENMP=OFF. Exiting.")
33 | endif()
34 | string(APPEND dialect " ${OpenMP_Fortran_FLAGS}")
35 | endif()
36 |
37 | list(APPEND CMAKE_Fortran_FLAGS_DEBUG ${bounds})
38 | list(APPEND CMAKE_Fortran_FLAGS ${dialect})
39 |
40 | # add VERSION variable
41 | # see https://cmake.org/pipermail/cmake/2018-October/068389.html
42 | find_package(Git QUIET)
43 |
44 | if(GIT_EXECUTABLE)
45 | execute_process(
46 | COMMAND "${GIT_EXECUTABLE}" describe --abbrev=4 --dirty --always --tags
47 | WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
48 | RESULT_VARIABLE res
49 | OUTPUT_VARIABLE CACTUS_VERSION
50 | # ERROR_QUIET
51 | OUTPUT_STRIP_TRAILING_WHITESPACE)
52 |
53 | set_property(GLOBAL APPEND
54 | PROPERTY CMAKE_CONFIGURE_DEPENDS
55 | "${CMAKE_SOURCE_DIR}/.git/index")
56 | else(NOT GIT_EXECUTABLE)
57 | # no git found
58 | set(CACTUS_VERSION "no-git")
59 | endif()
60 |
61 | # get appropriate path separator
62 | if(MINGW OR WIN32 OR MSYS)
63 | set(SEP "\\")
64 | else() # e.g. Linux
65 | set(SEP "/")
66 | endif()
67 |
68 | # add definitions
69 | add_definitions(-DVERSION="${CACTUS_VERSION}")
70 | add_definitions(-DPATHSEP="${SEP}")
71 |
72 | # create source lists
73 | file(GLOB_RECURSE mod_sources ${PROJECT_SOURCE_DIR}/mod/**.f90)
74 | file(GLOB_RECURSE prog_sources ${PROJECT_SOURCE_DIR}/src/*.f90)
75 |
76 | add_executable(cactus ${prog_sources} ${mod_sources})
77 | target_compile_options(cactus PUBLIC ${LAPACK_LINKER_FLAGS})
78 | if(MINGW OR WIN32 OR MSYS)
79 | target_link_libraries(cactus ${LAPACK_LIBRARIES} -static)
80 | else()
81 | target_link_libraries(cactus ${LAPACK_LIBRARIES})
82 | endif()
83 |
84 | # build to ./bin
85 | set_target_properties(cactus
86 | PROPERTIES
87 | RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin/")
88 |
--------------------------------------------------------------------------------
/DAKOTA/Example1/OptTSR.dak:
--------------------------------------------------------------------------------
1 | # Runs local surrogate based optimization on CACTUS. The local surrogate is fit to the CACTUS cost function using "global" sampling within a trust
2 | # region defined for the surrogate function. This keeps the method from having to use the slightly noisy CACTUS results in gradient calculations.
3 | # This method is faster (especially when using async parallel CACTUS evalutations in the fitting of the surrogate function) and more robust than
4 | # direct gradient optimization on CACTUS output.
5 | # Note: Apparently, surrogate optimization methods can only minimize a cost function (their trust region method fails for maximization)...
6 |
7 | strategy
8 | single_method
9 | tabular_graphics_data
10 | tabular_graphics_file = 'Opt_Data_Out.dat'
11 | method_pointer = 'SBLO'
12 |
13 | method
14 | id_method = 'SBLO'
15 | surrogate_based_local
16 | max_iterations = 50
17 | convergence_tolerance = 1e-02
18 | soft_convergence_limit = 3 # terminate after this number of surrogate model iterations with improvement less than tolerance
19 | trust_region
20 | initial_size = 0.5
21 | minimum_size = 1.0e-2
22 | output silent
23 | # Surrogate model and minimization method pointers
24 | model_pointer = 'SURROGATE'
25 | approx_method_pointer = 'NLP'
26 |
27 | method
28 | id_method = 'NLP'
29 | output silent
30 | dot_bfgs
31 | max_iterations = 50
32 | convergence_tolerance = 1e-8
33 |
34 | model
35 | id_model = 'SURROGATE'
36 | surrogate
37 | global # Surrogate fit with "global" sampling within trust region
38 | # Surrogate function
39 | #polynomial quadratic # simple
40 | gaussian_process surfpack # kriging
41 | # Total samples to take in fitting the surrogate
42 | total_points = 5 # oversample (more than recommended point count) to smooth noisy cost function output
43 | #recommended_points
44 | reuse_samples region # reuse samples that are still in the current trust region
45 | # Sampling method and responses pointers
46 | responses_pointer = 'SURROGATE_RESP'
47 | dace_method_pointer = 'SAMPLING'
48 | # Surrogate model fit diagnostics
49 | #diagnostics
50 |
51 | variables
52 | continuous_design = 1
53 | initial_point = 6
54 | lower_bounds = 2
55 | upper_bounds = 10
56 | descriptors = 'Ut'
57 | continuous_state = 1
58 | initial_state = 52
59 | descriptors = 'RPM'
60 |
61 | responses
62 | id_responses = 'SURROGATE_RESP'
63 | num_objective_functions = 1
64 | # Must apply CACTUS descriptors to the surrogate responses as well as the true responses as these descriptors
65 | # somehow propagate to, and overwrite, the true response descriptors in the input files passed to the analysis driver (probably a bug)...
66 | descriptors = 'CostFunc_MaxCP'
67 | numerical_gradients
68 | method_source dakota
69 | interval_type central
70 | fd_gradient_step_size = 1.e-6
71 | no_hessians
72 |
73 | method
74 | id_method = 'SAMPLING'
75 | model_pointer = 'TRUTH'
76 | dace lhs
77 | seed = 1000
78 | # Set samples to minimum number of new truth model samples to be added to the
79 | # surrogate model basis on each surrogate model iteration. Do a couple per dimension
80 | # to hedge against directional bias potentially introduced by reusing samples...
81 | samples = 2
82 |
83 | model
84 | id_model = 'TRUTH'
85 | single
86 | interface_pointer = 'TRUE_FN'
87 | responses_pointer = 'TRUE_RESP'
88 |
89 | interface
90 | id_interface = 'TRUE_FN'
91 | analysis_drivers = '../Driver/CACTUS_Driver.py'
92 | analysis_components = '/home/jmurray/Project/CACTUS/stable/cactus' 'TestVAWTNom.in' 'N'
93 | fork
94 | parameters_file = 'Inputs.in'
95 | results_file = 'Outputs.out'
96 | # need to tag files for async calculation
97 | file_tag
98 | asynchronous
99 | evaluation_concurrency = 10
100 |
101 | responses
102 | id_responses = 'TRUE_RESP'
103 | num_objective_functions = 1
104 | descriptors = 'CostFunc_MaxCP'
105 | no_gradients
106 | no_hessians
107 |
108 |
--------------------------------------------------------------------------------
/DAKOTA/Example1/Opt_Data_Out.dat:
--------------------------------------------------------------------------------
1 | %iter_no Ut CostFunc_MaxCP
2 | 1 6 -0.5131645
3 | 2 6.30782117 -0.5164985
4 | 3 6.30782117 -0.5164985
5 | 4 6.30782117 -0.5164985
6 |
--------------------------------------------------------------------------------
/DAKOTA/Example1/ParamTSR.dak:
--------------------------------------------------------------------------------
1 | # DAKOTA parameter study on CACTUS
2 | strategy
3 | tabular_graphics_data
4 | tabular_graphics_file = 'Param_Data_Out.dat'
5 | single_method
6 |
7 | method
8 | multidim_parameter_study
9 | partitions = 16 0 0
10 |
11 | model
12 | single
13 |
14 | variables
15 | continuous_design = 1
16 | lower_bounds = 2
17 | upper_bounds = 10
18 | descriptors = 'Ut'
19 | continuous_state = 2
20 | lower_bounds = 16 0
21 | upper_bounds = 16 0
22 | descriptors = 'nti' 'iut'
23 |
24 | interface
25 | analysis_drivers = '../Driver/CACTUS_Driver.py'
26 | analysis_components = '/home/jmurray/Project/CACTUS/stable/cactus' 'TestVAWTNom.in' 'N'
27 | fork
28 | parameters_file = 'Inputs.in'
29 | results_file = 'Outputs.out'
30 | # need to tag files for async calculation
31 | file_tag
32 | asynchronous
33 | evaluation_concurrency = 10
34 |
35 | responses
36 | num_response_functions = 2
37 | descriptors = 'Cp' 'CostFunc_MaxCP'
38 | no_gradients
39 | no_hessians
40 |
41 |
--------------------------------------------------------------------------------
/DAKOTA/Example1/Param_Data_Out.dat:
--------------------------------------------------------------------------------
1 | %eval_id Ut nti iut Cp CostFunc_MaxCP
2 | 1 2 16 0 0.09752095 -0.09752095
3 | 2 2.5 16 0 0.1868504 -0.1868504
4 | 3 3 16 0 0.2996178 -0.2996178
5 | 4 3.5 16 0 0.3673699 -0.3673699
6 | 5 4 16 0 0.4183855 -0.4183855
7 | 6 4.5 16 0 0.4504832 -0.4504832
8 | 7 5 16 0 0.4760848 -0.4760848
9 | 8 5.5 16 0 0.4938385 -0.4938385
10 | 9 6 16 0 0.5131645 -0.5131645
11 | 10 6.5 16 0 0.5157003 -0.5157003
12 | 11 7 16 0 0.5107957 -0.5107957
13 | 12 7.5 16 0 0.5040709 -0.5040709
14 | 13 8 16 0 0.486893 -0.486893
15 | 14 8.5 16 0 0.4607304 -0.4607304
16 | 15 9 16 0 0.4259402 -0.4259402
17 | 16 9.5 16 0 0.3863714 -0.3863714
18 | 17 10 16 0 0.3379339 -0.3379339
19 |
--------------------------------------------------------------------------------
/DAKOTA/Example1/TestVAWTNom.in:
--------------------------------------------------------------------------------
1 | &ConfigInputs
2 |
3 | GPFlag = 0
4 |
5 | nr = 10
6 | nti = 16
7 | convrg = .0001
8 | iut = 0
9 |
10 | ifc = 0
11 | ixterm = 0
12 |
13 | ntif = 16
14 | iutf = 1
15 | nric = 9
16 | convrgf = .0001
17 |
18 | /End
19 |
20 |
21 | &CaseInputs
22 |
23 | jbtitle = 'Test VAWT'
24 |
25 | rho = .002378
26 | vis = .3739E-6
27 | tempr = 60.0
28 | hBLRef = 56.57
29 | slex = 0.0
30 | hAG = 15.0
31 |
32 | RPM = 52.0
33 | Ut = 5.0
34 |
35 | ! Turbine geometry
36 | GeomFilePath='../../Test/TestGeom/TestVAWT.geom'
37 |
38 | ! Airfoil section data
39 | nSect = 1
40 | AFDPath = '../../Airfoil_Section_Data/NACA_0015.dat'
41 |
42 | /End
43 |
44 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2013, Sandia Corporation, under the terms of Contract DE-AC04-94AL85000. The U.S. Government retains certain rights in this software.
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | 3. Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | # CACTUS (Code for Axial and Cross-flow TUrbine Simulation)
3 |
4 | CACTUS (**C**ode for **A**xial and **C**ross-flow **TU**rbine **S**imulations),
5 | developed at Sandia National Laboratories, is a turbine simulation code based on a free wake vortex method.
6 |
7 |
8 | ### Compiling
9 |
10 | CACTUS can be compiled via CMake. For details, see [doc/compile.md](doc/compile.md)
11 |
12 | #### Tests
13 | Simple regression tests are included. After compiling, navigate to `test/RegTest/` and run:
14 |
15 | ```
16 | PATH=$PATH:../../bin pytest runreg.py
17 | ```
18 |
19 | ### Directory Structure
20 |
21 | - `bin`: Default target for compiled executables
22 | - `DAKOTA`: DAKOTA drivers (by Jon Murray) and examples
23 | - `doc`: Documentation -- user's manual, install instructions, DAKOTA drivers manual, relevant publications
24 | - `make`: Makefiles for various compilers and platforms
25 | - `src`: Source code
26 | - `test`: Test cases (regression tests, example HAWT/VAWT input files, airfoil files)
27 |
28 | ### Post-processing
29 |
30 | Tools for post-processing data from CACTUS simulations are available in the
31 | [CACTUS-tools](https://github.com/SNL-WaterPower/CACTUS-tools) repository.
32 |
33 |
34 | ### References
35 |
36 | For details about the development of CACTUS, please see
37 |
38 | - Murray, J., and Barone, M., “The Development of CACTUS, a Wind and Marine Turbine Performance Simulation Code,” _49th AIAA Aerospace Sciences Meeting including the New Horizons Forum and Aerospace Exposition_, Reston, Virginia: American Institute of Aeronautics and Astronautics, 2011, pp. 1–21.
39 |
40 | ### Disclaimer
41 |
42 | A CACTUS model V&V studies (Michelen et al. 2014, Wosnik et al. 2016) for cross-flow hydrokinetic turbines demonstrated it accurately predicts performance characteristics for axial-flow turbines, but it should not be used for cross-flow geometries.
43 |
44 | - Michelen, C., V.S. Neary, J. Murray, and M. Barone, M. (2014). CACTUS open-source code for hydrokinetic turbine design and analysis: Model performance evaluation and public dissemination as open-source tool. Proceedings of 2nd Marine Energy Technology Symposium 2014 (METS2014), at the 7th Annual Global Marine Renewable Energy Conference (GMREC 2014), Seattle, WA, April 15-18.
45 |
46 | - Wosnik M., Bachant P., Neary V.S., and A.W. Murphy (2016). Evaluation of Design & Analysis Code, CACTUS, for Predicting Cross-flow Hydrokinetic Turbine Performance. SAND2016-9787, September 2016. 34 pages.
47 |
48 |
--------------------------------------------------------------------------------
/bin/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/doc/DAKOTA/CACTUS_DAKOTA.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sandialabs/CACTUS/4ae4fafdf1d598428d9df43f1951a80170dc7780/doc/DAKOTA/CACTUS_DAKOTA.docx
--------------------------------------------------------------------------------
/doc/DAKOTA/CACTUS_DAKOTA.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sandialabs/CACTUS/4ae4fafdf1d598428d9df43f1951a80170dc7780/doc/DAKOTA/CACTUS_DAKOTA.pdf
--------------------------------------------------------------------------------
/doc/compile.md:
--------------------------------------------------------------------------------
1 | # Compiling CACTUS
2 |
3 | ## General instructions
4 | Compilation requires CMake, which can be installed by your OS package manager, or manually (see https://cmake.org/install/)
5 |
6 | 1. Clone the repo, or download all source files to a folder.
7 |
8 | 2. In the root of the repo, create a `build/` directory, and run `cmake`/`make`
9 | ```
10 | mkdir -p build
11 | cd build
12 | cmake ../
13 | make
14 | ```
15 | This will produce a `cactus` executable in the `bin/` folder.
16 | Support for OpenMP may be disabled by adding the `DOPENMP=OFF` flag to the `cmake` call, as below.
17 | ```
18 | cmake -DOPENMP=OFF ../
19 | ```
20 |
21 | 3. The compilation can be tested with the bundled regression tests.
22 | Navigate into the `test/RegTest/` and run
23 | ```
24 | PATH=$PATH:../../bin/ pytest ./runreg.py
25 | ```
26 | This requires a Python 3 installation with the `pytest` package installed.
27 |
28 | ## Windows Tips
29 | For Windows, it is recommended to compile in MingW under MSYS2.
30 |
31 | 1. Install MSYS2 (https://www.msys2.org/)
32 | 2. Open MingW terminal (likely located at `C:\tools\msys64\mingw64.exe`)
33 | 3. Install the following packages using `pacman`
34 | ```
35 | pacman -Sy git make cmake mingw-w64-x86_64-gcc-fortran mingw-w64-x86_64-lapack
36 | ```
37 | 4. Follow the general install instructions above.
38 |
39 | For MSYS builds, the executables will be statically linked for portability.
40 |
--------------------------------------------------------------------------------
/doc/paper/AIAA-2011-147-194.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sandialabs/CACTUS/4ae4fafdf1d598428d9df43f1951a80170dc7780/doc/paper/AIAA-2011-147-194.pdf
--------------------------------------------------------------------------------
/doc/paper/Michelen_etal_METS2014.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sandialabs/CACTUS/4ae4fafdf1d598428d9df43f1951a80170dc7780/doc/paper/Michelen_etal_METS2014.pdf
--------------------------------------------------------------------------------
/doc/paper/Wosnik et al CACTUS Model Eval HK Cross Flow Turbine SAND 2016 9787.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sandialabs/CACTUS/4ae4fafdf1d598428d9df43f1951a80170dc7780/doc/paper/Wosnik et al CACTUS Model Eval HK Cross Flow Turbine SAND 2016 9787.pdf
--------------------------------------------------------------------------------
/doc/user-guide/User Guide.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sandialabs/CACTUS/4ae4fafdf1d598428d9df43f1951a80170dc7780/doc/user-guide/User Guide.pdf
--------------------------------------------------------------------------------
/doc/user-guide/tex/.gitignore:
--------------------------------------------------------------------------------
1 | ## Core latex/pdflatex auxiliary files:
2 | *.aux
3 | *.lof
4 | *.log
5 | *.lot
6 | *.fls
7 | *.out
8 | *.toc
9 |
10 | ## Intermediate documents:
11 | *.dvi
12 | *-converted-to.*
13 | # these rules might exclude image files for figures etc.
14 | # *.ps
15 | # *.eps
16 | # *.pdf
17 |
18 | ## Bibliography auxiliary files (bibtex/biblatex/biber):
19 | *.bbl
20 | *.bcf
21 | *.blg
22 | *-blx.aux
23 | *-blx.bib
24 | *.brf
25 | *.run.xml
26 |
27 | ## Build tool auxiliary files:
28 | *.fdb_latexmk
29 | *.synctex
30 | *.synctex.gz
31 | *.synctex.gz(busy)
32 | *.pdfsync
33 |
--------------------------------------------------------------------------------
/doc/user-guide/tex/User Guide.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sandialabs/CACTUS/4ae4fafdf1d598428d9df43f1951a80170dc7780/doc/user-guide/tex/User Guide.pdf
--------------------------------------------------------------------------------
/doc/user-guide/tex/User Guide.tex:
--------------------------------------------------------------------------------
1 | % !TEX TS-program = pdflatex
2 | % !TEX encoding = UTF-8 Unicode
3 |
4 | % Example of the Memoir class, an alternative to the default LaTeX classes such as article and book, with many added features built into the class itself.
5 |
6 | \documentclass[12pt,a4paper,oneside,obeyspaces]{memoir} % for a long document
7 | % \documentclass[12pt,a4paper,article]{memoir} % for a short document
8 | \usepackage[utf8]{inputenc} % set input encoding to utf8
9 | \usepackage[hidelinks]{hyperref}
10 | \usepackage{listings}
11 | \usepackage{longtable}
12 | \usepackage{cleveref}
13 | \usepackage{graphicx}
14 | \usepackage{amsmath}
15 | \usepackage{color}
16 | \usepackage{booktabs}
17 | \usepackage[table]{xcolor} % to highlight table rows
18 | \usepackage{url} % for typesetting file paths
19 | \usepackage{siunitx} % for units
20 |
21 | % Don't forget to read the Memoir manual: memman.pdf
22 |
23 | \definecolor{highlightcolor}{rgb}{.95,.95,1.0} % highlight color for table rows
24 | \definecolor{lstcolor}{rgb}{.95,.95,0.95} % background color for listings
25 |
26 | % set the options for listing
27 | \sbox0{\small\ttfamily A}
28 | \edef\mybasewidth{\the\wd0 }
29 | \lstset{
30 | basicstyle=\small\ttfamily,% print whole listing small
31 | columns=fixed,
32 | basewidth=\mybasewidth,
33 | breaklines=true, % break lines
34 | postbreak=\raisebox{0ex}[0ex][0ex]{\ensuremath{\color{red}\hookrightarrow\space}}, % add a red arrow at new broken line
35 | backgroundcolor=\color{lstcolor}, % background color of listing box
36 | framexleftmargin=6pt, % padding
37 | framextopmargin=6pt, % padding
38 | framexrightmargin=6pt, % padding
39 | framexbottommargin=6pt, % padding
40 | frame=tb, framerule=0pt, % padding
41 | }
42 | \newsavebox\lstbox
43 |
44 | % set up appendix
45 | \renewcommand*{\cftappendixname}{Appendix\space}
46 |
47 | % set up command to stretch table height
48 | \newcommand{\ra}[1]{\renewcommand{\arraystretch}{#1}}
49 |
50 | % set up command for degree fahrehnheit
51 | \DeclareSIUnit\Fahrenheit{\degree F}
52 |
53 | %%% Examples of Memoir customization
54 | %%% enable, disable or adjust these as desired
55 |
56 | %%% PAGE DIMENSIONS
57 | % Set up the paper to be as close as possible to both A4 & letter:
58 | \settrimmedsize{11in}{210mm}{*} % letter = 11in tall; a4 = 210mm wide
59 | \setlength{\trimtop}{0pt}
60 | \setlength{\trimedge}{\stockwidth}
61 | \addtolength{\trimedge}{-\paperwidth}
62 | \settypeblocksize{22cm}{16cm}{*}
63 | \setulmargins{*}{*}{*} % 50pt upper margins
64 | \setlrmargins{*}{*}{1} % set the margins to be equal
65 | \checkandfixthelayout
66 |
67 | % This is from memman.pdf
68 |
69 | %%% ToC (table of contents) APPEARANCE
70 | \maxtocdepth{subsection} % include subsections
71 | \renewcommand{\cftchapterpagefont}{}
72 | % \renewcommand{\cftchapterfont}{} % no bold!
73 |
74 | %%% HEADERS & FOOTERS
75 | \pagestyle{ruled} % try also: empty , plain , headings , ruled , Ruled , companion
76 |
77 | %%% CHAPTERS
78 | \chapterstyle{hangnum} % try also: default , section , hangnum , companion , article, demo
79 |
80 | \renewcommand{\chaptitlefont}{\Huge\sffamily\raggedright} % set sans serif chapter title font
81 | \renewcommand{\chapnumfont}{\Huge\sffamily\raggedright} % set sans serif chapter number font
82 |
83 | %%% SECTIONS
84 | \hangsecnum % hang the section numbers into the margin to match \chapterstyle{hangnum}
85 | \maxsecnumdepth{subsection} % number subsections
86 |
87 | \setsecheadstyle{\Large\sffamily\raggedright} % set sans serif section font
88 | \setsubsecheadstyle{\large\sffamily\raggedright} % set sans serif subsection font
89 |
90 | %% END Memoir customization
91 |
92 | %%% BEGIN DOCUMENT
93 | \begin{document}
94 |
95 | % set bib style
96 | \bibliographystyle{plain}
97 |
98 | % render titlepage
99 | \input{./content/titlepage.tex}
100 |
101 | \tableofcontents % the asterisk means that the contents itself isn't put into the ToC
102 | \listoffigures
103 | \listoftables
104 |
105 | \cleartorecto % new page before the first chapter
106 | \ra{1.3} % set the table row spacing for all chapters
107 | \input{./content/introduction.tex}
108 | \input{./content/normalization.tex}
109 | \input{./content/input_description.tex}
110 | \input{./content/output_description.tex}
111 | \input{./content/details.tex}
112 |
113 | \bibliography{refs}
114 | \appendix
115 | \input{./content/appendix.tex}
116 |
117 | \end{document}
--------------------------------------------------------------------------------
/doc/user-guide/tex/content/appendix.tex:
--------------------------------------------------------------------------------
1 | %!TEX root = ../User Guide.tex
2 | \chapter{Example geometry script}
3 | \label{sec:example_geometry_script}
4 | An example geometry creation MATLAB script for is provided here for a vertical axis wind turbine. This example can also be found in Test/TestGeom/TestVAWT.m in the CACTUS repository.
5 |
6 | \begin{lstlisting}[language=matlab]
7 | clear
8 | close all
9 |
10 | % Creates test VAWT geometry file
11 |
12 | % Add geom creation scripts to path
13 | path(path,'../../CreateGeom');
14 |
15 | % Params
16 | R=31.5; % Center radius (ft)
17 | HR=2.64; % Height to radius ratio
18 | CRr=0.07408; % Root chord to radius
19 | eta=.42; % Blade mount point ratio (mount point behind leading edge as a fraction of chord)
20 | NBlade=2;
21 | NBElem=5;
22 | NStrut=2; % number of struts
23 | NSElem=5;
24 | CRs=CRr; % strut chord to radius
25 | TCs=.15; % strut thickness to chord
26 |
27 | % Output filename
28 | FN='TestVAWT.geom';
29 |
30 | % Plot data?
31 | PlotTurbine=1;
32 |
33 | % Convert
34 | dToR=pi/180;
35 |
36 | % Create basic parabolic blade VAWT
37 | Type='VAWT';
38 | BShape=1;
39 | T=CreateTurbine(NBlade,NBElem,NStrut,NSElem,R,[],[],[],Type,1,CRr,HR,eta,BShape,CRs,TCs);
40 |
41 | % Write geom file
42 | WriteTurbineGeom(FN,T);
43 |
44 | % Plot if desired
45 | if PlotTurbine
46 |
47 | % Plot animated turbine rotation
48 | XLim=[-4,4];
49 | YLim=[-2,4];
50 | ZLim=[-4,4];
51 |
52 | % Plot controls
53 | PlotVec=1;
54 | SFVec=.5;
55 | Trans=.5;
56 |
57 | hf=figure(1);
58 | set(hf,'Position',[303 124 956 610])
59 | set(gca,'Position',[5.2743e-002 5.1245e-002 8.9979e-001 8.8141e-001])
60 | set(gca,'CameraPosition',[-52.1999 30.4749 62.2119])
61 | set(gca,'CameraUpVector',[1.8643e-001 9.7433e-001 -1.2615e-001])
62 | set(gca,'CameraViewAngle',6.3060e+000)
63 | grid on
64 | set(gcf,'Color','white');
65 | hl=light('Position',[-1,0,0]);
66 | set(gca,'Color','white');
67 | set(gca,'DataAspectRatio',[1,1,1])
68 | set(gca,'XLim',XLim,'YLim',YLim,'ZLim',ZLim)
69 |
70 | HIn=[];
71 | PhasePlot=linspace(0,2*pi,150);
72 | for i=1:length(PhasePlot)
73 | H=PlotTurbineGeom(T,hf,PhasePlot(i),HIn,Trans,PlotVec,SFVec);
74 | HIn=H;
75 | pause(.01);
76 | end
77 |
78 | end
79 | \end{lstlisting}
--------------------------------------------------------------------------------
/doc/user-guide/tex/content/details.tex:
--------------------------------------------------------------------------------
1 | %!TEX root = ../User Guide.tex
2 | \chapter{Details}
3 | \section{OpenMP acceleration}
4 | OpenMP acceleration is enabled in the main Biot-Savart calculation loop as well as in the calculation of field velocities as described in 5.5. OpenMP is enabled by default, provided the appropriate flags are selected during compilation and that the OpenMP libraries are properly linked.
5 |
6 | If OpenMP is enabled, CACTUS will write to standard out a few lines stating so. If CACTUS has not been compiled correctly with OpenMP flags and libraries, or if OpenMP is otherwise disabled, those lines about OpenMP will be omitted.
7 |
8 | If the number of threads displayed is less than the number of cores/threads available on the machine, check that the environment variable \path{OMP_NUM_THREADS} is correctly set.
9 |
10 | \begin{lstlisting}[language=bash]
11 | [phil@localhost CACTUS-CK]$ ./bin/cactus
12 | Starting CACTUS Execution.
13 | --------------------------
14 | OpenMP is Enabled.
15 | Executing with 2 threads.
16 |
17 |
18 | Please call the program with the name of the input file on the command line. Ex. CACTUS INPUTFILE.in
19 | \end{lstlisting}
--------------------------------------------------------------------------------
/doc/user-guide/tex/content/introduction.tex:
--------------------------------------------------------------------------------
1 | %!TEX root = ../User Guide.tex
2 | \chapter{Introduction and overview}
3 | CACTUS (\textbf{C}ode for \textbf{A}xial and \textbf{C}rossflow \textbf{TU}rbine \textbf{S}imulation) is a turbine performance simulation code using a blade element discretization and a free vortex line description of the turbine wake. The code was originally based on \path{VDART3}, a free vortex wake simulation of the Darrieus wind turbine, developed by Strickland \cite{Strickland1979}. The codebase has been largely upgraded to the Fortran 9x standard, and a number of modifications have been made to the original \path{VDART3} methods including updates to the blade loads models, and new models to handle generic device geometry and marine turbine specific physics.
4 |
5 | This document serves as a user's manual for CACTUS. A brief overview of the code is provided below, however, more detailed information on the methods used can be found in \cite{Murray2011}.
6 |
7 | \section{Blade loads and wake models}
8 | CACTUS simulates a turbine device consisting of an arbitrary configuration of blade element sections. Each section can be assigned arbitrary load coefficient vs. angle of attack characteristics, which typically correspond to two-dimensional lift and drag coefficient data for a particular foil section. Since data from two-dimensional wind tunnel tests or foil performance calculations are used to represent element loads, it is generally assumed that these elements are in locally two-dimensional flow.
9 |
10 | A rotor blade consisting of an arbitrary planform shape and foil sections can be modeled by the synthesis of a number of blade elements. The blade loads and wake of the turbine rotor are evolved in time over a certain number of rotor revolutions, until the revolution-averaged rotor power is converged. The code output includes the blade aerodynamic forces, wake vortex trajectories, and performance metrics such as torque and power.
11 |
12 | CACTUS uses a potential flow model comprised of free vortex line elements to represent the turbine wake flow field. The vortex line structure attached to a single blade element is shown in \Cref{fig:vortex_lattice}. At each point in time, the bound vorticity ($\Gamma_B$) on each blade element is related to the element lift coefficient through the Kutta-Joukowski theorem, and the spanwise ($\Gamma_S$) and trailing vorticity ($\Gamma_T$) are recovered through the application of the Helmholz theorem of conservation of circulation along a vortex line \cite{katz2001low}.
13 |
14 | \begin{figure}
15 | \includegraphics{figures/vortex_lattice.png}
16 | \label{fig:vortex_lattice}
17 | \caption{Blade element with associated vortex lattice system.}
18 | \end{figure}
19 |
20 | \subsection{Dynamic blade loads}
21 | The operational cycle of some turbines, most notably cross-flow turbines or axial flow turbines in yawed flow conditions, cause the turbine blades to operate in dynamically variable flow conditions. The effects of blade rotation with respect to the surrounding fluid and the effects of dynamically variable flow angle of attack are captured with additional models.
22 |
23 | The effects of blade section pitch rate (rotation around an axis normal to the section plane) are captured by analogy to an analytical solution for a pitching flat plate. Improvements have been made to the original methodology used in \path{VDART3}, and modifications have been made to handle non-zero section pitching moment due to cambered foil sections.
24 |
25 | Under certain operational conditions, the turbine blades may operate at angles of attack beyond their steady-state stall limits for significant lengths of time. The transient behavior of the blade section loads during this ``dynamic stalling'' process must be modeled as it is not captured by the steady load coefficient data input for each foil section. The primary effect of dynamic stalling is a delay in the appearance of stalled flow effects on blade loads to higher angles of attack than would be expected in steady flow.
26 |
27 | Two models for dynamic stall effects on blade section loads are included in CACTUS. The modified Boeing-Vertol method of Gormont is the default. This algebraic method approximates dynamic stall effects with a ``lagged'' angle of attack, where the magnitude of the lag is empirically correlated to the angle of attack rate. The Leishman-Beddoes model incorporates more physical models and attempts to model the temporal evolution of dynamic stall flow phenomena and associated effects on blade loads. This model may provide more accurate results than the algebraic Boeing-Vertol method, but requires many more simulation time steps to be taken per turbine revolution to achieve converged results.
28 |
29 | \section{Solid and free-surface boundaries}
30 | CACTUS can simulate the effects of proximity to a ground plane or free (water) surface on turbine performance. The boundary conditions, either zero normal flow for a ground plane or constant surface pressure for a free surface, are applied using rectangular source panel elements. The free surface boundary condition is currently implemented as a quasi-static boundary, allowing it to respond only to the average flow created by the turbine and wake over a full revolution.
31 |
32 | The user is allowed to specify the time step interval between updates to the wall panel system. For the free surface model, the wall update interval specifies the number of time steps between updates to the revolution averaged quantities. It is often not necessary to update these quantities on every time step. If it's possible to reduce the frequency of wall panel system updates and still obtain convergence of the simulation output of interest, this can reduce simulation run time considerably.
33 |
34 | A recent addition to CACTUS is the ability to model \emph{general} solid boundaries, or walls, using generalized quadrilateral source panels. A 3-D mesh file describing the wall geometry must be generated externally and passed as an input to CACTUS.
--------------------------------------------------------------------------------
/doc/user-guide/tex/content/normalization.tex:
--------------------------------------------------------------------------------
1 | %!TEX root = ../User Guide.tex
2 | \chapter{Normalization parameters}
3 | Some definitions of parameters used in normalize CACTUS input and output parameters are given in \Cref{tbl:normalization_parameters}.
4 |
5 | \begin{table}[!htbp]
6 | \centering
7 | \caption{Parameters used for non-dimensionalization.}
8 | \label{tbl:normalization_parameters}
9 | \begin{tabular}{p{0.20\textwidth}p{0.70\textwidth}}
10 | \toprule
11 | Variable & Description \\
12 | \midrule
13 | $\rho$ & Fluid density \\
14 | $U_\infty$ & Freestream fluid flow speed \\
15 | $A_T$ & Turbine reference area. Typically this reference area is chosen to be the projected frontal area of the volume swept by the rotor. \\
16 | $R$ & Turbine reference radius \\
17 | $\omega$ & Turbine rotation rate \\
18 | $U_\textrm{local}$& Fluid flow speed local to an element. \\
19 | $U_\textrm{tip}$ & Turbine tip speed. Defined as $U_\textrm{tip} = \omega R$. \\
20 | $c$ & Element chord length \\
21 | $A_E$ & Element planform area \\
22 | \bottomrule
23 | \end{tabular}
24 | \end{table}
25 |
26 | In general, most parameters have been normalized at the machine scale. Unless otherwise noted in the input and output descriptions below, the force, torque, and power coefficients are normalized as:
27 |
28 | $$ C_F = \frac{F}{\frac{1}{2} \rho U_\infty^2 A_T} $$
29 | $$ C_T = \frac{T}{\frac{1}{2} \rho U_\infty^2 A_T R} $$
30 | $$ C_P = \frac{P}{\frac{1}{2} \rho U_\infty^3 A_T} $$
--------------------------------------------------------------------------------
/doc/user-guide/tex/content/titlepage.tex:
--------------------------------------------------------------------------------
1 | %!TEX root = ../User Guide.tex
2 | \begin{titlingpage}
3 | \clearpage\thispagestyle{empty}
4 |
5 | \begin{center}
6 | {\sffamily\huge CACTUS User Guide\\}
7 | % ----------------------------------------------------------------
8 | \vspace{1.5cm}
9 | {\Large Jonathan C. Murray \\ Aerosciences Department}\\[5pt]
10 |
11 | \vspace{1.5cm}
12 | {\Large Matthew Barone \\ Wind Energy Technologies}\\[5pt]
13 |
14 | \vspace{1.5cm}
15 | {\Large Phillip K. Chiu \\ Wind Energy Technologies}\\[5pt]
16 | % ----------------------------------------------------------------
17 | \vspace{8cm}
18 | {\Large Sandia National Laboratories \\
19 | P.O. Box 5800 \\
20 | Albuquerque, NM 87185 \\}
21 | % ----------------------------------------------------------------
22 | \end{center}
23 | \end{titlingpage}
--------------------------------------------------------------------------------
/doc/user-guide/tex/figures/vortex_lattice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sandialabs/CACTUS/4ae4fafdf1d598428d9df43f1951a80170dc7780/doc/user-guide/tex/figures/vortex_lattice.png
--------------------------------------------------------------------------------
/doc/user-guide/tex/refs.bib:
--------------------------------------------------------------------------------
1 | @inproceedings{Murray2011,
2 | address = {Reston, Virginia},
3 | author = {Murray, Jonathan and Barone, Matthew},
4 | booktitle = {49th AIAA Aerospace Sciences Meeting including the New Horizons Forum and Aerospace Exposition},
5 | doi = {10.2514/6.2011-147},
6 | file = {:/snl/Home/pchiu/papers/AIAA-2011-147-194.pdf:pdf},
7 | isbn = {978-1-60086-950-1},
8 | mendeley-groups = {Sandia,Prospectus},
9 | month = jan,
10 | number = {January},
11 | pages = {1--21},
12 | publisher = {American Institute of Aeronautics and Astronautics},
13 | title = {{The Development of CACTUS, a Wind and Marine Turbine Performance Simulation Code}},
14 | url = {http://arc.aiaa.org/doi/abs/10.2514/6.2011-147},
15 | year = {2011}
16 | }
17 | @book{katz2001low,
18 | author = {Katz, Joseph and Plotkin, Allen},
19 | publisher = {Cambridge University Press},
20 | title = {{Low-speed aerodynamics}},
21 | year = {2001}
22 | }
23 | @article{Strickland1979,
24 | author = {Strickland, J. H. and Webster, B. T. and Nguyen, T.},
25 | doi = {10.1115/1.3449018},
26 | issn = {00982202},
27 | journal = {Journal of Fluids Engineering},
28 | number = {4},
29 | pages = {500},
30 | title = {{A Vortex Model of the Darrieus Turbine: An Analytical and Experimental Study}},
31 | url = {http://fluidsengineering.asmedigitalcollection.asme.org/article.aspx?articleid=1424807},
32 | volume = {101},
33 | year = {1979}
34 | }
--------------------------------------------------------------------------------
/media/CACTUS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sandialabs/CACTUS/4ae4fafdf1d598428d9df43f1951a80170dc7780/media/CACTUS.png
--------------------------------------------------------------------------------
/src/AeroCoeffs.f90:
--------------------------------------------------------------------------------
1 | subroutine AeroCoeffs(nElem,alpha75,alpha5,Re,wPNorm,adotnorm,umach,SectInd,IsBE,CL,CD,CN,CT,CLCirc,CM25)
2 |
3 | use airfoil
4 | use dystl
5 | use pidef
6 |
7 | implicit none
8 |
9 | integer :: SectInd, IsBE, nElem
10 | real :: alpha75, alpha5, adotnorm, wPNorm, Re, umach, CL, CD, CN, CT, CLCirc
11 | real :: CLstat75, CLstat5, CDstat75, CLdyn5, CDdyn5, dCLAD, dCTAM, dCNAM, CL5, CD5, C, C1, CM25stat, CM25
12 | real :: alphaL, alphaD, aref, Fac
13 |
14 |
15 | ! Calc static characteristics
16 | CALL intp(Re,alpha75*condeg,CLstat75,CDstat75,CM25stat,SectInd)
17 | CALL intp(Re,alpha5*condeg,CLstat5,C,C1,SectInd)
18 |
19 | ! Apply pitch rate effects by analogy to pitching flat plate potential flow theory (SAND report)
20 | CL5=CLstat75
21 | CD5=CDstat75
22 | CM25=CM25stat+cos(alpha5)*(CLstat75-CLstat5)/4.0
23 | alphaL=alpha75
24 | CLCirc=CLstat75
25 |
26 | ! If no dynamic stall, use static values, else calc dynamic stall
27 | if (DSFlag/=0) then
28 |
29 | if (DSFlag==1) then
30 | ! Modified Boeing-Vertol approach
31 | Call BV_DynStall(nElem,CL5,CD5,alphaL,adotnorm,umach,Re,SectInd,CLdyn5,CDdyn5)
32 | else
33 | ! Leishman-Beddoes model
34 | Call LB_DynStall(nElem,CL5,CD5,alphaL,alpha5,umach,Re,SectInd,CLdyn5,CDdyn5)
35 | end if
36 |
37 | CL5=CLdyn5
38 | CD5=CDdyn5
39 | CLCirc=CLdyn5
40 |
41 | end if
42 |
43 | ! Tangential and normal coeffs
44 | CN=CL5*cos(alpha5)+CD5*sin(alpha5)
45 | CT=-CL5*sin(alpha5)+CD5*cos(alpha5)
46 |
47 | ! Calc tangential added mass increment by analogy to pitching flat plate potential flow theory (SAND report)
48 | dCTAM=2.0/cos(alpha5)*wPNorm*CM25stat-CLstat5/2.0*wPNorm
49 | ! Add in alphadot added mass effects (Theodorsen flat plate approx., Katz ch. 13)
50 | dCLAD=pi*adotnorm
51 | dCTAM=dCTAM-dCLAD*sin(alpha5)
52 | dCNAM=dCLAD*cos(alpha5)
53 |
54 | ! Add in added mass effects at low AOA (models not accurate at high AOA)
55 | Fac=1.0
56 | aref=abs(alpha5)
57 | if ((aref > pi/4.0) .AND. (aref < 3.0*pi/4.0)) then
58 | Fac = abs(1-4.0/pi*(aref-pi/4.0))
59 | end if
60 | CT=CT+Fac*dCTAM
61 | CN=CN+Fac*dCNAM
62 |
63 | ! Calc total lift and drag coefficient based on flow direction at half-chord for reference
64 | CL=CN*cos(alpha5)-CT*sin(alpha5)
65 | CD=CN*sin(alpha5)+CT*cos(alpha5)
66 |
67 | return
68 | end subroutine AeroCoeffs
69 |
--------------------------------------------------------------------------------
/src/BGeomSetup.f90:
--------------------------------------------------------------------------------
1 | subroutine BGeomSetup()
2 |
3 | use parameters
4 |
5 | use configr
6 | use element
7 | use pidef
8 | use util
9 |
10 | implicit none
11 |
12 | integer i, j, nei, nej
13 | real dx, dy, dz, vMag, ds
14 |
15 | ! Sets up blade geometry arrays for each blade, starting from the root of the first blade, and continuing for each blade.
16 |
17 | ! Set reference cr
18 | CrRef=0.0
19 | do i=1,nb
20 | do j=1,nbe
21 | CrRef=max(CrRef,Blades(i)%ECtoR(j))
22 | end do
23 | end do
24 |
25 | ! Set representative geometry discretization level (for vortex core calculation)
26 | dSGeom=0.0
27 | do i=1,nb
28 | do j=1,nbe
29 | dx=Blades(i)%QCx(j+1)-Blades(i)%QCx(j)
30 | dy=Blades(i)%QCy(j+1)-Blades(i)%QCy(j)
31 | dz=Blades(i)%QCz(j+1)-Blades(i)%QCz(j)
32 | ds=sqrt(dx**2+dy**2+dz**2)
33 | dsGeom=max(dsGeom,ds)
34 | end do
35 | end do
36 |
37 | ! Set initial turbine geometry (zero theta)
38 | do i=1,nb
39 |
40 | nei=1+(i-1)*(nbe+1)
41 |
42 | ! Set blade end geometry
43 | do j=0,nbe
44 | nej=nei+j ! element index
45 |
46 | xBE(nej)=Blades(i)%QCx(j+1)
47 | yBE(nej)=Blades(i)%QCy(j+1)
48 | zBE(nej)=Blades(i)%QCz(j+1)
49 |
50 | txBE(nej)=Blades(i)%tx(j+1)
51 | tyBE(nej)=Blades(i)%ty(j+1)
52 | tzBE(nej)=Blades(i)%tz(j+1)
53 |
54 | CtoR(nej)=Blades(i)%CtoR(j+1)
55 | end do
56 |
57 |
58 | ! Calc element geometry
59 |
60 | ! JCM: currently, although these values are for each element, they are held in arrays sized for element ends, where the first value for each blade
61 | ! is simply ignored in bsload (where these values are used).
62 |
63 | ! JCM: These zeros are ignored...
64 | eArea(nei)=0.0
65 | eChord(nei)=0.0
66 | iSect(nei)=0.0
67 | xBC(nei)=0.0
68 | yBC(nei)=0.0
69 | zBC(nei)=0.0
70 | nxBC(nei)=0.0
71 | nyBC(nei)=0.0
72 | nzBC(nei)=0.0
73 | txBC(nei)=0.0
74 | tyBC(nei)=0.0
75 | tzBC(nei)=0.0
76 | sxBC(nei)=0.0
77 | syBC(nei)=0.0
78 | szBC(nei)=0.0
79 | Call CalcBEGeom(i)
80 |
81 | do j=1,nbe
82 | nej=nei+j
83 | iSect(nej)=Blades(i)%iSect(j)
84 | end do
85 |
86 | end do
87 |
88 | return
89 | end subroutine BGeomSetup
90 |
--------------------------------------------------------------------------------
/src/BV_DynStall.f90:
--------------------------------------------------------------------------------
1 | subroutine BV_DynStall(nElem,CLstat,CDstat,alpha,adotnorm,umach,Re,SectInd,CL,CD)
2 |
3 | use airfoil
4 | use dystl
5 | use pidef
6 |
7 | implicit none
8 |
9 | real :: CLstat, CDstat, CL, CD, alpha, adotnorm, umach, Re
10 | integer :: SectInd, nElem
11 |
12 | integer :: isgn
13 | real :: dalphaRefMax, TransA, dalphaLRef, dalphaDRef, dalphaL, dalphaD, Fac
14 | real :: diff, smachl, hmachl, gammaxl, dgammal, smachm, hmachm, gammaxm, dgammam, gammal, gammam
15 | real :: alssn, alssp, alrefL, alLagD, alrefD, delN, delP, C, C1, AOA0
16 |
17 | ! Calculate the static stall envelope limits and other params
18 | CALL CalcBVStallAOALim(Re,SectInd,alssp,alssn)
19 | AOA0=alzer(SectInd)
20 | diff=0.06-tc(SectInd)
21 | smachl=0.4+5.0*diff
22 | hmachl=0.9+2.5*diff
23 | gammaxl=1.4-6.0*diff
24 | dgammal=gammaxl/(hmachl-smachl)
25 | smachm=0.2
26 | hmachm=0.7+2.5*diff
27 | gammaxm=1.0-2.5*diff
28 | dgammam=gammaxm/(hmachm-smachm)
29 |
30 | ! Limit reference dalpha to a maximum to keep sign of CL the same for
31 | ! alpha and lagged alpha (considered a reasonable lag...). Note:
32 | ! magnitude increasing and decreasing effect ratios are maintained.
33 | Fac=.9 ! Margin to ensure that dalphaRef is never large enough to make alrefL == AOA0 (blows up linear expansion model)
34 | dalphaRefMax=Fac*min(abs(alssp-AOA0),abs(alssn-AOA0))/max(k1pos,k1neg)
35 | TransA=.5*dalphaRefMax ! transition region for fairing lagged AOA in pure lag model
36 |
37 | isgn=sign(1.0,adotnorm)
38 |
39 | ! Modified Boeing-Vertol approach
40 |
41 | ! Lift
42 | gammal=gammaxl-(umach-smachl)*dgammal
43 | dalphaLRef=gammal*sqrt(abs(adotnorm))
44 | dalphaLRef=min(dalphaLRef,dalphaRefMax)
45 |
46 | if ((adotnorm*(alpha-AOA0)) < 0.0) then
47 | ! Magnitude of CL decreasing
48 | dalphaL=k1neg*dalphaLRef
49 | alrefL=alpha-dalphaL*isgn
50 |
51 | ! Only switch DS off using lagged alpha
52 | if (BV_DynamicFlagL(nElem) == 1 .AND. (alrefL > alssn .AND. alrefL < alssp)) then
53 | BV_DynamicFlagL(nElem)=0
54 | end if
55 |
56 | else
57 | ! Magnitude of CL increasing
58 | dalphaL=dalphaLRef*k1pos
59 | alrefL=alpha-dalphaL*isgn
60 |
61 | ! switch DS on or off using alpha
62 | if (alpha <= alssn .OR. alpha >= alssp) then
63 | BV_DynamicFlagL(nElem)=1
64 | else
65 | BV_DynamicFlagL(nElem)=0
66 | end if
67 | end if
68 |
69 | ! Drag
70 | gammam=gammaxm-(umach-smachm)*dgammam
71 | if (umach < smachm) then
72 | gammam=gammaxm
73 | end if
74 | dalphaDRef=gammam*sqrt(abs(adotnorm))
75 | dalphaDRef=min(dalphaDRef,dalphaRefMax)
76 |
77 | if ((adotnorm*(alpha-AOA0)) < 0.0) then
78 | ! Magnitude of CL decreasing
79 | dalphaD=k1neg*dalphaDRef
80 | alLagD=alpha-dalphaD*isgn
81 |
82 | ! Only switch DS off using lagged alpha
83 | if (BV_DynamicFlagD(nElem) == 1) then
84 | delN=alssn-alLagD
85 | delP=alLagD-alssp
86 | else
87 | delN=0.0
88 | delP=0.0
89 | end if
90 | else
91 | ! Magnitude of CL increasing
92 | dalphaD=dalphaDRef*k1pos
93 | alLagD=alpha-dalphaD*isgn
94 |
95 | ! switch DS on or off using alpha
96 | delN=alssn-alpha
97 | delP=alpha-alssp
98 | end if
99 |
100 | if (delN > TransA .OR. delP > TransA) then
101 | alrefD=alLagD
102 | BV_DynamicFlagD(nElem)=1
103 | elseif (delN > 0 .AND. delN < TransA) then
104 | ! Transition region (fairing effect...)
105 | alrefD=alpha+(alLagD-alpha)*delN/TransA
106 | BV_DynamicFlagD(nElem)=1
107 | elseif (delP > 0 .AND. delP < TransA) then
108 | ! Transition region (fairing effect...)
109 | alrefD=alpha+(alLagD-alpha)*delP/TransA
110 | BV_DynamicFlagD(nElem)=1
111 | else
112 | BV_DynamicFlagD(nElem)=0
113 | end if
114 |
115 | ! Static or dynamic model
116 | if (BV_DynamicFlagL(nElem) == 1) then
117 | ! Dynamic stall characteristics
118 | ! Linear expansion model for linear region coeffs
119 | CALL intp(Re,alrefL*condeg,CL,C,C1,SectInd)
120 | CL=CL/(alrefL-AOA0)*(alpha-AOA0)
121 | else
122 | ! Static characteristics
123 | CL=CLstat
124 | end if
125 |
126 | if (BV_DynamicFlagD(nElem) == 1) then
127 | ! Dynamic characteristics
128 | ! Pure lag model for drag
129 | CALL intp(Re,alrefD*condeg,C,CD,C1,SectInd)
130 | else
131 | ! Static characteristics
132 | CD=CDstat
133 | end if
134 |
135 |
136 | ! Diagnostic output
137 | BV_alpha=alpha*condeg
138 | BV_adotnorm=adotnorm
139 | BV_alrefL=alrefL*condeg
140 | BV_alrefD=alrefD*condeg
141 |
142 | return
143 | end subroutine BV_DynStall
144 |
--------------------------------------------------------------------------------
/src/BladeIndVel.f90:
--------------------------------------------------------------------------------
1 | subroutine BladeIndVel(NT,ntTerm,NBE,NB,NE,XP,YP,ZP,UP,VP,WP,DUDX,Mode,CalcDer)
2 |
3 | use blade
4 | use wake
5 |
6 | integer :: nt, ntTerm, nbe, nb, ne, Mode, CalcDer
7 | real :: XP, YP, ZP, UP, VP, WP, DUDX
8 | integer :: i, j, k, nei, nej, nt1
9 | integer :: VFlag
10 |
11 | ! COMPUTE THE VORTEX INDUCED VELOCITY AT POINT XP,YP,ZP FROM THE BLADE SYSTEM
12 | ! Mode: 0 -> Calc whole system
13 | ! 1 -> Calc bound vorticity only
14 | ! 2 -> Calc whole system minus bound vorticity
15 | ! CalcDer: 1 to calc DUDX
16 |
17 | NT1=NT-1
18 | UP=0.0
19 | VP=0.0
20 | WP=0.0
21 | DUDX=0.0
22 |
23 | if (Mode == 0) then
24 | ! Calc induced velocity from the whole blade system (bound, spanwise, and trailing wake vorticity)
25 |
26 | ! CALCULATE THE VELOCITY CONTRIBUTIONS DUE TO TRAILING VORTICIES ( GT(1:NT-1,:) )
27 | ! ntTerm represents the furthest away wake elements that are to be considered. (Calculated using user input xstop)
28 | VFlag=1
29 | do i=1,ne
30 | do j=ntTerm,NT1
31 | Call VorIVel(VFlag,CalcDer,GT(j,i),X(j,i),Y(j,i),Z(j,i),X(j+1,i),Y(j+1,i),Z(j+1,i),XP,YP,ZP,UP,VP,WP,DUDX)
32 | end do
33 | end do
34 |
35 | ! CALCULATE THE VELOCITY CONTRIBUTIONS DUE TO SPANWISE VORTICIES, including current bound vorticity ( GS(1:NT,:) )
36 | do i=1,nb
37 | nei=(i-1)*(nbe+1)
38 | do j=1,nbe
39 | nej=nei+j
40 | do k=ntTerm,NT
41 | if (k==NT) then
42 | VFlag=0
43 | else
44 | VFlag=2
45 | end if
46 | Call VorIVel(VFlag,CalcDer,GS(k,nej),X(k,nej),Y(k,nej),Z(k,nej),X(k,nej+1),Y(k,nej+1),Z(k,nej+1),XP,YP,ZP,UP,VP,WP,DUDX)
47 | end do
48 | end do
49 | end do
50 |
51 | else if (Mode == 1) then
52 | ! Calc induced velocity from bound vorticity only
53 |
54 | VFlag=0
55 | do i=1,nb
56 | nei=(i-1)*(nbe+1)
57 | do j=1,nbe
58 | nej=nei+j
59 | k=NT
60 | Call VorIVel(VFlag,CalcDer,GS(k,nej),X(k,nej),Y(k,nej),Z(k,nej),X(k,nej+1),Y(k,nej+1),Z(k,nej+1),XP,YP,ZP,UP,VP,WP,DUDX)
61 | end do
62 | end do
63 |
64 | else
65 | ! Calc induced velocity from spanwise and trailing wake vorticity (exclude bound vorticity)
66 |
67 | ! CALCULATE THE VELOCITY CONTRIBUTIONS DUE TO TRAILING VORTICIES ( GT(1:NT-1,:) )
68 | ! ntTerm represents the furthest away wake elements that are to be considered. (Calculated using user input xstop)
69 | VFlag=1
70 | do i=1,ne
71 | do j=ntTerm,NT1
72 | Call VorIVel(VFlag,CalcDer,GT(j,i),X(j,i),Y(j,i),Z(j,i),X(j+1,i),Y(j+1,i),Z(j+1,i),XP,YP,ZP,UP,VP,WP,DUDX)
73 | end do
74 | end do
75 |
76 | ! CALCULATE THE VELOCITY CONTRIBUTIONS DUE TO SPANWISE VORTICIES, NOT including current bound vorticity ( GS(1:NT-1,:) )
77 | VFlag=2
78 | do i=1,nb
79 | nei=(i-1)*(nbe+1)
80 | do j=1,nbe
81 | nej=nei+j
82 | do k=ntTerm,NT1
83 | Call VorIVel(VFlag,CalcDer,GS(k,nej),X(k,nej),Y(k,nej),Z(k,nej),X(k,nej+1),Y(k,nej+1),Z(k,nej+1),XP,YP,ZP,UP,VP,WP,DUDX)
84 | end do
85 | end do
86 | end do
87 |
88 | end if
89 |
90 | return
91 | end subroutine BladeIndVel
92 |
--------------------------------------------------------------------------------
/src/CalcBladeVel.f90:
--------------------------------------------------------------------------------
1 | subroutine CalcBladeVel(wx,wy,wz,rx,ry,rz,uBlade,vBlade,wBlade)
2 |
3 | use util
4 |
5 | real wx,wy,wz,rx,ry,rz,uBlade,vBlade,wBlade
6 |
7 | ! Blade rotation velocity (w x r)
8 | CALL cross(wx,wy,wz,rx,ry,rz,uBlade,vBlade,wBlade)
9 |
10 | return
11 | end subroutine CalcBladeVel
12 |
--------------------------------------------------------------------------------
/src/CalcFreestream.f90:
--------------------------------------------------------------------------------
1 | subroutine CalcFreestream(xElem,yElem,zElem,u,v,w,ygcErr)
2 |
3 | use shear
4 | use configr
5 | use iecgust
6 | use tower
7 |
8 | real u, v, w
9 | real xElem,yElem,zElem
10 | integer ygcErr
11 |
12 | real IECGustVel, Vxtower
13 |
14 | ! Freestream velocity (with ground shear model)
15 | ! At y/R = 0, u/Uinf = 0. At y/R = yref, u/Uinf = 1
16 | ! slex = 0 : Constant freestream
17 | ! slex = 1/2 : Laminar shear layer (approx)
18 | ! slex = 1/7 : Turbulent shear layer (approx)
19 |
20 | if (Igust .EQ. 1) then
21 | u = IECGustVel((nt-1)*dt,xElem)
22 | v = 0.0
23 | w = 0.0
24 | !Write(*,'(F15.8)') u
25 | return
26 | end if
27 |
28 | if ((yElem+ygc) <= 0.0) then
29 | ! reflect across zero...
30 | ygcErr=1
31 | u=(-(yElem+ygc)/yref)**slex
32 | u=max(u,.01) ! limit to some non zero value...
33 | v=0.0
34 | w=0.0
35 | else
36 | u=((yElem+ygc)/yref)**slex
37 | u=max(u,.01) ! limit to some non zero value...
38 | v=0.0
39 | w=0.0
40 | end if
41 |
42 | if (Itower .EQ. 1) then
43 | Vxtower = wake_defect_velocity(xElem,yElem,zElem)
44 | !Write(20,'(4F20.12)') xElem,yElem,zElem,Vxtower
45 | u = u - Vxtower
46 | u = max(u,.01)
47 | !If (Vxtower .GT. .001) Write(20,'(F20.12)') u+Vxtower, u
48 | end if
49 |
50 | return
51 | end subroutine CalcFreestream
52 |
53 |
54 | REAL FUNCTION IECGustVel(time,x)
55 |
56 | Use iecgust
57 | Use pidef
58 |
59 | real time, x
60 |
61 | real tr
62 |
63 | tr = time - x - gustX0
64 |
65 | if ((tr .LE. gustT) .AND. (tr .GT. 0)) then
66 | IECGustVel = 1.0 - 0.37 * gustA * sin(3*pi*tr/gustT) &
67 | * (1.0 - cos(2*pi*tr/gustT))
68 | !Write(*,'(2F20.12)') tr,IECGustVel
69 | else
70 | IECGustVel = 1.0
71 | end if
72 |
73 | return
74 |
75 | End FUNCTION IECGustVel
76 |
--------------------------------------------------------------------------------
/src/CalcIndVel.f90:
--------------------------------------------------------------------------------
1 | subroutine CalcIndVel(NT,ntTerm,NBE,NB,NE,Px,Py,Pz,Vx,Vy,Vz)
2 |
3 | use wallsoln
4 | use blade
5 |
6 | ! Calculate wall and wake induced velocity (including bound vorticity component)
7 |
8 | real :: Px, Py, Pz, Vx, Vy, Vz, dUdX
9 | integer :: nt, ntTerm, nbe, nb, ne
10 |
11 | real :: Point(3), dVel(3), Vel(3)
12 |
13 | ! Calc wake induced velocity at wake locations
14 | CALL BladeIndVel(NT,ntTerm,NBE,NB,NE,Px,Py,Pz,Vx,Vy,Vz,dUdX,0,0)
15 |
16 | ! Calculate wall induced velocities at wake locations
17 | Point=[Px,Py,Pz]
18 | Call WallIndVel(Point,dVel)
19 | Vx=Vx+dVel(1)
20 | Vy=Vy+dVel(2)
21 | Vz=Vz+dVel(3)
22 |
23 | return
24 | end subroutine CalcIndVel
25 |
26 |
--------------------------------------------------------------------------------
/src/EndRev.f90:
--------------------------------------------------------------------------------
1 | subroutine EndRev()
2 |
3 | use util
4 | use configr
5 | use output
6 | use time
7 | use fnames
8 |
9 | Call cpu_time(time2)
10 | !$ time2 = omp_get_wtime()
11 |
12 | dtime=time2-time1
13 | etime=time2-t0
14 | time1=etime
15 | !$ time1=omp_get_wtime()
16 |
17 | ! Calc average power over last revolution
18 | CPAve=CPSum/nti
19 | CTRAve=CTRSum/nti
20 | CFxAve=CFxSum/nti
21 | CFyAve=CFySum/nti
22 | CFzAve=CFzSum/nti
23 | ! Torque in ft-lbs
24 | TorqueAve=CTRAve*TorqueC
25 | ! Power coefficient based on tip speed
26 | KPAve=CPAve/ut**3
27 | ! Power in kW
28 | PowerAve=KPave*PowerC
29 |
30 | ! Set revolution average output
31 | RevOutData(1,1)=irev
32 | RevOutData(1,2)=CPAve
33 | RevOutData(1,3)=KPAve
34 | RevOutData(1,4)=CTRAve
35 | RevOutData(1,5)=CFxAve
36 | RevOutData(1,6)=CFyAve
37 | RevOutData(1,7)=CFzAve
38 | RevOutData(1,8)=PowerAve
39 | RevOutData(1,9)=TorqueAve
40 |
41 | ! Write to revolution average data csv file
42 | OPEN(9, FILE=RevOutputFN, POSITION='append')
43 | Call csvwrite(9,RevOutHead,RevOutData,0,1)
44 | CLOSE(9)
45 |
46 | ! Reset rev average sums
47 | CPSum=0.0
48 | CTRSum=0.0
49 | CFxSum=0.0
50 | CFySum=0.0
51 | CFzSum=0.0
52 |
53 | return
54 | end subroutine EndRev
55 |
--------------------------------------------------------------------------------
/src/EndTS.f90:
--------------------------------------------------------------------------------
1 | subroutine EndTS()
2 |
3 | use util
4 | use configr
5 | use output
6 | use element
7 | use strut
8 | use regtest
9 |
10 | Implicit None
11 |
12 | real :: CP, CTR, CFx, CFy, CFz
13 | integer :: i, offset
14 |
15 | ! Collect timestep results and compile output
16 |
17 | ! Machine output
18 | CP=CP_B+CP_S
19 | CTR=CTR_B+CTR_S
20 | CFx=CFx_B+CFx_S
21 | CFy=CFy_B+CFy_S
22 | CFz=CFz_B+CFz_S
23 |
24 | ! Sums for rev averages
25 | CPSum=CPSum+CP
26 | CTRSum=CTRSum+CTR
27 | CFxSum=CFxSum+CFx
28 | CFySum=CFySum+CFy
29 | CFzSum=CFzSum+CFz
30 |
31 | TSOutData(1,1)=TimeN ! Normalized simulation time (t*Uinf/Rmax)
32 | TSOutData(1,2)=Theta ! Turbine phase angle (rad)
33 | TSOutData(1,3)=irev
34 | TSOutData(1,4)=CTR ! Torque coeff
35 | TSOutData(1,5)=CP ! Power coeff
36 | TSOutData(1,6)=CFx ! Fx coeff
37 | TSOutData(1,7)=CFy ! Fy coeff
38 | TSOutData(1,8)=CFz ! Fz coeff
39 | ! Blade output
40 | do i=1,nb
41 | offset=8+(i-1)*4
42 | TSOutData(1,offset+1)=Blades(i)%CFx ! Blade Fx coeff
43 | TSOutData(1,offset+2)=Blades(i)%CFy ! Blade Fy coeff
44 | TSOutData(1,offset+3)=Blades(i)%CFz ! Blade Fz coeff
45 | TSOutData(1,offset+4)=Blades(i)%CTR ! Blade torque coeff
46 | end do
47 | do i=1,NStrut
48 | offset=8+nb*4+(i-1)*4
49 | TSOutData(1,offset+1)=Struts(i)%CFx ! Strut Fx coeff
50 | TSOutData(1,offset+2)=Struts(i)%CFy ! Strut Fy coeff
51 | TSOutData(1,offset+3)=Struts(i)%CFz ! Strut Fz coeff
52 | TSOutData(1,offset+4)=Struts(i)%CTR ! Strut torque coeff
53 | end do
54 |
55 | ! Write to timestep data csv file
56 | Call csvwrite(10,TSOutHead,TSOutData,0,1)
57 |
58 | ! Write to element loads data csv file
59 | if (BladeElemOutFlag == 1) then
60 | Call csvwrite(11,BladeElemOutHead,BladeElemOutData,0,-1)
61 | end if
62 |
63 | ! Write to dynamic stall diagnostic data csv file
64 | if (DynStallOutFlag == 1) then
65 | if (DynStallOutType == 1) then
66 | Call csvwrite(16,DynStallOutBVHead,DynStallOutBVData,0,-1)
67 | else if (DynStallOutType == 2) then
68 | Call csvwrite(16,DynStallOutLBHead,DynStallOutLBData,0,-1)
69 | end if
70 | end if
71 |
72 | ! Reg test
73 | if (RegTFlag == 1) then
74 | Reg_CPOut=CP
75 | end if
76 |
77 | return
78 | end subroutine EndTS
79 |
--------------------------------------------------------------------------------
/src/InputGeom.f90:
--------------------------------------------------------------------------------
1 | subroutine InputGeom(FN)
2 |
3 | use element
4 | use strut
5 | use varscale
6 | use configr
7 |
8 | implicit none
9 |
10 | integer, parameter :: MaxReadLine = 1000
11 | character(MaxReadLine) :: FN ! path to geometry input file
12 |
13 | integer :: NElem, i
14 | character(MaxReadLine) :: ReadLine
15 |
16 | ! Read geometry data file
17 |
18 | ! Format example:
19 | ! NBlade: 3
20 | ! NStrut: 3
21 | ! RotN: 0 0 1
22 | ! RotP: 0 0 0
23 | ! RefAR: 2.0
24 | ! RefR: 10.0
25 | ! Type: VAWT
26 | ! Blade 1:
27 | ! NElem: 5
28 | ! FlipN: 0
29 | ! QCx: 0 0 0 0 0 0
30 | ! QCy: 1 2 3 4 5 6
31 | ! QCz: 1 1 1 1 1 1
32 | ! tx: 1 1 1 1 1 1
33 | ! ty: 0 0 0 0 0 0
34 | ! tz: 0 0 0 0 0 0
35 | ! CtoR: .1 .1 .1 .1 .1 .1
36 | ! PEx: 0 0 0 0 0
37 | ! PEy: 1 2 3 4 5
38 | ! PEz: 1 1 1 1 1
39 | ! tEx: 0 0 0 0 0
40 | ! tEy: 1 2 3 4 5
41 | ! tEz: 1 1 1 1 1
42 | ! nEx: 0 0 0 0 0
43 | ! nEy: 1 2 3 4 5
44 | ! nEz: 1 1 1 1 1
45 | ! sEx: 1 1 1 1 1
46 | ! sEy: 0 0 0 0 0
47 | ! sEz: 1 2 3 4 5
48 | ! ECtoR: .1 .1 .1 .1 .1
49 | ! EAreaR: .1 .1 .1 .1 .1
50 | ! iSect: 1 1 1 1 1
51 | ! Blade 2:
52 | ! ...
53 | ! Strut 1:
54 | ! NElem: 5
55 | ! TtoC: .15
56 | ! MCx: 0 0 0 0 0 0
57 | ! MCy: 1 2 3 4 5 6
58 | ! MCz: 1 1 1 1 1 1
59 | ! CtoR: .1 .1 .1 .1 .1 .1
60 | ! PEx: 0 0 0 0 0
61 | ! PEy: 1 2 3 4 5
62 | ! PEz: 1 1 1 1 1
63 | ! sEx: 0 0 0 0 0
64 | ! sEy: 1 2 3 4 5
65 | ! sEz: 1 1 1 1 1
66 | ! ECtoR: .1 .1 .1 .1 .1
67 | ! EAreaR: .1 .1 .1 .1 .1
68 | ! BIndS: 0
69 | ! EIndS: 0
70 | ! BIndE: 1
71 | ! EIndE: 3
72 | ! Strut 2:
73 | ! ...
74 |
75 |
76 | ! Open input file for this section
77 | open(15, file=FN)
78 |
79 | ! Read header data
80 | read(15,'(A)') ReadLine
81 | read(ReadLine(index(ReadLine,':')+1:),*) nb
82 |
83 | read(15,'(A)') ReadLine
84 | read(ReadLine(index(ReadLine,':')+1:),*) NStrut
85 |
86 | read(15,'(A)') ReadLine
87 | read(ReadLine(index(ReadLine,':')+1:),*) RotX, RotY, RotZ
88 |
89 | read(15,'(A)') ReadLine
90 | read(ReadLine(index(ReadLine,':')+1:),*) RotPX, RotPY, RotPZ
91 |
92 | read(15,'(A)') ReadLine
93 | read(ReadLine(index(ReadLine,':')+1:),*) at
94 |
95 | read(15,'(A)') ReadLine
96 | read(ReadLine(index(ReadLine,':')+1:),*) Rmax
97 |
98 | read(15,'(A)') ReadLine
99 |
100 | ! Allocate blades struct
101 | allocate(Blades(nb))
102 |
103 | ! Allocate struts struct
104 | if (NStrut>0) then
105 | allocate(Struts(NStrut))
106 | end if
107 |
108 | ! Read blade data
109 | do i=1,nb
110 |
111 | read(15,'(A)') ReadLine
112 |
113 | read(15,'(A)') ReadLine
114 | read(ReadLine(index(ReadLine,':')+1:),*) NElem
115 |
116 | ! Allocate arrays in blade structure
117 | Call blade_geom_cns(i,NElem)
118 |
119 | ! JCM: Currently (until blade/wake geometry module is restructured), NElem must be the same for all blades.
120 | nbe=NElem
121 |
122 | read(15,'(A)') ReadLine
123 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%FlipN
124 |
125 | read(15,'(A)') ReadLine
126 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%QCx(1:NElem+1)
127 |
128 | read(15,'(A)') ReadLine
129 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%QCy(1:NElem+1)
130 |
131 | read(15,'(A)') ReadLine
132 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%QCz(1:NElem+1)
133 |
134 | read(15,'(A)') ReadLine
135 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%tx(1:NElem+1)
136 |
137 | read(15,'(A)') ReadLine
138 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%ty(1:NElem+1)
139 |
140 | read(15,'(A)') ReadLine
141 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%tz(1:NElem+1)
142 |
143 | read(15,'(A)') ReadLine
144 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%CtoR(1:NElem+1)
145 |
146 | read(15,'(A)') ReadLine
147 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%PEx(1:NElem)
148 |
149 | read(15,'(A)') ReadLine
150 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%PEy(1:NElem)
151 |
152 | read(15,'(A)') ReadLine
153 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%PEz(1:NElem)
154 |
155 | read(15,'(A)') ReadLine
156 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%tEx(1:NElem)
157 |
158 | read(15,'(A)') ReadLine
159 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%tEy(1:NElem)
160 |
161 | read(15,'(A)') ReadLine
162 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%tEz(1:NElem)
163 |
164 | read(15,'(A)') ReadLine
165 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%nEx(1:NElem)
166 |
167 | read(15,'(A)') ReadLine
168 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%nEy(1:NElem)
169 |
170 | read(15,'(A)') ReadLine
171 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%nEz(1:NElem)
172 |
173 | read(15,'(A)') ReadLine
174 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%sEx(1:NElem)
175 |
176 | read(15,'(A)') ReadLine
177 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%sEy(1:NElem)
178 |
179 | read(15,'(A)') ReadLine
180 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%sEz(1:NElem)
181 |
182 | read(15,'(A)') ReadLine
183 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%ECtoR(1:NElem)
184 |
185 | read(15,'(A)') ReadLine
186 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%EAreaR(1:NElem)
187 |
188 | read(15,'(A)') ReadLine
189 | read(ReadLine(index(ReadLine,':')+1:),*) Blades(i)%iSect(1:NElem)
190 |
191 | end do
192 |
193 | ! Read strut data
194 | do i=1,NStrut
195 |
196 | read(15,'(A)') ReadLine
197 |
198 | read(15,'(A)') ReadLine
199 | read(ReadLine(index(ReadLine,':')+1:),*) NElem
200 |
201 | read(15,'(A)') ReadLine
202 | read(ReadLine(index(ReadLine,':')+1:),*) Struts(i)%TtoC
203 |
204 | ! Allocate arrays in blade structure
205 | Call strut_comp_cns(i,NElem)
206 |
207 | read(15,'(A)') ReadLine
208 | read(ReadLine(index(ReadLine,':')+1:),*) Struts(i)%MCx(1:NElem+1)
209 |
210 | read(15,'(A)') ReadLine
211 | read(ReadLine(index(ReadLine,':')+1:),*) Struts(i)%MCy(1:NElem+1)
212 |
213 | read(15,'(A)') ReadLine
214 | read(ReadLine(index(ReadLine,':')+1:),*) Struts(i)%MCz(1:NElem+1)
215 |
216 | read(15,'(A)') ReadLine
217 | read(ReadLine(index(ReadLine,':')+1:),*) Struts(i)%CtoR(1:NElem+1)
218 |
219 | read(15,'(A)') ReadLine
220 | read(ReadLine(index(ReadLine,':')+1:),*) Struts(i)%PEx(1:NElem)
221 |
222 | read(15,'(A)') ReadLine
223 | read(ReadLine(index(ReadLine,':')+1:),*) Struts(i)%PEy(1:NElem)
224 |
225 | read(15,'(A)') ReadLine
226 | read(ReadLine(index(ReadLine,':')+1:),*) Struts(i)%PEz(1:NElem)
227 |
228 | read(15,'(A)') ReadLine
229 | read(ReadLine(index(ReadLine,':')+1:),*) Struts(i)%sEx(1:NElem)
230 |
231 | read(15,'(A)') ReadLine
232 | read(ReadLine(index(ReadLine,':')+1:),*) Struts(i)%sEy(1:NElem)
233 |
234 | read(15,'(A)') ReadLine
235 | read(ReadLine(index(ReadLine,':')+1:),*) Struts(i)%sEz(1:NElem)
236 |
237 | read(15,'(A)') ReadLine
238 | read(ReadLine(index(ReadLine,':')+1:),*) Struts(i)%ECtoR(1:NElem)
239 |
240 | read(15,'(A)') ReadLine
241 | read(ReadLine(index(ReadLine,':')+1:),*) Struts(i)%EAreaR(1:NElem)
242 |
243 | read(15,'(A)') ReadLine
244 | read(ReadLine(index(ReadLine,':')+1:),*) Struts(i)%BIndS
245 |
246 | read(15,'(A)') ReadLine
247 | read(ReadLine(index(ReadLine,':')+1:),*) Struts(i)%EIndS
248 |
249 | read(15,'(A)') ReadLine
250 | read(ReadLine(index(ReadLine,':')+1:),*) Struts(i)%BIndE
251 |
252 | read(15,'(A)') ReadLine
253 | read(ReadLine(index(ReadLine,':')+1:),*) Struts(i)%EIndE
254 |
255 | end do
256 |
257 | ! Close input file
258 | close(15)
259 |
260 | return
261 | end subroutine InputGeom
262 |
--------------------------------------------------------------------------------
/src/LB_DynStall.f90:
--------------------------------------------------------------------------------
1 | subroutine LB_DynStall(nElem,CLstat,CDstat,alphaL,alpha5,umach,Re,SectInd,CL,CD)
2 |
3 | use airfoil
4 | use dystl
5 | use pidef
6 |
7 | implicit none
8 |
9 | real :: CLstat, CDstat, alphaL, alpha5, umach, Re, CL, CD
10 | integer :: SectInd, nElem
11 |
12 | real :: AOA0, CLID, Trans, dCLRefLE, dAOARefLE, AOARefLE, CLstatF, C, C1, CLIDF, CLRatio, CLsep, CLF, dCDF, KD, CLa, NOF, dCLv, dCDv, acut
13 |
14 | ! Leishman-Beddoes dynamic stall model (incompressible reduction).
15 |
16 | ! Airfoil data
17 | AOA0=alzer(SectInd)
18 | Call CalcLBStallAOALim(Re,SectInd,CLa,CLCritP(nElem),CLCritN(nElem))
19 |
20 | ! Model constants
21 | KD=.1 ! TE separation drag factor
22 |
23 | ! Evaluate ideal CL curve at current AOA
24 | Call LB_EvalIdealCL(alphaL,AOA0,CLa,1,CLRef(nElem))
25 | Call LB_EvalIdealCL(alphaL,AOA0,CLa,0,CLID)
26 |
27 | ! calc lagged ideal CL for comparison with critical LE separation CL
28 | Trans=(cos(alphaL-AOA0))**2 ! fair effect to zero at 90 deg. AOA...
29 | dCLRefLE=Trans*dp(nElem) ! dp is lagged CLRef change
30 | dAOARefLE=dCLRefLE/CLa
31 |
32 | ! define reference LE CL and AOA
33 | CLRefLE(nElem)=CLRef(nElem)-dCLRefLE
34 | if (CLRefLE(nElem)*(CLRefLE(nElem)-CLRefLE_Last(nElem)) > 0) then
35 | CLRateFlag(nElem)=1
36 | else
37 | CLRateFlag(nElem)=0
38 | end if
39 | AOARefLE=alphaL-dAOARefLE
40 | Call Force180(AOARefLE)
41 |
42 | ! calc effective static TE separation point using effective LE AOA
43 | Call intp(Re,AOARefLE*condeg,CLstatF,C,C1,SectInd)
44 | Call LB_EvalIdealCL(AOARefLE,AOA0,CLa,0,CLIDF)
45 | if (abs(CLIDF)<0.001) then
46 | CLRatio=999
47 | else
48 | CLRatio=CLstatF/CLIDF;
49 | end if
50 |
51 | if (CLRatio > 0.25) then
52 | Fstat(nElem)=min((sqrt(4.0*CLRatio)-1.0)**2,1.0)
53 |
54 | ! Test logic
55 | LB_LogicOutputs(nElem,1)=1
56 | else
57 | Fstat(nElem)=0
58 |
59 | ! Test logic
60 | LB_LogicOutputs(nElem,1)=2
61 | end if
62 | ! calc lagged Fstat to represent dynamic TE separation point
63 | F(nElem)=Fstat(nElem)-dF(nElem)
64 | ! force limits on lagged F (needed due to discretization error...)
65 | F(nElem)=min(max(F(nElem),0.0),1.0)
66 |
67 | ! Calc dynamic CL due to TE separation as fairing between fully attached and fully separated predictions from the Kirchoff approximation at current AOA
68 | if (abs(CLID)<0.001) then
69 | CLRatio=999
70 | else
71 | CLRatio=CLstat/CLID
72 | end if
73 |
74 | if (CLRatio > 1.0) then
75 | CLID=CLstat
76 |
77 | ! Test logic
78 | LB_LogicOutputs(nElem,2)=1
79 | end if
80 |
81 | if (CLRatio > 0.25) then
82 | CLsep=CLID/4.0
83 |
84 | ! Test logic
85 | LB_LogicOutputs(nElem,3)=1
86 | else
87 | CLsep=CLstat
88 |
89 | ! Test logic
90 | LB_LogicOutputs(nElem,3)=2
91 | end if
92 | CLF=CLsep+CLID*0.25*(F(nElem)+2.0*sqrt(F(nElem)))
93 | dCDF=KD*(CLstat-CLF)*sign(1.0,CLstat)
94 |
95 | ! LE vortex lift component, dCNv is a lagged change in the added normal force due
96 | ! to LE vortex shedding. Assumed to affect lift coeff as an added circulation...
97 | dCLv=dCNv(nElem)*cos(alpha5)
98 | dCDv=dCNv(nElem)*sin(alpha5)
99 | ! vortex feed is given by the rate at which lift (circulation) is being shed due to dynamic separation. Lift component due to separation is defined by the
100 | ! difference between the ideal lift and the lift including dynamic separation effects.
101 | cv(nElem)=CLID-CLF
102 | dcv(nElem)=cv(nElem)-cv_Last(nElem)
103 | ! If the sign of dcv is opposite the reference LE CL, set to zero to disallow negative vorticity from shedding from the leading edge. Also, limit the model
104 | ! at AOA>acut or if the magnitude of the reference CL is decreasing...
105 | acut=50.0*conrad
106 | if (sign(1.0,dcv(nElem)*CLRefLE(nElem))<0 .OR. abs(alphaL-AOA0)>acut .OR. CLRateFlag(nElem)<0) then
107 | dcv=0.0
108 |
109 | ! Test logic
110 | LB_LogicOutputs(nElem,4)=1
111 | end if
112 |
113 | ! Total lift and drag
114 | CL=CLF+dCLv
115 | CD=CDstat+dCDF+dCDv
116 |
117 | return
118 | end subroutine LB_DynStall
119 |
--------------------------------------------------------------------------------
/src/RotateTurbine.f90:
--------------------------------------------------------------------------------
1 | subroutine RotateTurbine
2 |
3 | use configr
4 | use element
5 | use blade
6 | use strut
7 |
8 | ! Updates current turbine geometry
9 |
10 | ! Rotate turbine axis of rotation and origin if necessary...
11 |
12 | ! Rotate blades
13 | do i=1,nb
14 | Call RotateBlade(i,delt,RotX,RotY,RotZ,RotPX,RotPY,RotPZ)
15 | end do
16 |
17 | ! Rotate struts
18 | do i=1,NStrut
19 | Call RotateStrut(i,delt,RotX,RotY,RotZ,RotPX,RotPY,RotPZ)
20 | end do
21 |
22 | return
23 | end subroutine RotateTurbine
24 |
--------------------------------------------------------------------------------
/src/SGeomSetup.f90:
--------------------------------------------------------------------------------
1 | subroutine SGeomSetup()
2 |
3 | use parameters
4 |
5 | use configr
6 | use element
7 | use strut
8 | use airfoil
9 | use pidef
10 |
11 | implicit none
12 |
13 | integer :: i, j, NElem, BIndS, EIndS, BIndE, EIndE, iSc
14 | real :: sx, sy, sz, VMag, LR
15 |
16 | ! Sets up strut geometry arrays.
17 |
18 | ! Set initial geometry (zero theta)
19 | do i=1,NStrut
20 | NElem=Struts(i)%NElem
21 | BIndS=Struts(i)%BIndS
22 | EIndS=Struts(i)%EIndS
23 | BIndE=Struts(i)%BIndE
24 | EIndE=Struts(i)%EIndE
25 |
26 | ! Get blade thickness to chord
27 | if (BIndS > 0) then
28 | iSc=Blades(BIndS)%iSect(EIndS)
29 | Struts(i)%tcS=tc(iSc)
30 | else
31 | Struts(i)%tcS=0.0
32 | end if
33 | if (BIndE > 0) then
34 | iSc=Blades(BIndE)%iSect(EIndE)
35 | Struts(i)%tcE=tc(iSc)
36 | else
37 | Struts(i)%tcE=0.0
38 | end if
39 |
40 | ! Init length sum
41 | LR=0.0
42 | do j=1,NElem
43 | VMag=sqrt(Struts(i)%sEx(j)**2+Struts(i)%sEy(j)**2+Struts(i)%sEz(j)**2)
44 | ! Sum length
45 | LR=LR+VMag
46 | end do
47 | Struts(i)%LR=LR
48 |
49 | ! Calc element geometry
50 | Call CalcSEGeom(i)
51 |
52 | end do
53 |
54 | return
55 | end subroutine SGeomSetup
56 |
--------------------------------------------------------------------------------
/src/SetBoundWake.f90:
--------------------------------------------------------------------------------
1 | subroutine SetBoundWake()
2 |
3 | use configr
4 | use element
5 | use blade
6 | use wake
7 |
8 | ! Set new wake element positions
9 | do i=1,nb
10 |
11 | nei=1+(i-1)*(nbe+1)
12 |
13 | ! Blade element end locations (quarter chord).
14 | do j=0,nbe
15 | nej=nei+j ! element index
16 | x(nt,nej)=xBE(nej)
17 | y(nt,nej)=yBE(nej)
18 | z(nt,nej)=zBE(nej)
19 | end do
20 | end do
21 |
22 |
23 | return
24 | end subroutine SetBoundWake
25 |
--------------------------------------------------------------------------------
/src/UpdateBladeVel.f90:
--------------------------------------------------------------------------------
1 | subroutine UpdateBladeVel(IFLG)
2 |
3 | use configr
4 | use blade
5 | use wake
6 | use wallsoln
7 |
8 | integer :: i,ygcErr
9 | real :: Point(3), dVel(3), dUdX
10 |
11 | ! Calculate the velocity induced on the blades by wake, wall, and freestream
12 |
13 |
14 | if (iflg .eq. 0) then
15 | ! re-initialize uiwake viwake wiwake as we are beginning a new time step
16 | uiwake(:)=0.0
17 | viwake(:)=0.0
18 | wiwake(:)=0.0
19 | end if
20 |
21 |
22 | do I=1,NE
23 |
24 | ! If flag is set, just recompute the velocity contiribution due to the bound vorticies on the blades.
25 | ! Otherwise, calculate all wake, wall and freestream induced velocity.
26 | if (IFLG .eq. 0) then
27 |
28 | ! Calculate freestream velocity at blade elements
29 | CALL CalcFreestream(X(NT,I),Y(NT,I),Z(NT,I),UFSB(I),VFSB(I),WFSB(I),ygcErr)
30 |
31 | ! Set freestream velocity of next shed wake elements to that calculated on the blade
32 | UFS(NT,I)=UFSB(I)
33 | VFS(NT,I)=VFSB(I)
34 | WFS(NT,I)=WFSB(I)
35 |
36 |
37 | USUM=0.0
38 | VSUM=0.0
39 | WSUM=0.0
40 | if (NT > 1) then
41 |
42 | ! Calculate wake velocity at blade elements (excluding bound vorticity component)
43 | Call BladeIndVel(NT,ntTerm,NBE,NB,NE,X(NT,I),Y(NT,I),Z(NT,I),USUM,VSUM,WSUM,dUdX,2,0)
44 |
45 | ! Calculate wall induced velocities at blade locations
46 | Point=[X(NT,I),Y(NT,I),Z(NT,I)]
47 | Call WallIndVel(Point,dVel)
48 | USUM=USUM+dVel(1)
49 | VSUM=VSUM+dVel(2)
50 | WSUM=WSUM+dVel(3)
51 |
52 | end if
53 |
54 | uiwake(I)=USUM
55 | viwake(I)=VSUM
56 | wiwake(I)=WSUM
57 | else
58 | USUM=uiwake(I)
59 | VSUM=viwake(I)
60 | WSUM=wiwake(I)
61 | end if
62 |
63 | ! CALCULATE THE VELOCITY CONTRIBUTIONS DUE TO JUST THE BOUND VORTICIES ON THE BLADES ( GS(NT,:) )
64 | Call BladeIndVel(NT,ntTerm,NBE,NB,NE,X(NT,I),Y(NT,I),Z(NT,I),UP,VP,WP,dUdX,1,0)
65 |
66 | ! Set wake and wall velocities on blade
67 | UB(I)=USUM+UP
68 | VB(I)=VSUM+VP
69 | WB(I)=WSUM+WP
70 |
71 | ! Set induced velocity of next shed wake elements
72 | if (iut .eq. -2) then
73 | ! Fix wake velocities at freestream velocity
74 | U(NT,I)=0.0
75 | V(NT,I)=0.0
76 | W(NT,I)=0.0
77 | else
78 | U(NT,I)=UB(I)
79 | V(NT,I)=VB(I)
80 | W(NT,I)=WB(I)
81 | end if
82 |
83 | end do
84 |
85 | return
86 | end subroutine UpdateBladeVel
87 |
--------------------------------------------------------------------------------
/src/UpdateStrutLoads.f90:
--------------------------------------------------------------------------------
1 | subroutine UpdateStrutLoads()
2 |
3 | use strut
4 | use configr
5 |
6 | Implicit None
7 |
8 | integer :: i, j, NElem
9 | integer :: ygcerr
10 | real :: xs, ys, zs, xj, yj, zj
11 | real :: uFSs, vFSs, wFSs, uBlade, vBlade, wBlade, us, vs, ws
12 | real :: uTot, vTot, wTot, ur, ReStrut
13 | real :: Delem
14 | real :: Fx, Fy, Fz, TRx, TRy, TRz, te
15 | real :: t_ave, carea, cdj, djunc
16 |
17 | ! Updates loads on struts
18 |
19 | ! Zero out current strut loads sum
20 | CP_S=0.0
21 | CTR_S=0.0
22 | CFx_S=0.0
23 | CFy_S=0.0
24 | CFz_S=0.0
25 |
26 | do i=1,NStrut
27 |
28 | ! Zero out current strut loads
29 | Struts(i)%CP=0.0
30 | Struts(i)%CTR=0.0
31 | Struts(i)%CFx=0.0
32 | Struts(i)%CFy=0.0
33 | Struts(i)%CFz=0.0
34 |
35 | NElem=Struts(i)%NElem
36 | do j=1,NElem
37 |
38 | xs=Struts(i)%PEx(j)
39 | ys=Struts(i)%PEy(j)
40 | zs=Struts(i)%PEz(j)
41 |
42 | ! Freestream velocity at strut location
43 | Call CalcFreestream(xs,ys,zs,uFSs,vFSs,wFSs,ygcerr)
44 |
45 | ! Blade velocity due to rotation
46 | CALL CalcBladeVel(wRotX,wRotY,wRotZ,xs,ys,zs,uBlade,vBlade,wBlade)
47 |
48 | ! Induced velocity at strut element
49 | Call CalcIndVel(NT,ntTerm,NBE,NB,NE,xs,ys,zs,us,vs,ws)
50 |
51 | ! Calculate relative velocity magnitude at strut element
52 | uTot = us+uFSs-uBlade
53 | vTot = vs+vFSs-vBlade
54 | wTot = ws+wFSs-wBlade
55 | ur = sqrt(uTot*uTot + vTot*vTot + wTot*wTot)
56 | ReStrut = ReM*ur*Struts(i)%ECtoR(j) ! Strut chord Reynolds number
57 |
58 | ! Fill current flow quantities at strut element
59 | Struts(i)%u(j)=uTot
60 | Struts(i)%v(j)=vTot
61 | Struts(i)%w(j)=wTot
62 | Struts(i)%ur(j)=ur
63 | Struts(i)%ReStrut(j)=ReStrut
64 |
65 | ! Calculate strut element coeffs
66 | Call StrutElemCoeffs(i,j)
67 |
68 | ! Drag coeff vector from this strut element, re-referenced to full turbine scale
69 | ! (D/(1/2*rho*Uinf^2*At)
70 | Delem = Struts(i)%Cd0(j) * Struts(i)%EAreaR(j) / at * ur**2
71 | Fx=Delem*uTot/ur
72 | Fy=Delem*vTot/ur
73 | Fz=Delem*wTot/ur
74 | ! Corresponding torque coeff. (T/(1/2*rho*Uinf^2*At*R))
75 | CALL cross(xs-RotPX,ys-RotPY,zs-RotPZ,Fx,Fy,Fz,TRx,TRy,TRz)
76 | te=(TRx*RotX+TRy*RotY+TRz*RotZ)
77 |
78 | ! Add to strut output
79 | Struts(i)%CTR=Struts(i)%CTR + te
80 | Struts(i)%CP=Struts(i)%CP + te*ut
81 | Struts(i)%CFx=Struts(i)%CFx + Fx
82 | Struts(i)%CFy=Struts(i)%CFy + Fy
83 | Struts(i)%CFz=Struts(i)%CFz + Fz
84 | end do
85 |
86 | ! Blade/strut junction interference drag
87 | ! First strut element
88 | if (Struts(i)%BIndS > 0) then
89 | carea=Struts(i)%ECtoR(1)**2
90 | xj=Struts(i)%PEx(1)
91 | yj=Struts(i)%PEy(1)
92 | zj=Struts(i)%PEz(1)
93 | t_ave = 0.5 * (Struts(i)%TtoC + Struts(i)%tcS)
94 | uTot=Struts(i)%u(1)
95 | vTot=Struts(i)%v(1)
96 | wTot=Struts(i)%w(1)
97 | ur=Struts(i)%ur(1)
98 | Cdj = t_ave*t_ave * (17.0 * t_ave*t_ave - 0.05)
99 | !Cdj = 0.0112 ! t/c_avg = 0.165
100 | !Cdj = 0.0535 ! t/c_avg = 0.24
101 | Cdj = Cdj + Cdpar ! Additional user-specified parasitic drag
102 | ! Drag coeff vector, re-referenced to full turbine scale
103 | ! (D/(1/2*rho*Uinf^2*At)
104 | Djunc = Cdj * carea / at * ur**2
105 | Fx=Djunc*uTot/ur
106 | Fy=Djunc*vTot/ur
107 | Fz=Djunc*wTot/ur
108 | ! Corresponding torque coeff. (T/(1/2*rho*Uinf^2*At*R))
109 | CALL cross(xj-RotPX,yj-RotPY,zj-RotPZ,Fx,Fy,Fz,TRx,TRy,TRz)
110 | te=(TRx*RotX+TRy*RotY+TRz*RotZ)
111 | ! Add to strut output
112 | Struts(i)%CTR=Struts(i)%CTR + te
113 | Struts(i)%CP=Struts(i)%CP + te*ut
114 | Struts(i)%CFx=Struts(i)%CFx + Fx
115 | Struts(i)%CFy=Struts(i)%CFy + Fy
116 | Struts(i)%CFz=Struts(i)%CFz + Fz
117 | end if
118 | ! Last strut element
119 | if (Struts(i)%BIndE > 0) then
120 | carea=Struts(i)%ECtoR(NElem)**2
121 | xj=Struts(i)%PEx(NElem)
122 | yj=Struts(i)%PEy(NElem)
123 | zj=Struts(i)%PEz(NElem)
124 | t_ave = 0.5 * (Struts(i)%TtoC + Struts(i)%tcE)
125 | uTot=Struts(i)%u(NElem)
126 | vTot=Struts(i)%v(NElem)
127 | wTot=Struts(i)%w(NElem)
128 | ur=Struts(i)%ur(NElem)
129 | Cdj = t_ave*t_ave * (17.0 * t_ave*t_ave - 0.05)
130 | !Cdj = 0.0112 ! t/c_avg = 0.165
131 | !Cdj = 0.0535 ! t/c_avg = 0.24
132 | Cdj = Cdj + Cdpar ! Additional user-specified parasitic drag
133 | ! Drag coeff vector, re-referenced to full turbine scale
134 | ! (D/(1/2*rho*Uinf^2*At)
135 | Djunc = Cdj * carea / at * ur**2
136 | Fx=Djunc*uTot/ur
137 | Fy=Djunc*vTot/ur
138 | Fz=Djunc*wTot/ur
139 | ! Corresponding torque coeff. (T/(1/2*rho*Uinf^2*At*R))
140 | CALL cross(xj-RotPX,yj-RotPY,zj-RotPZ,Fx,Fy,Fz,TRx,TRy,TRz)
141 | te=(TRx*RotX+TRy*RotY+TRz*RotZ)
142 | ! Add to strut output
143 | Struts(i)%CTR=Struts(i)%CTR + te
144 | Struts(i)%CP=Struts(i)%CP + te*ut
145 | Struts(i)%CFx=Struts(i)%CFx + Fx
146 | Struts(i)%CFy=Struts(i)%CFy + Fy
147 | Struts(i)%CFz=Struts(i)%CFz + Fz
148 | end if
149 |
150 | ! Add to total struts output
151 | CTR_S=CTR_S + Struts(i)%CTR
152 | CP_S=CP_S + Struts(i)%CP
153 | CFx_S=CFx_S + Struts(i)%CFx
154 | CFy_S=CFy_S + Struts(i)%CFy
155 | CFz_S=CFz_S + Struts(i)%CFz
156 |
157 | end do
158 |
159 | return
160 | end subroutine UpdateStrutLoads
161 |
--------------------------------------------------------------------------------
/src/UpdateTowerVelocity.f90:
--------------------------------------------------------------------------------
1 | subroutine UpdateTowerVelocity()
2 |
3 | use tower
4 | use configr
5 |
6 | implicit none
7 |
8 | real :: uFSt,vFSt,wFSt,utow,vtow,wtow
9 | integer :: i,ygcerr
10 |
11 |
12 | Do i = 1,tower_Npts
13 |
14 | ! Freestream velocity at tower location
15 | Call CalcFreestream(tower_x,tower_y(i),0.0,uFSt,vFSt,wFSt,ygcerr)
16 |
17 | ! Induced velocity at tower location
18 | Call CalcIndVel(NT,ntTerm,NBE,NB,NE,tower_x,tower_y(i),0.0,utow,vtow,wtow)
19 |
20 | tower_Vx(i) = uFSt + utow
21 |
22 | End Do
23 |
24 |
25 | return
26 |
27 | end subroutine UpdateTowerVelocity
28 |
--------------------------------------------------------------------------------
/src/UpdateWakeVel.f90:
--------------------------------------------------------------------------------
1 | subroutine UpdateWakeVel()
2 |
3 | use configr
4 | use blade
5 | use wake
6 | use regtest
7 |
8 | integer :: ygcErr
9 | real :: x_t, y_t, z_t, u_t, v_t, w_t
10 |
11 | integer :: NTHREADS, TID, OMP_GET_NUM_THREADS, OMP_GET_THREAD_NUM, N, CHUNKSIZE, CHUNK
12 |
13 | ! Calculate the induced velocity at each lattice point in the wake from wake (including bound vorticity), wall, and freestream
14 |
15 | if (NT .ge. 1) then
16 |
17 | NT1=NT-1
18 |
19 | ! Update the old wake velocity values
20 | do I=1,NE
21 | do J=ntTerm,NT1
22 | UO(J,I)=U(J,I)+UFS(J,I)
23 | VO(J,I)=V(J,I)+VFS(J,I)
24 | WO(J,I)=W(J,I)+WFS(J,I)
25 | end do
26 | end do
27 |
28 | ! Calculate freestream velocity at wake locations
29 | ygcErr=0
30 | do I=1,NE
31 | do J=ntTerm,NT1
32 | CALL CalcFreestream(X(J,I),Y(J,I),Z(J,I),UFS(J,I),VFS(J,I),WFS(J,I),ygcErr)
33 | end do
34 | end do
35 |
36 | ! If this is proper time step, calculate system influence on wake velocities
37 | if (NT .eq. NSW) then
38 | !$omp parallel do private(j)
39 | do I=1,NE
40 | do J=ntTerm,NT1
41 | Call CalcIndVel(NT,ntTerm,NBE,NB,NE,X(J,I),Y(J,I),Z(J,I),U(J,I),V(J,I),W(J,I))
42 | end do
43 | end do
44 | !$omp end parallel do
45 |
46 | ! Set the next update timestep
47 | nsw=nt+iut
48 | end if
49 |
50 | end if
51 |
52 | ! Regression test
53 | if (RegTFlag == 1) then
54 | Reg_MaxWVM=0.0
55 | ! Max wake velocity mag in first wake shed
56 | do I=1,NE
57 | Reg_MaxWVM=max(Reg_MaxWVM,sqrt(U(1,I)**2+V(1,I)**2+W(1,I)**2))
58 | end do
59 | end if
60 |
61 | return
62 | end subroutine UpdateWakeVel
63 |
--------------------------------------------------------------------------------
/src/UpdateWall.f90:
--------------------------------------------------------------------------------
1 | subroutine UpdateWall()
2 |
3 | use configr
4 | use blade
5 | use wallsoln
6 | use wallsystem
7 | use regtest
8 | use util
9 |
10 | integer :: ygcErr, i, IBCInd
11 | real :: Point(3), dVel(3), NVelSum, TVelSum, dUdX, dUdXSum
12 |
13 | ! If this is a wall update timestep...
14 | if (nt == nsWall) then
15 |
16 | if (GPFlag == 1 .or. WPFlag == 1) then
17 | ! Ground plane
18 |
19 | ! Calculate the velocities at wall panels from wake (including bound vorticity), and freestream.
20 | ! Update wall RHS and calc new panel source strengths
21 |
22 | !$omp parallel do private(i, dVel, NVelSum, dUdX)
23 | do i=1,NumWP_total
24 |
25 | ! Calculate freestream velocity at panel locations normal to panel
26 | CALL CalcFreestream(WCPoints(i,1),WCPoints(i,2)&
27 | &,WCPoints(i,3),dVel(1),dVel(2),dVel(3),ygcErr)
28 | NVelSum=sum(W3Vec(i,1:3)*dVel)
29 |
30 | ! Calc wake induced velocity at wall panel locations normal to panel
31 | CALL BladeIndVel(NT,ntTerm,NBE,NB,NE,WCPoints(i,1),WCPoints(i,2),WCPoints(i,3),dVel(1),dVel(2),dVel(3),dUdX,0,0)
32 | NVelSum=NVelSum+sum(W3Vec(i,1:3)*dVel)
33 |
34 | ! Calc FS induced velocity normal to panel
35 | if (FSFlag == 1) then
36 | Point=[WCPoints(i,1),WCPoints(i,2),WCPoints(i,3)]
37 | Call FSIndVel(Point,0,dVel,dUdX)
38 | NVelSum=NVelSum+sum(W3Vec(i,1:3)*dVel)
39 | end if
40 |
41 | ! Set RHS
42 | WRHS(i,1)=-NVelSum
43 |
44 | end do
45 | !$omp end parallel do
46 |
47 | ! Calc new wall panel source strengths
48 | WSource=matmul(WInfI,WRHS)
49 |
50 | end if
51 |
52 |
53 | if (FSFlag == 1) then
54 | ! Free Surface
55 |
56 | ! Calculate the velocities at wall panels from wake (including bound vorticity), and freestream.
57 | ! Update wall RHS and calc new panel source strengths
58 | if (UseFSWall) then
59 | ! Wall BC
60 | do i=1,NumFSCP
61 |
62 | ! Calculate freestream velocity at panel locations
63 | CALL CalcFreestream(FSCPPoints(i,1),FSCPPoints(i&
64 | &,2),FSCPPoints(i,3),dVel(1),dVel(2),dVel(3),ygcErr)
65 | NVelSum=sum(FSCZVec(i,1:3)*dVel)
66 | TVelSum=sum(FSCXVec(i,1:3)*dVel)
67 |
68 | ! Calc wake induced velocity at wall panel locations
69 | CALL BladeIndVel(NT,ntTerm,NBE,NB,NE,FSCPPoints(i,1),FSCPPoints(i,2),FSCPPoints(i,3),dVel(1),dVel(2),dVel(3),dUdX,0,0)
70 | NVelSum=NVelSum+sum(FSCZVec(i,1:3)*dVel)
71 | TVelSum=TVelSum+sum(FSCXVec(i,1:3)*dVel)
72 |
73 | ! Calc GP induced velocity
74 | if (GPFlag == 1) then
75 | Point=[FSCPPoints(i,1),FSCPPoints(i,2),FSCPPoints(i,3)]
76 | Call GPIndVel(Point,0,dVel,dUdX)
77 |
78 | NVelSum=NVelSum+sum(FSCZVec(i,1:3)*dVel)
79 | TVelSum=TVelSum+sum(FSCXVec(i,1:3)*dVel)
80 | end if
81 |
82 | ! Calc wall induced velocity
83 | if (WPFlag == 1) then
84 | Point=[FSCPPoints(i,1),FSCPPoints(i,2),FSCPPoints(i,3)]
85 | Call WPIndVel(Point,0,dVel,dUdX)
86 |
87 | NVelSum=NVelSum+sum(FSCZVec(i,1:3)*dVel)
88 | TVelSum=TVelSum+sum(FSCXVec(i,1:3)*dVel)
89 | end if
90 |
91 | ! Set RHS in list for running averages
92 | FSRHS(i,FSRHSInd)=-NVelSum
93 | ! Set new average RHS value. Note: Must build average over at least one revolution...
94 | FSRHSAve(i,1)=sum(FSRHS(i,1:NFSRHSAve))/real(NFSRHSAve)
95 |
96 | ! Set induced tangent velocity at colocation points in list for running averages (used for output)
97 | FSVT(i,FSRHSInd)=TVelSum
98 | ! Set new average VT value. Note: Must build average over at least one revolution...
99 | FSVTAve(i,1)=sum(FSVT(i,1:NFSRHSAve))/real(NFSRHSAve)
100 |
101 | end do
102 |
103 | else
104 | ! Free surface BC
105 | do i=1,NumFSCP
106 |
107 | ! Calculate freestream velocity at panel locations
108 | CALL CalcFreestream(FSCPPoints(i,1),FSCPPoints(i&
109 | &,2),FSCPPoints(i,3),dVel(1),dVel(2),dVel(3),ygcErr)
110 | NVelSum=sum(FSCZVec(i,1:3)*dVel)
111 | TVelSum=sum(FSCXVec(i,1:3)*dVel)
112 | dUdXSum=0.0
113 |
114 | ! Calc wake induced velocity at wall panel locations
115 | CALL BladeIndVel(NT,ntTerm,NBE,NB,NE,FSCPPoints(i,1),FSCPPoints(i,2),FSCPPoints(i,3),dVel(1),dVel(2),dVel(3),dUdX,0,1)
116 | NVelSum=NVelSum+sum(FSCZVec(i,1:3)*dVel)
117 | TVelSum=TVelSum+sum(FSCXVec(i,1:3)*dVel)
118 | dUdXSum=dUdXSum+dUdX
119 |
120 | ! Calc GP induced velocity
121 | if (GPFlag == 1) then
122 | Point=[FSCPPoints(i,1),FSCPPoints(i,2),FSCPPoints(i,3)]
123 | Call GPIndVel(Point,1,dVel,dUdX)
124 | NVelSum=NVelSum+sum(FSCZVec(i,1:3)*dVel)
125 | TVelSum=TVelSum+sum(FSCXVec(i,1:3)*dVel)
126 | dUdXSum=dUdXSum+dUdX
127 | end if
128 |
129 | ! Set RHS in list for running averages
130 | FSRHS(i,FSRHSInd)=(FnR**2)*dUdXSum-NVelSum
131 | ! Set new average RHS value. Note: Must build average over at least one revolution...
132 | FSRHSAve(i,1)=sum(FSRHS(i,1:NFSRHSAve))/real(NFSRHSAve)
133 |
134 | ! Set BC RHS if this is a BC colocation point
135 | if (FSBCRow(i)>0) then
136 | FSRHS(FSBCRow(i),FSRHSInd)=-dUdXSum
137 | FSRHSAve(FSBCRow(i),1)=sum(FSRHS(FSBCRow(i),1:NFSRHSAve))/real(NFSRHSAve)
138 | end if
139 |
140 | ! Set induced tangent velocity at colocation points in list for running averages (used for output)
141 | FSVT(i,FSRHSInd)=TVelSum
142 | ! Set new average VT value. Note: Must build average over at least one revolution...
143 | FSVTAve(i,1)=sum(FSVT(i,1:NFSRHSAve))/real(NFSRHSAve)
144 |
145 | end do
146 |
147 | end if
148 |
149 | ! Calc new free surface panel source strengths with running average (over approx 360 deg turbine rotation)
150 | ! RHS values.
151 | FSSource=matmul(FSSMatI,FSRHSAve)
152 |
153 | ! Reset running average index (once entire buffer is filled, overwrites old data at beginning of average buffer)
154 | if (FSRHSInd= vCutOffRad) then
53 | if (ivtxcor > 0 .and. CCAV < A2*VRAD2) then
54 | if (ivtxcor == 1) then
55 | ! Constant velocity (approx) core
56 | CCAV = A2*VRAD2 ! limit denominator to value at core radius
57 | VF = (ADBDB-ADCDC)*G/(12.56637*CCAV)
58 | if (CalcDer == 1) then
59 | DVFDX = G/12.56637*((AX/C-CX*ADC/C**3-AX/B+BX*ADB/B**3)/CCAV)
60 | end if
61 | else if (ivtxcor == 2) then
62 | ! Linear velocity (approx) core
63 | RRAT=sqrt(CCAV/(A2*VRAD2)) ! radius to core radius ratio
64 | CCAV = A2*VRAD2 ! limit denominator to value at core radius
65 | VF = RRAT*(ADBDB-ADCDC)*G/(12.56637*CCAV)
66 | if (CalcDer == 1) then
67 | DVFDX = G/12.56637*((AX/C-CX*ADC/C**3-AX/B+BX*ADB/B**3)/CCAV)
68 | end if
69 | end if
70 | else
71 | VF = (ADBDB-ADCDC)*G/(12.56637*CCAV)
72 | if (CalcDer == 1) then
73 | DVFDX = G/12.56637*((AX/C-CX*ADC/C**3-AX/B+BX*ADB/B**3)/CCAV-2*(ADBDB-ADCDC)/CCAV**2*(AZ*CCAY-AY*CCAZ))
74 | end if
75 | end if
76 | else
77 | VF = 0.0
78 | DVFDX = 0.0
79 | end if
80 |
81 | UP = UP+CCAX*VF
82 | VP = VP+CCAY*VF
83 | WP = WP+CCAZ*VF
84 | if (CalcDer == 1) then
85 | DUDX = DUDX+CCAX*DVFDX
86 | end if
87 |
88 | return
89 | end subroutine VorIVel
90 |
--------------------------------------------------------------------------------
/src/WSolnSetup.f90:
--------------------------------------------------------------------------------
1 | subroutine WSolnSetup()
2 |
3 | use util
4 | use wallsoln
5 | use wallsystem
6 | !$ use omp_lib
7 |
8 | integer :: i, j, Self, IBCInd, BCRow
9 | integer :: INFO
10 | integer, allocatable :: IPIV(:)
11 | real :: R(3,3), Point(3), dPG(3), dVel(3), dVelG(3), dudx
12 |
13 | real :: t0,t1 ! cpu time variables for matrix inversion
14 |
15 | ! Set up ground plane system
16 | if (GPFlag==1) then
17 |
18 | !! This assumes WGeomSetup() has already been called, and that the ground plane has been configured
19 | ! as a wall system of one wall.
20 |
21 | ! Setup wall self influence matrix
22 | write(*,*) 'Generating wall influence matrix...'
23 | call cpu_time(t0)
24 | !$ t0 = omp_get_wtime()
25 | call gen_influence_matrix()
26 | call cpu_time(t1)
27 | !$ t1 = omp_get_wtime()
28 | print '("Time to generate influence matrix = ",f15.3," seconds.")',t1-t0
29 |
30 | ! Store wall solution matrix and inverse
31 | write(*,*) 'Inverting wall influence matrix...'
32 | call cpu_time(t0)
33 | !$ t0 = omp_get_wtime()
34 | call invert_influence_matrix()
35 | call cpu_time(t1)
36 | !$ t1 = omp_get_wtime()
37 | print '("Time to invert influence matrix = ",f15.3," seconds.")',t1-t0
38 |
39 | end if
40 |
41 |
42 | ! Set up generic wall system
43 | if (WPFlag==1) then
44 |
45 | !! This assumes that the wall geometry has already been loaded as a wall system of one wall.
46 |
47 | ! Setup wall self influence matrix
48 | write(*,*) 'Generating wall influence matrix...'
49 | call cpu_time(t0)
50 | !$ t0 = omp_get_wtime()
51 | call gen_influence_matrix()
52 | call cpu_time(t1)
53 | !$ t1 = omp_get_wtime()
54 | print '("Time to generate influence matrix = ",f15.3," seconds.")',t1-t0
55 |
56 | ! Store wall solution matrix and inverse
57 | write(*,*) 'Inverting wall influence matrix...'
58 | call cpu_time(t0)
59 | !$ t0 = omp_get_wtime()
60 | call invert_influence_matrix()
61 | call cpu_time(t1)
62 | !$ t1 = omp_get_wtime()
63 | print '("Time to invert influence matrix = ",f15.3," seconds.")',t1-t0
64 |
65 | end if
66 |
67 |
68 | ! Set up free surface system
69 | if (FSFlag==1) then
70 |
71 | ! Setup free surface self influence matrix
72 | do i=1,NumFSP
73 | do j=1,NumFSCP
74 | ! Rotation from global to panel i
75 | R(1,1:3)=FSXVec(i,1:3)
76 | R(2,1:3)=FSYVec(i,1:3)
77 | R(3,1:3)=FSZVec(i,1:3)
78 |
79 | ! Calc influence in panel frame
80 | dPG=FSCPPoints(j,1:3)-FSCPoints(i,1:3)
81 | Call CalcRotation3(R,dPG,Point,0)
82 | Call RectSourceVel(Point,FSPL(i),FSPW(i),1.0,0,FSEdgeTol,1,dVel,dudx)
83 |
84 | ! Rotate to global frame
85 | Call CalcRotation3(R,dVel,dVelG,1)
86 | FSInCoeffN(j,i)=sum(dVelG*FSCZVec(j,1:3))
87 | FSInCoeffT(j,i)=sum(dVelG*FSCXVec(j,1:3))
88 | FSInCoeffdUdX(j,i)=dudx
89 | end do
90 | end do
91 |
92 | ! Create free surface solution matrix and inverse
93 | if (UseFSWall) then
94 | ! Wall solution matrix
95 | FSSMat=FSInCoeffN
96 | else
97 | ! Free surface solution matrix
98 | FSSMat(1:NumFSCP,1:NumFSP)=FSInCoeffN-(FnR**2)*FSInCoeffdUdX
99 | ! Add inflow BCs
100 | FSBCRow(:)=0
101 | do i=1,NumFSCPz
102 | IBCInd=1+(i-1)*NumFSCPx
103 | BCRow=NumFSCP+i
104 | FSSMat(BCRow,1:NumFSP)=FSInCoeffdUdX(IBCInd,1:NumFSP)
105 | ! Set BC row index
106 | FSBCRow(IBCInd)=BCRow
107 | end do
108 | end if
109 |
110 | ! LAPACK => DGESV: Linear equation solution A*X=B where A(N,N) X(N,NRHS) B(N,NRHS)
111 | ! Note that if NRHS = N, and B is the identity, X is the inverse of A...
112 | ! Initialize inverse to the identity
113 | FSSMatI(:,:)=0.0
114 | do i=1,NumFSP
115 | do j=1,NumFSP
116 | if (j==i) then
117 | FSSMatI(i,j)=1.0
118 | end if
119 | end do
120 | end do
121 |
122 | allocate(IPIV(NumFSP)) ! allocation storage for pivot array
123 | Call DGESV(NumFSP,NumFSP,FSSMat,NumFSP,IPIV,FSSMatI,NumFSP,INFO)
124 | if (INFO>0) then
125 | write(6,'(A)') 'Matrix inversion failed in WSolnSetup. Exiting...'
126 | stop
127 | end if
128 |
129 | ! Initialize source strengths and RHS to zero
130 | FSSource(:,:)=0.0
131 | FSRHS(:,:)=0.0
132 | FSRHSInd=1
133 |
134 | end if
135 |
136 | return
137 | end subroutine WSolnSetup
138 |
139 |
--------------------------------------------------------------------------------
/src/WriteFieldData.f90:
--------------------------------------------------------------------------------
1 | subroutine WriteFieldData()
2 |
3 | ! Write field data
4 |
5 | use fielddata
6 | use blade
7 | use wake
8 | use wallsoln
9 | use configr
10 | use fnames
11 |
12 | implicit none
13 |
14 | real :: xnode, ynode, znode
15 | integer :: ygcErr
16 | character(len=10) :: nt_str
17 |
18 | ! Open file for writing - a new file at each timestep
19 | write(nt_str,'(I5.5)') nt
20 | FieldOutputFN=adjustl(trim(FieldOutputPath))//path_separator//trim(FNBase)//'_FieldData_'//trim(nt_str)//'.csv'
21 | OPEN(13, FILE=FieldOutputFN)
22 | write(13,'(A)') trim(FieldOutHead)
23 |
24 | !! Compute wake data on specified cartesian grid
25 | ! Compute blade, wake, and wall induced velocity
26 | do zcount=1,nzgrid
27 | !$omp parallel do private(xcount,xnode,ynode,znode)
28 | do ycount=1,nygrid
29 | do xcount=1,nxgrid
30 | ! Get the grid node location
31 | xnode = XGrid(xcount,ycount,zcount)
32 | ynode = YGrid(xcount,ycount,zcount)
33 | znode = ZGrid(xcount,ycount,zcount)
34 |
35 | ! Calculate wall and wake induced velocities at grid locations
36 | Call CalcIndVel(NT,ntTerm,NBE,NB,NE, &
37 | xnode,ynode,znode, &
38 | VXInd(xcount,ycount,zcount),VYInd(xcount,ycount,zcount),VZInd(xcount,ycount,zcount))
39 |
40 | ! Calculate free stream velocities at grid locations
41 | Call CalcFreestream(xnode,ynode,znode, &
42 | UfsGrid(xcount,ycount,zcount),VfsGrid(xcount,ycount,zcount),WfsGrid(xcount,ycount,zcount), &
43 | ygcErr)
44 | end do
45 | end do
46 | !$omp end parallel do
47 | end do
48 |
49 |
50 | ! Output blade, wake, and wall induced velocity
51 | do zcount=1,nzgrid
52 | do ycount=1,nygrid
53 | do xcount=1,nxgrid
54 | ! Write to file
55 | write(13,'(E14.7,",",$)') TimeN ! Normalized simulation time (t*Uinf/Rmax)
56 | write(13,'(E14.7,",",$)') XGrid(xcount,ycount,zcount)
57 | write(13,'(E14.7,",",$)') YGrid(xcount,ycount,zcount) ! grid node locations
58 | write(13,'(E14.7,",",$)') ZGrid(xcount,ycount,zcount)
59 | write(13,'(E14.7,",",$)') VXInd(xcount,ycount,zcount)
60 | write(13,'(E14.7,",",$)') VYInd(xcount,ycount,zcount) ! induced velocities
61 | write(13,'(E14.7,",",$)') VZInd(xcount,ycount,zcount)
62 | write(13,'(E14.7,",",$)') UfsGrid(xcount,ycount,zcount)
63 | write(13,'(E14.7,",",$)') VfsGrid(xcount,ycount,zcount) ! freestream velocities
64 | write(13,'(E14.7)' ) WfsGrid(xcount,ycount,zcount) ! Dont suppress carriage return on last column
65 | end do
66 | end do
67 | end do
68 |
69 | ! close the output file
70 | CLOSE(13)
71 |
72 | return
73 | end subroutine WriteFieldData
74 |
--------------------------------------------------------------------------------
/src/WriteFinalOutput.f90:
--------------------------------------------------------------------------------
1 | subroutine WriteFinalOutput()
2 |
3 | use util
4 | use airfoil, only : ilxtp, iuxtp
5 |
6 | implicit none
7 |
8 | ! Check error flags and write notifications to stdout
9 | if (ilxtp .gt. 0) write (6,615)
10 | if (iuxtp .gt. 0) write (6,618)
11 |
12 | return
13 | 618 FORMAT ('AT LEAST ONE BLADE REYNOLDS NUMBER WAS ABOVE TABLE LIMIT. UPPER LIMIT USED.')
14 | 615 FORMAT ('AT LEAST ONE BLADE REYNOLDS NUMBER WAS BELOW TABLE LIMIT. LOWER LIMIT USED.')
15 | end subroutine WriteFinalOutput
16 |
--------------------------------------------------------------------------------
/src/WriteRegTOutput.f90:
--------------------------------------------------------------------------------
1 | subroutine WriteRegTOutput(Flag)
2 |
3 | use regtest
4 |
5 | integer :: Flag
6 | if (Flag == 0) then
7 | write(7,'(A)') 'Timestep NLIter ElemNum ElemAOA ElemCirc dElemCirc BVDynFlagL LBCheck'
8 | else if (Flag == 1) then
9 | ! Write data during non-linear iteration
10 | write(7,'(3I3,3E13.5,2I3)') Reg_TS, Reg_NLIter, Reg_ElemNum, Reg_ElemAOA, Reg_ElemCirc, Reg_dElemCirc, Reg_DFL, Reg_LBC
11 | else if (Flag == 2) then
12 | ! Write cp average over timesteps
13 | write(7,'(A)') 'CPave'
14 | write(7,'(E13.5)') Reg_CPOut
15 | ! Write max calculated wall source strength
16 | write(7,'(A)') 'MaxWS'
17 | write(7,'(E13.5)') Reg_MaxWS
18 | ! Write max calculated velocity in first wake shed
19 | write(7,'(A)') 'MaxWakeV'
20 | write(7,'(E13.5)') Reg_MaxWVM
21 | end if
22 |
23 | return
24 | end subroutine WriteRegTOutput
25 |
--------------------------------------------------------------------------------
/src/WriteWakeElementData.f90:
--------------------------------------------------------------------------------
1 | subroutine WriteWakeElemData()
2 |
3 | ! Write wake element positions and velocity
4 |
5 | use wakedata
6 | use blade
7 | use wake
8 | use wallsoln
9 | use configr
10 | use fnames
11 | use pathseparator
12 |
13 | implicit none
14 |
15 | integer :: tCount, tCountMax, wcount, node_id
16 | character(len=10) :: nt_str
17 |
18 | ! Optional wake element data output
19 | write(nt_str,'(I5.5)') nt
20 | WakeOutputFN=adjustl(trim(WakeElemOutputPath))//path_separator//trim(FNBase)//'_WakeElemData_'//trim(nt_str)//'.csv'
21 | OPEN(12, FILE=WakeOutputFN)
22 | write(12,'(A)') trim(WakeOutHead)
23 |
24 | tCountMax=nt
25 | do wcount=1,NWakeInd+nb
26 | do tCount=1,tCountMax
27 | ! compute the unique ID number of the current wake filament
28 | ! (higher numbers correspond to newer elements)
29 | node_id = (NWakeInd+nb)*(tcount-1) + wcount
30 |
31 | write(12,'(E14.7,",",$)') TimeN ! Normalized simulation time (t*Uinf/Rmax)
32 | write(12,'(I0,",",$)') node_id ! Unique node ID
33 | write(12,'(I0,",",$)') wcount ! Node number that wake element originated from
34 | write(12,'(E14.7,",",$)') X(tCount,wcount) ! Wake element X position
35 | write(12,'(E14.7,",",$)') Y(tCount,wcount) ! Wake element Y position
36 | write(12,'(E14.7,",",$)') Z(tCount,wcount) ! Wake element Z position
37 | write(12,'(E14.7,",",$)') U(tCount,wcount) ! Wake element X velocity
38 | write(12,'(E14.7,",",$)') V(tCount,wcount) ! Wake element Y velocity
39 | ! Dont suppress carriage return on last column
40 | write(12,'(E14.7)') W(tCount,wcount) ! Wake element Z velocity
41 | end do
42 | end do
43 |
44 | ! close the output file
45 | CLOSE(12)
46 |
47 | return
48 | end subroutine WriteWakeElemData
49 |
--------------------------------------------------------------------------------
/src/bsload.f90:
--------------------------------------------------------------------------------
1 | subroutine bsload(nElem,IsBE,alpha,alpha5,alpha75,adotnorm,Re,umach,ur,CL,CD,CM25,CLCirc,CN,CT,Fx,Fy,Fz,te)
2 |
3 | use element
4 | use blade
5 | use pidef
6 | use configr
7 | use airfoil
8 | use dystl
9 | use util
10 |
11 | implicit none
12 |
13 | integer nElem, IsBE
14 | real alpha, alpha5, alpha75, adotnorm, Re, umach, ur, CL, CD, CM25, CLCirc, CN, CT, Fx, Fy, Fz, te
15 |
16 | integer SectInd, nElem1
17 | real ElemAreaR, ElemChordR, xe, ye, ze, nxe, nye, nze, txe, tye, tze, sxe, sye, sze
18 | real dal, wP, wPNorm
19 | real uAve, vAve, wAve, uFSAve, vFSAve, wFSAve, uBlade, vBlade, wBlade, urdn, urdc
20 | real xe5, ye5, ze5, xe75, ye75, ze75, uBlade5, vBlade5, wBlade5, uBlade75, vBlade75, wBlade75
21 | real urdn5, ur5, urdn75, ur75
22 | real FN, FT, MS, TRx, TRy, TRz, CircDir
23 |
24 |
25 | ! Calculates aero loads on a blade element. Static and dynamic airfoil characteristics calculated here...
26 |
27 | nElem1=nElem-1 ! Blade element is referenced by its upper end location index nElem, with nElem-1 being the lower end location index.
28 |
29 |
30 | ! Retrieve the blade segment geometric information
31 |
32 | ! Element span
33 | ElemAreaR=eArea(nElem)
34 | ElemChordR=eChord(nElem)
35 |
36 | ! Quarter chord location
37 | xe=xBC(nElem)
38 | ye=yBC(nElem)
39 | ze=zBC(nElem)
40 |
41 | ! Element normal, tangential, and spanwise vectors
42 | nxe=nxBC(nElem)
43 | nye=nyBC(nElem)
44 | nze=nzBC(nElem)
45 | txe=txBC(nElem)
46 | tye=tyBC(nElem)
47 | tze=tzBC(nElem)
48 | sxe=sxBC(nElem)
49 | sye=syBC(nElem)
50 | sze=szBC(nElem)
51 |
52 | ! Direction of circulation in wake grid at positive lift
53 | CircDir=CircSign(nElem)
54 |
55 | ! Airfoil section
56 | SectInd=isect(nElem)
57 |
58 |
59 | ! Calculate the local blade segment angle of attack
60 |
61 | ! Wall and wake induced velocity
62 | uAve=(uB(nElem)+uB(nElem1))/2.0
63 | vAve=(vB(nElem)+vB(nElem1))/2.0
64 | wAve=(wB(nElem)+wB(nElem1))/2.0
65 | ! Freestream velocity
66 | uFSAve=(uFSB(nElem)+uFSB(nElem1))/2.0
67 | vFSAve=(vFSB(nElem)+vFSB(nElem1))/2.0
68 | wFSAve=(wFSB(nElem)+wFSB(nElem1))/2.0
69 | ! Blade velocity due to rotation
70 | CALL CalcBladeVel(wRotX,wRotY,wRotZ,xe,ye,ze,uBlade,vBlade,wBlade)
71 |
72 | ! Calc element normal and tangential velocity components. Calc element pitch rate.
73 | urdn = (nxe*(uAve+uFSAve-uBlade)+nye*(vAve+vFSAve-vBlade)+nze*(wAve+wFSAve-wBlade)) ! Normal
74 | urdc = (txe*(uAve+uFSAve-uBlade)+tye*(vAve+vFSAve-vBlade)+tze*(wAve+wFSAve-wBlade)) ! Tangential
75 | wP = sxe*wRotX+sye*wRotY+sze*wRotZ
76 |
77 | ur=sqrt(urdn**2+urdc**2)
78 | alpha=atan2(urdn,urdc)
79 | wPNorm=wP*ElemChordR/(2.0*max(ur,0.001)) ! wP*c/(2*U)
80 |
81 | Re=ReM*ElemChordR*ur
82 | umach=ur*Minf
83 |
84 | !---------
85 | ! These .5c and .75c locations are used to calc pitch rate effects
86 | if (PRFlag/=0) then
87 | xe5=xe+0.25*ElemChordR*txe
88 | ye5=ye+0.25*ElemChordR*tye
89 | ze5=ze+0.25*ElemChordR*tze
90 | xe75=xe+0.5*ElemChordR*txe
91 | ye75=ye+0.5*ElemChordR*tye
92 | ze75=ze+0.5*ElemChordR*tze
93 | CALL CalcBladeVel(wRotX,wRotY,wRotZ,xe5,ye5,ze5,uBlade5,vBlade5,wBlade5)
94 | CALL CalcBladeVel(wRotX,wRotY,wRotZ,xe75,ye75,ze75,uBlade75,vBlade75,wBlade75)
95 | urdn5 = (nxe*(uAve+uFSAve-uBlade5)+nye*(vAve+vFSAve-vBlade5)+nze*(wAve+wFSAve-wBlade5))
96 | ur5=sqrt(urdn5**2+urdc**2)
97 | alpha5=atan2(urdn5,urdc)
98 | urdn75 = (nxe*(uAve+uFSAve-uBlade75)+nye*(vAve+vFSAve-vBlade75)+nze*(wAve+wFSAve-wBlade75))
99 | ur75=sqrt(urdn75**2+urdc**2)
100 | alpha75=atan2(urdn75,urdc)
101 | else
102 | alpha5=alpha
103 | alpha75=alpha
104 | wPNorm=0.0
105 | end if
106 |
107 | dal=alpha75-AOA_Last(nelem1)
108 | adotnorm=dal/DT*ElemChordR/(2.0*max(ur,0.001)) ! adot*c/(2*U)
109 | !--------
110 |
111 | ! Evaluate aero coefficients and dynamic stall effects as appropriate
112 | Call AeroCoeffs(nElem,alpha75,alpha5,Re,wPNorm,adotnorm,umach,SectInd,IsBE,CL,CD,CN,CT,CLCirc,CM25)
113 |
114 | ! Bound vortex strength from CL via Kutta-Joukowski analogy.
115 | ! Save corresponding AOA as well
116 | GB_Raw(nElem1)=CircDir*(CLCirc*ElemChordR*ur/2.0)
117 | ! AOA(nElem1)=alpha
118 | AOA(nElem1)=alpha75
119 | ! normalized time step used to update states in the LB model
120 | ds(nElem)=2.0*ur*DT/ElemChordR
121 |
122 | ! Force and moment coeff. from this blade element, re-referenced to full turbine scale
123 | ! (F/(1/2*rho*Uinf^2*At) and M/(1/2*rho*Uinf^2*At*R)
124 | FN=CN*(ElemAreaR/at)*ur**2
125 | FT=CT*(ElemAreaR/at)*ur**2
126 | MS=CM25*ElemChordR*(ElemAreaR/at)*ur**2
127 | ! Corresponding torque coeff. (T/(1/2*rho*Uinf^2*At*R))
128 | Fx=FN*nxe+FT*txe
129 | Fy=FN*nye+FT*tye
130 | Fz=FN*nze+FT*tze
131 | CALL cross(xe-RotPX,ye-RotPY,ze-RotPZ,Fx,Fy,Fz,TRx,TRy,TRz)
132 | te=(TRx*RotX+TRy*RotY+TRz*RotZ)+MS*(sxe*RotX+sye*RotY+sze*RotZ)
133 |
134 | return
135 | end subroutine bsload
136 |
--------------------------------------------------------------------------------
/src/conlp.f90:
--------------------------------------------------------------------------------
1 | subroutine conlp()
2 |
3 | use configr
4 | use blade
5 | use wake
6 |
7 | logical NotDone
8 |
9 | ! Calculate the convection of shed vortex lattice points
10 |
11 | NT1=NT-1
12 | do I=1,NE
13 | if (NT > 1) then
14 | do J=ntTerm,NT1
15 |
16 | if (iut == 1) then
17 | ! Temporally "upwinded" calculation for the velocity to use in
18 | ! convecting the wake (use velocity extrapolated to t=t+.5*dt)
19 | X(J,I)=X(J,I)+(3.0*(U(J,I)+UFS(J,I))-UO(J,I))*DT/2.0
20 | Y(J,I)=Y(J,I)+(3.0*(V(J,I)+VFS(J,I))-VO(J,I))*DT/2.0
21 | Z(J,I)=Z(J,I)+(3.0*(W(J,I)+WFS(J,I))-WO(J,I))*DT/2.0
22 | else
23 | ! If wake velocities are not updated every timestep, simply
24 | ! assume constant velocity over a single timestep.
25 | X(J,I)=X(J,I)+(U(J,I)+UFS(J,I))*DT
26 | Y(J,I)=Y(J,I)+(V(J,I)+VFS(J,I))*DT
27 | Z(J,I)=Z(J,I)+(W(J,I)+WFS(J,I))*DT
28 | end if
29 |
30 | end do
31 | end if
32 |
33 | ! Use straight integration for newest wake points for which there is no old data
34 | X(NT,I)=X(NT,I)+(U(NT,I)+UFS(NT,I))*DT
35 | Y(NT,I)=Y(NT,I)+(V(NT,I)+VFS(NT,I))*DT
36 | Z(NT,I)=Z(NT,I)+(W(NT,I)+WFS(NT,I))*DT
37 | end do
38 |
39 | ! If ixterm is 1, update ntTerm as the farthest index in the wake that has at least one point below XSTOP
40 | if (IXTERM .eq. 1) then
41 | NotDone = .true.
42 | do while (NotDone .AND. ntTerm < NT1)
43 | ! Check
44 | do I=1,NE
45 | if (X(ntTerm,I) < XSTOP) then
46 | NotDone=.false.
47 | end if
48 | end do
49 |
50 | if (NotDone) then
51 | ntTerm=ntTerm+1
52 | end if
53 | end do
54 | end if
55 |
56 | return
57 | end subroutine conlp
58 |
--------------------------------------------------------------------------------
/src/mod/blade.f90:
--------------------------------------------------------------------------------
1 | module blade
2 |
3 | ! Blade data
4 |
5 | ! Blade loading data
6 | real, allocatable :: GB(:) ! Bound vorticity
7 | real, allocatable :: OGB(:) ! Old bound vorticity (previous time step)
8 | real, allocatable :: GB_Raw(:) ! Raw (pre-filter) bound vorticity
9 | real, allocatable :: AOA(:) ! AOA on blade elements
10 | real, allocatable :: AOA_Last(:) ! Last value of AOA on blade elements
11 |
12 | real, allocatable :: UIWake(:) ! Velocity induced at blade from wake
13 | real, allocatable :: VIWake(:) ! Velocity induced at blade from wake
14 | real, allocatable :: WIWake(:) ! Velocity induced at blade from wake
15 |
16 | ! Induced velocity on blade lattice points
17 | real, allocatable :: UB(:) ! Lattice point x velocity for each element end lattice point
18 | real, allocatable :: VB(:) ! Lattice point y velocity for each element end lattice point
19 | real, allocatable :: WB(:) ! Lattice point z velocity for each element end lattice point
20 |
21 | ! Freestream velocity on blade lattice points
22 | real, allocatable :: UFSB(:) ! Lattice point x velocity for each element end lattice point
23 | real, allocatable :: VFSB(:) ! Lattice point y velocity for each element end lattice point
24 | real, allocatable :: WFSB(:) ! Lattice point z velocity for each element end lattice point
25 |
26 | ! Timestep filter
27 | integer :: TSFilFlag ! 1 to enable timestep filtering, 0 for no filtering (default)
28 | integer :: ntsf ! Number of timesteps over which the bound vorticity is filtered smooth (if TSFilFlag = 1)
29 | real :: KTF
30 |
31 | contains
32 |
33 | subroutine blade_cns(MaxSegEnds)
34 |
35 | ! Constructor - allocates memory for arrays containing element induced velocities on all blades
36 |
37 | integer :: MaxSegEnds
38 |
39 | allocate(GB(MaxSegEnds))
40 | allocate(OGB(MaxSegEnds))
41 | allocate(GB_Raw(MaxSegEnds))
42 | allocate(AOA(MaxSegEnds))
43 | allocate(AOA_Last(MaxSegEnds))
44 | allocate(UIWake(MaxSegEnds))
45 | allocate(VIWake(MaxSegEnds))
46 | allocate(WIWake(MaxSegEnds))
47 | allocate(UB(MaxSegEnds))
48 | allocate(VB(MaxSegEnds))
49 | allocate(WB(MaxSegEnds))
50 | allocate(UFSB(MaxSegEnds))
51 | allocate(VFSB(MaxSegEnds))
52 | allocate(WFSB(MaxSegEnds))
53 |
54 |
55 | end subroutine blade_cns
56 |
57 | subroutine UpdateAOALast(ne)
58 |
59 | integer :: ne
60 | integer :: k
61 |
62 | ! Save last AOA values for each element
63 |
64 | do k=1,ne
65 | AOA_Last(k)=AOA(k)
66 | end do
67 |
68 | end subroutine UpdateAOALast
69 |
70 | subroutine UpdateTSFilter(ne)
71 |
72 | integer :: ne
73 | integer :: k
74 |
75 | ! Update filtered bound vorticity (filtered smooth over approx. ntsf timesteps using a first order discrete filter)
76 |
77 | do k=1,ne
78 | GB(k)=KTF*GB_Raw(k) + (1.0-KTF)*GB(k)
79 | end do
80 |
81 | end subroutine UpdateTSFilter
82 |
83 | end module blade
84 |
--------------------------------------------------------------------------------
/src/mod/compiler.f90:
--------------------------------------------------------------------------------
1 | #define xstr(s) str(s)
2 | #define str(s) #s
3 |
4 | module compiler
5 |
6 | ! compiler module
7 |
8 | implicit none
9 |
10 | ! Include mkl.fi include file if using Intel Fortran compiler
11 | #if defined(__INTEL_COMPILER)
12 | include "mkl.fi"
13 | #endif
14 |
15 | character(80) :: COMPILER_STRING, COMPILER_VER
16 |
17 | contains
18 |
19 |
20 | subroutine print_compiler_info()
21 |
22 | ! print_compiler_info() : print compiler info to stdout
23 |
24 | #if defined(__GFORTRAN__)
25 | COMPILER_VER = __VERSION__
26 | write(*,'(A,A)') "Compiled with GNU Fortran version: ", COMPILER_VER
27 | #elif defined(__INTEL_COMPILER)
28 | COMPILER_VER = xstr(__INTEL_COMPILER)
29 | write(*,'(A,A,".",A)') "Compiled with Intel Fortran version: ", COMPILER_VER(1:2), COMPILER_VER(3:4)
30 | #else
31 | write(*,'(A)') "Compiled with unknown compiler."
32 | #endif
33 |
34 | ! Alert if MKL is enabled
35 | #if defined(__INTEL_COMPILER)
36 | !dec$if defined(__INTEL_MKL__)
37 | write(*,'(A)') "Intel MKL enabled."
38 | !dec$endif
39 | #endif
40 |
41 | ! print the Git version (VERSION must be defined via Makefile macro and defined
42 | ! with compiler flag -DVERSION='')
43 | #if defined(VERSION)
44 | write(*,'(A,A)') "Git version: ", VERSION
45 | #else
46 | write(*,'(A)') "Git version: unknown"
47 | #endif
48 |
49 | end subroutine print_compiler_info
50 |
51 |
52 | end module compiler
53 |
--------------------------------------------------------------------------------
/src/mod/configr.f90:
--------------------------------------------------------------------------------
1 | module configr
2 |
3 | ! Configuration data
4 |
5 | ! Input flags
6 | character*80 :: jbtitle ! job title
7 | integer :: ifc ! Flag to use final convergence step
8 | integer :: iXTerm ! Flag to ignore wake points beyond xstop
9 | integer :: DiagOutFlag ! Set to 1 to print iteration info to stdout
10 | integer :: PRFlag ! 0 for no pitch rate aero effects, 1 to include these effects
11 |
12 | ! Params
13 | integer :: nb ! Number of blades
14 | integer :: nbe ! Number of blade segments/elements in a blade
15 | integer :: ne ! Number of blade segment ends (total)
16 | integer :: iut ! Number of time steps over which the wake velocities are left constant (Note: values of 0, -1, and -2 have defined meanings)
17 | integer :: nr ! Number of revolutions to perform
18 | integer :: iRev ! Revolution counter
19 | integer :: nt ! Time step counter
20 | integer :: nti ! Number of time steps per revolution
21 | integer :: nric ! Intermediate rev at which to switch to final convergence (if ifc = 1)
22 | integer :: ntif ! final number of time steps per revolution (used instead of nti during final convergence step if ifc = 1)
23 | integer :: iutf ! final number of time steps between updating the wake velocities (used instead of iut during final convergence step if ifc = 1)
24 | integer :: ntTerm ! Time step level beyond which wake is ignored (if iXTerm = 1)
25 | integer :: nSect ! Number of airfoil section data tables used
26 | integer :: nsw ! Next iteration at which wake velocities will be calculated
27 | integer :: nsWall ! Next iteration at which the wall models will be updated
28 | real :: convrg ! Convergence level (Note: for no convergence check, input -1)
29 | real :: convrgf ! Convergence level to be used for final convergence step (if ifc = 1) (Note: for no convergence check, input -1)
30 | real :: XStop ! If iXTerm = 1, ignore wake beyond x = xstop
31 | real :: CTExcrM ! Additional machine level excrescence torque based on tip speed and Rmax
32 | real :: VCRFB ! Vortex core radius factor (on max blade chord) for bound vortex
33 | real :: VCRFT ! Vortex core radius factor (on blade discretization level) for trailing wake vorticies
34 | real :: VCRFS ! Vortex core radius factor (on temporal discretization level) for spanwise wake vorticies
35 |
36 | integer :: WakeElemOutIntervalTimesteps ! Number of revolutions between writing wake data
37 | integer :: WakeElemOutStartTimestep ! Revolution number at which to start writing wake data
38 | integer :: WakeElemOutEndTimestep ! Revolution number at which to stop writing wake data
39 |
40 | integer :: FieldOutIntervalTimesteps ! Number of revolutions between writing wake data
41 | integer :: FieldOutStartTimestep ! Revolution number at which to start writing wake data
42 | integer :: FieldOutEndTimestep ! Revolution number at which to stop writing wake data
43 |
44 | integer :: WallOutIntervalTimesteps ! Number of revolutions between writing wall data
45 | integer :: WallOutStartTimestep ! Revolution number at which to start writing wall data
46 | integer :: WallOutEndTimestep ! Revolution number at which to stop writing wall data
47 |
48 | real :: ut ! Tip speed ratio
49 | real :: dt ! Normalized timestep
50 | real :: delt ! Phase angle step
51 | real :: wRotX ! Normalized machine angular velocity X
52 | real :: wRotY ! Normalized machine angular velocity Y
53 | real :: wRotZ ! Normalized machine angular velocity Z
54 | real :: RotX ! Machine rotation axis X
55 | real :: RotY ! Machine rotation axis Y
56 | real :: RotZ ! Machine rotation axis Z
57 | real :: RotPX ! Rotation origin X
58 | real :: RotPY ! Rotation origin Y
59 | real :: RotPZ ! Rotation origin Z
60 | real :: ReM ! Machine Reynolds number based on Rmax
61 | real :: MInf ! Freestream mach number
62 |
63 | real :: ForceC ! Output force normalization
64 | real :: TorqueC ! Output torque normalization
65 | real :: PowerC ! Output power normalization
66 | real :: AT ! Normalized frontal area (frontal area / (equitorial radius)^2)
67 | real :: AreaT ! Frontal area
68 |
69 | ! Current normalized time and phase angle (Theta)
70 | real :: Theta ! Current turbine rotation phase angle
71 | real :: TimeN ! Current normalized time
72 |
73 | ! Rev average quantities
74 | real :: CPAve ! Rev average power coeff
75 | real :: KPAve ! Rev average tip power coeff
76 | real :: CTRAve ! Rev average torque coeff
77 | real :: CFxAve ! Rev average Fx coeff
78 | real :: CFyAve ! Rev average Fy coeff
79 | real :: CFzAve ! Rev average Fz coeff
80 | real :: PowerAve ! Rev average power
81 | real :: TorqueAve ! Rev average torque
82 | real :: CPSum
83 | real :: CTRSum
84 | real :: CFxSum
85 | real :: CFySum
86 | real :: CFzSum
87 |
88 | end module configr
89 |
--------------------------------------------------------------------------------
/src/mod/fielddata.f90:
--------------------------------------------------------------------------------
1 | module fielddata
2 |
3 | implicit none
4 |
5 | ! module to initialize and store field velocity data for output
6 | ! used by WriteFieldData.f95
7 |
8 | integer :: FieldOutFlag
9 |
10 | ! Velocity calculation on a grid
11 | character(1000) :: FieldOutHead = 'Normalized Time (-),X/R (-),Y/R (-),Z/R (-),U/Uinf (-),V/Uinf (-),W/Uinf (-),Ufs/Uinf (-),Vfs/Uinf (-),Wfs/Uinf (-)'
12 |
13 | ! number of grid elements in each direction
14 | integer :: nxgrid
15 | integer :: nygrid
16 | integer :: nzgrid
17 |
18 | ! grid extents
19 | real :: xgridL
20 | real :: xgridU
21 | real :: ygridL
22 | real :: ygridU
23 | real :: zgridL
24 | real :: zgridU
25 |
26 | ! grid spacing
27 | real :: dxgrid, dygrid, dzgrid
28 |
29 | ! arrays holding grid locations
30 | real, allocatable :: XGrid(:,:,:)
31 | real, allocatable :: YGrid(:,:,:)
32 | real, allocatable :: ZGrid(:,:,:)
33 | real, allocatable :: VXInd(:,:,:)
34 | real, allocatable :: VYInd(:,:,:)
35 | real, allocatable :: VZInd(:,:,:)
36 | real, allocatable :: UfsGrid(:,:,:)
37 | real, allocatable :: VfsGrid(:,:,:)
38 | real, allocatable :: WfsGrid(:,:,:)
39 |
40 | ! array index counters
41 | integer :: xcount, ycount, zcount
42 |
43 | contains
44 |
45 | subroutine fielddata_cns()
46 |
47 | ! allocate location and velocity arrays
48 | allocate(XGrid(nxgrid,nygrid,nzgrid))
49 | allocate(YGrid(nxgrid,nygrid,nzgrid))
50 | allocate(ZGrid(nxgrid,nygrid,nzgrid))
51 | allocate(VXInd(nxgrid,nygrid,nzgrid))
52 | allocate(VYInd(nxgrid,nygrid,nzgrid))
53 | allocate(VZInd(nxgrid,nygrid,nzgrid))
54 | allocate(UfsGrid(nxgrid,nygrid,nzgrid))
55 | allocate(VfsGrid(nxgrid,nygrid,nzgrid))
56 | allocate(WfsGrid(nxgrid,nygrid,nzgrid))
57 |
58 | ! Compute the appropriate grid spacing
59 | ! (if number of grids is 1 in any direction, catch a divide-by-zero error)
60 | if (nxgrid==1) then
61 | dxgrid=0.0
62 | else
63 | dxgrid=(xgridU-xgridL)/(nxgrid-1)
64 | end if
65 |
66 | if (nygrid==1) then
67 | dygrid=0.0
68 | else
69 | dygrid=(ygridU-ygridL)/(nygrid-1)
70 | end if
71 |
72 | if (nzgrid==1) then
73 | dzgrid=0.0
74 | else
75 | dzgrid=(zgridU-zgridL)/(nzgrid-1)
76 | end if
77 |
78 | ! Set up grid node locations and initialize induced velocities
79 | do zcount=1,nzgrid
80 | do ycount=1,nygrid
81 | do xcount=1,nxgrid
82 | ! Setup grid node locations
83 | XGrid(xcount,ycount,zcount)=xgridL+(xcount-1)*dxgrid
84 | YGrid(xcount,ycount,zcount)=ygridL+(ycount-1)*dygrid
85 | ZGrid(xcount,ycount,zcount)=zgridL+(zcount-1)*dzgrid
86 |
87 | ! Initialize induced velocities to zero
88 | VXInd(xcount,ycount,zcount)=0.0
89 | VYInd(xcount,ycount,zcount)=0.0
90 | VZInd(xcount,ycount,zcount)=0.0
91 |
92 | ! Initialize freestream velocities to zero
93 | UfsGrid(xcount,ycount,zcount)=0.0
94 | VfsGrid(xcount,ycount,zcount)=0.0
95 | WfsGrid(xcount,ycount,zcount)=0.0
96 | end do
97 | end do
98 | end do
99 | end subroutine fielddata_cns
100 |
101 | end module fielddata
--------------------------------------------------------------------------------
/src/mod/fnames.f90:
--------------------------------------------------------------------------------
1 | Module fnames
2 |
3 | use pathseparator
4 |
5 | implicit none
6 |
7 | integer :: nargin, FNLength, status, DecInd
8 | character(80) :: InputFN, SFOutputFN, RevOutputFN, TSOutputFN, ELOutputFN, RegOutputFN, WakeOutputFN, FieldOutputFN, GPOutputFN, FSOutputFN, DSOutputFN, FNBase
9 | character(255) :: OutputPath,WakeElemOutputPath,WallOutputPath,FieldOutputPath,ProbeOutputPath
10 |
11 | contains
12 |
13 | subroutine get_FNBase()
14 | logical :: back
15 |
16 | Call get_command_argument(1,InputFN,FNLength,status)
17 |
18 | back=.true.
19 | FNBase=InputFN((index(InputFN,'/',back)+1):len(InputFN))
20 | DecInd=index(FNBase,'.',back)
21 |
22 | if (DecInd > 1) then
23 | FNBase=FNBase(1:(DecInd-1))
24 | end if
25 |
26 | end subroutine get_FNBase
27 |
28 | End Module fnames
29 |
--------------------------------------------------------------------------------
/src/mod/iecgust.f90:
--------------------------------------------------------------------------------
1 | module iecgust
2 |
3 | ! Parameters defining IEC Gust
4 |
5 | integer :: Igust ! Flag for IEC Gust
6 | real :: gustamp ! Gust Amplitude (m/s)
7 | real :: gusttime ! Gust Timescale (sec)
8 | real :: gustA ! Non-dimensional gust amplitude (U'/Uinf)
9 | real :: gustT ! Non-dimensional gust timescale (T*Uinf/Rmax)
10 | real :: gustX0 ! Starting position of the gust upstream (# rotor radii)
11 |
12 | end module iecgust
13 |
--------------------------------------------------------------------------------
/src/mod/output.f90:
--------------------------------------------------------------------------------
1 | module output
2 |
3 | ! Arrays to be output to csv data files
4 |
5 | ! Output flags
6 | integer :: BladeElemOutFlag ! Set to 1 to output loads data at each timestep for each element, 0 to omit this output
7 | integer :: DynStallOutFlag ! Set to 1 to output dynamic stall diagnostic output
8 |
9 | ! Scale parameters and flow properties
10 | character(10000) :: ParamsOutHead = 'R (ft),Frontal Area (ft^2),RPM,U (ft/s),rho (slugs/ft^3),tempr (degF),vis (slugs/(ft*s)),q (lb/ft^2),TSR (-),ReM (-),FnR (-)'
11 | real :: ParamsOutData(1,11) ! Data
12 |
13 | ! Revolution average data
14 | character(10000) :: RevOutHead = 'Rev,Power Coeff. (-),Tip Power Coeff. (-),Torque Coeff. (-),Fx Coeff. (-),Fy Coeff. (-),Fz Coeff. (-),Power (kW),Torque (ft-lbs)'
15 | real :: RevOutData(1,9) ! Revolution average data for each revolution
16 |
17 | ! Timestep data
18 | character(10000) :: TSOutHead = 'Normalized Time (-),Theta (rad),Rev,Torque Coeff. (-),Power Coeff. (-),Fx Coeff. (-),Fy Coeff. (-),Fz Coeff. (-)'
19 | character(1000) :: TSOutHeadBlade = 'Blade Fx Coeff. (-),Blade Fy Coeff. (-),Blade Fz Coeff. (-),Blade Torque Coeff. (-)'
20 | character(1000) :: TSOutHeadStrut = 'Strut Fx Coeff. (-),Strut Fy Coeff. (-),Strut Fz Coeff. (-),Strut Torque Coeff. (-)'
21 | integer :: TSOutNCols ! Number of timestep outputs
22 | real, allocatable :: TSOutData(:,:) ! Timestep data for each timestep
23 |
24 | ! Element loads data
25 | character(10000) :: BladeElemOutHead = 'Normalized Time (-),Theta (rad),Blade,Element,Rev,x/R (-),y/R (-),z/R (-),AOA25 (deg),AOA50 (deg),AOA75 (deg),AdotNorm (-),Re (-),Mach (-),Ur (-),IndU (-),IndV (-),IndW (-),GB (?),CL (-),CD (-),CM25 (-),&
26 | &CLCirc (-),CN (-),CT (-),Fx (-),Fy (-),Fz (-),te (-)'
27 | integer :: BladeElemOutNCols = 29 ! Number of element loads outputs
28 | integer :: BladeElemOutRow ! Rows of element row output
29 | real, allocatable :: BladeElemOutData(:,:) ! Element loads data for each timestep
30 |
31 | ! Dynamic stall diagnostic output
32 | integer :: DynStallOutType
33 | character(10000) :: DynStallOutBVHead = 'Normalized Time (-),Theta (rad),Blade,Element,Rev,alpha (deg),adotnorm (-),alrefL (deg),alrefD (deg),DynamicFlagL,DynamicFlagD'
34 | integer :: DynStallOutBVNCols = 11 ! Number of BV diagnostic outputs
35 | integer :: DynStallOutBVRow ! Rows of element row output
36 | real, allocatable :: DynStallOutBVData(:,:) ! Diagnostic data for each timestep
37 | character(10000) :: DynStallOutLBHead = 'Normalized Time (-),Theta (rad),Blade,Element,Rev,L1,L2,L3,L4,L5,L6,L7,L8,L9,Check'
38 | integer :: DynStallOutLBNCols = 15 ! Number of LB diagnostic outputs
39 | integer :: DynStallOutLBRow ! Rows of element row output
40 | real, allocatable :: DynStallOutLBData(:,:) ! Diagnostic data for each timestep
41 |
42 | contains
43 |
44 | subroutine output_cns(MaxSeg, MaxBlades, MaxStruts, DSFlag)
45 |
46 | ! Constructor for the arrays in this module
47 |
48 | integer :: MaxSeg, MaxBlades, MaxStruts, DSFlag
49 |
50 | integer :: i
51 |
52 | ! Calculate number of timestep outputs
53 | TSOutNCols=8+4*MaxBlades+4*MaxStruts
54 | allocate(TSOutData(1,TSOutNCols))
55 | do i=1,MaxBlades
56 | TSOutHead=trim(TSOutHead)//','//trim(TSOutHeadBlade)
57 | end do
58 | do i=1,MaxStruts
59 | TSOutHead=trim(TSOutHead)//','//trim(TSOutHeadStrut)
60 | end do
61 |
62 | if (BladeElemOutFlag == 1) then
63 | allocate(BladeElemOutData(MaxSeg,BladeElemOutNCols))
64 | end if
65 |
66 | if (DynStallOutFlag == 1 .AND. DSFlag == 1) then
67 | DynStallOutType = 1
68 | allocate(DynStallOutBVData(MaxSeg,DynStallOutBVNCols))
69 | else if (DynStallOutFlag == 1 .AND. DSFlag == 2) then
70 | DynStallOutType = 2
71 | allocate(DynStallOutLBData(MaxSeg,DynStallOutLBNCols))
72 | end if
73 |
74 |
75 | end subroutine output_cns
76 |
77 | end module output
78 |
--------------------------------------------------------------------------------
/src/mod/parameters.f90:
--------------------------------------------------------------------------------
1 | module parameters
2 |
3 | ! Sizes for arrays
4 |
5 | integer :: MaxBlades, MaxSegPerBlade, MaxSegEndPerBlade, MaxSegEnds, MaxSeg ! Maximums for blade geometry arrays
6 | integer :: MaxStruts ! Maximums for strut arrays
7 | integer :: MaxAirfoilSect, MaxReVals, MaxAOAVals ! Maximums for airfoil coeff data arrays
8 | integer :: MaxRevs ! Max revolutions
9 | integer :: MaxTimeStepPerRev ! Max time increments in a revolution
10 | integer :: MaxTimeSteps ! Max total time increments (MaxRevs * MaxTimeStepPerRev)
11 | integer :: MaxWakeNodes ! Max number of points in wake for an element (MaxRevs * MaxTimeStepPerRev)
12 | integer :: MaxNLIters ! Max iterations to perform in converging non linear component of blade element system
13 |
14 | end module parameters
15 |
--------------------------------------------------------------------------------
/src/mod/pathseparator.f90:
--------------------------------------------------------------------------------
1 | module pathseparator
2 |
3 | implicit none
4 |
5 | character(1) :: path_separator
6 |
7 | contains
8 |
9 | subroutine get_path_separator()
10 |
11 | #if defined(PATHSEP)
12 | path_separator = PATHSEP
13 | #else
14 | ! fallback
15 | path_separator = '/'
16 | #endif
17 | end subroutine get_path_separator
18 |
19 | end module pathseparator
--------------------------------------------------------------------------------
/src/mod/probesystem.f90:
--------------------------------------------------------------------------------
1 | module probesystem
2 |
3 | ! probe module for computing and writing velocities at a specified location
4 |
5 | use configr
6 | use pathseparator
7 |
8 | implicit none
9 |
10 | ! probe output options
11 | integer :: ProbeFlag ! flag for enabling probes (1 for probes, 0 for none)
12 | integer :: ProbeOutIntervalTimesteps ! interval between probe writes
13 | integer :: ProbeOutStartTimestep ! timestep at which to start writing probe data
14 | integer :: ProbeOutEndTimestep ! timestep at which to end writing probe data
15 |
16 | ! probe output header flag
17 | logical :: ProbeHeadersWritten = .False.
18 |
19 | type ProbeType
20 |
21 | real :: p(3) ! probe location
22 | real :: vel(3) ! probe velocity (at a single instant)
23 | real :: vel_fs(3) ! probe free-stream velocity (at a single instant)
24 | character(80) :: output_filename
25 |
26 | end type ProbeType
27 |
28 | type(ProbeType), allocatable :: probes(:) ! array to hold probes
29 | integer :: nprobes ! number of probes
30 | integer, parameter :: probe_iounit = 28 ! IO unit for probes
31 |
32 |
33 | contains
34 |
35 | subroutine compute_probe_vel(probe)
36 |
37 | ! compute_probe_vel() : computes the induced velocity and free-stream velocity
38 | ! at the probe location and stores it to the probe variables
39 |
40 | type(ProbeType), intent(inout) :: probe
41 | integer :: ygcErr ! error flag for CalcFreeStream
42 |
43 | ! Calculate wall and wake induced velocities at grid locations
44 | Call CalcIndVel(NT,ntTerm,NBE,NB,NE, &
45 | probe%p(1),probe%p(2),probe%p(3), &
46 | probe%vel(1),probe%vel(2),probe%vel(3))
47 |
48 | ! Calculate free stream velocities at grid locations
49 | Call CalcFreestream(probe%p(1),probe%p(2),probe%p(3), &
50 | probe%vel_fs(1),probe%vel_fs(2),probe%vel_fs(3), &
51 | ygcErr)
52 |
53 | end subroutine compute_probe_vel
54 |
55 |
56 | subroutine read_probes(probespec_filename)
57 | ! read_probes() : reads in the coordinates of probes from a file
58 | ! Coordinates should be formatted as
59 | ! nprobes
60 | ! x1 y1 z1
61 | ! x2 y2 z2
62 | ! ...
63 | ! EOF
64 |
65 | integer i
66 | integer, parameter :: iounit = 20
67 | character(len=*), intent(in) :: probespec_filename ! Probe specification filename
68 |
69 | ! open file for reading
70 | open(unit=iounit, form='formatted', file=probespec_filename)
71 |
72 | ! read number of probes
73 | read(iounit,*) nprobes
74 |
75 | ! allocate for probes
76 | allocate(probes(nprobes))
77 |
78 | ! read probe coordinates
79 | do i=1,nprobes
80 | read(iounit,*) probes(i)%p(1), probes(i)%p(2), probes(i)%p(3)
81 | end do
82 |
83 | ! close file
84 | close(unit=iounit)
85 |
86 | end subroutine read_probes
87 |
88 |
89 | subroutine write_probe_headers(probe_output_path)
90 |
91 | ! write_probe_headers() : writes the headers for the probe output files to the specified
92 | ! probe_output_path
93 |
94 | integer :: probe_num
95 | character(7198), parameter :: probe_file_header='X/R (-),Y/R (-),Z/R (-)'
96 | character(7198), parameter :: probe_data_header='Normalized Time (-),U/Uinf (-),V/Uinf (-),W/Uinf (-),Ufs/Uinf (-),Vfs/Uinf (-),Wfs/Uinf (-)'
97 | character(80) :: probe_num_str
98 | character(80) :: probe_output_path
99 |
100 | ! write header files
101 | do probe_num=1,nprobes
102 |
103 | ! assemble the probe filename in the pattern
104 | ! [run_name]_probe_[probe_num].csv
105 | write(probe_num_str,'(I5.5)') probe_num
106 | probes(probe_num)%output_filename = 'probe_'//trim(probe_num_str)//'.csv'
107 |
108 | ! open/write header/close file
109 | open(probe_iounit, file=trim(adjustl(probe_output_path))//path_separator//probes(probe_num)%output_filename)
110 |
111 | ! write probe location
112 | write(probe_iounit,'(A)') trim(probe_file_header)
113 | write(probe_iounit,'(E13.7,",",$)') probes(probe_num)%p(1)
114 | write(probe_iounit,'(E13.7,",",$)') probes(probe_num)%p(2) ! probe location
115 | write(probe_iounit,'(E13.7,"," )') probes(probe_num)%p(3)
116 |
117 | ! write probe data header
118 | write(probe_iounit,'(A)') trim(probe_data_header)
119 |
120 | ! close file
121 | close(probe_iounit)
122 |
123 | end do
124 |
125 | end subroutine write_probe_headers
126 |
127 |
128 | subroutine write_probes(probe_output_path)
129 |
130 | ! write_probes() : Computes the velocity for all probes and writes the data to the
131 | ! corresponding probe output file.
132 |
133 | type(ProbeType) :: probe
134 | integer :: probe_num
135 | character(80) :: probe_output_path
136 |
137 | ! Write the headers to the probe output files if they haven't been written already
138 | if (ProbeHeadersWritten .eqv. .False.) then
139 | call write_probe_headers(probe_output_path)
140 | ProbeHeadersWritten = .True.
141 | end if
142 |
143 | ! Write the probe data
144 | do probe_num=1,nprobes
145 |
146 | ! get the current probe
147 | probe = probes(probe_num)
148 |
149 | ! open file for writing
150 | open(probe_iounit, file=trim(adjustl(probe_output_path))//path_separator//probe%output_filename, POSITION='append')
151 |
152 | ! compute velocity at probe location
153 | call compute_probe_vel(probe)
154 |
155 | ! write the position, velocity, and free-stream velocity
156 | write(probe_iounit,'(E13.7,",",$)') TimeN ! Normalized simulation time (t*Uinf/Rmax)
157 | write(probe_iounit,'(E13.7,",",$)') probe%vel(1)
158 | write(probe_iounit,'(E13.7,",",$)') probe%vel(2) ! induced velocities
159 | write(probe_iounit,'(E13.7,",",$)') probe%vel(3)
160 | write(probe_iounit,'(E13.7,",",$)') probe%vel_fs(1)
161 | write(probe_iounit,'(E13.7,",",$)') probe%vel_fs(2) ! freestream velocities
162 | write(probe_iounit,'(E13.7)' ) probe%vel_fs(3) ! Dont suppress carriage return on last column
163 |
164 | ! close file
165 | close(probe_iounit)
166 |
167 | end do
168 |
169 | end subroutine write_probes
170 |
171 | end module probesystem
--------------------------------------------------------------------------------
/src/mod/regtest.f90:
--------------------------------------------------------------------------------
1 | module regtest
2 |
3 | ! Regression test outputs
4 |
5 | integer :: RegTFlag ! Set to 1 to perform a single iteration regression test, 0 for normal operation
6 | integer :: Reg_TS ! timestep number
7 | integer :: Reg_NLIter ! non linear convergence loop iteration
8 | integer :: Reg_ElemNum ! element number
9 | integer :: Reg_DFL ! BV lift dynamic stall flag
10 | integer :: Reg_LBC ! LB dynamic stall model logic checksum
11 |
12 | real :: Reg_CPOut ! cp after one time step
13 | real :: Reg_ElemAOA ! AOA on each element
14 | real :: Reg_ElemCirc ! Circulation on each element
15 | real :: Reg_dElemCirc ! delta circulation between NL iterations
16 | real :: Reg_MaxWS ! max wall source value
17 | real :: Reg_MaxWVM ! max velocity mag in first wake shed
18 |
19 | end module regtest
20 |
--------------------------------------------------------------------------------
/src/mod/shear.f90:
--------------------------------------------------------------------------------
1 | module shear
2 |
3 | ! Ground shear layer model data
4 |
5 | real :: yGC ! Ground clearance height to radius ratio
6 | real :: yRef ! Freestream reference height to radius ratio (99% location maybe)
7 | real :: slex ! Exponent for shear layer model (Ex. 1/2 for parabolic BL model, 0 for constant freestream...)
8 | real :: Tempr ! Temperature in degF
9 |
10 | end module shear
11 |
--------------------------------------------------------------------------------
/src/mod/strut.f90:
--------------------------------------------------------------------------------
1 | module strut
2 |
3 | ! Strut geometry and loads outputs
4 |
5 | use util
6 |
7 | implicit none
8 |
9 | type StrutType
10 | integer :: NElem
11 | real :: TtoC ! Strut thickness to chord ratio
12 |
13 | ! Strut element end locations
14 | real, allocatable :: MCx(:)
15 | real, allocatable :: MCy(:)
16 | real, allocatable :: MCz(:)
17 | ! Strut chord to radius at element ends
18 | real, allocatable :: CtoR(:)
19 | ! Strut element center locations
20 | real, allocatable :: PEx(:)
21 | real, allocatable :: PEy(:)
22 | real, allocatable :: PEz(:)
23 | ! Strut element spanwise vector
24 | real, allocatable :: sEx(:)
25 | real, allocatable :: sEy(:)
26 | real, allocatable :: sEz(:)
27 | ! Strut element chord to radius and norm. area
28 | real, allocatable :: ECtoR(:)
29 | real, allocatable :: EAreaR(:)
30 | ! Blade and element indicies to which the strut connects (for interference drag calc)
31 | ! For struts that are attached to the rotor shaft at one end (not to another blade), set the appropriate BInd and EInd values to zero.
32 | ! BIndS, EIndS : first strut element
33 | ! BIndE, EIndE : last strut element
34 | integer :: BIndS
35 | integer :: EIndS
36 | integer :: BIndE
37 | integer :: EIndE
38 |
39 | real :: LR ! Strut length to radius ratio
40 | ! Interference drag calc parameters
41 | real :: tcS ! Thickness to chord of the blade element at the strut-blade junction (first strut element)
42 | real :: tcE ! Thickness to chord of the blade element at the strut-blade junction (last strut element)
43 |
44 | ! Current flow quantities at each element center
45 | real, allocatable :: ReStrut(:)
46 | real, allocatable :: u(:) ! u velocity over Uinf
47 | real, allocatable :: v(:) ! v velocity over Uinf
48 | real, allocatable :: w(:) ! w velocity over Uinf
49 | real, allocatable :: ur(:) ! velocity mag over Uinf
50 |
51 | ! Current strut element coeffs (normalized by strut element scale parameters)
52 | real, allocatable :: Cd0(:)
53 |
54 | ! Current total strut output (nomalized by machine scale parameters)
55 | real :: CP ! Power coefficient due to this strut
56 | real :: CTR ! Torque coefficient due to this strut
57 | real :: CFx ! Fx coefficient due to this strut
58 | real :: CFy ! Fy coefficient due to this strut
59 | real :: CFz ! Fz coefficient due to this strut
60 |
61 | end type StrutType
62 |
63 | type(StrutType), allocatable :: Struts(:)
64 |
65 | integer :: NStrut ! number of struts
66 |
67 | real, parameter :: ReCrit=3.0e5
68 | real :: Cdpar ! Additional strut parasitic interference drag coefficient based on "chord area" (chord squared)
69 |
70 | ! Current sum of output over all struts (nomalized by machine scale parameters)
71 | real :: CP_S ! Power coefficient due to all struts
72 | real :: CTR_S ! Torque coefficient due to all struts
73 | real :: CFx_S ! Fx coefficient due to all struts
74 | real :: CFy_S ! Fy coefficient due to all struts
75 | real :: CFz_S ! Fz coefficient due to all struts
76 |
77 |
78 | contains
79 |
80 |
81 | subroutine strut_comp_cns(SInd,NElem)
82 |
83 | implicit none
84 |
85 | ! Constructor for the arrays for a strut component
86 |
87 | integer :: SInd, NElem
88 |
89 | Struts(SInd)%NElem=NElem
90 | allocate(Struts(SInd)%MCx(NElem+1))
91 | allocate(Struts(SInd)%MCy(NElem+1))
92 | allocate(Struts(SInd)%MCz(NElem+1))
93 | allocate(Struts(SInd)%CtoR(NElem+1))
94 | allocate(Struts(SInd)%PEx(NElem))
95 | allocate(Struts(SInd)%PEy(NElem))
96 | allocate(Struts(SInd)%PEz(NElem))
97 | allocate(Struts(SInd)%sEx(NElem))
98 | allocate(Struts(SInd)%sEy(NElem))
99 | allocate(Struts(SInd)%sEz(NElem))
100 | allocate(Struts(SInd)%ECtoR(NElem))
101 | allocate(Struts(SInd)%EAreaR(NElem))
102 | allocate(Struts(SInd)%ReStrut(NElem))
103 | allocate(Struts(SInd)%u(NElem))
104 | allocate(Struts(SInd)%v(NElem))
105 | allocate(Struts(SInd)%w(NElem))
106 | allocate(Struts(SInd)%ur(NElem))
107 | allocate(Struts(SInd)%Cd0(NElem))
108 |
109 | end subroutine strut_comp_cns
110 |
111 |
112 | subroutine RotateStrut(SNum,delt,nrx,nry,nrz,px,py,pz)
113 |
114 | implicit none
115 |
116 | integer :: SNum, j, NElem
117 | real :: delt,nrx,nry,nrz,px,py,pz
118 | real :: vrx,vry,vrz
119 |
120 | ! Rotates data in strut arrays. Rotate element end geometry and recalculate element geometry.
121 |
122 | NElem=Struts(SNum)%NElem
123 |
124 | ! Strut end locations
125 | do j=1,NElem+1
126 | Call QuatRot(Struts(SNum)%MCx(j),Struts(SNum)%MCy(j),Struts(SNum)%MCz(j),delt,nrx,nry,nrz,px,py,pz,vrx,vry,vrz)
127 | Struts(SNum)%MCx(j)=vrx
128 | Struts(SNum)%MCy(j)=vry
129 | Struts(SNum)%MCz(j)=vrz
130 | end do
131 |
132 | ! Calc element geometry
133 | Call CalcSEGeom(SNum)
134 |
135 |
136 | end subroutine RotateStrut
137 |
138 |
139 | subroutine CalcSEGeom(SNum)
140 |
141 | implicit none
142 |
143 | integer :: SNum, NElem, j
144 | real :: sE(3)
145 | real :: sEM
146 |
147 | ! Calculates element geometry from element end geometry
148 |
149 | NElem=Struts(SNum)%NElem
150 |
151 | do j=1,NElem
152 |
153 | ! Element center locations
154 | Struts(SNum)%PEx(j)=0.5*(Struts(SNum)%MCx(j+1)+Struts(SNum)%MCx(j))
155 | Struts(SNum)%PEy(j)=0.5*(Struts(SNum)%MCy(j+1)+Struts(SNum)%MCy(j))
156 | Struts(SNum)%PEz(j)=0.5*(Struts(SNum)%MCz(j+1)+Struts(SNum)%MCz(j))
157 |
158 | ! Set spanwise vectors
159 | sE=(/Struts(SNum)%MCx(j+1)-Struts(SNum)%MCx(j),Struts(SNum)%MCy(j+1)-Struts(SNum)%MCy(j),Struts(SNum)%MCz(j+1)-Struts(SNum)%MCz(j)/)
160 | sEM=sqrt(dot_product(sE,sE))
161 | sE=sE/sEM
162 | Struts(SNum)%sEx(j)=sE(1)
163 | Struts(SNum)%sEy(j)=sE(2)
164 | Struts(SNum)%sEz(j)=sE(3)
165 |
166 | ! Calc element area and chord
167 | Struts(SNum)%ECtoR(j)=0.5*(Struts(SNum)%CtoR(j+1)+Struts(SNum)%CtoR(j))
168 | Struts(SNum)%EAreaR(j)=sEM*Struts(SNum)%ECtoR(j)
169 |
170 | end do
171 |
172 |
173 | end subroutine CalcSEGeom
174 |
175 |
176 | subroutine StrutElemCoeffs(SInd,EInd)
177 |
178 | implicit none
179 |
180 | integer :: SInd, EInd
181 | real :: st, ReS, vs, AOS, pc
182 | real :: Cflam, Cdlam, Cfturb, Cdturb, Fblend
183 |
184 | ! Updates strut element coeffs for current flow states
185 |
186 | ! Calc sideslip angle
187 | vs=Struts(SInd)%u(EInd)*Struts(SInd)%sEx(EInd)+Struts(SInd)%v(EInd)*Struts(SInd)%sEy(EInd)+Struts(SInd)%w(EInd)*Struts(SInd)%sEz(EInd)
188 | AOS=asin(vs/Struts(SInd)%ur(EInd))
189 |
190 | ! Effective section thickness and Re referenced to nominal flow path length rather than chord (p/c = 1/cos(AOS))
191 | ! Allows estimation at high element sideslip (azimuthal struts).
192 | pc=1.0/abs(cos(AOS))
193 | pc=min(pc,Struts(SInd)%LR/Struts(SInd)%ECtoR(EInd)) ! Limit p to strut length
194 | st=Struts(SInd)%TtoC/pc
195 | ReS=Struts(SInd)%ReStrut(EInd)*pc
196 |
197 | ! Calculate strut element profile drag
198 | Cflam = 2.66 / SQRT(ReS) ! Laminar friction drag coefficient
199 | Cdlam = 2.0 * Cflam * (1 + st) + st**2 ! Laminar drag coefficient
200 | Cfturb = 0.044 / ReS**(1.0/6.0) ! Turbulent friction drag coefficient
201 | Cdturb = 2.0 * Cfturb * (1.0 + 2.0*st + 60.0*st**4) ! Turbulent drag coefficient
202 | Fblend = 0.5 * (1.0 + TANH((LOG10(ReS)-LOG10(ReCrit))/0.2)) ! Blending function for transition between laminar and turbulent drag
203 | Struts(SInd)%Cd0(EInd) = (1.0-Fblend) * Cdlam + Fblend * Cdturb ! Profile drag coefficient
204 |
205 |
206 | end subroutine StrutElemCoeffs
207 |
208 | end module strut
209 |
--------------------------------------------------------------------------------
/src/mod/time.f90:
--------------------------------------------------------------------------------
1 | module time
2 |
3 | ! Info about program real time usage
4 |
5 | real :: t0 ! Time in seconds marker for the beginning of the performance iteration
6 | real :: Time1 ! Time at end of last rev
7 | real :: Time2 ! Time at end of this rev
8 | real :: dtime ! Delta time over this rev
9 | real :: etime ! Elapsed time from begining at end of this rev
10 |
11 | end module time
12 |
--------------------------------------------------------------------------------
/src/mod/tower.f90:
--------------------------------------------------------------------------------
1 | module tower
2 |
3 | implicit none
4 |
5 | ! user inputs
6 | real :: tower_D, tower_x, tower_ybot, tower_ytop
7 | integer :: Itower, tower_Npts
8 |
9 | real :: tower_CD, theta0
10 | real, parameter :: Delta_wake = 0.289, W0_wake = 1.75
11 | real, parameter :: S_wake = 0.083, alpha_wake = 0.693
12 |
13 | real, allocatable, dimension(:) :: tower_y, tower_Vx
14 |
15 | contains
16 |
17 | subroutine setup_tower
18 |
19 | implicit none
20 |
21 | integer :: i
22 |
23 | Allocate(tower_y(tower_Npts), tower_Vx(tower_Npts))
24 |
25 |
26 | If (tower_Npts .LE. 1) Then
27 | Write(*,'(''Inadequate number of tower points.'')')
28 | End If
29 |
30 | Do i = 1,tower_Npts
31 | tower_y(i) = tower_ybot + (REAL(i)-1) / &
32 | (REAL(tower_Npts) - 1) * (tower_ytop-tower_ybot)
33 | tower_Vx(i) = 0.0
34 | End Do
35 |
36 | theta0 = 0.5 * tower_CD * tower_D
37 |
38 | end subroutine setup_tower
39 |
40 |
41 | FUNCTION interp_tower_Vx(y)
42 |
43 | implicit none
44 |
45 | real :: interp_tower_Vx, y
46 | integer :: i
47 |
48 | interp_tower_Vx = 0.0
49 | Do i = 1,tower_Npts-1
50 | If ((y .GE. tower_y(i)) .AND. (y .LE. tower_y(i+1))) Then
51 | interp_tower_Vx = tower_Vx(i) + (y - tower_y(i)) &
52 | / (tower_y(i+1) - tower_y(i)) * (tower_Vx(i+1) - &
53 | tower_Vx(i))
54 | Exit
55 | End If
56 | End Do
57 |
58 | return
59 |
60 | END FUNCTION interp_tower_Vx
61 |
62 |
63 | FUNCTION wake_defect_velocity(x,y,z)
64 |
65 | implicit none
66 |
67 | real :: x, y, z, wake_defect_velocity
68 | real :: W, xi, Vx_tow, u0, f_wake
69 |
70 | If (x .LE. tower_x) Then
71 | wake_defect_velocity = 0.0
72 | return
73 | End If
74 |
75 | If ((y .LT. tower_ybot) .OR. (y .GT. tower_ytop)) Then
76 | wake_defect_velocity = 0.0
77 | return
78 | End If
79 |
80 |
81 | ! Calculate the wake half-width at this x location
82 | W = Delta_wake * sqrt((x-tower_x)*theta0)
83 | xi = z / W ! non-dimensional transverse coordinate
84 | Vx_tow = interp_tower_Vx(y) ! x-dir fluid velocity at tower
85 | !Write(20,'(F20.12)') Vx_tow
86 | u0 = W0_wake * sqrt(theta0/(x-tower_x )) * Vx_tow ! max velocity
87 | ! defect at this x location
88 | f_wake = exp(-alpha_wake * xi * xi) ! non-dimensional velocity defect
89 | wake_defect_velocity = u0 * f_wake ! dimensional velocity defect
90 |
91 | if (wake_defect_velocity .GT. 0.9 * Vx_tow) then
92 | wake_defect_velocity = 0.9 * Vx_tow
93 | end if
94 |
95 | return
96 |
97 | END FUNCTION wake_defect_velocity
98 |
99 | end module tower
100 |
--------------------------------------------------------------------------------
/src/mod/util/geomutils.f90:
--------------------------------------------------------------------------------
1 | module geomutils
2 |
3 | ! module geomutils : functions for querying 2-D and 3-D geometries
4 |
5 | implicit none
6 |
7 | private
8 | public dist_point_to_segment_2d, is_point_in_quad
9 |
10 | contains
11 |
12 | function dist_point_to_segment_2d(x,y,x1,y1,x2,y2)
13 |
14 | ! dist_point_to_segment_2d() : returns the shortest distance from a point p to a line segment.
15 | ! Based on the method of http://paulbourke.net/geometry/pointlineplane/
16 | !
17 | ! Inputs:
18 | ! =======
19 | ! x,y : the coordinates of the point
20 | !
21 | ! Output:
22 | ! =======
23 | ! x1,y1,x2,y2 : the start and end points of the line segment
24 | !
25 |
26 | real, intent(in) :: x,y,x1,y1,x2,y2
27 | real :: dist_point_to_segment_2d
28 |
29 | real :: u, dx, dy
30 | real :: x_closest, y_closest
31 |
32 | ! check if points are coincident
33 | if (x1==x2 .and. y1==y2) then
34 | write(*,*) 'Error in dist_point_to_segment_2d : Points are coincident!'
35 | stop
36 | end if
37 |
38 | dx = x2-x1
39 | dy = y2-y1
40 |
41 | u = ((x-x1)*dx + (y-y1)*dy) / abs(dx**2 - dy**2)
42 |
43 | if (u < 0) then
44 | x_closest = x1
45 | y_closest = y1
46 | else if (u > 1) then
47 | x_closest = x2
48 | y_closest = y2
49 | else
50 | x_closest = x1+u*dx
51 | y_closest = y1+u*dy
52 | end if
53 |
54 | dist_point_to_segment_2d = ((x_closest-x)**2 + (y_closest-y)**2)**0.5
55 |
56 | end function dist_point_to_segment_2d
57 |
58 |
59 | function is_point_in_quad(p_list, p)
60 |
61 | ! is_point_in_quad() : For a quadrilateral whose corners are given in p_list, returns
62 | ! True if the point p lies within the polygon and False if the point p lies outside of it.
63 | ! Based on the method of http://paulbourke.net/geometry/polygonmesh/
64 | !
65 | ! Inputs:
66 | ! =======
67 | ! p_list : a (4,3) array of points. expects that p(:,3) = 0 (the z-coordinate is zero)
68 | !
69 | ! Outputs:
70 | ! ========
71 | ! is_point_in_quad (logical) : .True. if point is inside the quadrilateral, .False. otherwise
72 |
73 | real, intent(in) :: p_list(4,3)
74 | real, intent(in) :: p(3)
75 | logical :: is_point_in_quad
76 |
77 | integer, parameter :: nsides = 4
78 | real :: p1(3), p2(3)
79 | real :: xinters
80 |
81 | integer :: i, counter
82 |
83 | counter = 0
84 |
85 | p1 = p_list(1,1:3)
86 |
87 | do i=1,nsides
88 | p2 = p_list(mod(i,nsides)+1,1:3)
89 | if (p(2) > min(p1(2),p2(2))) then
90 | if (p(2) <= max(p1(2),p2(2))) then
91 | if (p(1) <= max(p1(1),p2(1))) then
92 | if (p1(2) /= p2(2)) then
93 | xinters = (p(2)-p1(2))*(p2(1)-p1(1))/(p2(2)-p1(2)) + p1(1)
94 | if (p1(1) == p2(1) .or. p(1) <= xinters) then
95 | counter = counter + 1
96 | end if
97 | end if
98 | end if
99 | end if
100 | end if
101 | p1 = p2
102 | end do
103 |
104 | if (mod(counter,2) == 0) then
105 | is_point_in_quad = .False.
106 | else
107 | is_point_in_quad = .True.
108 | end if
109 |
110 | end function is_point_in_quad
111 |
112 |
113 | end module geomutils
--------------------------------------------------------------------------------
/src/mod/util/pidef.f90:
--------------------------------------------------------------------------------
1 | module pidef
2 |
3 | ! Value of pi
4 | real, parameter :: pi = 4.0*atan(1.0)
5 | real, parameter :: conrad = pi/180.0
6 | real, parameter :: condeg = 180.0/pi
7 |
8 | end module pidef
9 |
--------------------------------------------------------------------------------
/src/mod/util/plot3d.f90:
--------------------------------------------------------------------------------
1 | module plot3d
2 |
3 | ! plot3d : subroutines for reading plot3d mesh data
4 |
5 | implicit none
6 |
7 | private
8 | public read_p3d_multiblock
9 |
10 | integer, parameter :: IOunit = 20
11 |
12 | contains
13 |
14 | subroutine read_p3d_multiblock(xyz_filename,nblocks,ni,nj,nk,x,y,z)
15 | ! read_p3d_multiblock() : Read a formatted (ASCII) Plot3D multi-block mesh file.
16 | ! Coordinates must be specified one number per line.
17 | !
18 | ! Inputs
19 | ! ======
20 | ! xyz_filename : Plot3d mesh filename (*.xyz)
21 | !
22 | ! Outputs
23 | ! =======
24 | ! nblocks : number of blocks
25 | ! ni : mesh dimensions in first direction
26 | ! nj : mesh dimensions in second direciton
27 | ! nk : mesh dimensions in third direction
28 |
29 | character(len=*), intent(in) :: xyz_filename ! Plot3d mesh filename (*.xyz)
30 |
31 | integer, intent (out) :: nblocks ! number of blocks
32 | integer, allocatable, intent(out) :: ni(:), nj(:), nk(:) ! mesh dimensions
33 | real, allocatable, intent(out) :: x(:,:,:,:), y(:,:,:,:), z(:,:,:,:) ! mesh coordinates
34 |
35 | integer :: i,j,k
36 | integer :: m,n
37 | integer :: nimax, njmax, nkmax
38 |
39 | ! open file
40 | open(unit=IOunit, form='formatted', file=xyz_filename)
41 |
42 | ! read number of blocks
43 | read(IOunit,*) nblocks
44 |
45 | ! allocate for the dimensions of the mesh blocks
46 | allocate(ni(nblocks))
47 | allocate(nj(nblocks))
48 | allocate(nk(nblocks))
49 |
50 | ! read dimensions of each block
51 | do m=1,nblocks
52 | read(IOunit,*) ni(m),nj(m),nk(m)
53 | end do
54 |
55 | ! compute maximum mesh size and allocate
56 | nimax = maxval(ni)
57 | njmax = maxval(nj)
58 | nkmax = maxval(nk)
59 |
60 | allocate(x(nimax,njmax,nkmax,nblocks))
61 | allocate(y(nimax,njmax,nkmax,nblocks))
62 | allocate(z(nimax,njmax,nkmax,nblocks))
63 |
64 | ! read in data for each block
65 | do m = 1, nblocks
66 | read(IOunit,*) &
67 | ((( x(i,j,k,m), i=1,ni(m)), j=1,nj(m)), k=1,nk(m)), &
68 | ((( y(i,j,k,m), i=1,ni(m)), j=1,nj(m)), k=1,nk(m)), &
69 | ((( z(i,j,k,m), i=1,ni(m)), j=1,nj(m)), k=1,nk(m))
70 | enddo
71 |
72 | ! close file
73 | close(IOunit)
74 |
75 | end subroutine read_p3d_multiblock
76 |
77 |
78 | end module plot3d
--------------------------------------------------------------------------------
/src/mod/util/util.f90:
--------------------------------------------------------------------------------
1 | module util
2 |
3 | ! Utilities used in other modules and elsewhere in the code
4 |
5 | contains
6 |
7 | subroutine cross(ax,ay,az,bx,by,bz,cx,cy,cz)
8 |
9 | real ax,ay,az,bx,by,bz,cx,cy,cz
10 |
11 | cx = ay*bz - az*by
12 | cy = az*bx - ax*bz
13 | cz = ax*by - ay*bx
14 |
15 | end subroutine cross
16 |
17 |
18 | subroutine QuatRot(vx,vy,vz,Theta,nRx,nRy,nRz,Ox,Oy,Oz,vRx,vRy,vRz)
19 |
20 | ! % Perform rotation of vector v around normal vector nR using the
21 | ! % quaternion machinery.
22 | ! % v: input vector
23 | ! % Theta: rotation angle (rad)
24 | ! % nR: normal vector around which to rotate
25 | ! % Origin: origin point of rotation
26 | ! %
27 | ! % vR: Rotated vector
28 |
29 | real :: vx,vy,vz,Theta,nRx,nRy,nRz,Ox,Oy,Oz,vRx,vRy,vRz
30 |
31 | real :: p(4,1), pR(4,1), q(4), qbar(4), nRMag, vOx, vOy, vOz
32 | real :: QL(4,4), QbarR(4,4)
33 |
34 | ! Force normalize nR
35 | nRMag=sqrt(nRx**2+nRy**2+nRz**2)
36 | nRx=nRx/nRMag
37 | nRy=nRy/nRMag
38 | nRz=nRz/nRMag
39 |
40 | ! Quaternion form of v
41 | vOx=vx-Ox
42 | vOy=vy-Oy
43 | vOz=vz-Oz
44 | p=reshape((/0.0,vOx,vOy,vOz/),(/4,1/))
45 |
46 | ! Rotation quaternion and conjugate
47 | q=(/cos(Theta/2),nRx*sin(Theta/2),nRy*sin(Theta/2),nRz*sin(Theta/2)/)
48 | qbar=(/q(1),-q(2),-q(3),-q(4)/)
49 |
50 | QL=transpose(reshape((/q(1), -q(2), -q(3), -q(4), &
51 | q(2), q(1), -q(4), q(3), &
52 | q(3), q(4), q(1), -q(2), &
53 | q(4), -q(3), q(2), q(1)/),(/4,4/)))
54 |
55 | QbarR=transpose(reshape((/qbar(1), -qbar(2), -qbar(3), -qbar(4), &
56 | qbar(2), qbar(1), qbar(4), -qbar(3), &
57 | qbar(3), -qbar(4), qbar(1), qbar(2), &
58 | qbar(4), qbar(3), -qbar(2), qbar(1)/),(/4,4/)))
59 |
60 | ! Rotate p
61 | pR=matmul(matmul(QbarR,QL),p)
62 | vRx=pR(2,1)+Ox
63 | vRy=pR(3,1)+Oy
64 | vRz=pR(4,1)+Oz
65 |
66 | end subroutine QuatRot
67 |
68 |
69 | subroutine csvwrite(FID,Header,Data,WriteHead,NRows)
70 |
71 | integer :: FID, WriteHead, NRows
72 | character(10000) :: Header
73 | real :: Data(:,:)
74 |
75 | integer :: nRow, nCol, i, j
76 |
77 | ! Writes comma separated data to file specified by FID (assumed already opened with the open command).
78 | ! If WriteHead is 1, will write the input header line, else will skip header line.
79 | ! NRows is the number of rows of Data to write. If NRows<0, all rows of Data will be written...
80 |
81 | ! Write header
82 | if (WriteHead>0) then
83 | write (FID,'(A)') trim(Header)
84 | end if
85 |
86 | ! Write data
87 | if (NRows>=0) then
88 | nRow=NRows
89 | else
90 | nRow=size(Data,1)
91 | end if
92 | nCol=size(Data,2)
93 | do i=1,nRow
94 | do j=1,nCol
95 | if (j