diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 48085ccba8..7a137bbb85 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -14,18 +14,16 @@ **Additional supporting information** +**Generative AI usage** + + **Test results, if applicable** - - +- [ ] r-test branch merging required diff --git a/.github/workflows/automated-dev-tests.yml b/.github/workflows/automated-dev-tests.yml index bdb408b54b..cc3dbcd597 100644 --- a/.github/workflows/automated-dev-tests.yml +++ b/.github/workflows/automated-dev-tests.yml @@ -366,10 +366,12 @@ jobs: path: | ${{github.workspace}}/build/reg_tests/glue-codes/openfast-cpp ${{github.workspace}}/build/reg_tests/glue-codes/python + ${{github.workspace}}/build/reg_tests/glue-codes/other ${{github.workspace}}/build/reg_tests/modules/aerodyn ${{github.workspace}}/build/reg_tests/modules/moordyn ${{github.workspace}}/build/reg_tests/modules/inflowwind ${{github.workspace}}/build/reg_tests/modules/hydrodyn + ${{github.workspace}}/build/reg_tests/modules/seastate !${{github.workspace}}/build/reg_tests/glue-codes/openfast-cpp/5MW_Baseline @@ -529,7 +531,7 @@ jobs: uses: actions/upload-artifact@v4 if: failure() with: - name: rtest-OF-offshore + name: rtest-OF-multirotor path: | ${{github.workspace}}/build/reg_tests/glue-codes/openfast !${{github.workspace}}/build/reg_tests/glue-codes/openfast/5MW_Baseline diff --git a/CMakeLists.txt b/CMakeLists.txt index a730094f27..62eab1775b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -126,6 +126,11 @@ if (OPENMP) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") link_libraries("${OpenMP_CXX_LIBRARIES}") endif() +elseif (NOT BLA_VENDOR) + # If we're not using OpenMP, and a specific BLAS vendor has not been set, + # set MKL threading to sequential to avoid potential issues with + # small calculations taking longer due to threading overhead (turbsim). + set(MKL_THREADING "sequential") endif() #------------------------------------------------------------------------------- diff --git a/README.rst b/README.rst index f63e070680..6787bf7a10 100644 --- a/README.rst +++ b/README.rst @@ -16,7 +16,7 @@ OpenFAST is a wind turbine simulation tool which builds on FAST v8. FAST.Farm extends the capability of OpenFAST to simulate multi-turbine wind farms. They were created with the goal of being community models developed and used by research laboratories, academia, and industry. They are managed by a dedicated team at the -National Renewable Energy Lab. Our objective is to ensure that OpenFAST and FAST.Farm +National Laboratory of the Rockies. Our objective is to ensure that OpenFAST and FAST.Farm are sustainable software that are well tested and well documented. If you'd like to contribute, see the `Developer Documentation `_ and any open GitHub issues with the @@ -30,11 +30,11 @@ tag. Part of the WETO Stack ---------------------- -OpenFAST is primarily developed with the support of the U.S. Department of Energy and is part of the `WETO Software Stack `_. For more information and other integrated modeling software, see: +OpenFAST is primarily developed with the support of the U.S. Department of Energy and is part of the `WETO Software Stack `_. For more information and other integrated modeling software, see: -* `Portfolio Overview `_ -* `Entry Guide `_ -* `OpenFAST Workshop `_ +* `Portfolio Overview `_ +* `Entry Guide `_ +* `OpenFAST Workshop `_ FAST v8 - OpenFAST @@ -155,8 +155,8 @@ Please use `GitHub Issues `_ to: * report bugs * request code enhancements -Users and developers may also be interested in the NREL National Wind -Technology Center (NWTC) `phpBB Forum `_, +Users and developers may also be interested in the NLR National Wind +Technology Center (NWTC) `Forum `_, which is still maintained and has a long history of FAST-related questions and answers. @@ -164,9 +164,9 @@ Acknowledgments --------------- OpenFAST and FAST.Farm are maintained and developed by researchers and software -engineers at the `National Renewable Energy Laboratory `_ -(NREL), with support from the US Department of Energy's Wind Energy Technology -Office. NREL gratefully acknowledges development contributions from the following +engineers at the `National Laboratory of the Rockies `_ +(NLR), with support from the US Department of Energy's Wind Energy Technology +Office. NLR gratefully acknowledges development contributions from the following organizations: * Envision Energy USA, Ltd diff --git a/docs/conf.py b/docs/conf.py index c63f31292c..06a4c7e2f2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,6 +20,7 @@ import sys import subprocess import re +import datetime #sys.path.append(os.path.abspath('_extensions/')) @@ -127,18 +128,18 @@ def runDoxygen(sourcfile, doxyfileIn, doxyfileOut): master_doc = 'index' # General information about the project. -project = u'OpenFAST' -copyright = u'2023, National Renewable Energy Laboratory' -author = u'OpenFAST Team' +project = f'OpenFAST' +copyright = f'{datetime.date.today().year}, National Renewable Energy Laboratory' +author = f'OpenFAST Team' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = u'4.1' +version = f'4.1' # The full version, including alpha/beta/rc tags. -release = u'v4.1.2' +release = f'v4.1.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -226,8 +227,8 @@ def runDoxygen(sourcfile, doxyfileIn, doxyfileOut): ( master_doc, 'Openfast.tex', - u'OpenFAST Documentation', - u'National Renewable Energy Laboratory', + f'OpenFAST Documentation', + f'National Renewable Energy Laboratory', 'manual' ), ] @@ -241,7 +242,7 @@ def runDoxygen(sourcfile, doxyfileIn, doxyfileOut): ( master_doc, 'openfast', - u'OpenFAST Documentation', + f'OpenFAST Documentation', [author], 1 ) @@ -257,7 +258,7 @@ def runDoxygen(sourcfile, doxyfileIn, doxyfileOut): ( master_doc, 'OpenFAST', - u'OpenFAST Documentation', + f'OpenFAST Documentation', author, 'OpenFAST', 'One line description of project.', diff --git a/docs/index.rst b/docs/index.rst index 9dd5504e5f..56a4b46189 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -27,7 +27,7 @@ and underlying modules are mostly written in Fortran (adhering to the 2003 standard), and modules can also be written in C or C++. It was created with the goal of being a community model developed and used by research laboratories, academia, and industry. It is -managed by a dedicated team at the National Renewable Energy Lab. +managed by a dedicated team at the National Lab of the Rockies. Our objective is to ensure that OpenFAST is well tested, well documented, and self-sustaining software. To that end, we are continually improving the documentation and test coverage for existing code, and we diff --git a/docs/source/install/install_vs_windows.rst b/docs/source/install/install_vs_windows.rst index a682d4d5e4..d5a309f64c 100644 --- a/docs/source/install/install_vs_windows.rst +++ b/docs/source/install/install_vs_windows.rst @@ -11,21 +11,21 @@ Prerequisites 1. A version of Visual Studio (VS). - - Currently VS 2013 Professional and VS 2015 Community Edition have been tested with OpenFAST. + - NOTE: not all VS Studio versions are supported by the Intel compilers. In general, the Fortran compiler must be newer than Visual Studio. A list of Intel Fortran compatible VS versions and specific installation notes are found `here `_. - - A list of Intel Fortran compatible VS versions and specific installation notes are found `here `_. + - Currently VS 2019 Community Edition, VS 2022 Professional, and VS 2022 Community Edition have been tested with OpenFAST. Download VS 2022 Community `here `__. - - The included C/C++ project files for MAP++ and the Registry are compatible with VS 2013, but will upgrade seemlessly to a newer version of VS. + - When installing Visual Studio, select the ``Desktop development with C++`` under ``Workloads``. - - If you download and install `Visual Studio 2015 Community Edition `__, you will need to be sure and select the ``C/C++ component`` using the ``Customize`` option. + - Note: The included C/C++ project files for MAP++ and the Registry are compatible with VS 2019, but will upgrade seemlessly to a newer version of VS. 2. Intel Fortran Compiler - - Currently only version 2017.1 has been tested with OpenFAST, but any newer version should be compatible. + - We recommend compiling with the IFX compiler from Intel. This is included in the ``Intel Fortran Essentials`` installation package. Currently tested with version 2025.3 - - You can download an Intel Fortran compiler `here `__. + - You can download ``Intel Fortran Essentials`` `here `__. Note: do not install the ``oneAPI HPC Toolkit`` - - Only install Intel Fortran after you have completed your Visual Studio installation. + - Only install Intel Fortran after you have completed your Visual Studio installation. Note that Intel Fortran must be compatible with your version of Visual Studio. See `here `__ for compatibility tables. 3. Git for Windows diff --git a/docs/source/testing/index.rst b/docs/source/testing/index.rst index 77fea3c218..cc91f40846 100644 --- a/docs/source/testing/index.rst +++ b/docs/source/testing/index.rst @@ -38,10 +38,9 @@ pushing new commits will trigger the tests. Obtaining and configuring the test suite ---------------------------------------- Portions of the test suite are linked to the OpenFAST repository through a -`git submodule`. Specifically, the following two repositories are included: +`git submodule`. Specifically, the following repository is included: - `r-test `__ -- `pFUnit `__ .. tip:: @@ -56,8 +55,8 @@ build process with an additional CMake flag: # BUILD_TESTING - Build the testing tree (Default: OFF) cmake .. -DBUILD_TESTING:BOOL=ON -Aside from this flag, the default CMake configuration is suitable for most systems. -See the :ref:`understanding_cmake` section for more details on configuring -the CMake targets. While the unit tests must be built with CMake due to its external -dependencies, the regression test may be executed without CMake, as described in -:ref:`python_driver`. +Aside from this flag, the default CMake configuration is suitable for most +systems. See the :ref:`understanding_cmake` section for more details on +configuring the CMake targets. While the unit tests must be built with CMake due +to its dependency on `test_drive` (included in the source code), the regression +test may be executed without CMake, as described in :ref:`python_driver`. diff --git a/docs/source/testing/unit_test.rst b/docs/source/testing/unit_test.rst index 0b3cb15a0a..8e421df93d 100644 --- a/docs/source/testing/unit_test.rst +++ b/docs/source/testing/unit_test.rst @@ -113,4 +113,3 @@ Some useful topics to consider when developing and testing for OpenFAST are: - `Test driven development `__ - `Separation of concerns `__ -- `pFUnit usage `__ diff --git a/docs/source/user/elastodyn/input.rst b/docs/source/user/elastodyn/input.rst index 098795709a..466cf16011 100644 --- a/docs/source/user/elastodyn/input.rst +++ b/docs/source/user/elastodyn/input.rst @@ -195,7 +195,9 @@ Mass and Inertia **HubMass** - Hub mass (kg) -**HubIner** - Hub inertia about rotor axis [3 blades] or teeter axis [2 blades] (kg m^2) +**HubIner** - Hub inertia about rotor axis (2 or 3-blades) (kg m^2) + +**HubIner_Teeter** - Hub inertia about teeter axis (2-blades) (kg m^2) **GenIner** - Generator inertia about HSS (kg m^2) diff --git a/docs/source/user/fast.farm/InputFiles.rst b/docs/source/user/fast.farm/InputFiles.rst index 334b9ec0c6..d51bb97b40 100644 --- a/docs/source/user/fast.farm/InputFiles.rst +++ b/docs/source/user/fast.farm/InputFiles.rst @@ -637,11 +637,14 @@ These are used only if WAT=2. **WAT_DxDyDz**: [three floats, comma separated] Distances (in meters) between points in the x, y, and z directions of the WAT_BoxFile These are used only if WAT=2. -When **WAT=1** the dimensions in each directions are taken as :math:`dx=dy=dz=0.03*\text{RotorDiamRef}`. +When **WAT=1** the dimensions will be set to **[dX_high, dY_high, dZ_high]** if +that is the same for all turbines, otherwise the dimeinaiona will be calculated +using the guidance with :math:`dx=dy=dz=0.03*\text{RotorDiamRef}`. + **WAT_ScaleBox**: [flag] When set to True, the input turbulence box is scaled so that it has zero mean and unit standard deviation at every node. -DEFAULT is False. +DEFAULT is True. **WAT_k_Def** [five floats, comma separated] :math:`[k_\text{def}, k_\text{FMin}, k_\text{DMin}, k_\text{DMax}, e]` Tuning parameters for quasi-steady wake deficit effect in the wake-added diff --git a/docs/source/user/hydrodyn/input_files.rst b/docs/source/user/hydrodyn/input_files.rst index 4dde04b2c1..a1a12a4ba3 100644 --- a/docs/source/user/hydrodyn/input_files.rst +++ b/docs/source/user/hydrodyn/input_files.rst @@ -561,9 +561,9 @@ flow is directed away from the endplate where flow separation is expected, not when the relative flow is impinging on the endplate where flow separation is unlikely. Option 0 is suitable for strip-theory-only members, whereas option 1 might be better suited for -hybrid potential-flow members with drag force. Note that option 1 +hybrid potential-flow members with drag force. Note that option 0 uses a leading coefficient of 1/4 when computing the drag force, while -option 2 uses the more common leading coefficient of 1/2 since drag +option 1 uses the more common leading coefficient of 1/2 since drag is usually only applied to one of the two endplates of the member instead of on both. diff --git a/docs/source/user/index.rst b/docs/source/user/index.rst index f2d22f797d..f06e595df3 100644 --- a/docs/source/user/index.rst +++ b/docs/source/user/index.rst @@ -32,6 +32,7 @@ This section contains documentation for the OpenFAST module-coupling environment TurbSim FAST.Farm C++ API + WaveTank Additional module documentation diff --git a/docs/source/user/other/index.rst b/docs/source/user/other/index.rst new file mode 100644 index 0000000000..4aba57b5bd --- /dev/null +++ b/docs/source/user/other/index.rst @@ -0,0 +1,40 @@ +.. _WaveTank: + +WaveTank +======== + +The WaveTank glue-code is an experimental code for coupling hardware-in-the-loop +MHK models in a wavetank to software simulating the MHK turbine loads that +cannot be physically modeled in the wave tank. The *OpenFAST* modules +*SeaState*, *AeroDyn*, *MoorDyn*, and *InflowWind* are statically linked into a +single dynamic library (``cmake`` target ``wavetanktesting_c_binding``) with a +c-binding based interface. This library can be called from *LabView* or another +code. + +Inputs to the library include the time and motions, including the velocities and +accelerations, located at a single reference poitn at each time step. The +resulting forces and moments are returned to the calling code. + +Restrictions +~~~~~~~~~~~~ +The current setup WaveTank library has several restrictions: + +- rigid structure including platform, tower, and nacelle +- no yaw DOF +- rigid rotor +- constant rotor RPM for entire simulation +- no option for controller interfacing at present +- visualization limitted to *AeroDyn* and *SeaState* +- Current implementation only supports floating MHK turbines (``MHK = 2``). Other modes are present but not fully implemented. + + + + +Input File +~~~~~~~~~~ + + +.. toctree:: + :maxdepth: 2 + + wavetank_input.rst diff --git a/docs/source/user/other/wavetank_input.rst b/docs/source/user/other/wavetank_input.rst new file mode 100644 index 0000000000..5ef7490064 --- /dev/null +++ b/docs/source/user/other/wavetank_input.rst @@ -0,0 +1,242 @@ +.. _WaveTank-Input: + +Input File +---------- + +This document describes the WaveTank configuration input file (``wavetankconfig_input.txt``) used to set up and run the WaveTank model for marine hydrokinetic (MHK) turbine testing. + +- The file is read by the WaveTank library during initialization. + +Conventions and Units +--------------------- + +- SI units are used throughout: m, s, kg, N, Pa. +- Angles are in degrees unless otherwise specified. +- Rotational speed is in rpm where noted. +- Positions and heights are referenced to Mean Sea Level (MSL) unless otherwise noted. +- Input files for modules may be relative or absolute paths. + +File and Simulation Control +--------------------------- + +OutRootName (string) + Root name used when writing summary or other files. + Example: ``FRM1Q_Floating_tank_test``. + +DT (s) + Nominal timestep for WaveTank internal scheduling. Currently unused/reserved. + +TMax (s) + Maximum simulation time for WaveTank internal scheduling. Currently unused/reserved. + +MHK (switch) + MHK turbine type switch: + + - 0: Not an MHK turbine + - 1: Fixed MHK turbine + - 2: Floating MHK turbine + + Only the floating option (2) is supported at present. + +InterpOrd (-) + Interpolation order for internal data interpolation. Currently unused/reserved. + +DebugLevel (switch) + Controls logging and visualization detail: + + - 0: none + - 1: I/O summary + - 2: + positions/orientations passed + - 3: + input file + - 4: + all meshes + +.. note:: + Parameters marked “unused” are reserved for future development and are currently ignored by the code path. + +Froude Scaling (disabled) +------------------------- + +The following parameters may appear but are typically commented out. Froude scaling is not complete in the current code. Do not use unless explicitly enabled. + +ScaleFact (-) + Froude scaling factor λ = (full-size dimension) / (model-size dimension). Expected > 1 for scale-model testing. + +DensFact (-) + Density ratio ρ_full / ρ_model, used with Froude scaling of forces/moments. + +Environment +----------- + +Gravity (m/s^2) + Gravitational acceleration. + +WtrDens (kg/m^3) + Water (working fluid) density. + +WtrVisc (m^2/s) + Kinematic viscosity of the working fluid. + +SpdSound (m/s) + Speed of sound in the working fluid. + +Patm (Pa) + Atmospheric pressure. Used for cavitation checks. + +Pvap (Pa) + Vapor pressure of the working fluid. Used for cavitation checks. + +WtrDpth (m) + Water depth. + +MSL2SWL (m) + Offset between still-water level (SWL) and mean sea level (MSL); positive upward. + +Sea State +--------- + +SS_InputFile (string) + Path to SeaState input file defining wave conditions. Ensure path is valid relative to the run directory or use an absolute path. + +WaveTimeShift (s) + Time shift applied to the SeaState wave time series to adjust phase and match tank conditions. + +MoorDyn +------- + +MD_InputFile (string) + Path to MoorDyn input file defining mooring system properties and connections. + +AeroDyn and InflowWind +---------------------- + +AD_InputFile (string) + Path to AeroDyn input file defining aerodynamic model configuration (used for hydro/aero coupling as applicable in MHK context). + +IfW_InputFile (string) + Path to InflowWind input file defining inflow conditions for the rotor (e.g., currents or wind, depending on model setup). + +Turbine Geometry and Reference Frames +------------------------------------- + +NumBl (-) + Number of blades on the rotor. + +HubRad (m) + Distance from the rotor apex to the blade root. + +PreCone (deg) + Blade cone angle. + +OverHang (m) + Distance from the yaw axis (tower centerline) to the rotor apex. Negative values indicate rotor apex aft of the yaw axis under the model’s convention. + +ShftTilt (deg) + Rotor shaft tilt angle. + +Twr2Shft (m) + Vertical distance from tower-top to the rotor shaft center (nacelle center). Negative values are below tower-top. + +TowerHt (m) + Height of the tower relative to MSL. Tower is vertically aligned with ``TowerBsPt`` (sloped towers not supported). + +TowerBsPt (m, m, m) + Tower base location relative to the platform reference position in x and y, and relative to MSL in z: + + - x: along surge axis + - y: along sway axis + - z: height relative to MSL + +PtfmRefPos (m, m, m) + Platform reference point position relative to MSL. All platform motions and loads connect at this point. + +PtfmRefOrient (deg, deg, deg) + Platform reference orientation given as Euler angles [roll, pitch, yaw]. + +Turbine Initial Conditions +-------------------------- + +RotSpeed (rpm) + Initial rotational speed of the rotor (in rotor coordinates). + +NacYaw (deg) + Initial or fixed nacelle yaw angle. + +BldPitch (deg) + Initial blade 1 pitch angle. If a multi-blade model is used, blade pitch control typically applies per blade in other modules; here this initializes blade 1. + +Azimuth (deg) + Initial rotor azimuth angle. + +Wave Buoy +--------- + +WaveBuoyLoc (m, m) + Location of the wave elevation measurement buoy in the tank coordinate frame. SeaState data is returned at each timestep at this location. + +Output +------ + +SendScreenToFile (flag) + If true, send screen output to a file named ``.screen.log``. + +OutFile (switch) + Controls tabular output of channels: + + - 0: no output file of channels + - 1: output file in text format (written at default DT) + +OutFmt (string) + Format specifier for text tabular output channels (excluding the time channel). Uses a Fortran-like format string. + Example: ``ES20.6E2``. + +VTK Visualization Output +------------------------ + +WrVTK_Dir (string) + Output directory for VTK visualization files. + +WrVTK (switch) + VTK visualization data output: + + - 0: none + - 1: initialization data only + - 2: animation + - 3: mode shapes + +WrVTK_type (switch) + Type of VTK visualization data: + + - 1: surfaces + - 2: basic meshes (lines/points) + - 3: all meshes (debug) + +.. note:: + Only lines/points may be supported in some builds. If surfaces are not + supported, use ``WrVTK_type = 2`` to visualize line/point data. + +WrVTK_DT (s) + Timestep for writing VTK files. + +VTKNacDim (m, m, m, m, m, m) + Nacelle dimensions for VTK surface rendering in the format ``[x0, y0, z0, Lx, Ly, Lz]``: + + - ``x0, y0, z0``: nacelle origin offsets + - ``Lx, Ly, Lz``: nacelle extents along x, y, z + +Implementation Notes and Best Practices +--------------------------------------- + +- Only floating MHK (``MHK = 2``) is currently supported; other MHK modes will + not perform as expected. +- Ensure external file paths (*SeaState*, *MoorDyn*, *AeroDyn*, *InflowWind*) + are valid relative to the working directory or specify absolute paths. +- Coordinate conventions: + + - Positions and heights are referenced to MSL unless otherwise noted. + - The platform reference point (``PtfmRefPos``) is the coupling point for + motions and loads. + - The tower base is defined relative to ``PtfmRefPos`` in x and y, and to MSL + in z. + +- Choose ``OutFmt`` to balance precision and file size. The example ``ES20.6E2`` + is suitable for scientific notation with fixed width. diff --git a/docs/source/user/servodyn-stc/StC_Theory.rst b/docs/source/user/servodyn-stc/StC_Theory.rst index ea6608f380..f452065fb5 100644 --- a/docs/source/user/servodyn-stc/StC_Theory.rst +++ b/docs/source/user/servodyn-stc/StC_Theory.rst @@ -473,7 +473,7 @@ Therefore :math:`\ddot{z}_{_{TMD_Z/P_N}}` is governed by the equations -The forces :math:`F_{X_{_{TMD_Z/O_N}}}` and :math:`F_{Z_{_{TMD_Z/O_N}}}` +The forces :math:`F_{X_{_{TMD_Z/O_N}}}` and :math:`F_{Y_{_{TMD_Z/O_N}}}` are solved noting :math:`\ddot{x}_{_{TMD_Z/P_N}} = \ddot{y}_{_{TMD_Z/P_N}} = 0`: @@ -565,11 +565,11 @@ first-order equations of the form A(\vec{u}) = \left[ \begin{array}{cccccc} 0& 1 &0&0&0&0 \\ - (\dot{\phi}_{_{P/O_N}}^2 + \dot{\psi}_{_{P/O_N}}^2-\frac{k_x}{m_x}) & - (\frac{c_x}{m_x}) &0&0&0&0 \\ + (\dot{\phi}_{_{N/O_N}}^2 + \dot{\psi}_{_{N/O_N}}^2-\frac{k_x}{m_x}) & - (\frac{c_x}{m_x}) &0&0&0&0 \\ 0&0&0& 1 &0&0 \\ - 0&0& (\dot{\theta}_{_{P/O_N}}^2 + \dot{\psi}_{_{P/O_N}}^2-\frac{k_y}{m_y}) & - (\frac{c_y}{m_y}) &0&0 \\ + 0&0& (\dot{\theta}_{_{N/O_N}}^2 + \dot{\psi}_{_{N/O_N}}^2-\frac{k_y}{m_y}) & - (\frac{c_y}{m_y}) &0&0 \\ 0&0&0&0&0& 1 \\ - 0&0&0&0& (\dot{\theta}_{_{P/O_N}}^2 + \dot{\phi}_{_{P/O_N}}^2-\frac{k_z}{m_z}) & - (\frac{c_z}{m_z}) \\ + 0&0&0&0& (\dot{\theta}_{_{N/O_N}}^2 + \dot{\phi}_{_{N/O_N}}^2-\frac{k_z}{m_z}) & - (\frac{c_z}{m_z}) \\ \end{array} \right] and @@ -615,9 +615,9 @@ The output includes reaction forces corresponding to \begin{aligned} \vec{F}_{_{P_G}} = R^T_{_{N/G}} & \left[ \begin{array}{l} - k_x {x}_{_{TMD/P_N}} + c_x \dot{x}_{_{TMD/P_N}} - F_{StopFrc_{X}} - F_{ext_x} - F_{X_{_{TMD_Y/O_N}}} - F_{X_{_{TMD_Z/O_N}}} \\ - k_y {y}_{_{TMD/P_N}} + c_y \dot{y}_{_{TMD/P_N}} - F_{StopFrc_{Y}} - F_{ext_y} - F_{Y_{_{TMD_X/O_N}}} - F_{Y_{_{TMD_Z/O_N}}} \\ - k_z {z}_{_{TMD/P_N}} + c_z \dot{z}_{_{TMD/P_N}} - F_{StopFrc_{Z}} - F_{ext_z} - F_{Z_{_{TMD_X/O_N}}} - F_{Z_{_{TMD_Y/O_N}}} - F_{Z_{PreLoad}} + k_x {x}_{_{TMD_X/P_N}} + c_x \dot{x}_{_{TMD_X/P_N}} - F_{StopFrc_{X}} - F_{ext_x} - F_{X_{_{TMD_Y/O_N}}} - F_{X_{_{TMD_Z/O_N}}} \\ + k_y {y}_{_{TMD_Y/P_N}} + c_y \dot{y}_{_{TMD_Y/P_N}} - F_{StopFrc_{Y}} - F_{ext_y} - F_{Y_{_{TMD_X/O_N}}} - F_{Y_{_{TMD_Z/O_N}}} \\ + k_z {z}_{_{TMD_Z/P_N}} + c_z \dot{z}_{_{TMD_Z/P_N}} - F_{StopFrc_{Z}} - F_{ext_z} - F_{Z_{_{TMD_X/O_N}}} - F_{Z_{_{TMD_Y/O_N}}} - F_{Z_{PreLoad}} \end{array} \right] \end{aligned} diff --git a/glue-codes/fast-farm/src/FASTWrapper.f90 b/glue-codes/fast-farm/src/FASTWrapper.f90 index 65b8e90f1a..3207a139b2 100644 --- a/glue-codes/fast-farm/src/FASTWrapper.f90 +++ b/glue-codes/fast-farm/src/FASTWrapper.f90 @@ -76,6 +76,8 @@ SUBROUTINE FWrap_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, Init TYPE(FAST_ExternInitType) :: ExternInitData INTEGER(IntKi) :: j,k,nb + REAL(ReKi) :: p0(3) ! hub location (in FAST with 0,0,0 as turbine reference) + REAL(R8Ki) :: orientation(3,3) ! temp orientation array INTEGER(IntKi) :: ErrStat2 ! local error status CHARACTER(ErrMsgLen) :: ErrMsg2 ! local error message @@ -196,12 +198,19 @@ SUBROUTINE FWrap_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, Init ) if (Failed()) return; - ! set node initial position/orientation - ! shortcut for - ! call MeshPositionNode(m%ADRotorDisk(k), j, [0,0,r(j)], errStat2, errMsg2) - m%ADRotorDisk(k)%Position(3,:) = p%r ! this will get overwritten later, but we check that we have no zero-length elements in MeshCommit() + ! set node initial position/orientation + ! NOTE: the mesh data for ADRotorDisk gets overwritten before use so it isn't actually important + ! that this match the method used later in the code. We can't use the method from later + ! in the code since the `hub_theta_x_root` is not known at this point. So instead, we + ! will use the input blade root orientation to set the direction. This does not result + ! in a flat disk, but should allow the mesh mapping to work. + p0 = m%Turbine%AD%Input(1)%rotors(1)%HubMotion%Position(:,1) + m%Turbine%AD%Input(1)%rotors(1)%HubMotion%TranslationDisp(:,1) + m%ADRotorDisk(k)%RefOrientation(:,:,1) = m%Turbine%AD%Input(1)%rotors(1)%BladeRootMotion(k)%Orientation(:,:,1) + do j=1,p%nr + m%ADRotorDisk(k)%Position(:,j) = p0 + p%r(j)*m%ADRotorDisk(k)%RefOrientation(3,:,1) + end do m%ADRotorDisk(k)%TranslationDisp = 0.0_R8Ki ! this happens by default, anyway.... - + ! create line2 elements do j=1,p%nr-1 call MeshConstructElement( m%ADRotorDisk(k), ELEMENT_LINE2, errStat2, errMsg2, p1=j, p2=j+1 ); if (Failed()) return; diff --git a/glue-codes/fast-farm/src/FAST_Farm_IO.f90 b/glue-codes/fast-farm/src/FAST_Farm_IO.f90 index c1c578d7ae..e34f2c9df5 100644 --- a/glue-codes/fast-farm/src/FAST_Farm_IO.f90 +++ b/glue-codes/fast-farm/src/FAST_Farm_IO.f90 @@ -168,7 +168,23 @@ SUBROUTINE Farm_PrintSum( farm, WD_InputFileData, ErrStat, ErrMsg ) end select WRITE (UnSum,'(2X,A)') 'Calibrated parameter for wake meandering (-): '//trim(Num2LStr(farm%AWAE%p%C_Meander)) -!FIXME: add summary info about WAT + if (farm%p%WAT == 0) then + write (UnSum,'(/,2X,A)') 'Wake added turbulence: off' + else + write (UnSum,'(/,2X,A)') 'Wake-Added Turbulence (WAT):' + write (UnSum,'(4X,A,3(I8,1X))') 'WAT_NxNyNz: ',farm%p%WAT_NxNyNz(1:3) + write (UnSum,'(4X,A,3(f9.3))') 'WAT_DxDyDz: ',farm%p%WAT_DxDyDz(1:3) + if (farm%p%WAT_ScaleBox) then + write (UnSum,'(4X,A,A)') 'WAT_ScaleBox: ','.TRUE.' + else + write (UnSum,'(4X,A,A)') 'WAT_ScaleBox: ','.FALSE.' + endif + write (UnSum,'(4X,A)') 'coefficients:' + write (UnSum,'(16X,A)') 'k_c f_min D_min D_max e' + write (UnSum,'(A12,5(f9.3))') 'k_Def', WD_InputFileData%WAT_k_Def_k_c, WD_InputFileData%WAT_k_Def_FMin, WD_InputFileData%WAT_k_Def_DMin, WD_InputFileData%WAT_k_Def_DMax, WD_InputFileData%WAT_k_Def_Exp + write (UnSum,'(A12,5(f9.3))') 'k_Grad',WD_InputFileData%WAT_k_Grad_k_c,WD_InputFileData%WAT_k_Grad_FMin,WD_InputFileData%WAT_k_Grad_DMin,WD_InputFileData%WAT_k_Grad_DMax,WD_InputFileData%WAT_k_Grad_Exp + endif + WRITE (UnSum,'(/,A)' ) 'Time Steps' WRITE (UnSum,'(2X,A)') 'Component Time Step Subcyles' @@ -846,7 +862,7 @@ SUBROUTINE Farm_ReadPrimaryFile( InputFile, p, WD_InitInp, AWAE_InitInp, OutList CALL ReadVar( UnIn, InputFile, p%WAT_BoxFile, 'WAT_BoxFile', "Filepath to the file containing the u-component of the turbulence box (either predefined or user-defined) (quoted string)", ErrStat2, ErrMsg2, UnEc ); if(failed()) return call ReadAry( UnIn, InputFile, p%WAT_NxNyNz, 3, "WAT_NxNyNz", "Number of points in the x, y, and z directions of the WAT_BoxFile [used only if WAT=2] (m)", ErrStat2, ErrMsg2, UnEc ); if(failed()) return call ReadAry( UnIn, InputFile, p%WAT_DxDyDz, 3, "WAT_DxDyDz", "Distance (in meters) between points in the x, y, and z directions of the WAT_BoxFile [used only if WAT=2] (m)", ErrStat2, ErrMsg2, UnEc ); if(failed()) return - call ReadVarWDefault( UnIn, InputFile, p%WAT_ScaleBox, "WAT_ScaleBox", "Flag to scale the input turbulence box to zero mean and unit standard deviation at every node", .False., ErrStat2, ErrMsg2, UnEc); if(failed()) return + call ReadVarWDefault( UnIn, InputFile, p%WAT_ScaleBox, "WAT_ScaleBox", "Flag to scale the input turbulence box to zero mean and unit standard deviation at every node", .True., ErrStat2, ErrMsg2, UnEc); if(failed()) return call ReadAryWDefault( UnIn, InputFile, TmpRAry5, 5, "WAT_k_Def", & "Calibrated parameters for the influence of the maximum wake deficit on wake-added turbulence (set of 5 parameters: k_Def , DMin, DMax, FMin, Exp) (-) [>=0.0, >=0.0, >DMin, >=0.0 and <=1.0, >=0.0] or DEFAULT [DEFAULT=[0.6, 0.0, 0.0, 2.0, 1.0 ]]", & (/0.6_ReKi, 0.0_ReKi, 0.0_ReKi, 2.0_ReKi, 1.00_ReKi/), ErrStat2, ErrMsg2, UnEc); if(failed()) return @@ -1096,9 +1112,9 @@ SUBROUTINE Farm_ValidateInput( p, WD_InitInp, AWAE_InitInp, ErrStat, ErrMsg ) ! summary table call WrScr(' Wake-Added Turbulence (WAT): coefficients:') call WrScr(' k_c f_min D_min D_max e') - write(tmpStr,'(A6,A6,6(f9.3))') '','k_Def', WD_InitInp%WAT_k_Def_k_c, WD_InitInp%WAT_k_Def_FMin, WD_InitInp%WAT_k_Def_DMin, WD_InitInp%WAT_k_Def_DMax, WD_InitInp%WAT_k_Def_Exp + write(tmpStr,'(A12,5(f9.3))') 'k_Def', WD_InitInp%WAT_k_Def_k_c, WD_InitInp%WAT_k_Def_FMin, WD_InitInp%WAT_k_Def_DMin, WD_InitInp%WAT_k_Def_DMax, WD_InitInp%WAT_k_Def_Exp call WrScr(tmpStr) - write(tmpStr,'(A6,A6,6(f9.3))') '','k_Grad',WD_InitInp%WAT_k_Grad_k_c,WD_InitInp%WAT_k_Grad_FMin,WD_InitInp%WAT_k_Grad_DMin,WD_InitInp%WAT_k_Grad_DMax,WD_InitInp%WAT_k_Grad_Exp + write(tmpStr,'(A12,5(f9.3))') 'k_Grad',WD_InitInp%WAT_k_Grad_k_c,WD_InitInp%WAT_k_Grad_FMin,WD_InitInp%WAT_k_Grad_DMin,WD_InitInp%WAT_k_Grad_DMax,WD_InitInp%WAT_k_Grad_Exp call WrScr(tmpStr) endif diff --git a/glue-codes/fast-farm/src/FAST_Farm_Subs.f90 b/glue-codes/fast-farm/src/FAST_Farm_Subs.f90 index f337e20121..3608268f74 100644 --- a/glue-codes/fast-farm/src/FAST_Farm_Subs.f90 +++ b/glue-codes/fast-farm/src/FAST_Farm_Subs.f90 @@ -352,8 +352,7 @@ SUBROUTINE WAT_init( p, WAT_IfW, AWAE_InitInput, ErrStat, ErrMsg ) call MannLibDims(BoxFileRoot, p%RotorDiamRef, p%WAT_NxNyNz, p%WAT_DxDyDz, ErrStat2, ErrMsg2); if (Failed()) return write(sDummy, '(3(I8,1X))') p%WAT_NxNyNz call WrScr(' WAT: NxNyNz set to: '//trim(sDummy)//' (inferred from filename)') - write(sDummy, '(3(F8.3,1X))') p%WAT_DxDyDz - call WrScr(' WAT: DxDyDz set to: '//trim(sDummy)//' (based on rotor diameter)') + call Set_WAT_DxDyDz() ! Use turbine high res deltas if all same endif ! Sanity check if (any(p%WAT_NxNyNz<2)) then @@ -488,7 +487,7 @@ subroutine MannLibDims(BoxFileRoot,RotorDiamRef,Nxyz,Dxyz,ErrStat3,ErrMsg3) ErrStat3 = ErrID_None ErrMsg3 = "" - ! Set Dxyz + ! Calculate Dxyz based on guidance Dxyz=real(RotorDiamRef,ReKi)*ScaleFact ! --- Create a string made of digits and "x" only, starting from the end of the filename @@ -525,6 +524,38 @@ subroutine MannLibDims(BoxFileRoot,RotorDiamRef,Nxyz,Dxyz,ErrStat3,ErrMsg3) ErrStat3=ErrID_None ErrMsg3 ="" end subroutine MannLibDims + subroutine Set_WAT_DxDyDz() + real(ReKi) :: TmpDx,TmpDy,TmpDz + logical :: HResDimsSame + ! If Mod_AmbWind<2, we don't read high res discretizations + if (AWAE_InitInput%InputFileData%Mod_AmbWind < 2) then + write(sDummy, '(3(F8.3,1X))') p%WAT_DxDyDz + call WrScr(' WAT: DxDyDz set to: '//trim(sDummy)//' (calculated based on guidance for Mod_AmbWind==1)') + return + endif + ! Check if all turbines use the same high res deltas + HResDimsSame = .true. + TmpDx = AWAE_InitInput%InputFileData%dX_high(1) + TmpDy = AWAE_InitInput%InputFileData%dY_high(1) + TmpDz = AWAE_InitInput%InputFileData%dZ_high(1) + do i=2,size(AWAE_InitInput%InputFileData%dX_high) + if (.not. EqualRealNos(TmpDx,AWAE_InitInput%InputFileData%dX_high(i))) HResDimsSame = .false. + if (.not. EqualRealNos(TmpDy,AWAE_InitInput%InputFileData%dY_high(i))) HResDimsSame = .false. + if (.not. EqualRealNos(TmpDz,AWAE_InitInput%InputFileData%dZ_high(i))) HResDimsSame = .false. + enddo + ! if all turbines use same high res spacing, use that for WAT spacing + if (HResDimsSame) then + p%WAT_DxDyDz(1) = TmpDx + p%WAT_DxDyDz(2) = TmpDy + p%WAT_DxDyDz(3) = TmpDz + write(sDummy, '(3(F8.3,1X))') p%WAT_DxDyDz + call WrScr(' WAT: DxDyDz set to: '//trim(sDummy)//' (using high res grid resolution)') + ! otherwise fall back to calculated values from MannLibDims + else + write(sDummy, '(3(F8.3,1X))') p%WAT_DxDyDz + call WrScr(' WAT: DxDyDz set to: '//trim(sDummy)//' (high res grids are not identical for all turbines, calculated based on guidance instead)') + endif + end subroutine Set_WAT_DxDyDz end subroutine WAT_init !> Remove mean from all grid nodes and set standard deviation to 1 at all nodes diff --git a/glue-codes/labview/CMakeLists.txt b/glue-codes/labview/CMakeLists.txt index b14ef7ae4b..490a014a8e 100644 --- a/glue-codes/labview/CMakeLists.txt +++ b/glue-codes/labview/CMakeLists.txt @@ -14,15 +14,29 @@ # limitations under the License. # -add_library(wavetanktestinglib SHARED +if (GENERATE_TYPES) + generate_f90_types(src/WaveTank_Registry.txt ${CMAKE_CURRENT_LIST_DIR}/src/WaveTank_Types.f90 -noextrap) +endif() + +add_library(wavetanktesting_c_binding SHARED + src/WaveTank_Types.f90 + src/WaveTank_IO.f90 + src/WaveTank_Struct.f90 src/WaveTank.f90 ) -target_link_libraries(wavetanktestinglib aerodyn_inflow_c_binding moordyn_c_binding seastate_c_binding nwtclibs versioninfolib) +target_link_libraries( + wavetanktesting_c_binding + aerodyn_inflow_c_bind_static + moordyn_c_bind_static + seastate_c_bind_static + nwtclibs + versioninfolib +) if(APPLE OR UNIX) - target_compile_definitions(wavetanktestinglib PRIVATE IMPLICIT_DLLEXPORT) + target_compile_definitions(wavetanktesting_c_binding PRIVATE IMPLICIT_DLLEXPORT) endif() -install(TARGETS wavetanktestinglib +install(TARGETS wavetanktesting_c_binding EXPORT "${CMAKE_PROJECT_NAME}Libraries" RUNTIME DESTINATION bin LIBRARY DESTINATION lib diff --git a/glue-codes/labview/examples/WaveTank.vi b/glue-codes/labview/examples/WaveTank.vi new file mode 100644 index 0000000000..dbfcd8ded2 Binary files /dev/null and b/glue-codes/labview/examples/WaveTank.vi differ diff --git a/glue-codes/labview/src/OPENFAST_RT_DLL.f90 b/glue-codes/labview/src/OPENFAST_RT_DLL.f90 deleted file mode 100644 index 50cee2773f..0000000000 --- a/glue-codes/labview/src/OPENFAST_RT_DLL.f90 +++ /dev/null @@ -1,125 +0,0 @@ -! OPENFAST_RT_DLL.f90 -! (c) 2009, 2012 National Renewable Energy Laboratory -! Paul Fleming, National Wind Technology Center, September 2009, 2012 -! Bonnie Jonkman, National Wind Technology Center, October 2012 -! -! Modification of OPENFAST for Labview RT -! Also includes code from OPENFAST_Simulink Adaptation -!==================================================================================== - -subroutine OPENFAST_RT_DLL_INIT (FileName_RT_Byte, FLen) - - ! Expose subroutine OPENFAST_RT_DLL_INIT to users of this DLL - ! - !DEC$ ATTRIBUTES DLLEXPORT::FAST_RT_DLL_INIT - -USE NWTC_Library -USE General, ONLY : PriFile, Cmpl4LV - -USE OPENFAST_IO_Subs ! OPENFAST_Input(), OPENFAST_Begin() -USE OPENFASTSubs ! OPENFAST_Initialize() - - ! This sub-routine is called by RT to initialize all internal variables - -IMPLICIT NONE - -INTEGER, PARAMETER :: MaxFileNameLen = 100 -INTEGER(B1Ki) :: FileName_RT_Byte(MaxFileNameLen) ! FileName_RT_Byte - -CHARACTER(MaxFileNameLen) :: FileName_RT_Char ! FileName_RT_Byte converted to ASCII characters -INTEGER :: FLen ! trim length of FileName_RT_Byte -INTEGER :: I ! temporary loop counter - - -IF ( FLen > MaxFileNameLen ) CALL ProgAbort('File name is too long in OPENFAST_RT_DLL_INIT.') -DO I=1,FLen - FileName_RT_Char(I:I) = ACHAR(FileName_RT_Byte(I)) -END DO -!EQUIVALENCE(FileName_RT_Byte2,FileName_RT_Char) !Make the character filename equivalent to incoming filename byte array - -!FileName_RT_Byte2(:) = FileName_RT_Byte(:) - - - -!Assign PriFile based on passed in string -PriFile = FileName_RT_Char(1:FLen) - - - ! Open and read input files, initialize global parameters. -CALL OPENFAST_Begin( PriFile, RootName, DirRoot ) - - -!Set compiler flag for Simulink -Cmpl4LV = .TRUE. - -CALL OPENFAST_Input() - - ! Set up initial values for all degrees of freedom. -CALL OPENFAST_Initialize(p,x,y,OtherState) - - -end subroutine OPENFAST_RT_DLL_INIT - - - - -!==================================================================================== -subroutine OPENFAST_RT_DLL_SIM (BlPitchCom_RT, YawPosCom_RT, YawRateCom_RT, ElecPwr_RT, GenTrq_RT, OutData_RT, Time_RT, HSSBrFrac_RT) - - - ! Expose subroutine OPENFAST_RT_DLL_SIM to users of this DLL - ! - !DEC$ ATTRIBUTES DLLEXPORT::FAST_RT_DLL_SIM - - -USE SimCont !ZTime - -! These are needed for FirstTime = .FALSE. -USE DriveTrain ! GenTrq and now also HSSBrFrac -USE TurbCont ! BlPitch -USE TurbConf ! NumBl -USE Blades ! TipNode -USE Precision ! ReKi -USE Features ! CompAero -USE Output ! for WrOutHdr - -USE OPENFASTSubs ! TimeMarch() - -IMPLICIT NONE - - ! This sub-routine implements n-iterations of time step and returns outputs to Labview RT - - ! Variables -REAL(ReKi), INTENT(IN) :: GenTrq_RT ! Mechanical generator torque. -REAL(ReKi), INTENT(IN) :: ElecPwr_RT ! Electrical power -REAL(ReKi), INTENT(IN) :: YawPosCom_RT ! Yaw position -REAL(ReKi), INTENT(IN) :: YawRateCom_RT ! Yaw rate -REAL(ReKi), INTENT(IN) :: BlPitchCom_RT (*) -REAL(ReKi), INTENT(OUT) :: OutData_RT (*) -REAL(ReKi), INTENT(OUT) :: Time_RT -REAL(ReKi), INTENT(IN) :: HSSBrFrac_RT ! Brake Fraction - - !Copy in inputs from RT - BlPitchCom = BlPitchCom_RT(1:NumBl) - YawPosCom = YawPosCom_RT - YawRateCom = YawRateCom_RT - ElecPwr = ElecPwr_RT - GenTrq= GenTrq_RT - HSSBrFrac = HSSBrFrac_RT - - ! Set the command pitch angles to the actual pitch angles since we have no - ! built-in pitch actuator: - BlPitch = BlPitchCom - - -!Run simulation -Call TimeMarch( p_StrD, x_StrD, OtherSt_StrD, y_StrD, ErrStat, ErrMsg ) - - -!Copy outputs -OutData_RT(1:p_StrD%NumOuts) = OutData(1:p_StrD%NumOuts) -Time_RT = ZTime; -OutData_RT(p_StrD%NumOuts+1) = TMax; -OutData_RT(p_StrD%NumOuts+2) = Time_RT; - -end subroutine OPENFAST_RT_DLL_SIM diff --git a/glue-codes/labview/src/README.txt b/glue-codes/labview/src/README.txt new file mode 100644 index 0000000000..1952663266 --- /dev/null +++ b/glue-codes/labview/src/README.txt @@ -0,0 +1,17 @@ +2025.11.10 + +This is a work in progress. Some things are not complete or functional yet: + + +Froude scaling is not complete, nor is it tested!!!! + +At present, only some inputs are scaled, but equations +have not been verified yet. This has been disabled by +removing the reading of the `*Fact` input lines in the +input file parsing and input file. + +TODO: + - verify equations in FroudeScaling* functions + - scale resulting forces / moments + - add scaled time, pos, vel, acc, frc, mom to output + channels and add subscripting to differentiate diff --git a/glue-codes/labview/src/WaveTank.f90 b/glue-codes/labview/src/WaveTank.f90 index cca86d6d7b..08f272a639 100644 --- a/glue-codes/labview/src/WaveTank.f90 +++ b/glue-codes/labview/src/WaveTank.f90 @@ -1,210 +1,831 @@ - +!********************************************************************************************************************************** +! LICENSING +! Copyright (C) 2025 National Renewable Energy Laboratory +! +! This file is a module specific to an experimental wave tank at NREL. +! +! Licensed under the Apache License, Version 2.0 (the "License"); +! you may not use this file except in compliance with the License. +! You may obtain a copy of the License at +! +! http://www.apache.org/licenses/LICENSE-2.0 +! +! Unless required by applicable law or agreed to in writing, software +! distributed under the License is distributed on an "AS IS" BASIS, +! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +! See the License for the specific language governing permissions and +! limitations under the License. +!********************************************************************************************************************************** +! +! This code is designed to connect with LabView for a specific wave tank test case and likely will not work for other purposes. +! +! For this test, a physical platform is deployed in a wave tank with cable acutators that are controlled through LabView. This +! module is called to provide some loads that are not present in the physical tank setup. These include the following: +! - rotor loading from a fixed RPM MHK rotor from AeroDyn. This is calculated from either steady current provided by SeaState +! - Mooring loads from MoorDyn +! +! +!********************************************************************************************************************************** MODULE WaveTankTesting - USE ISO_C_BINDING - USE NWTC_Library - ! USE Precision - USE MoorDyn_C - USE SeaState_C_Binding - USE NWTC_C_Binding, ONLY: IntfStrLen, SetErrStat_C - - IMPLICIT NONE - SAVE - - PUBLIC :: WaveTank_Init - - REAL(C_DOUBLE) :: dt_c = 0.01_C_DOUBLE ! 100 hertz - REAL(C_FLOAT) :: g_c = 9.8065_C_FLOAT - REAL(C_FLOAT) :: rho_c = 1025.0_C_FLOAT - REAL(C_FLOAT) :: depth_c = 200.0_C_FLOAT - REAL(C_FLOAT), DIMENSION(6) :: ptfminit_c = 0.0_C_FLOAT - INTEGER(C_INT) :: interporder_c = 2 ! 1: linear (uses two time steps) or 2: quadratic (uses three time steps) - - INTEGER(C_INT) :: N_CAMERA_POINTS - - INTEGER(C_INT) :: load_period = 20 ! seconds - -CONTAINS - - - -SUBROUTINE WaveTank_Init( & - MD_InputFile_c, & - SS_InputFile_c, & - AD_InputFile_c, & - IfW_InputFile_c, & - n_camera_points_c, & - ErrStat_c, & - ErrMsg_c & -) BIND (C, NAME='WaveTank_Init') + use ISO_C_BINDING + use NWTC_Library + use SeaState_C_Binding, ONLY: SeaSt_C_PreInit, SeaSt_C_Init, SeaSt_C_CalcOutput, SeaSt_C_End, MaxOutPts, SeaSt_C_GetWaveFieldPointer, SeaSt_C_GetSurfElev + use SeaSt_WaveField_Types, ONLY: SeaSt_WaveFieldType + use AeroDyn_Inflow_C_BINDING, ONLY: ADI_C_PreInit, ADI_C_SetupRotor, ADI_C_Init, ADI_C_End, MaxADIOutputs, ADI_C_SetRotorMotion, ADI_C_UpdateStates, ADI_C_CalcOutput, ADI_C_GetRotorLoads + use MoorDyn_C, ONLY: MD_C_Init, MD_C_End, MD_C_SetWaveFieldData, MD_C_UpdateStates, MD_C_CalcOutput + use NWTC_C_Binding, ONLY: IntfStrLen, SetErrStat_C, SetErrStat_F2C, ErrMsgLen_C, StringConvert_F2C, FileNameFromCString, AbortErrLev_C + use WaveTank_Types + use WaveTank_IO + use WaveTank_Struct + + implicit none + save + + public :: WaveTank_Init + public :: WaveTank_CalcStep + public :: WaveTank_End + + ! output to screen or to file (LabView doesn't capture console output nicely) + integer(IntKi) :: ScreenLogOutput_Un = -1 + character(1024) :: ScreenLogOutput_File + + ! Simulation data storage + type(SimSettingsType), target :: SimSettings + + ! IO data storage for CalcStep + type(CalcStepIOdataType) :: CalcStepIO + real(c_double) :: TimePrev_c + + ! Output file writing: headers, units, data, filename, fileunit etc. + type(WrOutputDataType) :: WrOutputData + + ! Structural model data storage + type(MeshesMotionType), target :: MeshMotions ! motion meshes (inputs) + type(MeshesLoadsType ), target :: MeshLoads ! load meshes (output) + type(MeshesMapsType ) :: MeshMaps ! mappings + type(StructTmpType ) :: StructTmp ! temporary data - avoids reallocation + + ! time stuff + integer(IntKi) :: VTKn_Global ! global timestep for VTK + integer(IntKi) :: VTKn_last ! last global timestep for VTK + + +!TODO: +! - add echo file +! - add summary file +! - add scaling +! - Input for scaling already in place +! - add info into summary file on scaling +! - add unscaled interface IO outputs to file as well as the regular IO currently in there +! - add pre and post scaling routines for time, pos, vel, acc, force/moment + + +contains + +subroutine WaveTank_Init( & + WT_InputFile_C, & + RootName_C, & + VTKdir_C, & + ErrStat_C, & + ErrMsg_C & +) bind (C, name='WaveTank_Init') #ifndef IMPLICIT_DLLEXPORT !DEC$ ATTRIBUTES DLLEXPORT :: WaveTank_Init !GCC$ ATTRIBUTES DLLEXPORT :: WaveTank_Init #endif -CHARACTER(KIND=C_CHAR), INTENT(IN ), TARGET :: MD_InputFile_c(IntfStrLen) -CHARACTER(KIND=C_CHAR), INTENT(IN ), TARGET :: SS_InputFile_c(IntfStrLen) -CHARACTER(KIND=C_CHAR), INTENT(IN ), TARGET :: AD_InputFile_c(IntfStrLen) -CHARACTER(KIND=C_CHAR), INTENT(IN ), TARGET :: IfW_InputFile_c(IntfStrLen) -INTEGER(C_INT), INTENT(IN ) :: n_camera_points_c -INTEGER(C_INT), INTENT( OUT) :: ErrStat_C -CHARACTER(KIND=C_CHAR), INTENT( OUT) :: ErrMsg_C(ErrMsgLen_C) - -! Local variables -integer(c_int) :: numchannels_c -character(kind=c_char) :: outputchannelnames_c(100000) -character(kind=c_char) :: outputchannelunits_c(100000) -integer(c_int) :: input_file_passed = 0 ! We're passing paths to input files rather than input strings for all modules -! character(kind=c_char), pointer :: filestring_c(IntfStrLen) ! Point to input file path input argument - -print *, MD_InputFile_c -print *, SS_InputFile_c -print *, AD_InputFile_c -print *, IfW_InputFile_c - -N_CAMERA_POINTS = n_camera_points_c - -! filestring_c => MD_InputFile_c -! call MD_C_Init( & -! input_file_passed, & -! filestring_c, & -! IntfStrLen, & -! dt_c, & -! g_c, & -! rho_c, & -! depth_c, & -! ptfminit_c, & -! interporder_c, & -! numchannels_c, & -! outputchannelnames_c, & -! outputchannelunits_c, & -! ErrStat_C, ErrMsg_C & -! ) - -! call ADI_C_Init( & -! ADinputFilePassed, & ! integer(c_int), intent(in ) :: ADinputFilePassed !< Write VTK outputs [0: none, 1: init only, 2: animation] -! ADinputFileString_C, & ! type(c_ptr), intent(in ) :: ADinputFileString_C !< Input file as a single string with lines deliniated by C_NULL_CHAR -! ADinputFileStringLength_C, & ! integer(c_int), intent(in ) :: ADinputFileStringLength_C !< lenght of the input file string -! IfWinputFilePassed, & ! integer(c_int), intent(in ) :: IfWinputFilePassed !< Write VTK outputs [0: none, 1: init only, 2: animation] -! IfWinputFileString_C, & ! type(c_ptr), intent(in ) :: IfWinputFileString_C !< Input file as a single string with lines deliniated by C_NULL_CHAR -! IfWinputFileStringLength_C, & ! integer(c_int), intent(in ) :: IfWinputFileStringLength_C !< lenght of the input file string -! OutRootName_C, & ! character(kind=c_char), intent(in ) :: OutRootName_C(IntfStrLen) !< Root name to use for echo files and other -! OutVTKDir_C, & ! character(kind=c_char), intent(in ) :: OutVTKDir_C(IntfStrLen) !< Directory to put all vtk output -! gravity_C, & ! real(c_float), intent(in ) :: gravity_C !< Gravitational acceleration (m/s^2) -! defFldDens_C, & ! real(c_float), intent(in ) :: defFldDens_C !< Air density (kg/m^3) -! defKinVisc_C, & ! real(c_float), intent(in ) :: defKinVisc_C !< Kinematic viscosity of working fluid (m^2/s) -! defSpdSound_C, & ! real(c_float), intent(in ) :: defSpdSound_C !< Speed of sound in working fluid (m/s) -! defPatm_C, & ! real(c_float), intent(in ) :: defPatm_C !< Atmospheric pressure (Pa) [used only for an MHK turbine cavitation check] -! defPvap_C, & ! real(c_float), intent(in ) :: defPvap_C !< Vapour pressure of working fluid (Pa) [used only for an MHK turbine cavitation check] -! WtrDpth_C, & ! real(c_float), intent(in ) :: WtrDpth_C !< Water depth (m) -! MSL2SWL_C, & ! real(c_float), intent(in ) :: MSL2SWL_C !< Offset between still-water level and mean sea level (m) [positive upward] -! InterpOrder_C, & ! integer(c_int), intent(in ) :: InterpOrder_C !< Interpolation order to use (must be 1 or 2) -! DT_C, & ! real(c_double), intent(in ) :: DT_C !< Timestep used with AD for stepping forward from t to t+dt. Must be constant. -! TMax_C, & ! real(c_double), intent(in ) :: TMax_C !< Maximum time for simulation -! storeHHVel, & ! integer(c_int), intent(in ) :: storeHHVel !< Store hub height time series from IfW -! WrVTK_in, & ! integer(c_int), intent(in ) :: WrVTK_in !< Write VTK outputs [0: none, 1: init only, 2: animation] -! WrVTK_inType, & ! integer(c_int), intent(in ) :: WrVTK_inType !< Write VTK outputs as [1: surface, 2: lines, 3: both] -! WrVTK_inDT, & ! real(c_double), intent(in ) :: WrVTK_inDT !< Timestep between VTK writes -! VTKNacDim_in, & ! real(c_float), intent(in ) :: VTKNacDim_in(6) !< Nacelle dimension passed in for VTK surface rendering [0,y0,z0,Lx,Ly,Lz] (m) -! VTKHubRad_in, & ! real(c_float), intent(in ) :: VTKHubrad_in !< Hub radius for VTK surface rendering -! wrOuts_C, & -! DT_Outs_C, & -! NumChannels_C, & -! OutputChannelNames_C, & -! OutputChannelUnits_C, & -! ErrStat_C, ErrMsg_C & -! ) - - -! ! Input file info -! integer(c_int), intent(in ) :: ADinputFilePassed !< Write VTK outputs [0: none, 1: init only, 2: animation] -! type(c_ptr), intent(in ) :: ADinputFileString_C !< Input file as a single string with lines deliniated by C_NULL_CHAR -! integer(c_int), intent(in ) :: ADinputFileStringLength_C !< lenght of the input file string -! integer(c_int), intent(in ) :: IfWinputFilePassed !< Write VTK outputs [0: none, 1: init only, 2: animation] -! type(c_ptr), intent(in ) :: IfWinputFileString_C !< Input file as a single string with lines deliniated by C_NULL_CHAR -! integer(c_int), intent(in ) :: IfWinputFileStringLength_C !< lenght of the input file string -! character(kind=c_char), intent(in ) :: OutRootName_C(IntfStrLen) !< Root name to use for echo files and other -! character(kind=c_char), intent(in ) :: OutVTKDir_C(IntfStrLen) !< Directory to put all vtk output -! ! Environmental -! real(c_float), intent(in ) :: gravity_C !< Gravitational acceleration (m/s^2) -! real(c_float), intent(in ) :: defFldDens_C !< Air density (kg/m^3) -! real(c_float), intent(in ) :: defKinVisc_C !< Kinematic viscosity of working fluid (m^2/s) -! real(c_float), intent(in ) :: defSpdSound_C !< Speed of sound in working fluid (m/s) -! real(c_float), intent(in ) :: defPatm_C !< Atmospheric pressure (Pa) [used only for an MHK turbine cavitation check] -! real(c_float), intent(in ) :: defPvap_C !< Vapour pressure of working fluid (Pa) [used only for an MHK turbine cavitation check] -! real(c_float), intent(in ) :: WtrDpth_C !< Water depth (m) -! real(c_float), intent(in ) :: MSL2SWL_C !< Offset between still-water level and mean sea level (m) [positive upward] -! ! Interpolation -! integer(c_int), intent(in ) :: InterpOrder_C !< Interpolation order to use (must be 1 or 2) -! ! Time -! real(c_double), intent(in ) :: DT_C !< Timestep used with AD for stepping forward from t to t+dt. Must be constant. -! real(c_double), intent(in ) :: TMax_C !< Maximum time for simulation -! ! Flags -! integer(c_int), intent(in ) :: storeHHVel !< Store hub height time series from IfW -! ! VTK -! integer(c_int), intent(in ) :: WrVTK_in !< Write VTK outputs [0: none, 1: init only, 2: animation] -! integer(c_int), intent(in ) :: WrVTK_inType !< Write VTK outputs as [1: surface, 2: lines, 3: both] -! real(c_double), intent(in ) :: WrVTK_inDT !< Timestep between VTK writes -! real(c_float), intent(in ) :: VTKNacDim_in(6) !< Nacelle dimension passed in for VTK surface rendering [0,y0,z0,Lx,Ly,Lz] (m) -! real(c_float), intent(in ) :: VTKHubrad_in !< Hub radius for VTK surface rendering -! integer(c_int), intent(in ) :: wrOuts_C !< Write ADI output file -! real(c_double), intent(in ) :: DT_Outs_C !< Timestep to write output file from ADI -! ! Output -! integer(c_int), intent( out) :: NumChannels_C !< Number of output channels requested from the input file -! character(kind=c_char), intent( out) :: OutputChannelNames_C(ChanLen*MaxADIOutputs+1) !< NOTE: if MaxADIOutputs is sufficiently large, we may overrun the buffer on the Python side. -! character(kind=c_char), intent( out) :: OutputChannelUnits_C(ChanLen*MaxADIOutputs+1) -! integer(c_int), intent( out) :: ErrStat_C !< Error status -! character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) !< Error message (C_NULL_CHAR terminated) - -! Set compiler flag for Labview -! Cmpl4LV = .TRUE. - -END SUBROUTINE WaveTank_Init - -! delta_time, & -SUBROUTINE WaveTank_CalcOutput( & - frame_number, & - positions_x, & - positions_y, & - positions_z, & - rotation_matrix, & - loads, & - ErrStat_c, & - ErrMsg_c & -) BIND (C, NAME='WaveTank_CalcOutput') + character(c_char), intent(in ) :: WT_InputFile_C(IntfStrLen) + character(kind=c_char), intent( out) :: RootName_C(IntfStrLen) + character(kind=c_char), intent( out) :: VTKdir_C(IntfStrLen) + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + + integer(c_int) :: ErrStat_C2 + character(kind=c_char, len=ErrMsgLen_C) :: ErrMsg_C2 + integer(IntKi) :: ErrStat_F2 + character(ErrMsgLen) :: ErrMsg_F2 + character(1024) :: InputFile + integer(IntKi) :: i,k + integer(c_int), allocatable :: tmpMeshPtToBladeNum(:) + type(FileInfoType) :: FileInfo_In !< The derived type for holding the full input file for parsing -- we may pass this in the future + + ! debug level for passing to modules. Backing down the level by 1 for modules + integer(IntKi) :: DebugLevelMod + + ! local C variables for transferring names + character(kind=c_char) :: WrVTK_Dir_C(IntfStrLen) + character(kind=c_char) :: OutRootName_C(IntfStrLen) + + ! The length of these arrays much match what is set in the corresponding C binding modules, or be larger + character(kind=c_char) :: SS_WriteOutputHdr_C(ChanLen*MaxOutPts+1) + character(kind=c_char) :: SS_WriteOutputUnt_C(ChanLen*MaxOutPts+1) + character(kind=c_char) :: MD_WriteOutputHdr_C(ChanLen*1000) ! probably oversized + character(kind=c_char) :: MD_WriteOutputUnt_C(ChanLen*1000) ! probably oversized + character(kind=c_char) :: ADI_WriteOutputHdr_C(ChanLen*MaxADIOutputs+1) + character(kind=c_char) :: ADI_WriteOutputUnt_C(ChanLen*MaxADIOutputs+1) + + ! Filename conversions -- read in as fortran strings, but sent to other modules as c_char arrays + character(kind=c_char) :: SS_InputFile_C(IntfStrLen) + character(kind=c_char), target :: MD_InputFile_C(IntfStrLen) + character(kind=c_char), target :: AD_InputFile_C(IntfStrLen) + character(kind=c_char), target :: IfW_InputFile_C(IntfStrLen) + + ! temporary storage of number of output channels + integer(c_int) :: SS_NumChannels_C + integer(c_int) :: MD_NumChannels_C + integer(c_int) :: ADI_NumChannels_C + + ! set constants + call NWTC_Init() + + ! Initialize error handling + ErrStat_C = ErrID_None + ErrMsg_C = " "//C_NULL_CHAR + + InputFile = transfer(WT_InputFile_C, InputFile) + i = index(InputFile, char(0)) + InputFile = InputFile(1:i) + call ProcessComFile(InputFile, FileInfo_In, ErrStat_F2, ErrMsg_F2); if (Failed()) return + call ParseInputFile(FileInfo_In, SimSettings, ErrStat_F2, ErrMsg_F2); if (Failed()) return + + ! return rootname + RootName_C = c_null_char + RootName_C = transfer(trim(SimSettings%Sim%OutRootName),RootName_C) + + ! If SendScreenToFile - send to file .screen.log if true + if (SimSettings%Outs%SendScreenToFile) then + call GetNewUnit(ScreenLogOutput_Un, ErrStat_F2, ErrMsg_F2); if (Failed()) return + ScreenLogOutput_File = trim(SimSettings%Sim%OutRootName)//'.screen.log' + call OpenFOutFile(ScreenLogOutput_Un, ScreenLogOutput_File, ErrStat_F2, ErrMsg_F2); if (Failed()) return + call SetConsoleUnit(ScreenLogOutput_Un) ! this will redirect all screen output to a file instead + endif + + ! validate the settings now that the screen can be written to file + call ValidateInputFile(SimSettings, ErrStat_F2, ErrMsg_F2); if (Failed()) return + + ! debugging + if (SimSettings%Sim%DebugLevel > 0_c_int) call ShowPassedData() + if (SimSettings%Sim%DebugLevel > 2_c_int) call Print_FileInfo_Struct(CU,FileInfo_In) + + ! set debug level for modules (backing off by 1 to allow just checking wavetank io) + DebugLevelMod = max( 0_IntKi, SimSettings%Sim%DebugLevel-1_IntKi) + + ! VTK directory + WrVTK_Dir_C = c_null_char + WrVTK_Dir_C = transfer( trim(SimSettings%Viz%WrVTK_Dir), WrVTK_Dir_C ) + ! return VTKdir + VTKdir_C = c_null_char + if (SimSettings%Viz%WrVTK > 0_c_int) VTKdir_C = WrVTK_Dir_C + + ! Set a previous time (used in calcstep) + TimePrev_c = -SimSettings%Sim%DT ! we need this at T=0 + + !------------------------------ + ! Allocate temp storage + !------------------------------ + call AllocTmpStorage(ErrStat_F2, ErrMsg_F2) + if (Failed()) return + + + !------------------------------ + ! Build struct model + !------------------------------ + call StructCreate(SimSettings, MeshMotions, MeshLoads, MeshMaps, StructTmp, ErrStat_F2, ErrMsg_F2) + if (Failed()) return + + ! output VTK for struct model (if requested) + if (SimSettings%Viz%WrVTK > 0_c_int) then + ! create directory if doesn't exist + call MKDIR( trim(SimSettings%Viz%WrVTK_Dir) ) + ! write mesh refs + call WrVTK_Struct_Ref(SimSettings, MeshMotions, MeshLoads, ErrStat_F2, ErrMsg_F2) + if (Failed()) return + endif + + ! map the structural meshes (write vtk first in case of issues) + call StructCreateMeshMaps(SimSettings, MeshMotions, MeshLoads, MeshMaps, ErrStat_F2, ErrMsg_F2) + if (Failed()) return + + + !------------------------------ + ! Setup and initialize SeaState + !------------------------------ + call SeaSt_C_PreInit( & + SimSettings%Env%Gravity, & + SimSettings%Env%WtrDens, & + SimSettings%Env%WtrDpth, & + SimSettings%Env%MSL2SWL, & + DebugLevelMod, & + WrVTK_Dir_C, & + SimSettings%Viz%WrVTK, & + SimSettings%Viz%WrVTK_DT, & + ErrStat_C2, ErrMsg_C2 ) + if (Failed_c('SeaSt_C_PreInit')) return + + SS_InputFile_C = c_null_char + SS_InputFile_C = transfer(trim(SimSettings%ModSettings%SS_InputFile ), SS_InputFile_C ) + OutRootName_C = transfer(trim(SimSettings%Sim%OutRootName)//'.SeaSt'//c_null_char, OutRootName_C) + call SeaSt_C_Init( & + SS_InputFile_C, & + OutRootName_C, & + SimSettings%Sim%TMax, & + SimSettings%Sim%DT, & + SimSettings%ModSettings%WaveTimeShift, & + SS_NumChannels_C, & + SS_WriteOutputHdr_C, & + SS_WriteOutputUnt_C, & + ErrStat_C2, ErrMsg_C2 ) + if (Failed_c('SeaSt_C_Init')) return + + ! store channel info + WrOutputData%NumChans_SS = int(SS_NumChannels_c,IntKi) + call TransferOutChanNamesUnits(WrOutputData%NumChans_SS, 'WriteOutputHdr_SS', SS_WriteOutputHdr_c, WrOutputData%WriteOutputHdr_SS,ErrStat_F2,ErrMsg_F2); if (Failed()) return + call TransferOutChanNamesUnits(WrOutputData%NumChans_SS, 'WriteOutputUnt_SS', SS_WriteOutputUnt_c, WrOutputData%WriteOutputUnt_SS,ErrStat_F2,ErrMsg_F2); if (Failed()) return + + + !------------------------------ + ! Set the SeaState Wave Field pointer onto MoorDyn + !------------------------------ + call WaveTank_SetWaveFieldPointer(ErrStat_C2, ErrMsg_C2) + if (Failed_c('WaveTank_SetWaveFieldPointer')) return + + + !------------------------------ + ! Setup and initialize MoorDyn + !------------------------------ + ! set the platform position/orientation + call SetMDTmpMotion() +!FIXME: this interface will change!!! -- Split with PreInit +!FIXME: add WrVTK_Dir_C, SimSettings%Viz%WrVTK, SimSettings%Viz%WrVTK_DT + MD_InputFile_C = c_null_char + MD_InputFile_C = transfer(trim(SimSettings%ModSettings%MD_InputFile ), MD_InputFile_C ) + OutRootName_C = transfer(trim(SimSettings%Sim%OutRootName)//'.MD'//c_null_char, OutRootName_C) + call MD_C_Init( & + 0_c_int, & !< InputFilePassed: 0 for file, 1 for string + c_loc(MD_InputFile_C(1)), & + int(IntfStrLen,c_int), & !< InputFileStringLength_C + SimSettings%Sim%DT, & + SimSettings%Env%Gravity, & + SimSettings%Env%WtrDens, & + SimSettings%Env%WtrDpth, & + StructTmp%PtfmPosAng_c, & + SimSettings%Sim%InterpOrd, & + MD_NumChannels_C, & + MD_WriteOutputHdr_C, & + MD_WriteOutputUnt_C, & + ErrStat_C2, ErrMsg_C2 & + ) +!FIXME: add this when updating MD interface +! DebugLevelMod, & + if (Failed_c('MD_C_Init')) return + + ! store channel info + WrOutputData%NumChans_MD = int(MD_NumChannels_c,IntKi) + call TransferOutChanNamesUnits(WrOutputData%NumChans_MD, 'WriteOutputHdr_MD', MD_WriteOutputHdr_c, WrOutputData%WriteOutputHdr_MD,ErrStat_F2,ErrMsg_F2); if (Failed()) return + call TransferOutChanNamesUnits(WrOutputData%NumChans_MD, 'WriteOutputUnt_MD', MD_WriteOutputUnt_c, WrOutputData%WriteOutputUnt_MD,ErrStat_F2,ErrMsg_F2); if (Failed()) return + + !------------------------------ + ! Setup and initialize AeroDyn+Inflow + !------------------------------ + call ADI_C_PreInit( & + 1_c_int, & ! only one turbine + 0_c_int, & ! transpose DCM inside ADI (0=false) + 1_c_int, & ! PointLoadOutput - use line to point load mapping -- necessary for mapping to blade root without an actual blade structure + SimSettings%Env%Gravity, & + SimSettings%Env%WtrDens, & + SimSettings%Env%WtrVisc, & + SimSettings%Env%SpdSound, & + SimSettings%Env%Patm, & + SimSettings%Env%Pvap, & + SimSettings%Env%WtrDpth, & + SimSettings%Env%MSL2SWL, & + SimSettings%Sim%MHK, & + 0_c_int, & ! externFlowfield_in + WrVTK_Dir_C, & ! vtk directory to use + SimSettings%Viz%WrVTK, & ! VTK visualization data output: (switch) {0=none; 1=initialization data only; 2=animation; 3=mode shapes} + SimSettings%Viz%WrVTK_Type, & ! Type of VTK visualization data: (switch) {1=surfaces; 2=basic meshes (lines/points); 3=all meshes (debug)} [unused if WrVTK=0] + SimSettings%Viz%WrVTK_DT, & ! timestep of VTK writing + SimSettings%Viz%VTKNacDim, & ! Nacelle dimension passed in for VTK surface rendering [0,y0,z0,Lx,Ly,Lz] (m) + SimSettings%TrbCfg%HubRad, & ! Hub radius for VTK surface rendering + DebugLevelMod, & + ErrStat_C2, ErrMsg_C2 & + ) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'ADI_C_PreInit') + if (ErrStat_C >= AbortErrLev_C) then + call CleanUp() + return + endif + + ! Nacelle motion + call SetADITmpNacMotion() + + ! Hub motion + call SetADITmpHubMotion() + + ! Blade motion + call SetADITmpBldMotion() + + ! Mapping - one mesh point for each blade + call AllocAry(tmpMeshPtToBladeNum, 2, "tmpMeshPtToBladeNum", ErrStat_F2, ErrMsg_F2); if (Failed()) return + do k=1,SimSettings%TrbCfg%NumBl + tmpMeshPtToBladeNum(k) = k + enddo + + ! Setup the rotor + call ADI_C_SetupRotor(1_c_int, 1_c_int, & ! iWT -- turbine number, IsHAWT=True + StructTmp%PtfmPosAng_c(1:3), & ! Only x,y,z location, no orientation + StructTmp%HubPos_c, StructTmp%HubDCM_c, & ! HubPos, Hub orientation DCM, + StructTmp%NacPos_c, StructTmp%NacDCM_c, & ! NacPos, Nac orientation DCM, + int(SimSettings%TrbCfg%NumBl,c_int), & ! NumBlades + StructTmp%BldPos_c, StructTmp%BldDCM_c, & ! Blade root positions, blade root orientation DCM (flattened, concatenated) + int(SimSettings%TrbCfg%NumBl,c_int), & ! Num mesh points (only one per blade) + StructTmp%BldPos_c, StructTmp%BldDCM_c, & ! Blade root positions, blade root orientation DCM (flattened, concatenated) + tmpMeshPtToBladeNum, & ! MeshPtToBladeNum + ErrStat_C2, ErrMsg_C2 ) + if (Failed_c('ADI_C_SetupRotor')) return + + AD_InputFile_C = c_null_char + AD_InputFile_C = transfer(trim(SimSettings%ModSettings%AD_InputFile ), AD_InputFile_C ) + IfW_InputFile_C = c_null_char + IfW_InputFile_C = transfer(trim(SimSettings%ModSettings%IfW_InputFile), IfW_InputFile_C) + OutRootName_C = transfer(trim(SimSettings%Sim%OutRootName)//'.ADI'//c_null_char, OutRootName_C) + call ADI_C_Init( & + 0, & ! ADinputFilePassed; 0 for file, 1 for string + c_loc(AD_InputFile_C(1)), & ! ADinputFileString_C; Input file as a single string with lines delineated by C_NULL_CHAR + IntfStrLen, & ! ADinputFileStringLength_C; length of the input file string + 0, & ! IfWinputFilePassed; 0 for file, 1 for string + c_loc(IfW_InputFile_C(1)), & ! IfWinputFileString_C; Input file as a single string with lines delineated by C_NULL_CHAR + IntfStrLen, & ! IfWinputFileStringLength_C; length of the input file string + OutRootName_C, & ! Root name to use for echo files and other + SimSettings%Sim%InterpOrd, & ! interpolation order for extrap/interp + SimSettings%Sim%DT, & ! DT for simulation (used in checks only) + SimSettings%Sim%TMax, & ! Max time for simulation (not used here) + 0_c_int, & ! storeHHVel - Store hub height time series from IfW -- set to false since not used here + 1_c_int, & ! wrOuts_C -- Write ADI output file -- hard code to true for now + SimSettings%Sim%DT, & ! Timestep to write output file from ADI + ADI_NumChannels_C, ADI_WriteOutputHdr_C, ADI_WriteOutputUnt_C, & + ErrStat_C2, ErrMsg_C2) + if (Failed_c('ADI_C_Init')) return + + ! store channel info + WrOutputData%NumChans_ADI = int(ADI_NumChannels_c,IntKi) + call TransferOutChanNamesUnits(WrOutputData%NumChans_ADI, 'WriteOutputHdr_ADI', ADI_WriteOutputHdr_c, WrOutputData%WriteOutputHdr_ADI, ErrStat_F2, ErrMsg_F2); if (Failed()) return + call TransferOutChanNamesUnits(WrOutputData%NumChans_ADI, 'WriteOutputUnt_ADI', ADI_WriteOutputUnt_c, WrOutputData%WriteOutputUnt_ADI, ErrStat_F2, ErrMsg_F2); if (Failed()) return + + + !------------------------------ + ! Assemble data for output file + !------------------------------ + if (SimSettings%Outs%OutFile > 0_IntKi) then + WrOutputData%OutName = trim(SimSettings%Sim%OutRootName)//'.out' + call InitOutputFile(WrOutputData,ErrStat_F2,ErrMsg_F2); if (Failed()) return + + ! allocate storage for output channels from each of the modules, and c_float versions + call AllocAry(WrOutputData%OutData_SS, WrOutputData%Numchans_SS, 'OutData_SS', ErrStat_F2, ErrMsg_F2); if (Failed()) return + call AllocAry(WrOutputData%OutData_SS_c, WrOutputData%Numchans_SS, 'OutData_SS_c', ErrStat_F2, ErrMsg_F2); if (Failed()) return + call AllocAry(WrOutputData%OutData_MD, WrOutputData%Numchans_MD, 'OutData_MD', ErrStat_F2, ErrMsg_F2); if (Failed()) return + call AllocAry(WrOutputData%OutData_MD_c, WrOutputData%Numchans_MD, 'OutData_MD_c', ErrStat_F2, ErrMsg_F2); if (Failed()) return + call AllocAry(WrOutputData%OutData_ADI, WrOutputData%Numchans_ADI, 'OutData_ADI', ErrStat_F2, ErrMsg_F2); if (Failed()) return + call AllocAry(WrOutputData%OutData_ADI_c, WrOutputData%Numchans_ADI, 'OutData_ADI_c', ErrStat_F2, ErrMsg_F2); if (Failed()) return + endif + + !------------------------------ + ! Final cleanup + !------------------------------ + ! Initialize time counting for VTK + VTKn_Global = 0_IntKi + VTKn_last = -1_IntKi + call ShowReturnData() + +contains + logical function Failed_c(txt) + character(*), intent(in) :: txt + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, txt) + Failed_c = ErrStat_C >= AbortErrLev_C + if (Failed_c) call CleanUp() + end function Failed_c + logical function Failed() + call SetErrStat_F2C(ErrStat_F2, ErrMsg_F2, ErrStat_C, ErrMsg_C) + Failed = ErrStat_C >= AbortErrLev_C + if (Failed) call Cleanup() + end function Failed + subroutine Cleanup() + call NWTC_Library_DestroyFileInfoType(FileInfo_In, ErrStat_F2, ErrMsg_F2) ! ignore error from this + if (ScreenLogOutput_Un > 0) close(ScreenLogOutput_Un) + if (allocated(tmpMeshPtToBladeNum)) deallocate(tmpMeshPtToBladeNum) + end subroutine Cleanup + subroutine ShowPassedData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: WaveTank_Init input values") + call WrScr(" --------------------------------------------------------") + call WrScr(" WT_InputFile_C -> "//trim(InputFile)) + call WrScr(" --------------------------------------------------------") + end subroutine ShowPassedData + subroutine ShowReturnData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: WaveTank_Init returned values") + call WrScr(" --------------------------------------------------------") + call WrScr(" RootName_C <- "//trim(SimSettings%Sim%OutRootName)) + call WrScr(" WrVTK_Dir_C <- "//trim(SimSettings%Viz%WrVTK_Dir)) + call WrScr("-----------------------------------------------------------") + end subroutine + subroutine AllocTmpStorage(ErrStat3,ErrMsg3) + integer(IntKi), intent(out) :: ErrStat3 + character(ErrMsgLen), intent(out) :: ErrMsg3 + call AllocAry(CalcStepIO%FrcMom_ADI_c, 6*SimSettings%TrbCfg%NumBl, 'FrcMom_ADI_c', ErrStat3, ErrMsg3); if (ErrStat3 /= ErrID_None) return + call AllocAry(StructTmp%BldPos_c, 3*SimSettings%TrbCfg%NumBl, 'TmpBldPos_c', ErrStat3, ErrMsg3); if (ErrStat3 /= ErrID_None) return + call AllocAry(StructTmp%BldDCM_c, 9*SimSettings%TrbCfg%NumBl, 'TmpBldDCM_c', ErrStat3, ErrMsg3); if (ErrStat3 /= ErrID_None) return + call AllocAry(StructTmp%BldVel_c, 6*SimSettings%TrbCfg%NumBl, 'TmpBldVel_c', ErrStat3, ErrMsg3); if (ErrStat3 /= ErrID_None) return + call AllocAry(StructTmp%BldAcc_c, 6*SimSettings%TrbCfg%NumBl, 'TmpBldAcc_c', ErrStat3, ErrMsg3); if (ErrStat3 /= ErrID_None) return + end subroutine +end subroutine WaveTank_Init + +!subroutine DeallocEverything() +!end subroutine DeallocEverything + + +!> Step from T-dt to T and output values at T +subroutine WaveTank_CalcStep( & + time_c, & + pos_c, & + vel_c, & + acc_c, & + loads_c, & + buoyWaveElev_c, & + ErrStat_C, & + ErrMsg_C & +) BIND (C, NAME='WaveTank_CalcStep') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: WaveTank_CalcStep +!GCC$ ATTRIBUTES DLLEXPORT :: WaveTank_CalcStep +#endif + real(c_double), intent(in ) :: time_c + real(c_float), intent(in ) :: pos_c(6) ! [x,y,z,roll,pitch,yaw] + real(c_float), intent(in ) :: vel_c(6) ! [x_dot,y_dot,z_dot,roll_dot,pitch_dot,yaw_dot] + real(c_float), intent(in ) :: acc_c(6) ! [x_ddot,y_ddot,z_ddot,roll_ddot,pitch_ddot,yaw_ddot] + real(c_float), intent( out) :: loads_c(6) ! [Fx,Fy,Fz,Mx,My,Mz] + real(c_float), intent( out) :: buoyWaveElev_c ! wave elevation at buoy + integer(c_int), intent( out) :: ErrStat_C + character(c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer(c_int) :: ErrStat_C2 + character(c_char) :: ErrMsg_C2(ErrMsgLen_C) + integer(IntKi) :: ErrStat_F2 + character(ErrMsgLen) :: ErrMsg_F2 + integer(IntKi) :: i + + ! Initialize error handling + ErrStat_C = ErrID_None + ErrMsg_C = " "//C_NULL_CHAR + + + ! debugging + if (SimSettings%Sim%DebugLevel > 0_c_int) call ShowPassedData() + + ! zero loads in case of error + loads_c = 0.0_c_float + + ! Transfer CalcStepIO data (storing for output to file) + CalcStepIO%Time_c = time_C + CalcStepIO%PosAng_c = pos_c + CalcStepIO%Vel_c = vel_c + CalcStepIO%Acc_c = acc_c + + + !-------------------------------------- + ! Update motion meshes + !-------------------------------------- + call StructMotionUpdate(SimSettings, CalcStepIO, MeshMotions, MeshMaps, StructTmp, ErrStat_F2, ErrMsg_F2) + if (Failed()) return + + + !-------------------------------------- + ! Wave elevation at buoy, update buoy + !-------------------------------------- + StructTmp%BuoyPos_c(1:2) = real(SimSettings%WaveBuoy%XYLoc, c_float) + call SeaSt_C_GetSurfElev(Time_C, StructTmp%BuoyPos_c(1:2), buoyWaveElev_c, ErrStat_C2, ErrMsg_C2) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'WaveTank_CalcStep::SeaSt_C_GetSurfElev') + if (ErrStat_C >= AbortErrLev_C) return + MeshMotions%WaveBuoyMotion%TranslationDisp(:,1) = (/ 0.0_ReKi, 0.0_ReKi, real(buoyWaveElev_c, ReKi) /) + + + !-------------------------------------- + ! Write VTK if requested + ! Do this here in case failed calcs + !-------------------------------------- + if (SimSettings%Viz%WrVTK > 0_c_int) then + ! only write on desired time interval (same logic used in c-binding modules) + VTKn_Global = nint(Time_C / SimSettings%Viz%WrVTK_DT) + if (VTKn_Global /= VTKn_last) then ! already wrote this one + VTKn_last = VTKn_Global ! store the current number to make sure we don't write it twice + call WrVTK_Struct(VTKn_Global, SimSettings, MeshMotions, MeshLoads, ErrStat_F2, ErrMsg_F2) + if (Failed()) return + endif + endif + + + !-------------------------------------- + ! call SeaState_Calc (writes vis) + !-------------------------------------- + call SeaSt_C_CalcOutput(Time_C, WrOutputData%OutData_SS_c, ErrStat_C, ErrMsg_C) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'WaveTank_CalcStep::SeaSt_C_CalcOutput') + if (ErrStat_C >= AbortErrLev_C) return + ! transfer data for writing out + WrOutputData%OutData_SS = real(WrOutputData%OutData_SS_c, ReKi) + + + !-------------------------------------- + ! MD calculations + !-------------------------------------- + ! Platform positions at T + call SetMDTmpMotion() + + ! Update to T+DT + call MD_C_UpdateStates(TimePrev_c, Time_c, StructTmp%PtfmPosAng_c, StructTmp%PtfmVel_c, StructTmp%PtfmAcc_c, ErrStat_C2, ErrMsg_C2) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'WaveTank_CalcStep::MD_C_UpdateStates') + if (ErrStat_C >= AbortErrLev_C) return + + ! get loads at T+DT + call MD_C_CalcOutput(Time_c, StructTmp%PtfmPosAng_c, StructTmp%PtfmVel_c, StructTmp%PtfmAcc_c, CalcStepIO%FrcMom_MD_c, WrOutputData%OutData_MD, ErrStat_C2, ErrMsg_C2) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'WaveTank_CalcStep::MD_C_CalcOutput') + if (ErrStat_C >= AbortErrLev_C) return + + ! Put mooring loads onto mesh + call SetMDMeshLoads() + + + !-------------------------------------- + ! ADI calculations + !-------------------------------------- + ! Nacelle motion + call SetADITmpNacMotion() + + ! Hub motion + call SetADITmpHubMotion() + + ! Blade motion + call SetADITmpBldMotion() + + ! Set the rotor motion (assumed single rotor) + ! NOTE: ADI handles blade root and mesh seaparately. For our purposes, + ! the blade root and the first mesh node of the blade (using only + ! one point for a rigid blade) are identical. + call ADI_C_SetRotorMotion( 1_IntKi, & ! rotor number + StructTmp%HubPos_c, StructTmp%HubDCM_c, StructTmp%HubVel_c, StructTmp%HubAcc_c, & + StructTmp%NacPos_c, StructTmp%NacDCM_c, StructTmp%NacVel_c, StructTmp%NacAcc_c, & + StructTmp%BldPos_c, StructTmp%BldDCM_c, StructTmp%BldVel_c, StructTmp%BldAcc_c, & + int(SimSettings%TrbCfg%NumBl, c_int), & ! Number of mesh points (number of blade roots) + StructTmp%BldPos_c, StructTmp%BldDCM_c, StructTmp%BldVel_c, StructTmp%BldAcc_c, & + ErrStat_C2, ErrMsg_C2 ) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'WaveTank_CalcStep::ADI_C_SetRotorMotion') + if (ErrStat_C >= AbortErrLev_C) return + + ! Update ADI states to next time + call ADI_C_UpdateStates(TimePrev_c, Time_c, ErrStat_C2, ErrMsg_C2) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'WaveTank_CalcStep::ADI_C_UpdateStates') + if (ErrStat_C >= AbortErrLev_C) return + + ! calculate outputs from ADI + call ADI_C_CalcOutput(Time_c, WrOutputData%OutData_ADI, ErrStat_C2, ErrMsg_C2) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'WaveTank_CalcStep::ADI_C_CalcOutput') + if (ErrStat_C >= AbortErrLev_C) return + + ! get loads from rotor (assumed single rotor) + call ADI_C_GetRotorLoads( 1_IntKi, & ! rotor number + int(SimSettings%TrbCfg%NumBl, c_int), & ! Number of mesh points (number of blade roots) + CalcStepIO%FrcMom_ADI_c, & ! 6xNumMeshPts_C array [Fx,Fy,Fz,Mx,My,Mz] -- forces and moments (global) + CalcStepIO%HubVel_ADI_c, & ! Wind speed array [Vx,Vy,Vz] -- (m/s) (global) + ErrStat_C2, ErrMsg_C2 ) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'WaveTank_CalcStep::ADI_C_GetRotorLoads') + if (ErrStat_C >= AbortErrLev_C) return + + ! Set load on blade root mesh + call SetADIMeshLoads() + + + !-------------------------------------- + ! Transfer mesh loads back to platform + !-------------------------------------- + call StructLoadsMeshTransfer(SimSettings, CalcStepIO, MeshMotions, MeshLoads, MeshMaps, StructTmp, ErrStat_F2, ErrMsg_F2) + if (Failed()) return + + ! set output loads at platform + CalcStepIO%FrcMom_C(1:3) = real(MeshLoads%PtfmPtLoads%Force(1:3,1), c_float) + CalcStepIO%FrcMom_C(4:6) = real(MeshLoads%PtfmPtLoads%Moment(1:3,1), c_float) + loads_c = CalcStepIO%FrcMom_C + + ! debugging + if (SimSettings%Sim%DebugLevel > 0_c_int) call ShowReturnData() + + ! Transfer outputs and write to file + if (SimSettings%Outs%OutFile > 0_IntKi) then + ! output to file + call WriteOutputLine(SimSettings%Outs%OutFmt, CalcStepIO, StructTmp, WrOutputData, ErrStat_F2, ErrMsg_F2) + if (Failed()) return + endif + + ! keep track of the time + if (Time_c > TimePrev_c) TimePrev_c = Time_c + + +contains + logical function Failed() + call SetErrStat_F2C(ErrStat_F2, ErrMsg_F2, ErrStat_C, ErrMsg_C) + Failed = ErrStat_C >= AbortErrLev_C + !if (Failed) call Cleanup() + end function Failed + subroutine ShowPassedData() + character(120) :: TmpStr + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: WaveTank_CalcStep input values") + call WrScr(" --------------------------------------------------------") + call WrScr(" time_c -> "//trim(Num2LStr(time_c))) + write(TmpStr,'("(", *(f10.5, :, ","))') pos_c; TmpStr=trim(TmpStr)//" )" + call WrScr(" pos_c -> "//trim(TmpStr)) + write(TmpStr,'("(", *(f10.5, :, ","))') vel_c; TmpStr=trim(TmpStr)//" )" + call WrScr(" vel_c -> "//trim(TmpStr)) + write(TmpStr,'("(", *(f10.5, :, ","))') acc_c; TmpStr=trim(TmpStr)//" )" + call WrScr(" acc_c -> "//trim(TmpStr)) + call WrScr(" --------------------------------------------------------") + end subroutine ShowPassedData + subroutine ShowReturnData() + character(120) :: TmpStr + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: WaveTank_CalcStep returned values") + call WrScr(" --------------------------------------------------------") + write(TmpStr,'("(", *(f14.2, :, ","))') loads_c; TmpStr=trim(TmpStr)//" )" + call WrScr(" loads_c <- "//trim(TmpStr)) + call WrScr(" buoyWaveElev_c <- "//trim(Num2LStr(buoyWaveElev_c))) + call WrScr("-----------------------------------------------------------") + end subroutine +end subroutine + + + + + +!--------------------------------- +! routines to copy mesh info to temporary vars for transfer +subroutine SetMDTmpMotion() + type(MeshType), pointer :: Ptfm + Ptfm => MeshMotions%PtfmPtMotion + StructTmp%PtfmPosAng_c(1:3) = real(Ptfm%Position(1:3,1), c_float) + real(Ptfm%TranslationDisp(1:3,1), c_float) + StructTmp%PtfmPosAng_c(4:6) = real(CalcStepIO%PosAng_c(4:6), c_float) ! Euler angle set -- used to set Orientation + StructTmp%PtfmVel_c(1:3) = real(Ptfm%TranslationVel(1:3,1), c_float) + StructTmp%PtfmVel_c(4:6) = real(Ptfm%RotationVel(1:3,1), c_float) + StructTmp%PtfmAcc_c(1:3) = real(Ptfm%TranslationAcc(1:3,1), c_float) + StructTmp%PtfmAcc_c(4:6) = real(Ptfm%RotationAcc(1:3,1), c_float) +end subroutine + +subroutine SetMDMeshLoads() + type(MeshType), pointer :: MoorLd + integer(IntKi) :: i1,i2 + MoorLd => MeshLoads%MooringLoads + MoorLd%Force(1:3,1) = real(CalcStepIO%FrcMom_MD_c(1:3), ReKi) + MoorLd%Moment(1:3,1) = real(CalcStepIO%FrcMom_MD_c(4:6), ReKi) +end subroutine + +subroutine SetADITmpNacMotion() +! NOTE: we are treating the nacelle as the tower top and simply using that location +! NOTE: the nacelle drag isn't getting returned anyhow + type(MeshType), pointer :: Twr + Twr => MeshMotions%TowerMotion + StructTmp%NacPos_c(1:3) = real(Twr%Position(1:3,2), c_float) + real(Twr%TranslationDisp(1:3,2), c_float) + StructTmp%NacDCM_c(1:9) = real(reshape(Twr%Orientation(1:3,1:3,2), (/9/)), c_float) + StructTmp%NacVel_c(1:3) = real(Twr%TranslationVel(1:3,2), c_float) + StructTmp%NacVel_c(4:6) = real(Twr%RotationVel(1:3,2), c_float) + StructTmp%NacAcc_c(1:3) = real(Twr%TranslationAcc(1:3,2), c_float) + StructTmp%NacAcc_c(4:6) = real(Twr%RotationAcc(1:3,2), c_float) +end subroutine + +subroutine SetADITmpHubMotion() + type(MeshType), pointer :: Hub + Hub => MeshMotions%HubMotion + StructTmp%HubPos_c(1:3) = real(Hub%Position(1:3,1), c_float) + real(Hub%TranslationDisp(1:3,1), c_float) + StructTmp%HubDCM_c(1:9) = real(reshape(Hub%Orientation(1:3,1:3,1), (/9/)), c_float) + StructTmp%HubVel_c(1:3) = real(Hub%TranslationVel(1:3,1), c_float) + StructTmp%HubVel_c(4:6) = real(Hub%RotationVel(1:3,1), c_float) + StructTmp%HubAcc_c(1:3) = real(Hub%TranslationAcc(1:3,1), c_float) + StructTmp%HubAcc_c(4:6) = real(Hub%RotationAcc(1:3,1), c_float) +end subroutine + +subroutine SetADITmpBldMotion() + type(MeshType), pointer :: Root + integer(IntKi) :: k,i1,i2 + do k=1,SimSettings%TrbCfg%NumBl + Root => MeshMotions%BladeRootMotion(k) + ! position -- x,y,z + i1=(k-1)*3+1; i2=i1+2 + StructTmp%BldPos_c(i1:i2) = real(Root%Position(1:3,1), c_float) + real(Root%TranslationDisp(1:3,1), c_float) + ! orientation DCM unpacked flat -- 9 elements + i1=(k-1)*9+1; i2=i1+8 + StructTmp%BldDCM_c(i1:i2) = real(reshape(Root%Orientation(1:3,1:3,1),(/9/)), c_float) + ! Translation Vel / Accel -- TVx,TVy,TVz / TAx, TAy, TAz + i1=(k-1)*6+1; i2=i1+2 + StructTmp%BldVel_c(i1:i2) = real(Root%TranslationVel(1:3,1), c_float) + StructTmp%BldAcc_c(i1:i2) = real(Root%TranslationAcc(1:3,1), c_float) + ! Rotation Vel / Accel -- RVx,RVy,RVz / RAx, RAy, RAz + i1=(k-1)*6+4; i2=i1+2 + StructTmp%BldVel_c(i1:i2) = real(Root%RotationVel(1:3,1), c_float) + StructTmp%BldAcc_c(i1:i2) = real(Root%RotationAcc(1:3,1), c_float) + enddo +end subroutine + +subroutine SetADIMeshLoads() + type(MeshType), pointer :: RootLd + integer(IntKi) :: k,i1,i2 + do k=1,SimSettings%TrbCfg%NumBl + RootLd => MeshLoads%BladeRootLoads(k) + ! Forces + i1=(k-1)*6+1; i2=i1+2 + RootLd%Force(1:3,1) = real(CalcStepIO%FrcMom_ADI_c(i1:i2), ReKi) + ! Momment + i1=(k-1)*6+4; i2=i1+2 + RootLd%Moment(1:3,1) = real(CalcStepIO%FrcMom_ADI_c(i1:i2), ReKi) + enddo +end subroutine + + + +subroutine WaveTank_End(ErrStat_C, ErrMsg_C) bind (C, NAME="WaveTank_End") #ifndef IMPLICIT_DLLEXPORT -!DEC$ ATTRIBUTES DLLEXPORT :: WaveTank_CalcOutput -!GCC$ ATTRIBUTES DLLEXPORT :: WaveTank_CalcOutput +!DEC$ ATTRIBUTES DLLEXPORT :: WaveTank_End +!GCC$ ATTRIBUTES DLLEXPORT :: WaveTank_End #endif -! INTEGER(C_INT) :: delta_time -INTEGER(C_INT) :: frame_number -REAL(C_FLOAT), INTENT(IN ) :: positions_x(N_CAMERA_POINTS) -REAL(C_FLOAT), INTENT(IN ) :: positions_y(N_CAMERA_POINTS) -REAL(C_FLOAT), INTENT(IN ) :: positions_z(N_CAMERA_POINTS) -REAL(C_FLOAT), INTENT(IN ) :: rotation_matrix(9) -REAL(C_FLOAT), INTENT( OUT) :: loads(N_CAMERA_POINTS) -INTEGER(C_INT), INTENT( OUT) :: ErrStat_C -CHARACTER(KIND=C_CHAR), INTENT( OUT) :: ErrMsg_C(ErrMsgLen_C) + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) -INTEGER :: i + ! Local variables + integer(c_int) :: ErrStat_C2 + character(kind=c_char, len=ErrMsgLen_C) :: ErrMsg_C2 + integer(IntKi) :: ErrStat_F2 + character(ErrMsgLen) :: ErrMsg_F2 -IF ( MOD(frame_number / load_period, 2) == 0 ) THEN - loads = -1.0 -ELSE - loads = 1.0 -ENDIF + ErrStat_C = ErrID_None + ErrMsg_C = " "//C_NULL_CHAR -END SUBROUTINE + ! destroy mesh info + call StructDestroy(MeshMotions, MeshLoads, MeshMaps, StructTmp, ErrStat_F2, ErrMsg_F2) + call SetErrStat_F2C(ErrStat_F2, ErrMsg_F2, ErrStat_C, ErrMsg_C) -SUBROUTINE WaveTank_End() bind (C, NAME="WaveTank_End") + ! in case we were writing to a file instead of the screen + if (ScreenLogOutput_Un > 0) close(ScreenLogOutput_Un) + call MD_C_END(ErrStat_C2, ErrMsg_C2) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'MD_C_END') -IMPLICIT NONE + call SeaSt_C_END(ErrStat_C2, ErrMsg_C2) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'SeaSt_C_END') + call ADI_C_END(ErrStat_C2, ErrMsg_C2) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'ADI_C_END') + ! close output file + if (WrOutputData%OutUn > 0) then + close(WrOutputData%OutUn, iostat=ErrStat_F2) + if (ErrStat_F2 /= 0_IntKi) call SetErrStat_C(int(ErrID_Fatal,c_int), 'could no close output file '//trim(WrOutputData%OutName), ErrStat_C, ErrMsg_C, 'ADI_C_END') + WrOutputData%OutUn = -1 ! mark as closed - prevents faults + endif +end subroutine -END SUBROUTINE -end module WaveTankTesting +subroutine WaveTank_SetWaveFieldPointer(ErrStat_C, ErrMsg_C) bind (C, NAME="WaveTank_SetWaveFieldPointer") +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: WaveTank_SetWaveFieldPointer +!GCC$ ATTRIBUTES DLLEXPORT :: WaveTank_SetWaveFieldPointer +#endif + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer(c_int) :: ErrStat_C2 + character(kind=c_char, len=ErrMsgLen_C) :: ErrMsg_C2 + + ! Set the SeaState FlowField pointer onto MoorDyn + type(c_ptr) :: WaveFieldPointer_C + type(SeaSt_WaveFieldType), pointer :: WaveFieldPointer_F => NULL() ! used only in sanity check + + ErrStat_C = ErrID_None + ErrMsg_C = " "//C_NULL_CHAR + + call SeaSt_C_GetWaveFieldPointer(WaveFieldPointer_C, ErrStat_C2, ErrMsg_C2) + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'WaveTank_SetWaveFieldPointer') + if (ErrStat_C >= AbortErrLev_C) return + + call C_F_POINTER(WaveFieldPointer_C, WaveFieldPointer_F) + ! Verify that the data in the WaveField pointer has been set + if (WaveFieldPointer_F%WtrDpth == 0) then + ErrStat_C2 = ErrID_Fatal + ErrMsg_C2 = "SeaState WaveFieldPointer is WtrDpth is 0.0, so it it probably not initialized." + call SetErrStat_C(ErrStat_C2, ErrMsg_C2, ErrStat_C, ErrMsg_C, 'WaveTank_SetWaveFieldPointer') + return + endif + + ! There isn't a good way to check for an error here. Will get caught at init + call MD_C_SetWaveFieldData(WaveFieldPointer_C) + + ! Probably doesn't matter, but clear the fortran pointer just in case + WaveFieldPointer_F => NULL() +end subroutine + +END MODULE WaveTankTesting diff --git a/glue-codes/labview/src/WaveTank_IO.f90 b/glue-codes/labview/src/WaveTank_IO.f90 new file mode 100644 index 0000000000..ab7cd5f84c --- /dev/null +++ b/glue-codes/labview/src/WaveTank_IO.f90 @@ -0,0 +1,398 @@ +!********************************************************************************************************************************** +! LICENSING +! Copyright (C) 2025 National Renewable Energy Laboratory +! +! This file is a module specific to an experimental wave tank at NREL. +! +! Licensed under the Apache License, Version 2.0 (the "License"); +! you may not use this file except in compliance with the License. +! You may obtain a copy of the License at +! +! http://www.apache.org/licenses/LICENSE-2.0 +! +! Unless required by applicable law or agreed to in writing, software +! distributed under the License is distributed on an "AS IS" BASIS, +! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +! See the License for the specific language governing permissions and +! limitations under the License. +!********************************************************************************************************************************** +! +! This code is designed to connect with LabView for a specific wave tank test case and likely will not work for other purposes. +! +! For this test, a physical platform is deployed in a wave tank with cable acutators that are controlled through LabView. This +! module is called to provide some loads that are not present in the physical tank setup. These include the following: +! - rotor loading from a fixed RPM MHK rotor from AeroDyn. This is calculated from either steady current provided by SeaState +! - Mooring loads from MoorDyn +! +! +!********************************************************************************************************************************** +MODULE WaveTank_IO + use ISO_C_BINDING + use NWTC_Library + use NWTC_IO + use WaveTank_Types + + implicit none + private + + public :: ParseInputFile + public :: ValidateInputFile + public :: TransferOutChanNamesUnits + public :: InitOutputFile + public :: WriteOutputLine + + ! These channels are output by default + integer(IntKi), parameter :: NumDefChans = 42 + character(OutStrLenM1), parameter :: DefChanNames(NumDefChans) = (/ "Time ", & + "Ptfm_x ","Ptfm_y ","Ptfm_z ", & ! position (absolute global) + "Ptfm_Rx ","Ptfm_Ry ","Ptfm_Rz ", & ! Euler angles phi,theta,psi + "Ptfm_Vx ","Ptfm_Vy ","Ptfm_Vz ", & ! translation vel + "Ptfm_RVx ","Ptfm_RVy ","Ptfm_RVz ", & ! rotation vel + "Ptfm_Ax ","Ptfm_Ay ","Ptfm_Az ", & ! translation acc + "Ptfm_RAx ","Ptfm_RAy ","Ptfm_RAz ", & ! rotation acc + "Ptfm_Fx ","Ptfm_Fy ","Ptfm_Fz ", & ! Forces total + "Ptfm_Mx ","Ptfm_My ","Ptfm_Mz ", & ! Moments total + "Ptfm_MD_Fx ","Ptfm_MD_Fy ","Ptfm_MD_Fz ", & ! Forces from MD + "Ptfm_MD_Mx ","Ptfm_MD_My ","Ptfm_MD_Mz ", & ! Moments from MD + "Ptfm_ADI_Fx ","Ptfm_ADI_Fy ","Ptfm_ADI_Fz ", & ! Forces from ADI + "Ptfm_ADI_Mx ","Ptfm_ADI_My ","Ptfm_ADI_Mz ", & ! Moments from ADI + "Azimuth ","RotSpeed ","BlPitch ", & + "NacYaw ","BuoyElev " & + /) + character(OutStrLenM1), parameter :: DefChanUnits(NumDefChans) = (/ "(s) ", & + "(m) ","(m) ","(m) ", & + "(rad) ","(rad) ","(rad) ", & + "(m/s) ","(m/s) ","(m/s) ", & + "(rad/s) ","(rad/s) ","(rad/s) ", & + "(m/s^2) ","(m/s^2) ","(m/s^2) ", & + "(rad/s^2) ","(rad/s^2) ","(rad/s^2) ", & + "(N) ","(N) ","(N) ", & ! Forces total + "(N-m) ","(N-m) ","(N-m) ", & ! Moments total + "(N) ","(N) ","(N) ", & ! Forces from MD + "(N-m) ","(N-m) ","(N-m) ", & ! Moments from MD + "(N) ","(N) ","(N) ", & ! Forces from ADI + "(N-m) ","(N-m) ","(N-m) ", & ! Moments from ADI + "(deg) ","(RPM) ","(deg) ", & + "(deg) ","(m) " & + /) + +contains + +subroutine ParseInputFile(FileInfo_In, SimSettings, ErrStat, ErrMsg) + type(FileInfoType), intent(in ) :: FileInfo_In !< The derived type for holding the file information. + type(SimSettingsType), intent( out) :: SimSettings + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + + ! Local variables + integer :: CurLine + character(1024), target :: TmpPath + character(1024) :: FileName + integer(IntKi) :: ErrStat2 ! local status of error message + character(ErrMsgLen) :: ErrMsg2 ! local error message if errStat /= ErrID_None + character(*), parameter :: RoutineName = 'WaveTankTesting.ParseInputFile' + + ErrStat = ErrID_None + ErrMsg = " " + + CurLine = 1 + ! Separator/header lines skipped automatically + ! ----- Simulation control ------------- + call ParseVar( FileInfo_In, CurLine, 'DT', SimSettings%Sim%DT, ErrStat2, ErrMsg2); if(Failed()) return; ! timestep (unused) + call ParseVar( FileInfo_In, CurLine, 'TMax', SimSettings%Sim%TMax, ErrStat2, ErrMsg2); if(Failed()) return; ! Max sim time (used only with SeaState wavemod 5) + call ParseVar( FileInfo_In, CurLine, 'MHK', SimSettings%Sim%MHK, ErrStat2, ErrMsg2); if(Failed()) return; ! MHK turbine type (switch) {0=Not an MHK turbine; 1=Fixed MHK turbine; 2=Floating MHK turbine} + call ParseVar( FileInfo_In, CurLine, 'InterpOrd', SimSettings%Sim%InterpOrd, ErrStat2, ErrMsg2); if(Failed()) return; ! Interpolation order [unused] +!TODO: These are placeholders for later use. Some of the logic is incomplete which is why this has been commented out. +! call ParseVar( FileInfo_In, CurLine, 'ScaleFact', SimSettings%Sim%ScaleFact, ErrStat2, ErrMsg2); if(Failed()) return; ! scaling factor for scaling full size model to wavetank scale results (Froude scaling: lambda = full_dimension / scale_dimension) [>1 expected] (-) +! call ParseVar( FileInfo_In, CurLine, 'DensFact', SimSettings%Sim%DensFact, ErrStat2, ErrMsg2); if(Failed()) return; ! ratio of density - Density_full/Density_model (rho_F/rho_M). Used with Froude scaling of forces/moments" (-) + call ParseVar( FileInfo_In, CurLine, 'DebugLevel', SimSettings%Sim%DebugLevel, ErrStat2, ErrMsg2); if(Failed()) return; ! 0: none, 1: I/O summary, 2: +positions/orientations passed, 3:, 4: +all meshes + call ParseVar( FileInfo_In, CurLine, 'OutRootName', SimSettings%Sim%OutRootName, ErrStat2, ErrMsg2); if(Failed()) return; ! Root name for any summary or other files + ! -------- Environment ---------------- + call ParseVar( FileInfo_In, CurLine, 'Gravity', SimSettings%Env%Gravity, ErrStat2, ErrMsg2); if(Failed()) return; ! Gravitational acceleration (m/s^2) + call ParseVar( FileInfo_In, CurLine, 'WtrDens', SimSettings%Env%WtrDens, ErrStat2, ErrMsg2); if(Failed()) return; ! Water density (kg/m^3) + call ParseVar( FileInfo_In, CurLine, 'WtrVisc', SimSettings%Env%WtrVisc, ErrStat2, ErrMsg2); if(Failed()) return; ! Kinematic viscosity of working fluid (m^2/s) + call ParseVar( FileInfo_In, CurLine, 'SpdSound', SimSettings%Env%SpdSound, ErrStat2, ErrMsg2); if(Failed()) return; ! Speed of sound in working fluid (m/s) + call ParseVar( FileInfo_In, CurLine, 'Patm', SimSettings%Env%Patm, ErrStat2, ErrMsg2); if(Failed()) return; ! Atmospheric pressure (Pa) [used only for an MHK turbine cavitation check] + call ParseVar( FileInfo_In, CurLine, 'Pvap', SimSettings%Env%Pvap, ErrStat2, ErrMsg2); if(Failed()) return; ! Vapour pressure of working fluid (Pa) [used only for an MHK turbine cavitation check] + call ParseVar( FileInfo_In, CurLine, 'WtrDpth', SimSettings%Env%WtrDpth, ErrStat2, ErrMsg2); if(Failed()) return; ! Water depth (m) + call ParseVar( FileInfo_In, CurLine, 'MSL2SWL', SimSettings%Env%MSL2SWL, ErrStat2, ErrMsg2); if(Failed()) return; ! Offset between still-water level and mean sea level (m) [positive upward] + ! -------- SeaState ------------------- + call ParseVar( FileInfo_In, CurLine, 'SS_InputFile', SimSettings%ModSettings%SS_InputFile, ErrStat2, ErrMsg2); if(Failed()) return; ! SeaState input file + call ParseVar( FileInfo_In, CurLine, 'WaveTimeShift', SimSettings%ModSettings%WaveTimeShift, ErrStat2, ErrMsg2); if(Failed()) return; ! Shift the SeaState wavetime by this amount (for phase shifting waves to match tank) + ! -------- MoorDyn -------------------- + call ParseVar( FileInfo_In, CurLine, 'MD_InputFile', SimSettings%ModSettings%MD_InputFile, ErrStat2, ErrMsg2); if(Failed()) return; ! MoorDyn input file + ! -------- AeroDyn + InflowWind ------- + call ParseVar( FileInfo_In, CurLine, 'AD_InputFile', SimSettings%ModSettings%AD_InputFile, ErrStat2, ErrMsg2); if(Failed()) return; ! AeroDyn input file + call ParseVar( FileInfo_In, CurLine, 'IfW_InputFile', SimSettings%ModSettings%IfW_InputFile, ErrStat2, ErrMsg2); if(Failed()) return; ! InflowWind input file + ! -------- Turbine Configuration ------ + call ParseVar( FileInfo_In, CurLine, 'NumBl', SimSettings%TrbCfg%NumBl, ErrStat2, ErrMsg2); if(Failed()) return; ! Number of blades (-) + call ParseVar( FileInfo_In, CurLine, 'HubRad', SimSettings%TrbCfg%HubRad, ErrStat2, ErrMsg2); if(Failed()) return; ! The distance from the rotor apex to the blade root (meters) + call ParseVar( FileInfo_In, CurLine, 'PreCone', SimSettings%TrbCfg%PreCone, ErrStat2, ErrMsg2); if(Failed()) return; ! Blade cone angle (degrees) + SimSettings%TrbCfg%PreCone = D2R * SimSettings%TrbCfg%PreCone + call ParseVar( FileInfo_In, CurLine, 'OverHang', SimSettings%TrbCfg%OverHang, ErrStat2, ErrMsg2); if(Failed()) return; ! Distance from yaw axis to rotor apex [3 blades] or teeter pin [2 blades] (meters) + call ParseVar( FileInfo_In, CurLine, 'ShftTilt', SimSettings%TrbCfg%ShftTilt, ErrStat2, ErrMsg2); if(Failed()) return; ! Rotor shaft tilt angle (degrees) + SimSettings%TrbCfg%ShftTilt = D2R * SimSettings%TrbCfg%ShftTilt + call ParseVar( FileInfo_In, CurLine, 'Twr2Shft', SimSettings%TrbCfg%Twr2Shft, ErrStat2, ErrMsg2); if(Failed()) return; ! Vertical distance from the tower-top to the rotor shaft, center of nacelle (meters) + call ParseVar( FileInfo_In, CurLine, 'TowerHt', SimSettings%TrbCfg%TowerHt, ErrStat2, ErrMsg2); if(Failed()) return; ! Height of tower relative MSL + call ParseAry( FileInfo_In, CurLine, 'TowerBsPt', SimSettings%TrbCfg%TowerBsPt, 3, ErrStat2, ErrMsg2); if(Failed()) return; ! Height of tower base relative to PtfmRefPos in x,y, and water surface in z (meters) + call ParseAry( FileInfo_In, CurLine, 'PtfmRefPos', SimSettings%TrbCfg%PtfmRefPos, 3, ErrStat2, ErrMsg2); if(Failed()) return; ! Location of platform reference point, relative to MSL. Motions and loads all connect to this point + call ParseAry( FileInfo_In, CurLine, 'PtfmRefOrient', SimSettings%TrbCfg%PtfmRefOrient, 3, ErrStat2, ErrMsg2); if(Failed()) return; ! Orientation of platform reference point, Euler angle set of roll,pitch,yaw" (deg) + SimSettings%TrbCfg%PtfmRefOrient = D2R * SimSettings%TrbCfg%PtfmRefOrient + ! -------- Turbine Operating Point ---- + call ParseVar( FileInfo_In, CurLine, 'RotSpeed', SimSettings%TrbInit%RotSpeed, ErrStat2, ErrMsg2); if(Failed()) return; ! Rotational speed of rotor in rotor coordinates (rpm) + call ParseVar( FileInfo_In, CurLine, 'NacYaw', SimSettings%TrbInit%NacYaw, ErrStat2, ErrMsg2); if(Failed()) return; ! Initial or fixed nacelle-yaw angle (deg read) + call ParseVar( FileInfo_In, CurLine, 'BldPitch', SimSettings%TrbInit%BldPitch, ErrStat2, ErrMsg2); if(Failed()) return; ! Blade 1 pitch (deg read) + call ParseVar( FileInfo_In, CurLine, 'Azimuth', SimSettings%TrbInit%Azimuth, ErrStat2, ErrMsg2); if(Failed()) return; ! Initial azimuth (deg read) + ! angles are read as degrees, store as radians internally + SimSettings%TrbInit%NacYaw = D2R * SimSettings%TrbInit%NacYaw + SimSettings%TrbInit%BldPitch = D2R * SimSettings%TrbInit%BldPitch + SimSettings%TrbInit%Azimuth = D2R * SimSettings%TrbInit%Azimuth + ! -------- Wave Buoy------------------ + call ParseAry( FileInfo_In, CurLine, 'WaveBuoyLoc', SimSettings%WaveBuoy%XYLoc, 2, ErrStat2, ErrMsg2); if(Failed()) return; ! Location of the wave elevation measurement buoy. SeaState data returned at every timestep at this location (m) + ! -------- Output --------------------- + call ParseVar( FileInfo_In, CurLine, 'SendScreenToFile', SimSettings%Outs%SendScreenToFile, ErrStat2, ErrMsg2); if(Failed()) return; ! send to file .screen.log if true + call ParseVar( FileInfo_In, CurLine, 'OutFile', SimSettings%Outs%OutFile, ErrStat2, ErrMsg2); if(Failed()) return; ! 0: no output file of channels, 1: output file in text format (at default DT) + call ParseVar( FileInfo_In, CurLine, 'OutFmt', SimSettings%Outs%OutFmt, ErrStat2, ErrMsg2); if(Failed()) return; ! Format used for text tabular output, excluding the time channel. (quoted string) + ! -------- VTK output ----------------- + call ParseVar( FileInfo_In, CurLine, 'WrVTK_Dir', SimSettings%Viz%WrVTK_Dir, ErrStat2, ErrMsg2); if(Failed()) return; ! output directory for visualization + call ParseVar( FileInfo_In, CurLine, 'WrVTK', SimSettings%Viz%WrVTK, ErrStat2, ErrMsg2); if(Failed()) return; ! VTK visualization data output: (switch) {0=none; 1=initialization data only; 2=animation; 3=mode shapes} + call ParseVar( FileInfo_In, CurLine, 'WrVTK_type', SimSettings%Viz%WrVTK_type, ErrStat2, ErrMsg2); if(Failed()) return; ! Type of VTK visualization data: (switch) {1=surfaces; 2=basic meshes (lines/points); 3=all meshes (debug)} [unused if WrVTK=0] + call ParseVar( FileInfo_In, CurLine, 'WrVTK_DT', SimSettings%Viz%WrVTK_DT, ErrStat2, ErrMsg2); if(Failed()) return; ! DT for writing VTK files + call ParseAry( FileInfo_In, CurLine, 'VTKNacDim', SimSettings%Viz%VTKNacDim, 6, ErrStat2, ErrMsg2); if(Failed()) return; ! Nacelle dimension passed in for VTK surface rendering [0,y0,z0,Lx,Ly,Lz] (m) + +contains + logical function Failed() + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + Failed = ErrStat >= AbortErrLev + end function Failed +end subroutine + + + +subroutine ValidateInputFile(SimSettings, ErrStat, ErrMsg) + type(SimSettingsType), intent(inout) :: SimSettings + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WaveTankTesting.ValidateInputFile' + logical :: file_exists + + ErrStat = ErrID_None + ErrMsg = "" + + !------------------------ + ! Sim Control + !------------------------ + if (SimSettings%Sim%MHK /= 2_c_int) call SetErrStat(ErrID_Fatal, "WaveTank module only works for floating MHK turbines at present (MHK=2).",ErrStat,ErrMsg,RoutineName) + if (SimSettings%Sim%ScaleFact < 1.0_c_float) call SetErrStat(ErrID_Fatal, "ScaleFact should be > 1", ErrStat,ErrMsg,RoutineName) + if (SimSettings%Sim%ScaleFact > 1.0_c_float) then + call SetErrStat(ErrID_Warn, "ScaleFact should be == 1 for now. Scaling is untested and incomplete!!!!", ErrStat,ErrMsg,RoutineName) + call WrScr("/---------------------------------------------------------\") + call WrScr("|--- WARNING ---- WARNING ---- WARNING ---- WARNING ---|") + call WrScr("|---------------------------------------------------------|") + call WrScr("| |") + call WrScr("| Froude scaling is not complete, nor is it tested!!!! |") + call WrScr("| |") + call WrScr("| At present, only some inputs are scaled, but equations |") + call WrScr("| have not been verified yet. This is useful just for |") + call WrScr("| observing motions are occuring, but will corrupt your |") + call WrScr("| simulation. |") + call WrScr("| |") + call WrScr("| Set ScaleFact=1.0 in your input file. |") + call WrScr("| |") + call WrScr("| TODO: |") + call WrScr("| - verify equations in FroudeScaling* functions |") + call WrScr("| - scale resulting forces / moments |") + call WrScr("| - add scaled time, pos, vel, acc, frc, mom to output |") + call WrScr("| channels and add subscripting to differentiate |") + call WrScr("| |") + call WrScr("\---------------------------------------------------------/") + endif + + + !------------------------ + ! Environment + !------------------------ + + !------------------------ + ! Turbine Config + !------------------------ + + !------------------------ + ! Input files + !------------------------ + inquire(file=SimSettings%ModSettings%SS_InputFile, exist=file_exists); if (.not. file_exists) call SetErrStat(ErrID_Fatal,"Cannot find SeaState input file " //trim(SimSettings%ModSettings%SS_InputFile ),ErrStat,ErrMsg,RoutineName) + inquire(file=SimSettings%ModSettings%MD_InputFile, exist=file_exists); if (.not. file_exists) call SetErrStat(ErrID_Fatal,"Cannot find MoorDyn input file " //trim(SimSettings%ModSettings%MD_InputFile ),ErrStat,ErrMsg,RoutineName) + inquire(file=SimSettings%ModSettings%AD_InputFile, exist=file_exists); if (.not. file_exists) call SetErrStat(ErrID_Fatal,"Cannot find AeroDyn input file " //trim(SimSettings%ModSettings%AD_InputFile ),ErrStat,ErrMsg,RoutineName) + inquire(file=SimSettings%ModSettings%IfW_InputFile, exist=file_exists); if (.not. file_exists) call SetErrStat(ErrID_Fatal,"Cannot find InflowWind input file "//trim(SimSettings%ModSettings%IfW_InputFile),ErrStat,ErrMsg,RoutineName) + if (ErrStat >= AbortErrLev) return + + ! Wave time shift must be positive + if (SimSettings%ModSettings%WaveTimeShift < 0.0_DbKi) call SetErrStat(ErrID_Fatal, "WaveTimeShift must be >= 0",Errstat,ErrMsg,RoutineName) + + !------------------------ + ! Turbine Operating point + !------------------------ + + !------------------------ + ! Output + !------------------------ + + !------------------------ + ! VTK + !------------------------ + +contains + logical function Failed() + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + Failed = ErrStat >= AbortErrLev + end function Failed +end subroutine + + +!> Transfer names or units from c character arrays into fortran character arrays for output file writing +subroutine TransferOutChanNamesUnits(NumChans, name, NamesUnits_C, NamesUnits, ErrStat, ErrMsg) + integer(IntKi), intent(in ) :: NumChans + character(*), intent(in ) :: name + character(c_char), intent(in ) :: NamesUnits_C(:) + character(ChanLen), allocatable, intent( out) :: NamesUnits(:) + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi) :: i,idxStart,idxEnd + call AllocAry(NamesUnits, NumChans, name, ErrStat, ErrMsg) + if (ErrStat >= AbortErrLev) return + do i=1,NumChans + idxStart = (i-1)*ChanLen + 1 + idxEnd = i*ChanLen + NamesUnits(i) = transfer(NamesUnits_C(idxStart:idxEnd),NamesUnits(i)) + enddo +end subroutine + + +!> open the output file and populate the header +subroutine InitOutputFile(WrOutputData, ErrStat, ErrMsg) + type(WrOutputDataType), intent(inout) :: WrOutputData + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi) :: i + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'InitOutputFile' + if (WrOutputData%OutUn > 0) then + ErrStat = ErrID_Warn + ErrMsg = "Output file "//trim(WrOutputData%OutName)//" already open" + return + endif + + call GetNewUnit(WrOutputData%OutUn,ErrStat2,ErrMsg2) + call OpenFOutFile (WrOutputData%OutUn, trim(WrOutputData%OutName), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + +!FIXME: add version info here + ! write header line + write (WrOutputData%OutUn,'(/,A)') 'Simulation run on '//CurDate()//' at '//CurTime()//' using the WaveTank c-binding interface' + write (WrOutputData%OutUn,'()') + write (WrOutputData%OutUn,'()') + write (WrOutputData%OutUn,'()') + write (WrOutputData%OutUn,'()') + + !...................................................... + ! Write the names of the output parameters on one line: + !...................................................... + ! default outputs + call WrFileNR(WrOutputData%OutUn, DefChanNames(1)) ! time channel + do i=2,NumDefChans + call WrFileNR(WrOutputData%OutUn, tab//DefChanNames(i)) + enddo + ! SS + do i=1,WrOutputData%NumChans_SS + call WrFileNR(WrOutputData%OutUn, tab//WrOutputData%WriteOutputHdr_SS(i)) + enddo + ! MD + do i=1,WrOutputData%NumChans_MD + call WrFileNR(WrOutputData%OutUn, tab//WrOutputData%WriteOutputHdr_MD(i)) + enddo + ! ADI + do i=1,WrOutputData%NumChans_ADI + call WrFileNR(WrOutputData%OutUn, tab//WrOutputData%WriteOutputHdr_ADI(i)) + enddo + write (WrOutputData%OutUn,'()') + + !...................................................... + ! Write the units of the output parameters on one line: + !...................................................... + ! default outputs + call WrFileNR(WrOutputData%OutUn, DefChanUnits(1)) ! time channel + do i=2,NumDefChans + call WrFileNR(WrOutputData%OutUn, tab//DefChanUnits(i)) + enddo + ! SS + do i=1,WrOutputData%NumChans_SS + call WrFileNR(WrOutputData%OutUn, tab//WrOutputData%WriteOutputUnt_SS(i)) + enddo + ! MD + do i=1,WrOutputData%NumChans_MD + call WrFileNR(WrOutputData%OutUn, tab//WrOutputData%WriteOutputUnt_MD(i)) + enddo + ! ADI + do i=1,WrOutputData%NumChans_ADI + call WrFileNR(WrOutputData%OutUn, tab//WrOutputData%WriteOutputUnt_ADI(i)) + enddo + write (WrOutputData%OutUn,'()') + +end subroutine + + +subroutine WriteOutputLine(OutFmt, CalcStepIO, StructTmp, WrOutputData, ErrStat, ErrMsg) + character(*), intent(in ) :: OutFmt + type(CalcStepIOdataType), intent(in ) :: CalcStepIO + type(StructTmpType), intent(in ) :: StructTmp ! operating states are in here + type(WrOutputDataType), intent(in ) :: WrOutputData + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi) :: OutUnit + integer(IntKi) :: errStat2 ! Status of error message (we're going to ignore errors in writing to the file) + character(ErrMsgLen) :: errMsg2 ! Error message if ErrStat /= ErrID_None + character(200) :: frmt ! A string to hold a format specifier + character(15) :: tmpStr ! temporary string to print the time output as text + real(ReKi) :: TmpAry5(5) ! temporary array for RotSpeed, BlPitch, NacYaw, Azimuth, BuoyWaveElev + + ErrStat = ErrID_None + ErrMSg = "" + + frmt = '"'//tab//'"'//trim(OutFmt) ! format for array elements from individual modules + OutUnit = WrOutputData%OutUn + if (OutUnit <= 0_IntKi) then + ErrStat = ErrID_Severe + ErrMSg = 'Cannot write to output file '//trim(WrOutputData%OutName) + return + endif + + ! time + write( tmpStr, '(F15.6)' ) CalcStepIO%Time_c + call WrFileNR( OutUnit, tmpStr ) + ! position / orientation euler angles, velocity, accel, resulting force/moment + call WrNumAryFileNR(OutUnit, CalcStepIO%PosAng_c, frmt, errStat2, errMsg2); if (Failed()) return + call WrNumAryFileNR(OutUnit, CalcStepIO%Vel_c, frmt, errStat2, errMsg2); if (Failed()) return + call WrNumAryFileNR(OutUnit, CalcStepIO%Acc_c, frmt, errStat2, errMsg2); if (Failed()) return + call WrNumAryFileNR(OutUnit, CalcStepIO%FrcMom_c, frmt, errStat2, errMsg2); if (Failed()) return ! total + call WrNumAryFileNR(OutUnit, StructTmp%FrcMom_MD_at_Ptfm, frmt, errStat2, errMsg2); if (Failed()) return + call WrNumAryFileNR(OutUnit, StructTmp%FrcMom_ADI_at_Ptfm, frmt, errStat2, errMsg2); if (Failed()) return + TmpAry5 = (/ R2D*StructTmp%Azimuth, StructTmp%RotSpeed, R2D*StructTmp%BldPitch, R2D*StructTmp%NacYaw, CalcStepIO%BuoyWaveElev /) + call WrNumAryFileNR(OutUnit, TmpAry5, frmt, errStat2, errMsg2); if (Failed()) return + ! channels from modules + call WrNumAryFileNR(OutUnit, WrOutputData%OutData_SS, frmt, errStat2, ErrMsg2); if (Failed()) return + call WrNumAryFileNR(OutUnit, WrOutputData%OutData_MD, frmt, errStat2, ErrMsg2); if (Failed()) return + call WrNumAryFileNR(OutUnit, WrOutputData%OutData_ADI, frmt, errStat2, ErrMsg2); if (Failed()) return + ! write a new line (advance to the next line) + write (OutUnit,'()') +contains + logical function Failed() + call SetErrStat(errStat2, errMsg2, errStat, errMsg, 'WriteOutputLine') + Failed = errStat >= AbortErrLev + end function Failed +end subroutine + +END MODULE WaveTank_IO diff --git a/glue-codes/labview/src/WaveTank_Registry.txt b/glue-codes/labview/src/WaveTank_Registry.txt new file mode 100644 index 0000000000..f3072b5aca --- /dev/null +++ b/glue-codes/labview/src/WaveTank_Registry.txt @@ -0,0 +1,160 @@ +################################################################################################################################### +# Registry for WaveTank module +# This Registry file is used to create WaveTank_Types.f90 +# It also contains copy, destroy, pack, and unpack routines associated with each defined data types. +# See the NWTC Programmer's Handbook for further information on the format/contents of this file. +# +# Entries are of the form +# +# +# Use ^ as a shortcut for the value in the same column from the previous line. +################################################################################################################################### +# ...... Include files (definitions from NWTC Library) ............................................................................ +include Registry_NWTC_Library.txt +# + +typedef WaveTank/WT SimType c_double DT - - - "timestep" - +typedef ^ ^ c_double TMax - - - "Max sim time" - +typedef ^ ^ c_int MHK - - - "MHK turbine type (switch) {0=Not an MHK turbine; 1=Fixed MHK turbine; 2=Floating MHK turbine}" (-) +typedef ^ ^ c_int InterpOrd - 1 - "Interpolation order" - +typedef ^ ^ c_float ScaleFact - 1 - "scaling factor for scaling full size model to wavetank scale results (Froude scaling: lambda = full_dimension / scale_dimension) (>1 expected) (-) +typedef ^ ^ c_float DensFact - 1 - "ratio of density - Density_full/Density_model (rho_F/rho_M). Used with Froude scaling of forces/moments" (-) +typedef ^ ^ c_int DebugLevel - - - "Debug level for outputs" - +typedef ^ ^ character(1024) OutRootName - - - "Rootname for outputs" - + +typedef ^ EnvType c_float Gravity - - - "gravitational constant (positive for down)" (m/s^2) +typedef ^ ^ ^ WtrDens - - - "Water density" (kg/m^3) +typedef ^ ^ ^ WtrVisc - - - "fluid viscosity" (m^2/s) +typedef ^ ^ ^ SpdSound - - - "Speed of sound in working fluid" (m/s) +typedef ^ ^ ^ Patm - - - "Atmospheric pressure [used only for an MHK turbine cavitation check]" (Pa) +typedef ^ ^ ^ Pvap - - - "Vapour pressure of working fluid [used only for an MHK turbine cavitation check]" (Pa) +typedef ^ ^ ^ WtrDpth - - - "Water depth" (m) +typedef ^ ^ ^ MSL2SWL - - - "Mean sea level to still water level" (m) + +typedef ^ TurbConfigType IntKi NumBl - - - "Number of blades" (-) +typedef ^ ^ SiKi HubRad - - - "The distance from the rotor apex to the blade root" (m) +typedef ^ ^ ^ PreCone - - - "Blade cone angle" (deg) +typedef ^ ^ ^ OverHang - - - "Distance from yaw axis to rotor apex [3 blades] or teeter pin [2 blades]" (m) +typedef ^ ^ ^ ShftTilt - - - "Rotor shaft tilt angle" (deg) +typedef ^ ^ ^ Twr2Shft - - - "Vertical distance from the tower-top to the rotor shaft" (m) +typedef ^ ^ ^ TowerHt - - - "Height of tower relative MSL" (m) +typedef ^ ^ ^ TowerBsPt 3 - - "Tower base location relative to MSL. Consider absolute difference to PtfmRef [floating MHK]" (m) +typedef ^ ^ ^ PtfmRefPos 3 - - "Location of platform reference point, relative to MSL. Motions and loads all connect to this point" (m) +typedef ^ ^ ^ PtfmRefOrient 3 - - "Orientation of platform reference point, Euler angle set of roll,pitch,yaw" (rad) + +typedef ^ TurbInitCondType ReKi RotSpeed - - - "Rotor speed" (RPM) +typedef ^ ^ ^ NacYaw - - - "Initial or fixed nacelle-yaw angle - read as deg, convert to rad" (rad) +typedef ^ ^ ^ BldPitch - - - "Fixed blade pitch for full simulation - read as deg, convert to rad" (rad) +typedef ^ ^ ^ Azimuth - 0 - "Initial azimuth (actual azimuth calculated and not stored) - read as deg, convert to rad" (rad) + +typedef ^ WaveBuoyType ReKi XYLoc 2 - - "Location of the wave elevation measurement buoy. SeaState data returned at every timestep at this location" (m) + +typedef ^ OutFilesType logical SendScreenToFile - - - "send to file .screen.log if true" (-) +typedef ^ ^ c_int OutFile - - - "0: no output file of channels, 1: output file in text format (at DT)" (-) +typedef ^ ^ character(20) OutFmt - - - "Format used for text tabular output, excluding the time channel. (quoted string)" (-) + +typedef ^ VizType c_int WrVTK - - - "Write VTK?" - +typedef ^ ^ ^ WrVTK_type - - - "Write VTK outputs as [1: surface, 2: lines, 3: both]" - +typedef ^ ^ c_double WrVTK_DT - - - "Time step between VTK writes" - +typedef ^ ^ character(1024) WrVTK_dir - - - "Directory for VTK writing" - +typedef ^ ^ c_float VTKNacDim 6 - - "Nacelle dimension passed in for VTK surface rendering [0,y0,z0,Lx,Ly,Lz]" (m) +typedef ^ ^ IntKi Twidth - 6 - "Time width -- hard coded for now" (-) + +typedef ^ ModSettings character(1024) SS_InputFile - - - "SeaState input file" (-) +typedef ^ ^ DbKi WaveTimeShift - - - "Shift the SeaState wavetime by this amount (for phase shifting waves to match tank)" (s) +typedef ^ ^ character(1024) MD_InputFile - - - "MoorDyn input file" (-) +typedef ^ ^ ^ AD_InputFile - - - "AeroDyn input file" (-) +typedef ^ ^ ^ IfW_InputFile - - - "InflowWind input file" (-) + + +typedef ^ SimSettingsType SimType Sim - - - "Simulation settings" - +typedef ^ ^ EnvType Env - - - "Environment settings" - +typedef ^ ^ TurbConfigType TrbCfg - - - "Turbine configuration" - +typedef ^ ^ TurbInitCondType TrbInit - - - "Turbine initial operating point" - +typedef ^ ^ WaveBuoyType WaveBuoy - - - "Wave elevation buoy locat (x-y)" (m) +typedef ^ ^ OutFilesType Outs - - - "Output settings" - +typedef ^ ^ VizType Viz - - - "Vizualization settings" - +typedef ^ ^ ModSettings ModSettings - - - "Input files for each module" - + + +# Storage for IO to CalcStep +typedef ^ CalcStepIOdataType c_double Time_c - - - "IN: time" "(s)" +typedef ^ ^ c_float PosAng_c 6 - - "IN: Position + Euler Ang [x,y,z,phi,theta,psi]" "[(m) (rad)]" +typedef ^ ^ ^ Vel_c 6 - - "IN: Velocity [Vx,Vy,Vz,RVx,RVy,RVz]" "[(m/s) (rad/s)]" +typedef ^ ^ ^ Acc_c 6 - - "IN: Acceleration [Ax,Ay,Az,RAx,RAy,RAz]" "[(m/s^2) (rad/s^2)]" +typedef ^ ^ ^ FrcMom_c 6 - - "OUT: Acceleration [Fx,Fy,Fz,Mx,My,Mz]" "[(N) (N-m)]" +typedef ^ ^ ^ FrcMom_MD_c 6 - - "calculated forces/moments from MD" - +typedef ^ ^ ^ FrcMom_ADI_c : - - "calculated forces/moments from ADI" - +typedef ^ ^ ^ HubVel_ADI_c 3 - - "hub height wind vel from ADI" - +typedef ^ ^ ReKi BuoyWaveElev - - - "calculated wave elevation at buoy" - + + +# storage for output file data (num chans, WriteOutputHdr, out arrays per module, etc) +typedef ^ WrOutputDataType IntKi NumChans_cbind - 0 - "Number of output channels from c-bind" - +typedef ^ ^ ^ NumChans_SS - 0 - "Number of output channels from SS" - +typedef ^ ^ ^ NumChans_MD - 0 - "Number of output channels from MD" - +typedef ^ ^ ^ NumChans_ADI - 0 - "Number of output channels from ADI" - +typedef ^ ^ ^ NumChans_all - 0 - "Total number of channels (sum of above)" - +typedef ^ ^ character(ChanLen) WriteOutputHdr_SS : - - "output file header names from SS" - +typedef ^ ^ ^ WriteOutputUnt_SS : - - "output file header units from SS" - +typedef ^ ^ ^ WriteOutputHdr_MD : - - "output file header names from MD" - +typedef ^ ^ ^ WriteOutputUnt_MD : - - "output file header units from MD" - +typedef ^ ^ ^ WriteOutputHdr_ADI : - - "output file header names from ADI" - +typedef ^ ^ ^ WriteOutputUnt_ADI : - - "output file header units from ADI" - +typedef ^ ^ c_float OutData_SS_c : - - "output data from SS as passed c_float" - +typedef ^ ^ ^ OutData_MD_c : - - "output data from MD as passed c_float" - +typedef ^ ^ ^ OutData_ADI_c : - - "output data from ADI as passed c_float" - +typedef ^ ^ SiKi OutData_SS : - - "output data from SS" - +typedef ^ ^ ^ OutData_MD : - - "output data from MD" - +typedef ^ ^ ^ OutData_ADI : - - "output data from ADI" - +typedef ^ ^ character(1024) OutName - - - "Output file name" - +typedef ^ ^ IntKi OutUn - -1 - "Output unit" - + + +# Mesh structures and mappings +typedef ^ MeshesMotionType MeshType PtfmPtMotion - - - "Platform principle ref point. Also serves as tower base" - +typedef ^ ^ ^ TowerMotion - - - "Tower mesh (used only for vis)" - +typedef ^ ^ ^ HubMotion - - - "Hub mesh (for mappings, no loadings)" - +typedef ^ ^ ^ BladeRootMotion : - - "Blade root motions" - +typedef ^ ^ ^ WaveBuoyMotion - - - "wave measurement buoy motion (sensor only)" - + +typedef ^ MeshesLoadsType MeshType PtfmPtLoads - - - "Platform principle ref point loads output" - +typedef ^ ^ ^ PtfmPtLoadsTmp - - - "Platform principle ref point loads output - temp var for load summation" - +typedef ^ ^ ^ MooringLoads - - - "Mooring loads (always at PtfmPt, but separated for simplicity)" - +typedef ^ ^ ^ TowerLoads - - - "Tower mesh (unused)" - +typedef ^ ^ ^ HubLoads - - - "Hub mesh (for mappings, intermediate loads)" - +typedef ^ ^ ^ BladeRootLoads : - - "Blade root loads" - + +# NOTE: rigid geometry, no yaw, static pitch +typedef ^ MeshesMapsType MeshMapType Motion_PRP_2_Twr - - - "PRP to tower motion" - +typedef ^ ^ ^ Motion_PRP_2_Hub - - - "Twrtop to nacelle - add rotation afterwards" - +typedef ^ ^ ^ Motion_Hub_2_BldRoot : - - "Hub to blade root motion transfer" - +typedef ^ ^ ^ Load_BldRoot_2_Hub : - - "Blade root loads to hub" - +typedef ^ ^ ^ Load_Hub_2_PRP - - - "Hub to nacelle load transfer" - +typedef ^ ^ ^ Load_Twr_2_PRP - - - "Tower loads to PRP (unused)" - +typedef ^ ^ ^ Load_Moor_2_PRP - - - "Mooring loads to PRP" - + +# temporary data storage for structural model +typedef ^ StructTmpType ReKi Azimuth - 0 - "Current Azimuth" (rad) +typedef ^ ^ ^ RotSpeed - - - "Rotor speed" (RPM) +typedef ^ ^ ^ BldPitch - - - "Blade pitch" (rad) +typedef ^ ^ ^ NacYaw - - - "Nacelle-yaw angle" (rad) +typedef ^ ^ ^ FrcMom_ADI_at_Ptfm 6 - - "Total aero loading summed to Ptfm point" (N, N-m) +typedef ^ ^ ^ FrcMom_MD_at_Ptfm 6 - - "MoorDyn loading at Ptfm point" (N, N-m) +typedef ^ ^ c_float BuoyPos_c 2 - - "Buoy XY position" (m) +typedef ^ ^ ^ PtfmPosAng_c 6 - - "Temp position and euler angle" (m, rad) +typedef ^ ^ ^ PtfmVel_c 6 - - "Temp velocity " (m/s, rad/s) +typedef ^ ^ ^ PtfmAcc_c 6 - - "Temp acceleration " (m/s^2, rad/s^2) +typedef ^ ^ ^ NacPos_c 3 - - "Temp nacelle position " (m, rad) +typedef ^ ^ c_double NacDCM_c 9 - - "Temp nacelle orientation DCM" (-) +typedef ^ ^ c_float NacVel_c 6 - - "Temp nacelle velocity " (m/s, rad/s) +typedef ^ ^ ^ NacAcc_c 6 - - "Temp nacelle acceleration " (m/s^2, rad/s^2) +typedef ^ ^ ^ HubPos_c 3 - - "Temp hub position " (m, rad) +typedef ^ ^ c_double HubDCM_c 9 - - "Temp hub orientation DCM" (-) +typedef ^ ^ c_float HubVel_c 6 - - "Temp hub velocity " (m/s, rad/s) +typedef ^ ^ ^ HubAcc_c 6 - - "Temp hub acceleration " (m/s^2, rad/s^2) +typedef ^ ^ ^ BldPos_c : - - "Temp blade position -- sequential by blade" (m) +typedef ^ ^ c_double BldDCM_c : - - "Temp blade orientation DCM -- flat sequential by blade" (-) +typedef ^ ^ c_float BldVel_c : - - "Temp blade velocity -- sequential by blade" (m/s, rad/s) +typedef ^ ^ ^ BldAcc_c : - - "Temp blade acceleration -- sequential by blade" (m/s^2, rad/s^2) + diff --git a/glue-codes/labview/src/WaveTank_Struct.f90 b/glue-codes/labview/src/WaveTank_Struct.f90 new file mode 100644 index 0000000000..7c096b8e0b --- /dev/null +++ b/glue-codes/labview/src/WaveTank_Struct.f90 @@ -0,0 +1,666 @@ +!********************************************************************************************************************************** +! LICENSING +! Copyright (C) 2025 National Renewable Energy Laboratory +! +! This file is a module specific to an experimental wave tank at NREL. +! +! Licensed under the Apache License, Version 2.0 (the "License"); +! you may not use this file except in compliance with the License. +! You may obtain a copy of the License at +! +! http://www.apache.org/licenses/LICENSE-2.0 +! +! Unless required by applicable law or agreed to in writing, software +! distributed under the License is distributed on an "AS IS" BASIS, +! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +! See the License for the specific language governing permissions and +! limitations under the License. +!********************************************************************************************************************************** +! +! This module provides structural model for the wavetank interface +! +!********************************************************************************************************************************** +module WaveTank_Struct + use ISO_C_BINDING + use NWTC_Library + use WaveTank_Types + + implicit none + private + + save + + public :: StructCreate + public :: StructCreateMeshMaps + public :: StructDestroy + public :: StructMotionUpdate + public :: StructLoadsMeshTransfer + public :: WrVTK_Struct_Ref + public :: WrVTK_Struct + public :: FroudeScaleM2F_Disp + public :: FroudeScaleM2F_TVel + public :: FroudeScaleM2F_RVel + public :: FroudeScaleM2F_TAcc + public :: FroudeScaleM2F_RAcc + public :: FroudeScaleM2F_Time + public :: FroudeScaleF2M_Frc + public :: FroudeScaleF2M_Mom + +contains + + +!> create the structural model, allocate temp data storage, setup mesh mappings +subroutine StructCreate(SimSettings, MeshMotions, MeshLoads, MeshMaps, StructTmp, ErrStat, ErrMsg) + type(SimSettingsType), target, intent(in ) :: SimSettings + type(MeshesMotionType), target, intent(inout) :: MeshMotions + type(MeshesLoadsType ), target, intent(inout) :: MeshLoads + type(MeshesMapsType ), intent(inout) :: MeshMaps + type(StructTmpType ), intent(inout) :: StructTmp + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WaveTank::StructCreate' + real(ReKi) :: TmpPos(3) + real(DbKi) :: AzBlade ! temporary var for calculating blade mounting azimuth + real(DbKi) :: TmpAng(3) ! temporary euler angle + real(DbKi) :: Orient(3,3) ! temporary orientation + type(TurbConfigType), pointer :: TrbCfg ! to shorten notation + type(TurbInitCondType), pointer :: TrbInit ! to shorten notation + type(MeshType), pointer :: Ptfm, PtfmLd ! to shorten notation + type(MeshType), pointer :: Twr, TwrLd ! to shorten notation + type(MeshType), pointer :: Hub, HubLd ! to shorten notation + type(MeshType), pointer :: Root, RootLd ! to shorten notation + type(MeshType), pointer :: MoorLd ! to shorten notation + integer(IntKi) :: k ! blade counter + ErrStat = ErrID_None + ErrMsg = '' + + TrbCfg => SimSettings%TrbCfg + TrbInit => SimSettings%TrbInit + + ! Set some state information + StructTmp%RotSpeed = TrbInit%RotSpeed + StructTmp%BldPitch = TrbInit%BldPitch + StructTmp%NacYaw = TrbInit%NacYaw + StructTmp%Azimuth = TrbInit%Azimuth + + + !------------------------------- + ! Wave measurement buoy + !------------------------------- + TmpPos = 0.0_ReKi + TmpPos(1:2) = SimSettings%WaveBuoy%XYLoc(1:2) + call Eye(Orient, ErrStat2, ErrMsg2); if (Failed()) return + call CreateInputPointMesh(MeshMotions%WaveBuoyMotion, TmpPos, Orient, ErrStat2, ErrMsg2, hasMotion=.true., hasLoads=.false.); if (Failed()) return + + + !------------------------------- + ! create PRP platform mesh point + !------------------------------- + Ptfm => MeshMotions%PtfmPtMotion + TmpPos = real(TrbCfg%PtfmRefPos, ReKi) + Orient=WT_EulerToDCM_fromInput(TrbCfg%PtfmRefOrient) + call CreateInputPointMesh(Ptfm, TmpPos, Orient, ErrStat2, ErrMsg2, hasMotion=.true., hasLoads=.false.); if (Failed()) return + Ptfm%RemapFlag = .false. + + ! create platform load mesh + PtfmLd => MeshLoads%PtfmPtLoads + call MeshCopy( SrcMesh=Ptfm, DestMesh=PtfmLd, CtrlCode=MESH_SIBLING, IOS=COMPONENT_OUTPUT, ErrStat=ErrStat2, ErrMess=ErrMsg2, Force=.true., Moment=.true. ) + if (Failed()) return + PtfmLd%RemapFlag = .false. + + ! create a temporary load mesh + call MeshCopy( PtfmLd, MeshLoads%PtfmPtLoadsTmp, MESH_NEWCOPY, ErrStat2, ErrMsg2 ) + if (Failed()) return + PtfmLd%RemapFlag = .false. + + ! create a mooring mesh point + MoorLd => MeshLoads%MooringLoads + call MeshCopy( SrcMesh=Ptfm, DestMesh=MoorLd, CtrlCode=MESH_COUSIN, IOS=COMPONENT_OUTPUT, ErrStat=ErrStat2, ErrMess=ErrMsg2, Force=.true., Moment=.true. ) + if (Failed()) return + PtfmLd%RemapFlag = .false. + + + !------------------------------- + ! create 2 point tower mesh + !------------------------------- + Twr => MeshMotions%TowerMotion + call MeshCreate ( BlankMesh = Twr, IOS=COMPONENT_INPUT, Nnodes=2, ErrStat=ErrStat2, ErrMess=ErrMsg2, & + Orientation = .true., TranslationDisp = .true., TranslationVel = .true., RotationVel = .true., TranslationAcc = .TRUE., RotationAcc = .true.) + if (Failed()) return + + ! Tower bottom + TmpPos(1:2) = real(TrbCfg%TowerBsPt(1:2) + TrbCfg%PtfmRefPos(1:2), ReKi) ! relative to PtfmRefPos in (x,y) + TmpPos(3) = real(TrbCfg%TowerBsPt(3), ReKi) ! relative to MSL in (z) + call MeshPositionNode(Twr, 1, TmpPos, errStat2, errMsg2) ! orientation is identity by default + if (Failed()) return + + ! Tower top -- assumes vertical tower + TmpPos(3) = real(TrbCfg%TowerHt,ReKi) ! c_float to ReKi + call MeshPositionNode(Twr, 2, TmpPos, errStat2, errMsg2) ! orientation is identity by default + if (Failed()) return + + ! create line element + call MeshConstructElement( Twr, ELEMENT_LINE2, errStat2, errMsg2, p1=1, p2=2 ) + if (Failed()) return + + ! commit mesh + call MeshCommit(Twr, errStat2, errMsg2 ) + + ! initialize location + Twr%Orientation = Twr%RefOrientation + Twr%TranslationDisp = 0.0_R8Ki + Twr%TranslationVel = 0.0_ReKi + Twr%RemapFlag = .false. + + ! create tower load mesh + TwrLd => MeshLoads%TowerLoads + call MeshCopy( SrcMesh=Twr, DestMesh=TwrLd, CtrlCode=MESH_SIBLING, IOS=COMPONENT_OUTPUT, ErrStat=ErrStat2, ErrMess=ErrMsg2, Force=.true., Moment=.true. ) + if (Failed()) return + TwrLd%RemapFlag = .false. + TwrLd%Force = 0.0_ReKi + TwrLd%Moment = 0.0_ReKi + + + !------------------------------- + ! create hub mesh + !------------------------------- + ! NOTE: for a reference mesh position, nacelle yaw should be zero. since NacYaw is static in this + ! we are setting it once here. If it needs to be dynamic, zero it here and update the yaw + ! in the StructMotionUpdate routine below + Hub => MeshMotions%HubMotion + TmpPos(1:3) = Twr%Position(1:3,2) ! Tower top + TmpPos(1) = TmpPos(1) + cos(TrbInit%NacYaw) * TrbCfg%OverHang ! X, nacelle yaw, and overhang + TmpPos(2) = TmpPos(2) + sin(TrbInit%NacYaw) * TrbCfg%OverHang ! Y, nacelle yaw, and overhang + TmpPos(3) = TmpPos(3) + TrbCfg%Twr2Shft - abs(TrbCfg%OverHang) * tan(TrbCfg%ShftTilt) ! Z, shaft height above tower top, and shaft tilt + + TmpAng = (/ 0.0_DbKi, -real(TrbCfg%ShftTilt,DbKi), real(TrbInit%NacYaw,DbKi) /) ! Hub/rotor azimuth is zero for reference. Hub axis on upwind points towards nacelle. + Orient = EulerConstruct(TmpAng) + call CreateInputPointMesh(Hub, TmpPos, Orient, ErrStat2, ErrMsg2, hasMotion=.true., hasLoads=.false.); if (Failed()) return + Hub%RemapFlag = .false. + + ! create tower load mesh + HubLd => MeshLoads%HubLoads + call MeshCopy( SrcMesh=Hub, DestMesh=HubLd, CtrlCode=MESH_SIBLING, IOS=COMPONENT_OUTPUT, ErrStat=ErrStat2, ErrMess=ErrMsg2, Force=.true., Moment=.true. ) + if (Failed()) return + HubLd%RemapFlag = .false. + HubLd%Force = 0.0_ReKi + HubLd%Moment = 0.0_ReKi + + + !------------------------------- + ! create blade root mesh + !------------------------------- + ! NOTE: for a reference mesh position, blade pitch should be zero. since BldPitch is static in this + ! we are setting it once here. If it needs to be dynamic, zero it here and update the yaw + ! in the StructMotionUpdate routine below + allocate(MeshMotions%BladeRootMotion(TrbCfg%NumBl),STAT=ErrStat2) + if (ErrStat2 /= 0) then + ErrStat2 = ErrID_Fatal + ErrMsg2 = "Could not allocate BladeRootMotion mesh" + if (Failed()) return + endif + do k=1,TrbCfg%NumBl + Root => MeshMotions%BladeRootMotion(k) + AzBlade = TwoPi_D * real((k-1),DbKi)/real(TrbCfg%NumBl,DbKi) + TmpAng = (/ AzBlade, real(TrbCfg%PreCone,DbKi), real(-TrbInit%BldPitch,DbKi) /) ! Blade pitch does not follow RHR + Orient = EulerConstruct(TmpAng) + Orient = matmul(Orient,Hub%Orientation(1:3,1:3,1)) + TmpPos = Hub%Position(1:3,1) + TrbCfg%HubRad * real(Orient(3,1:3),ReKi) + call CreateInputPointMesh(Root, TmpPos, Orient, ErrStat2, ErrMsg2, hasMotion=.true., hasLoads=.false.); if (Failed()) return + Root%RemapFlag = .false. + enddo + + ! create blade root load mesh + allocate(MeshLoads%BladeRootLoads(TrbCfg%NumBl),STAT=ErrStat2) + if (ErrStat2 /= 0) then + ErrStat2 = ErrID_Fatal + ErrMsg2 = "Could not allocate BladeRootLoads mesh" + if (Failed()) return + endif + do k=1,TrbCfg%NumBl + Root => MeshMotions%BladeRootMotion(k) + RootLd => MeshLoads%BladeRootLoads(k) + call MeshCopy( SrcMesh=Root, DestMesh=RootLd, CtrlCode=MESH_SIBLING, IOS=COMPONENT_OUTPUT, ErrStat=ErrStat2, ErrMess=ErrMsg2, Force=.true., Moment=.true. ) + if (Failed()) return + RootLd%RemapFlag = .false. + RootLd%Force = 0.0_ReKi + RootLd%Moment = 0.0_ReKi + enddo + +contains + logical function Failed() + call SetErrStat(errStat2, errMsg2, errStat, errMsg, RoutineName) + Failed = errStat >= AbortErrLev + end function Failed +end subroutine + + + +!> create mesh mappings +subroutine StructCreateMeshMaps(SimSettings, MeshMotions, MeshLoads, MeshMaps, ErrStat, ErrMsg) + type(SimSettingsType), intent(in ) :: SimSettings + type(MeshesMotionType), intent(inout) :: MeshMotions + type(MeshesLoadsType ), intent(inout) :: MeshLoads + type(MeshesMapsType ), intent(inout) :: MeshMaps + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WaveTank::StructCreateMeshMaps' + integer(IntKi) :: k + + ErrStat = ErrID_None + ErrMsg = '' + + !------------------------------- + ! Mapping arrays + allocate(MeshMaps%Motion_Hub_2_BldRoot(SimSettings%TrbCfg%NumBl),STAT=ErrStat2) + if (ErrStat2 /= 0) then + ErrStat = ErrID_Fatal + ErrMsg = "Could not allocate Motion_Hub_2_BldRoot mesh mapping" + return + endif + allocate(MeshMaps%Load_BldRoot_2_Hub(SimSettings%TrbCfg%NumBl),STAT=ErrStat2) + if (ErrStat2 /= 0) then + ErrStat = ErrID_Fatal + ErrMsg = "Could not allocate Load_BldRoot_2_Hub mesh mapping" + return + endif + + !------------------------------- + ! Mesh motion mappings + call MeshMapCreate(MeshMotions%PtfmPtMotion, MeshMotions%TowerMotion, MeshMaps%Motion_PRP_2_Twr, errStat2, errMsg2); if(Failed())return + call MeshMapCreate(MeshMotions%PtfmPtMotion, MeshMotions%HubMotion, MeshMaps%Motion_PRP_2_Hub, errStat2, errMsg2); if(Failed())return + do k=1,SimSettings%TrbCfg%NumBl + call MeshMapCreate(MeshMotions%HubMotion, MeshMotions%BladeRootMotion(k), MeshMaps%Motion_Hub_2_BldRoot(k), errStat2, errMsg2); if(Failed())return + enddo + + !------------------------------- + ! Mesh load mappings + call MeshMapCreate(MeshLoads%TowerLoads, MeshLoads%PtfmPtLoads, MeshMaps%Load_Twr_2_PRP, errStat2, ErrMsg2); if(Failed()) return + call MeshMapCreate(MeshLoads%HubLoads, MeshLoads%PtfmPtLoads, MeshMaps%Load_Hub_2_PRP, errStat2, ErrMsg2); if(Failed()) return + do k=1,SimSettings%TrbCfg%NumBl + call MeshMapCreate(MeshLoads%BladeRootLoads(k), MeshLoads%HubLoads, MeshMaps%Load_BldRoot_2_Hub(k), errStat2, errMsg2); if(Failed())return + enddo + call MeshMapCreate(MeshLoads%MooringLoads, MeshLoads%PtfmPtLoads, MeshMaps%Load_Moor_2_PRP, errStat2, ErrMsg2); if(Failed()) return + +contains + logical function Failed() + call SetErrStat(errStat2, errMsg2, errStat, errMsg, RoutineName) + Failed = errStat >= AbortErrLev + end function Failed +end subroutine + + + +!> updates the structural meshes +subroutine StructMotionUpdate(SimSettings, CalcStepIO, MeshMotions, MeshMaps, StructTmp, ErrStat, ErrMsg) + type(SimSettingsType), target, intent(in ) :: SimSettings + type(CalcStepIOdataType), intent(in ) :: CalcStepIO + type(MeshesMotionType), target, intent(inout) :: MeshMotions + type(MeshesMapsType ), intent(inout) :: MeshMaps + type(StructTmpType ), intent(inout) :: StructTmp + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WaveTank::StructMotionUpdate' + real(R8Ki) :: TmpTransDisp(3) + real(DbKi) :: TmpAng(3) ! temporary euler angle + real(R8Ki) :: Orient(3,3) ! temporary orientation + type(TurbConfigType), pointer :: TrbCfg ! to shorten notation + type(TurbInitCondType), pointer :: TrbInit ! to shorten notation + type(MeshType), pointer :: Ptfm ! to shorten notation + type(MeshType), pointer :: Twr ! to shorten notation + type(MeshType), pointer :: Hub ! to shorten notation + type(MeshType), pointer :: Root ! to shorten notation + real(c_float) :: ScaleFact ! to shorten notation + integer(IntKi) :: k + + ErrStat = ErrID_None + ErrMsg = '' + + TrbCfg => SimSettings%TrbCfg + TrbInit => SimSettings%TrbInit + + ! scaling factor + ScaleFact = SimSettings%Sim%ScaleFact + + ! update PtfmPtMotion + Ptfm => MeshMotions%PtfmPtMotion + Ptfm%TranslationDisp(1:3,1) = FroudeScaleM2F_Disp(ScaleFact, CalcStepIO%PosAng_c(1:3), Ptfm%Position(1:3,1)) + Ptfm%Orientation(1:3,1:3,1) = WT_EulerToDCM_fromInput(CalcStepIO%PosAng_c(4:6)) ! angles don't scale + Ptfm%TranslationVel(1:3,1) = FroudeScaleM2F_TVel(ScaleFact, CalcStepIO%Vel_c(1:3)) + Ptfm%RotationVel(1:3,1) = FroudeScaleM2F_RVel(ScaleFact, CalcStepIO%Vel_c(4:6)) + Ptfm%TranslationAcc(1:3,1) = FroudeScaleM2F_TAcc(ScaleFact, CalcStepIO%Acc_c(1:3)) + Ptfm%RotationAcc(1:3,1) = FroudeScaleM2F_RAcc(ScaleFact, CalcStepIO%Acc_c(4:6)) + + !-------------------------------------- + ! transfer Ptfm to Tower + Twr => MeshMotions%TowerMotion + call Transfer_Point_to_Line2( Ptfm, Twr, MeshMaps%Motion_PRP_2_Twr, ErrStat2, ErrMsg2 ); if (Failed()) return; + + !-------------------------------------- + ! transfer Ptfm to hub (tower is rigid) + Hub => MeshMotions%HubMotion + call Transfer_Point_to_Point( Ptfm, Hub, MeshMaps%Motion_PRP_2_Hub, ErrStat2, ErrMsg2 ); if (Failed()) return; + + ! rotor azimuth + StructTmp%Azimuth = modulo(real(CalcStepIO%Time_c,ReKi)*StructTmp%RotSpeed + TrbInit%Azimuth, TwoPi ) + + ! update hub azimuth -- include initial azimuth + TmpAng = (/ real(StructTmp%Azimuth,DbKi), 0.0_DbKi, 0.0_DbKi /) + Orient = EulerConstruct(TmpAng) + Hub%Orientation(1:3,1:3,1) = matmul(Orient,Hub%Orientation(1:3,1:3,1)) + + !-------------------------------------- + ! hub to blades + do k=1,TrbCfg%NumBl + Root => MeshMotions%BladeRootMotion(k) + call Transfer_Point_to_Point( Hub, Root, MeshMaps%Motion_Hub_2_BldRoot(k), ErrStat2, ErrMsg2 ); if (Failed()) return; + enddo + +contains + logical function Failed() + call SetErrStat(errStat2, errMsg2, errStat, errMsg, RoutineName) + Failed = errStat >= AbortErrLev + end function Failed +end subroutine + + + +!> updates the structural load meshes and populates aggregated loads +subroutine StructLoadsMeshTransfer(SimSettings, CalcStepIO, MeshMotions, MeshLoads, MeshMaps, StructTmp, ErrStat, ErrMsg) + type(SimSettingsType), target, intent(in ) :: SimSettings + type(CalcStepIOdataType), intent(in ) :: CalcStepIO + type(MeshesMotionType), target, intent(inout) :: MeshMotions + type(MeshesLoadsType), target, intent(inout) :: MeshLoads + type(MeshesMapsType ), intent(inout) :: MeshMaps + type(StructTmpType ), intent(inout) :: StructTmp + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WaveTank::StructMotionUpdate' + real(R8Ki) :: TmpTransDisp(3) + real(DbKi) :: TmpAng(3) ! temporary euler angle + real(R8Ki) :: Orient(3,3) ! temporary orientation + type(TurbConfigType), pointer :: TrbCfg ! to shorten notation + type(TurbInitCondType), pointer :: TrbInit ! to shorten notation + type(MeshType), pointer :: Ptfm, PtfmLd, PtfmLdTmp ! to shorten notation + type(MeshType), pointer :: Twr, TwrLd ! to shorten notation + type(MeshType), pointer :: Hub, HubLd ! to shorten notation + type(MeshType), pointer :: Root, RootLd ! to shorten notation + type(MeshType), pointer :: MoorLd ! to shorten notation + real(c_float) :: ScaleFact ! to shorten notation + integer(IntKi) :: k + + ErrStat = ErrID_None + ErrMsg = '' + + ! shorthand pointers + TrbCfg => SimSettings%TrbCfg + TrbInit => SimSettings%TrbInit + Hub => MeshMotions%HubMotion + HubLd => MeshLoads%HubLoads + Twr => MeshMotions%TowerMotion + TwrLd => MeshLoads%TowerLoads + Ptfm => MeshMotions%PtfmPtMotion + PtfmLd => MeshLoads%PtfmPtLoads + PtfmLdTmp=> MeshLoads%PtfmPtLoadsTmp + MoorLd => MeshLoads%MooringLoads + + !----------------------------------------- + ! Aero loading + !----------------------------------------- + ! Transfer blade root loads to hub + do k=1,TrbCfg%NumBl + Root => MeshMotions%BladeRootMotion(k) + RootLd => MeshLoads%BladeRootLoads(k) + call Transfer_Point_To_Point( RootLd, HubLd, MeshMaps%Load_BldRoot_2_Hub(k), ErrStat2, ErrMsg2, Root, Hub ) + if (Failed()) return + enddo + + ! Transfer hub to platform + call Transfer_Point_To_Point( HubLd, PtfmLd, MeshMaps%Load_Hub_2_PRP, ErrStat2, ErrMsg2, Hub, Ptfm ) + if (Failed()) return + + + !----------------------------------------- + ! Transfer tower to platform + ! NOTE: no tower loads at present from ADI + !FIXME: add tower loads output transfer to mesh here + !call Transfer_Line2_To_Point( TwrLd, PtfmLdTmp, MeshMaps%Load_Twr_2_PRP, ErrStat2, ErrMsg2, Twr, Ptfm ) + !PtfmLd%Force(1:3,1) = PtfmLd%Force(1:3,1) + PtfmLdTmp%Force(1:3,1) + !PtfmLd%Moment(1:3,1) = PtfmLd%Moment(1:3,1) + PtfmLdTmp%Moment(1:3,1) + + ! Store the ADI summed foreces and moments for output + StructTmp%FrcMom_ADI_at_Ptfm(1:3) = PtfmLd%Force(1:3,1) + StructTmp%FrcMom_ADI_at_Ptfm(4:6) = PtfmLd%Moment(1:3,1) + + + !----------------------------------------- + ! Mooring loading + !----------------------------------------- + ! Transfer mooring load + call Transfer_Point_To_Point( MoorLd, PtfmLdTmp, MeshMaps%Load_Moor_2_PRP, ErrStat2, ErrMsg2, Ptfm, Ptfm ) + if (Failed()) return + PtfmLd%Force(1:3,1) = PtfmLd%Force(1:3,1) + PtfmLdTmp%Force(1:3,1) + PtfmLd%Moment(1:3,1) = PtfmLd%Moment(1:3,1) + PtfmLdTmp%Moment(1:3,1) + + ! Store the MD summed foreces and moments for output + StructTmp%FrcMom_MD_at_Ptfm(1:3) = PtfmLdTmp%Force(1:3,1) + StructTmp%FrcMom_MD_at_Ptfm(4:6) = PtfmLdTmp%Moment(1:3,1) + +contains + logical function Failed() + call SetErrStat(errStat2, errMsg2, errStat, errMsg, RoutineName) + Failed = errStat >= AbortErrLev + end function Failed +end subroutine + + + +!> destroy all structural model related info +subroutine StructDestroy(MeshMotions, MeshLoads, MeshMaps, StructTmp, ErrStat,ErrMsg) ! We are actually ignoring all errors from here + type(MeshesMotionType), intent(inout) :: MeshMotions + type(MeshesLoadsType ), intent(inout) :: MeshLoads + type(MeshesMapsType ), intent(inout) :: MeshMaps + type(StructTmpType ), intent(inout) :: StructTmp + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WaveTank::StructDestroy' + ErrStat = ErrID_None + ErrMsg = '' + call WT_DestroyMeshesMotionType(MeshMotions, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call WT_DestroyMeshesLoadsType(MeshLoads, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call WT_DestroyMeshesMapsType(MeshMaps, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call WT_DestroyStructTmpType(StructTmp, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) +end subroutine + + + +!> Convert an Euler angle set of Roll, Pitch, Yaw ordering to a DCM. +!! this routine exists for two reasons +!! 1. ordering may be different +!! 2. incoming Euler angle is c_float instead of R8Ki +!! NOTE: no Euler angles are exported, so we stick with the OF convention +!! for all internal conversions +function WT_EulerToDCM_fromInput(Ang) result(DCM) + real(c_float), intent(in ) :: Ang(3) + real(R8Ki) :: DCM(3,3) + !>>> Select one of the two following orders + ! 3-2-1 intrinsic rotation sequence of the 3 Tait-Bryan angles (1-2-3 extrinsic rotation) + DCM = EulerConstruct(real(Ang, DbKi)) + !! 1-2-3 intrinsic rotation sequence of the 3 Tait-Bryan angles (3-2-1 extrinsic rotation) + !DCM = EulerConstructZYX(real(Ang, DbKi)) +end function + + + +subroutine WrVTK_Struct_Ref(SimSettings, MeshMotions, MeshLoads, ErrStat, ErrMsg) + type(SimSettingsType), target, intent(in ) :: SimSettings + type(MeshesMotionType), intent(in ) :: MeshMotions + type(MeshesLoadsType ), intent(in ) :: MeshLoads + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WaveTank::WrVTK_Struct_Ref' + character(1024) :: DirRootName + real(SiKi) :: RefPt(3) + integer(IntKi) :: k + ErrStat = ErrID_None + ErrMsg = '' + RefPt = (/ 0.0_SiKi, 0.0_SiKi, 0.0_SiKi /) + DirRootName = trim(SimSettings%Viz%WrVTK_dir)//PathSep//trim(SimSettings%Sim%OutRootName) + ! Wave elevation measurement buoy + call MeshWrVTKreference(RefPt, MeshMotions%WaveBuoyMotion, trim(DirRootName)//'.WaveBuoyMotion', ErrStat2, ErrMsg2); if (Failed()) return + ! Platform point + call MeshWrVTKreference(RefPt, MeshMotions%PtfmPtMotion, trim(DirRootName)//'.Struct'//'.PtfmPtMotion', ErrStat2, ErrMsg2); if (Failed()) return + ! Tower + call MeshWrVTKreference(RefPt, MeshMotions%TowerMotion, trim(DirRootName)//'.Struct'//'.TowerMotion', ErrStat2, ErrMsg2); if (Failed()) return + ! hub point + call MeshWrVTKreference(RefPt, MeshMotions%HubMotion, trim(DirRootName)//'.Struct'//'.HubMotion', ErrStat2, ErrMsg2); if (Failed()) return + ! RootMotion points + do k=1,SimSettings%TrbCfg%NumBl + call MeshWrVTKreference(RefPt, MeshMotions%BladeRootMotion(k), trim(DirRootName)//'.Struct'//'.RootMotion'//trim(Num2LStr(k)), ErrStat2, ErrMsg2); if (Failed()) return + enddo +contains + logical function Failed() + call SetErrStat(errStat2, errMsg2, errStat, errMsg, RoutineName) + Failed = errStat >= AbortErrLev + end function Failed +end subroutine + +subroutine WrVTK_Struct(n_Global, SimSettings, MeshMotions, MeshLoads, ErrStat, ErrMsg) + integer(IntKi), intent(in ) :: n_Global + type(SimSettingsType), target, intent(in ) :: SimSettings + type(MeshesMotionType), intent(in ) :: MeshMotions + type(MeshesLoadsType ), intent(in ) :: MeshLoads + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WaveTank::WrVTK_Struct' + character(1024) :: DirRootName + real(SiKi) :: RefPt(3) + integer(IntKi) :: k + ErrStat = ErrID_None + ErrMsg = '' + RefPt = (/ 0.0_SiKi, 0.0_SiKi, 0.0_SiKi /) + DirRootName = trim(SimSettings%Viz%WrVTK_dir)//PathSep//trim(SimSettings%Sim%OutRootName) + ! Wave elevation measurement buoy + call MeshWrVTK(RefPt, MeshMotions%WaveBuoyMotion, trim(DirRootName)//'.WaveBuoyMotion', n_Global, .true., ErrStat2, ErrMsg2, Twidth=SimSettings%Viz%Twidth); if (Failed()) return + ! Platform point + call MeshWrVTK(RefPt, MeshMotions%PtfmPtMotion, trim(DirRootName)//'.Struct'//'.PtfmPtMotion', n_Global, .true., ErrStat2, ErrMsg2, Twidth=SimSettings%Viz%Twidth); if (Failed()) return + ! Tower + call MeshWrVTK(RefPt, MeshMotions%TowerMotion, trim(DirRootName)//'.Struct'//'.TowerMotion', n_Global, .true., ErrStat2, ErrMsg2, Twidth=SimSettings%Viz%Twidth); if (Failed()) return + ! Hub point + call MeshWrVTK(RefPt, MeshMotions%HubMotion, trim(DirRootName)//'.Struct'//'.HubMotion', n_Global, .true., ErrStat2, ErrMsg2, Twidth=SimSettings%Viz%Twidth); if (Failed()) return + ! RootMotion points + do k=1,SimSettings%TrbCfg%NumBl + call MeshWrVTK(RefPt, MeshMotions%BladeRootMotion(k), trim(DirRootName)//'.Struct'//'.RootMotion'//trim(Num2LStr(k)), n_Global, .true., ErrStat2, ErrMsg2, Twidth=SimSettings%Viz%Twidth); if (Failed()) return + enddo +contains + logical function Failed() + call SetErrStat(errStat2, errMsg2, errStat, errMsg, RoutineName) + Failed = errStat >= AbortErrLev + end function Failed +end subroutine + + + +!----------------------------------------------- +! Froude scaling from here: https://home.hvl.no/ansatte/gste/ftp/MarinLab_files/Litteratur/NTNU_Scaling_Laws.pdf, page 21 +! notation below: +! model scale: _m +! full scale: _f +! ScaleFact: length_f/length_m = lambda +! DensFact: rho_f/rho_m + +!> scale model displacements to full scale +!! length_full = length_model * lambda +function FroudeScaleM2F_Disp(ScaleFact, Pos_m, refPos_f) result(transDisp_f) + real(c_float), intent(in ) :: ScaleFact + real(c_float), intent(in ) :: Pos_m(3) + real(ReKi), intent(in ) :: refPos_f(3) + real(R8Ki) :: transdisp_f(3) + transDisp_f = real(ScaleFact*Pos_m,R8Ki) - real(refPos_f,R8Ki) +end function + +!> scale model translational velocity to full scale +!! TVel_full = TVel_model * sqrt( lambda ) TODO: check this!!!! +function FroudeScaleM2F_TVel(ScaleFact, TVel_m) result(TVel_f) + real(c_float), intent(in ) :: ScaleFact + real(c_float), intent(in ) :: TVel_m(3) + real(ReKi) :: TVel_f(3) + TVel_f = sqrt(real(ScaleFact,ReKi)) * real(TVel_m,ReKi) +end function + +!> scale model rotational velocity to full scale +!! RVel_full = RVel_model * sqrt(lambda) TODO: check this!!!! +function FroudeScaleM2F_RVel(ScaleFact, RVel_m) result(RVel_f) + real(c_float), intent(in ) :: ScaleFact + real(c_float), intent(in ) :: RVel_m(3) + real(ReKi) :: RVel_f(3) + RVel_f = real(RVel_m,ReKi) / sqrt(real(ScaleFact,ReKi)) +end function + +!> scale model translational acceleration to full scale +!! TAcc_full = TAcc_model ---> no scaling applied +function FroudeScaleM2F_TAcc(ScaleFact, TAcc_m) result(TAcc_f) + real(c_float), intent(in ) :: ScaleFact + real(c_float), intent(in ) :: TAcc_m(3) + real(ReKi) :: TAcc_f(3) + TAcc_f = real(TAcc_m,ReKi) +end function + +!> scale model rotational acceleration to full scale +!! RAcc_full = RAcc_model / lambda TODO: check this!!!! +function FroudeScaleM2F_RAcc(ScaleFact, RAcc_m) result(RAcc_f) + real(c_float), intent(in ) :: ScaleFact + real(c_float), intent(in ) :: RAcc_m(3) + real(ReKi) :: RAcc_f(3) + RAcc_f = real(RAcc_m,ReKi) / real(ScaleFact,ReKi) +end function + +!> scale model time to full scale +!! sqrt(lambda) = sqrt(Length_full/ Length_model) +function FroudeScaleM2F_Time(ScaleFact, Time_m) result(Time_f) + real(c_float), intent(in ) :: ScaleFact + real(c_double),intent(in ) :: Time_m + real(R8Ki) :: Time_f + Time_f = sqrt(real(ScaleFact,R8Ki)) * real(Time_m,R8Ki) +end function + +!> scale full scale force to model +!! lambda^3 * DensFact * Frc_model = Frc_full +function FroudeScaleF2M_Frc(ScaleFact, DensFact, Frc_f) result(Frc_m) + real(c_float), intent(in ) :: ScaleFact + real(c_float), intent(in ) :: DensFact + real(ReKi), intent(in ) :: Frc_f(3) + real(c_float) :: Frc_m(3) + Frc_m = real(Frc_f, c_float) / (ScaleFact**3 * DensFact) +end function + +!> scale full scale moment to model +!! lambda^4 * DensFact * Mom_model = Mom_full +function FroudeScaleF2M_Mom(ScaleFact, DensFact, Mom_f) result(Mom_m) + real(c_float), intent(in ) :: ScaleFact + real(c_float), intent(in ) :: DensFact + real(ReKi), intent(in ) :: Mom_f(3) + real(c_float) :: Mom_m(3) + Mom_m = real(Mom_f, c_float) / (ScaleFact**4 * DensFact) +end function + + +end module diff --git a/glue-codes/labview/src/WaveTank_Types.f90 b/glue-codes/labview/src/WaveTank_Types.f90 new file mode 100644 index 0000000000..822f48a3bd --- /dev/null +++ b/glue-codes/labview/src/WaveTank_Types.f90 @@ -0,0 +1,1674 @@ +!STARTOFREGISTRYGENERATEDFILE 'WaveTank_Types.f90' +! +! WARNING This file is generated automatically by the FAST registry. +! Do not edit. Your changes to this file will be lost. +! +! FAST Registry +!********************************************************************************************************************************* +! WaveTank_Types +!................................................................................................................................. +! This file is part of WaveTank. +! +! Copyright (C) 2012-2016 National Renewable Energy Laboratory +! +! Licensed under the Apache License, Version 2.0 (the "License"); +! you may not use this file except in compliance with the License. +! You may obtain a copy of the License at +! +! http://www.apache.org/licenses/LICENSE-2.0 +! +! Unless required by applicable law or agreed to in writing, software +! distributed under the License is distributed on an "AS IS" BASIS, +! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +! See the License for the specific language governing permissions and +! limitations under the License. +! +! +! W A R N I N G : This file was automatically generated from the FAST registry. Changes made to this file may be lost. +! +!********************************************************************************************************************************* +!> This module contains the user-defined types needed in WaveTank. It also contains copy, destroy, pack, and +!! unpack routines associated with each defined data type. This code is automatically generated by the FAST Registry. +MODULE WaveTank_Types +!--------------------------------------------------------------------------------------------------------------------------------- +USE ISO_C_BINDING +USE NWTC_Library +IMPLICIT NONE +! ========= SimType ======= + TYPE, PUBLIC :: SimType + REAL(c_double) :: DT = 0.0_R8Ki !< timestep [-] + REAL(c_double) :: TMax = 0.0_R8Ki !< Max sim time [-] + INTEGER(c_int) :: MHK = 0_IntKi !< MHK turbine type (switch) {0=Not an MHK turbine; 1=Fixed MHK turbine; 2=Floating MHK turbine} [(-)] + INTEGER(c_int) :: InterpOrd = 1 !< Interpolation order [-] + REAL(c_float) :: ScaleFact = 1 + REAL(c_float) :: DensFact = 1 !< ratio of density - Density_full/Density_model (rho_F/rho_M). Used with Froude scaling of forces/moments [(-)] + INTEGER(c_int) :: DebugLevel = 0_IntKi !< Debug level for outputs [-] + character(1024) :: OutRootName !< Rootname for outputs [-] + END TYPE SimType +! ======================= +! ========= EnvType ======= + TYPE, PUBLIC :: EnvType + REAL(c_float) :: Gravity = 0.0_R4Ki !< gravitational constant (positive for down) [(m/s^2)] + REAL(c_float) :: WtrDens = 0.0_R4Ki !< Water density [(kg/m^3)] + REAL(c_float) :: WtrVisc = 0.0_R4Ki !< fluid viscosity [(m^2/s)] + REAL(c_float) :: SpdSound = 0.0_R4Ki !< Speed of sound in working fluid [(m/s)] + REAL(c_float) :: Patm = 0.0_R4Ki !< Atmospheric pressure [used only for an MHK turbine cavitation check] [(Pa)] + REAL(c_float) :: Pvap = 0.0_R4Ki !< Vapour pressure of working fluid [used only for an MHK turbine cavitation check] [(Pa)] + REAL(c_float) :: WtrDpth = 0.0_R4Ki !< Water depth [(m)] + REAL(c_float) :: MSL2SWL = 0.0_R4Ki !< Mean sea level to still water level [(m)] + END TYPE EnvType +! ======================= +! ========= TurbConfigType ======= + TYPE, PUBLIC :: TurbConfigType + INTEGER(IntKi) :: NumBl = 0_IntKi !< Number of blades [(-)] + REAL(SiKi) :: HubRad = 0.0_R4Ki !< The distance from the rotor apex to the blade root [(m)] + REAL(SiKi) :: PreCone = 0.0_R4Ki !< Blade cone angle [(deg)] + REAL(SiKi) :: OverHang = 0.0_R4Ki !< Distance from yaw axis to rotor apex [3 blades] or teeter pin [2 blades] [(m)] + REAL(SiKi) :: ShftTilt = 0.0_R4Ki !< Rotor shaft tilt angle [(deg)] + REAL(SiKi) :: Twr2Shft = 0.0_R4Ki !< Vertical distance from the tower-top to the rotor shaft [(m)] + REAL(SiKi) :: TowerHt = 0.0_R4Ki !< Height of tower relative MSL [(m)] + REAL(SiKi) , DIMENSION(1:3) :: TowerBsPt = 0.0_R4Ki !< Tower base location relative to MSL. Consider absolute difference to PtfmRef [floating MHK] [(m)] + REAL(SiKi) , DIMENSION(1:3) :: PtfmRefPos = 0.0_R4Ki !< Location of platform reference point, relative to MSL. Motions and loads all connect to this point [(m)] + REAL(SiKi) , DIMENSION(1:3) :: PtfmRefOrient = 0.0_R4Ki !< Orientation of platform reference point, Euler angle set of roll,pitch,yaw [(rad)] + END TYPE TurbConfigType +! ======================= +! ========= TurbInitCondType ======= + TYPE, PUBLIC :: TurbInitCondType + REAL(ReKi) :: RotSpeed = 0.0_ReKi !< Rotor speed [(RPM)] + REAL(ReKi) :: NacYaw = 0.0_ReKi !< Initial or fixed nacelle-yaw angle - read as deg, convert to rad [(rad)] + REAL(ReKi) :: BldPitch = 0.0_ReKi !< Fixed blade pitch for full simulation - read as deg, convert to rad [(rad)] + REAL(ReKi) :: Azimuth = 0 !< Initial azimuth (actual azimuth calculated and not stored) - read as deg, convert to rad [(rad)] + END TYPE TurbInitCondType +! ======================= +! ========= WaveBuoyType ======= + TYPE, PUBLIC :: WaveBuoyType + REAL(ReKi) , DIMENSION(1:2) :: XYLoc = 0.0_ReKi !< Location of the wave elevation measurement buoy. SeaState data returned at every timestep at this location [(m)] + END TYPE WaveBuoyType +! ======================= +! ========= OutFilesType ======= + TYPE, PUBLIC :: OutFilesType + LOGICAL :: SendScreenToFile = .false. !< send to file .screen.log if true [(-)] + INTEGER(c_int) :: OutFile = 0_IntKi !< 0: no output file of channels, 1: output file in text format (at DT) [(-)] + character(20) :: OutFmt !< Format used for text tabular output, excluding the time channel. (quoted string) [(-)] + END TYPE OutFilesType +! ======================= +! ========= VizType ======= + TYPE, PUBLIC :: VizType + INTEGER(c_int) :: WrVTK = 0_IntKi !< Write VTK? [-] + INTEGER(c_int) :: WrVTK_type = 0_IntKi !< Write VTK outputs as [1: surface, 2: lines, 3: both] [-] + REAL(c_double) :: WrVTK_DT = 0.0_R8Ki !< Time step between VTK writes [-] + character(1024) :: WrVTK_dir !< Directory for VTK writing [-] + REAL(c_float) , DIMENSION(1:6) :: VTKNacDim = 0.0_R4Ki !< Nacelle dimension passed in for VTK surface rendering [0,y0,z0,Lx,Ly,Lz] [(m)] + INTEGER(IntKi) :: Twidth = 6 !< Time width -- hard coded for now [(-)] + END TYPE VizType +! ======================= +! ========= ModSettings ======= + TYPE, PUBLIC :: ModSettings + character(1024) :: SS_InputFile !< SeaState input file [(-)] + REAL(DbKi) :: WaveTimeShift = 0.0_R8Ki !< Shift the SeaState wavetime by this amount (for phase shifting waves to match tank) [(s)] + character(1024) :: MD_InputFile !< MoorDyn input file [(-)] + character(1024) :: AD_InputFile !< AeroDyn input file [(-)] + character(1024) :: IfW_InputFile !< InflowWind input file [(-)] + END TYPE ModSettings +! ======================= +! ========= SimSettingsType ======= + TYPE, PUBLIC :: SimSettingsType + TYPE(SimType) :: Sim !< Simulation settings [-] + TYPE(EnvType) :: Env !< Environment settings [-] + TYPE(TurbConfigType) :: TrbCfg !< Turbine configuration [-] + TYPE(TurbInitCondType) :: TrbInit !< Turbine initial operating point [-] + TYPE(WaveBuoyType) :: WaveBuoy !< Wave elevation buoy locat (x-y) [(m)] + TYPE(OutFilesType) :: Outs !< Output settings [-] + TYPE(VizType) :: Viz !< Vizualization settings [-] + TYPE(ModSettings) :: ModSettings !< Input files for each module [-] + END TYPE SimSettingsType +! ======================= +! ========= CalcStepIOdataType ======= + TYPE, PUBLIC :: CalcStepIOdataType + REAL(c_double) :: Time_c = 0.0_R8Ki !< IN: time [(s)] + REAL(c_float) , DIMENSION(1:6) :: PosAng_c = 0.0_R4Ki !< IN: Position + Euler Ang [x,y,z,phi,theta,psi] [[(m) (rad)]] + REAL(c_float) , DIMENSION(1:6) :: Vel_c = 0.0_R4Ki !< IN: Velocity [Vx,Vy,Vz,RVx,RVy,RVz] [[(m/s) (rad/s)]] + REAL(c_float) , DIMENSION(1:6) :: Acc_c = 0.0_R4Ki !< IN: Acceleration [Ax,Ay,Az,RAx,RAy,RAz] [[(m/s^2) (rad/s^2)]] + REAL(c_float) , DIMENSION(1:6) :: FrcMom_c = 0.0_R4Ki !< OUT: Acceleration [Fx,Fy,Fz,Mx,My,Mz] [[(N) (N-m)]] + REAL(c_float) , DIMENSION(1:6) :: FrcMom_MD_c = 0.0_R4Ki !< calculated forces/moments from MD [-] + REAL(c_float) , DIMENSION(:), ALLOCATABLE :: FrcMom_ADI_c !< calculated forces/moments from ADI [-] + REAL(c_float) , DIMENSION(1:3) :: HubVel_ADI_c = 0.0_R4Ki !< hub height wind vel from ADI [-] + REAL(ReKi) :: BuoyWaveElev = 0.0_ReKi !< calculated wave elevation at buoy [-] + END TYPE CalcStepIOdataType +! ======================= +! ========= WrOutputDataType ======= + TYPE, PUBLIC :: WrOutputDataType + INTEGER(IntKi) :: NumChans_cbind = 0 !< Number of output channels from c-bind [-] + INTEGER(IntKi) :: NumChans_SS = 0 !< Number of output channels from SS [-] + INTEGER(IntKi) :: NumChans_MD = 0 !< Number of output channels from MD [-] + INTEGER(IntKi) :: NumChans_ADI = 0 !< Number of output channels from ADI [-] + INTEGER(IntKi) :: NumChans_all = 0 !< Total number of channels (sum of above) [-] + character(ChanLen) , DIMENSION(:), ALLOCATABLE :: WriteOutputHdr_SS !< output file header names from SS [-] + character(ChanLen) , DIMENSION(:), ALLOCATABLE :: WriteOutputUnt_SS !< output file header units from SS [-] + character(ChanLen) , DIMENSION(:), ALLOCATABLE :: WriteOutputHdr_MD !< output file header names from MD [-] + character(ChanLen) , DIMENSION(:), ALLOCATABLE :: WriteOutputUnt_MD !< output file header units from MD [-] + character(ChanLen) , DIMENSION(:), ALLOCATABLE :: WriteOutputHdr_ADI !< output file header names from ADI [-] + character(ChanLen) , DIMENSION(:), ALLOCATABLE :: WriteOutputUnt_ADI !< output file header units from ADI [-] + REAL(c_float) , DIMENSION(:), ALLOCATABLE :: OutData_SS_c !< output data from SS as passed c_float [-] + REAL(c_float) , DIMENSION(:), ALLOCATABLE :: OutData_MD_c !< output data from MD as passed c_float [-] + REAL(c_float) , DIMENSION(:), ALLOCATABLE :: OutData_ADI_c !< output data from ADI as passed c_float [-] + REAL(SiKi) , DIMENSION(:), ALLOCATABLE :: OutData_SS !< output data from SS [-] + REAL(SiKi) , DIMENSION(:), ALLOCATABLE :: OutData_MD !< output data from MD [-] + REAL(SiKi) , DIMENSION(:), ALLOCATABLE :: OutData_ADI !< output data from ADI [-] + character(1024) :: OutName !< Output file name [-] + INTEGER(IntKi) :: OutUn = -1 !< Output unit [-] + END TYPE WrOutputDataType +! ======================= +! ========= MeshesMotionType ======= + TYPE, PUBLIC :: MeshesMotionType + TYPE(MeshType) :: PtfmPtMotion !< Platform principle ref point. Also serves as tower base [-] + TYPE(MeshType) :: TowerMotion !< Tower mesh (used only for vis) [-] + TYPE(MeshType) :: HubMotion !< Hub mesh (for mappings, no loadings) [-] + TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: BladeRootMotion !< Blade root motions [-] + TYPE(MeshType) :: WaveBuoyMotion !< wave measurement buoy motion (sensor only) [-] + END TYPE MeshesMotionType +! ======================= +! ========= MeshesLoadsType ======= + TYPE, PUBLIC :: MeshesLoadsType + TYPE(MeshType) :: PtfmPtLoads !< Platform principle ref point loads output [-] + TYPE(MeshType) :: PtfmPtLoadsTmp !< Platform principle ref point loads output - temp var for load summation [-] + TYPE(MeshType) :: MooringLoads !< Mooring loads (always at PtfmPt, but separated for simplicity) [-] + TYPE(MeshType) :: TowerLoads !< Tower mesh (unused) [-] + TYPE(MeshType) :: HubLoads !< Hub mesh (for mappings, intermediate loads) [-] + TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: BladeRootLoads !< Blade root loads [-] + END TYPE MeshesLoadsType +! ======================= +! ========= MeshesMapsType ======= + TYPE, PUBLIC :: MeshesMapsType + TYPE(MeshMapType) :: Motion_PRP_2_Twr !< PRP to tower motion [-] + TYPE(MeshMapType) :: Motion_PRP_2_Hub !< Twrtop to nacelle - add rotation afterwards [-] + TYPE(MeshMapType) , DIMENSION(:), ALLOCATABLE :: Motion_Hub_2_BldRoot !< Hub to blade root motion transfer [-] + TYPE(MeshMapType) , DIMENSION(:), ALLOCATABLE :: Load_BldRoot_2_Hub !< Blade root loads to hub [-] + TYPE(MeshMapType) :: Load_Hub_2_PRP !< Hub to nacelle load transfer [-] + TYPE(MeshMapType) :: Load_Twr_2_PRP !< Tower loads to PRP (unused) [-] + TYPE(MeshMapType) :: Load_Moor_2_PRP !< Mooring loads to PRP [-] + END TYPE MeshesMapsType +! ======================= +! ========= StructTmpType ======= + TYPE, PUBLIC :: StructTmpType + REAL(ReKi) :: Azimuth = 0 !< Current Azimuth [(rad)] + REAL(ReKi) :: RotSpeed = 0.0_ReKi !< Rotor speed [(RPM)] + REAL(ReKi) :: BldPitch = 0.0_ReKi !< Blade pitch [(rad)] + REAL(ReKi) :: NacYaw = 0.0_ReKi !< Nacelle-yaw angle [(rad)] + REAL(ReKi) , DIMENSION(1:6) :: FrcMom_ADI_at_Ptfm = 0.0_ReKi !< Total aero loading summed to Ptfm point [(N,] + REAL(ReKi) , DIMENSION(1:6) :: FrcMom_MD_at_Ptfm = 0.0_ReKi !< MoorDyn loading at Ptfm point [(N,] + REAL(c_float) , DIMENSION(1:2) :: BuoyPos_c = 0.0_R4Ki !< Buoy XY position [(m)] + REAL(c_float) , DIMENSION(1:6) :: PtfmPosAng_c = 0.0_R4Ki !< Temp position and euler angle [(m,] + REAL(c_float) , DIMENSION(1:6) :: PtfmVel_c = 0.0_R4Ki !< Temp velocity [(m/s,] + REAL(c_float) , DIMENSION(1:6) :: PtfmAcc_c = 0.0_R4Ki !< Temp acceleration [(m/s^2,] + REAL(c_float) , DIMENSION(1:3) :: NacPos_c = 0.0_R4Ki !< Temp nacelle position [(m,] + REAL(c_double) , DIMENSION(1:9) :: NacDCM_c = 0.0_R8Ki !< Temp nacelle orientation DCM [(-)] + REAL(c_float) , DIMENSION(1:6) :: NacVel_c = 0.0_R4Ki !< Temp nacelle velocity [(m/s,] + REAL(c_float) , DIMENSION(1:6) :: NacAcc_c = 0.0_R4Ki !< Temp nacelle acceleration [(m/s^2,] + REAL(c_float) , DIMENSION(1:3) :: HubPos_c = 0.0_R4Ki !< Temp hub position [(m,] + REAL(c_double) , DIMENSION(1:9) :: HubDCM_c = 0.0_R8Ki !< Temp hub orientation DCM [(-)] + REAL(c_float) , DIMENSION(1:6) :: HubVel_c = 0.0_R4Ki !< Temp hub velocity [(m/s,] + REAL(c_float) , DIMENSION(1:6) :: HubAcc_c = 0.0_R4Ki !< Temp hub acceleration [(m/s^2,] + REAL(c_float) , DIMENSION(:), ALLOCATABLE :: BldPos_c !< Temp blade position -- sequential by blade [(m)] + REAL(c_double) , DIMENSION(:), ALLOCATABLE :: BldDCM_c !< Temp blade orientation DCM -- flat sequential by blade [(-)] + REAL(c_float) , DIMENSION(:), ALLOCATABLE :: BldVel_c !< Temp blade velocity -- sequential by blade [(m/s,] + REAL(c_float) , DIMENSION(:), ALLOCATABLE :: BldAcc_c !< Temp blade acceleration -- sequential by blade [(m/s^2,] + END TYPE StructTmpType +! ======================= + +contains + +subroutine WT_CopySimType(SrcSimTypeData, DstSimTypeData, CtrlCode, ErrStat, ErrMsg) + type(SimType), intent(in) :: SrcSimTypeData + type(SimType), intent(inout) :: DstSimTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_CopySimType' + ErrStat = ErrID_None + ErrMsg = '' + DstSimTypeData%DT = SrcSimTypeData%DT + DstSimTypeData%TMax = SrcSimTypeData%TMax + DstSimTypeData%MHK = SrcSimTypeData%MHK + DstSimTypeData%InterpOrd = SrcSimTypeData%InterpOrd + DstSimTypeData%ScaleFact = SrcSimTypeData%ScaleFact + DstSimTypeData%DensFact = SrcSimTypeData%DensFact + DstSimTypeData%DebugLevel = SrcSimTypeData%DebugLevel + DstSimTypeData%OutRootName = SrcSimTypeData%OutRootName +end subroutine + +subroutine WT_DestroySimType(SimTypeData, ErrStat, ErrMsg) + type(SimType), intent(inout) :: SimTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroySimType' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine WT_PackSimType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(SimType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackSimType' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%DT) + call RegPack(RF, InData%TMax) + call RegPack(RF, InData%MHK) + call RegPack(RF, InData%InterpOrd) + call RegPack(RF, InData%ScaleFact) + call RegPack(RF, InData%DensFact) + call RegPack(RF, InData%DebugLevel) + call RegPack(RF, InData%OutRootName) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackSimType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(SimType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackSimType' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%DT); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%TMax); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%MHK); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%InterpOrd); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%ScaleFact); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%DensFact); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%DebugLevel); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%OutRootName); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_CopyEnvType(SrcEnvTypeData, DstEnvTypeData, CtrlCode, ErrStat, ErrMsg) + type(EnvType), intent(in) :: SrcEnvTypeData + type(EnvType), intent(inout) :: DstEnvTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_CopyEnvType' + ErrStat = ErrID_None + ErrMsg = '' + DstEnvTypeData%Gravity = SrcEnvTypeData%Gravity + DstEnvTypeData%WtrDens = SrcEnvTypeData%WtrDens + DstEnvTypeData%WtrVisc = SrcEnvTypeData%WtrVisc + DstEnvTypeData%SpdSound = SrcEnvTypeData%SpdSound + DstEnvTypeData%Patm = SrcEnvTypeData%Patm + DstEnvTypeData%Pvap = SrcEnvTypeData%Pvap + DstEnvTypeData%WtrDpth = SrcEnvTypeData%WtrDpth + DstEnvTypeData%MSL2SWL = SrcEnvTypeData%MSL2SWL +end subroutine + +subroutine WT_DestroyEnvType(EnvTypeData, ErrStat, ErrMsg) + type(EnvType), intent(inout) :: EnvTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroyEnvType' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine WT_PackEnvType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(EnvType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackEnvType' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%Gravity) + call RegPack(RF, InData%WtrDens) + call RegPack(RF, InData%WtrVisc) + call RegPack(RF, InData%SpdSound) + call RegPack(RF, InData%Patm) + call RegPack(RF, InData%Pvap) + call RegPack(RF, InData%WtrDpth) + call RegPack(RF, InData%MSL2SWL) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackEnvType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(EnvType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackEnvType' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%Gravity); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%WtrDens); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%WtrVisc); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%SpdSound); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Patm); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Pvap); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%WtrDpth); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%MSL2SWL); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_CopyTurbConfigType(SrcTurbConfigTypeData, DstTurbConfigTypeData, CtrlCode, ErrStat, ErrMsg) + type(TurbConfigType), intent(in) :: SrcTurbConfigTypeData + type(TurbConfigType), intent(inout) :: DstTurbConfigTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_CopyTurbConfigType' + ErrStat = ErrID_None + ErrMsg = '' + DstTurbConfigTypeData%NumBl = SrcTurbConfigTypeData%NumBl + DstTurbConfigTypeData%HubRad = SrcTurbConfigTypeData%HubRad + DstTurbConfigTypeData%PreCone = SrcTurbConfigTypeData%PreCone + DstTurbConfigTypeData%OverHang = SrcTurbConfigTypeData%OverHang + DstTurbConfigTypeData%ShftTilt = SrcTurbConfigTypeData%ShftTilt + DstTurbConfigTypeData%Twr2Shft = SrcTurbConfigTypeData%Twr2Shft + DstTurbConfigTypeData%TowerHt = SrcTurbConfigTypeData%TowerHt + DstTurbConfigTypeData%TowerBsPt = SrcTurbConfigTypeData%TowerBsPt + DstTurbConfigTypeData%PtfmRefPos = SrcTurbConfigTypeData%PtfmRefPos + DstTurbConfigTypeData%PtfmRefOrient = SrcTurbConfigTypeData%PtfmRefOrient +end subroutine + +subroutine WT_DestroyTurbConfigType(TurbConfigTypeData, ErrStat, ErrMsg) + type(TurbConfigType), intent(inout) :: TurbConfigTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroyTurbConfigType' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine WT_PackTurbConfigType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(TurbConfigType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackTurbConfigType' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%NumBl) + call RegPack(RF, InData%HubRad) + call RegPack(RF, InData%PreCone) + call RegPack(RF, InData%OverHang) + call RegPack(RF, InData%ShftTilt) + call RegPack(RF, InData%Twr2Shft) + call RegPack(RF, InData%TowerHt) + call RegPack(RF, InData%TowerBsPt) + call RegPack(RF, InData%PtfmRefPos) + call RegPack(RF, InData%PtfmRefOrient) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackTurbConfigType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(TurbConfigType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackTurbConfigType' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%NumBl); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HubRad); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%PreCone); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%OverHang); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%ShftTilt); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Twr2Shft); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%TowerHt); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%TowerBsPt); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%PtfmRefPos); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%PtfmRefOrient); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_CopyTurbInitCondType(SrcTurbInitCondTypeData, DstTurbInitCondTypeData, CtrlCode, ErrStat, ErrMsg) + type(TurbInitCondType), intent(in) :: SrcTurbInitCondTypeData + type(TurbInitCondType), intent(inout) :: DstTurbInitCondTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_CopyTurbInitCondType' + ErrStat = ErrID_None + ErrMsg = '' + DstTurbInitCondTypeData%RotSpeed = SrcTurbInitCondTypeData%RotSpeed + DstTurbInitCondTypeData%NacYaw = SrcTurbInitCondTypeData%NacYaw + DstTurbInitCondTypeData%BldPitch = SrcTurbInitCondTypeData%BldPitch + DstTurbInitCondTypeData%Azimuth = SrcTurbInitCondTypeData%Azimuth +end subroutine + +subroutine WT_DestroyTurbInitCondType(TurbInitCondTypeData, ErrStat, ErrMsg) + type(TurbInitCondType), intent(inout) :: TurbInitCondTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroyTurbInitCondType' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine WT_PackTurbInitCondType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(TurbInitCondType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackTurbInitCondType' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%RotSpeed) + call RegPack(RF, InData%NacYaw) + call RegPack(RF, InData%BldPitch) + call RegPack(RF, InData%Azimuth) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackTurbInitCondType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(TurbInitCondType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackTurbInitCondType' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%RotSpeed); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NacYaw); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%BldPitch); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Azimuth); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_CopyWaveBuoyType(SrcWaveBuoyTypeData, DstWaveBuoyTypeData, CtrlCode, ErrStat, ErrMsg) + type(WaveBuoyType), intent(in) :: SrcWaveBuoyTypeData + type(WaveBuoyType), intent(inout) :: DstWaveBuoyTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_CopyWaveBuoyType' + ErrStat = ErrID_None + ErrMsg = '' + DstWaveBuoyTypeData%XYLoc = SrcWaveBuoyTypeData%XYLoc +end subroutine + +subroutine WT_DestroyWaveBuoyType(WaveBuoyTypeData, ErrStat, ErrMsg) + type(WaveBuoyType), intent(inout) :: WaveBuoyTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroyWaveBuoyType' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine WT_PackWaveBuoyType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(WaveBuoyType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackWaveBuoyType' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%XYLoc) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackWaveBuoyType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(WaveBuoyType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackWaveBuoyType' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%XYLoc); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_CopyOutFilesType(SrcOutFilesTypeData, DstOutFilesTypeData, CtrlCode, ErrStat, ErrMsg) + type(OutFilesType), intent(in) :: SrcOutFilesTypeData + type(OutFilesType), intent(inout) :: DstOutFilesTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_CopyOutFilesType' + ErrStat = ErrID_None + ErrMsg = '' + DstOutFilesTypeData%SendScreenToFile = SrcOutFilesTypeData%SendScreenToFile + DstOutFilesTypeData%OutFile = SrcOutFilesTypeData%OutFile + DstOutFilesTypeData%OutFmt = SrcOutFilesTypeData%OutFmt +end subroutine + +subroutine WT_DestroyOutFilesType(OutFilesTypeData, ErrStat, ErrMsg) + type(OutFilesType), intent(inout) :: OutFilesTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroyOutFilesType' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine WT_PackOutFilesType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(OutFilesType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackOutFilesType' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%SendScreenToFile) + call RegPack(RF, InData%OutFile) + call RegPack(RF, InData%OutFmt) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackOutFilesType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(OutFilesType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackOutFilesType' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%SendScreenToFile); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%OutFile); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%OutFmt); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_CopyVizType(SrcVizTypeData, DstVizTypeData, CtrlCode, ErrStat, ErrMsg) + type(VizType), intent(in) :: SrcVizTypeData + type(VizType), intent(inout) :: DstVizTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_CopyVizType' + ErrStat = ErrID_None + ErrMsg = '' + DstVizTypeData%WrVTK = SrcVizTypeData%WrVTK + DstVizTypeData%WrVTK_type = SrcVizTypeData%WrVTK_type + DstVizTypeData%WrVTK_DT = SrcVizTypeData%WrVTK_DT + DstVizTypeData%WrVTK_dir = SrcVizTypeData%WrVTK_dir + DstVizTypeData%VTKNacDim = SrcVizTypeData%VTKNacDim + DstVizTypeData%Twidth = SrcVizTypeData%Twidth +end subroutine + +subroutine WT_DestroyVizType(VizTypeData, ErrStat, ErrMsg) + type(VizType), intent(inout) :: VizTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroyVizType' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine WT_PackVizType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(VizType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackVizType' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%WrVTK) + call RegPack(RF, InData%WrVTK_type) + call RegPack(RF, InData%WrVTK_DT) + call RegPack(RF, InData%WrVTK_dir) + call RegPack(RF, InData%VTKNacDim) + call RegPack(RF, InData%Twidth) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackVizType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(VizType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackVizType' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%WrVTK); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%WrVTK_type); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%WrVTK_DT); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%WrVTK_dir); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%VTKNacDim); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Twidth); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_CopyModSettings(SrcModSettingsData, DstModSettingsData, CtrlCode, ErrStat, ErrMsg) + type(ModSettings), intent(in) :: SrcModSettingsData + type(ModSettings), intent(inout) :: DstModSettingsData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_CopyModSettings' + ErrStat = ErrID_None + ErrMsg = '' + DstModSettingsData%SS_InputFile = SrcModSettingsData%SS_InputFile + DstModSettingsData%WaveTimeShift = SrcModSettingsData%WaveTimeShift + DstModSettingsData%MD_InputFile = SrcModSettingsData%MD_InputFile + DstModSettingsData%AD_InputFile = SrcModSettingsData%AD_InputFile + DstModSettingsData%IfW_InputFile = SrcModSettingsData%IfW_InputFile +end subroutine + +subroutine WT_DestroyModSettings(ModSettingsData, ErrStat, ErrMsg) + type(ModSettings), intent(inout) :: ModSettingsData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroyModSettings' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine WT_PackModSettings(RF, Indata) + type(RegFile), intent(inout) :: RF + type(ModSettings), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackModSettings' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%SS_InputFile) + call RegPack(RF, InData%WaveTimeShift) + call RegPack(RF, InData%MD_InputFile) + call RegPack(RF, InData%AD_InputFile) + call RegPack(RF, InData%IfW_InputFile) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackModSettings(RF, OutData) + type(RegFile), intent(inout) :: RF + type(ModSettings), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackModSettings' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%SS_InputFile); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%WaveTimeShift); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%MD_InputFile); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%AD_InputFile); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%IfW_InputFile); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_CopySimSettingsType(SrcSimSettingsTypeData, DstSimSettingsTypeData, CtrlCode, ErrStat, ErrMsg) + type(SimSettingsType), intent(in) :: SrcSimSettingsTypeData + type(SimSettingsType), intent(inout) :: DstSimSettingsTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WT_CopySimSettingsType' + ErrStat = ErrID_None + ErrMsg = '' + call WT_CopySimType(SrcSimSettingsTypeData%Sim, DstSimSettingsTypeData%Sim, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call WT_CopyEnvType(SrcSimSettingsTypeData%Env, DstSimSettingsTypeData%Env, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call WT_CopyTurbConfigType(SrcSimSettingsTypeData%TrbCfg, DstSimSettingsTypeData%TrbCfg, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call WT_CopyTurbInitCondType(SrcSimSettingsTypeData%TrbInit, DstSimSettingsTypeData%TrbInit, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call WT_CopyWaveBuoyType(SrcSimSettingsTypeData%WaveBuoy, DstSimSettingsTypeData%WaveBuoy, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call WT_CopyOutFilesType(SrcSimSettingsTypeData%Outs, DstSimSettingsTypeData%Outs, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call WT_CopyVizType(SrcSimSettingsTypeData%Viz, DstSimSettingsTypeData%Viz, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call WT_CopyModSettings(SrcSimSettingsTypeData%ModSettings, DstSimSettingsTypeData%ModSettings, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return +end subroutine + +subroutine WT_DestroySimSettingsType(SimSettingsTypeData, ErrStat, ErrMsg) + type(SimSettingsType), intent(inout) :: SimSettingsTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WT_DestroySimSettingsType' + ErrStat = ErrID_None + ErrMsg = '' + call WT_DestroySimType(SimSettingsTypeData%Sim, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call WT_DestroyEnvType(SimSettingsTypeData%Env, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call WT_DestroyTurbConfigType(SimSettingsTypeData%TrbCfg, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call WT_DestroyTurbInitCondType(SimSettingsTypeData%TrbInit, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call WT_DestroyWaveBuoyType(SimSettingsTypeData%WaveBuoy, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call WT_DestroyOutFilesType(SimSettingsTypeData%Outs, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call WT_DestroyVizType(SimSettingsTypeData%Viz, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call WT_DestroyModSettings(SimSettingsTypeData%ModSettings, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) +end subroutine + +subroutine WT_PackSimSettingsType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(SimSettingsType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackSimSettingsType' + if (RF%ErrStat >= AbortErrLev) return + call WT_PackSimType(RF, InData%Sim) + call WT_PackEnvType(RF, InData%Env) + call WT_PackTurbConfigType(RF, InData%TrbCfg) + call WT_PackTurbInitCondType(RF, InData%TrbInit) + call WT_PackWaveBuoyType(RF, InData%WaveBuoy) + call WT_PackOutFilesType(RF, InData%Outs) + call WT_PackVizType(RF, InData%Viz) + call WT_PackModSettings(RF, InData%ModSettings) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackSimSettingsType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(SimSettingsType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackSimSettingsType' + if (RF%ErrStat /= ErrID_None) return + call WT_UnpackSimType(RF, OutData%Sim) ! Sim + call WT_UnpackEnvType(RF, OutData%Env) ! Env + call WT_UnpackTurbConfigType(RF, OutData%TrbCfg) ! TrbCfg + call WT_UnpackTurbInitCondType(RF, OutData%TrbInit) ! TrbInit + call WT_UnpackWaveBuoyType(RF, OutData%WaveBuoy) ! WaveBuoy + call WT_UnpackOutFilesType(RF, OutData%Outs) ! Outs + call WT_UnpackVizType(RF, OutData%Viz) ! Viz + call WT_UnpackModSettings(RF, OutData%ModSettings) ! ModSettings +end subroutine + +subroutine WT_CopyCalcStepIOdataType(SrcCalcStepIOdataTypeData, DstCalcStepIOdataTypeData, CtrlCode, ErrStat, ErrMsg) + type(CalcStepIOdataType), intent(in) :: SrcCalcStepIOdataTypeData + type(CalcStepIOdataType), intent(inout) :: DstCalcStepIOdataTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(*), parameter :: RoutineName = 'WT_CopyCalcStepIOdataType' + ErrStat = ErrID_None + ErrMsg = '' + DstCalcStepIOdataTypeData%Time_c = SrcCalcStepIOdataTypeData%Time_c + DstCalcStepIOdataTypeData%PosAng_c = SrcCalcStepIOdataTypeData%PosAng_c + DstCalcStepIOdataTypeData%Vel_c = SrcCalcStepIOdataTypeData%Vel_c + DstCalcStepIOdataTypeData%Acc_c = SrcCalcStepIOdataTypeData%Acc_c + DstCalcStepIOdataTypeData%FrcMom_c = SrcCalcStepIOdataTypeData%FrcMom_c + DstCalcStepIOdataTypeData%FrcMom_MD_c = SrcCalcStepIOdataTypeData%FrcMom_MD_c + if (allocated(SrcCalcStepIOdataTypeData%FrcMom_ADI_c)) then + LB(1:1) = lbound(SrcCalcStepIOdataTypeData%FrcMom_ADI_c) + UB(1:1) = ubound(SrcCalcStepIOdataTypeData%FrcMom_ADI_c) + if (.not. allocated(DstCalcStepIOdataTypeData%FrcMom_ADI_c)) then + allocate(DstCalcStepIOdataTypeData%FrcMom_ADI_c(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstCalcStepIOdataTypeData%FrcMom_ADI_c.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstCalcStepIOdataTypeData%FrcMom_ADI_c = SrcCalcStepIOdataTypeData%FrcMom_ADI_c + end if + DstCalcStepIOdataTypeData%HubVel_ADI_c = SrcCalcStepIOdataTypeData%HubVel_ADI_c + DstCalcStepIOdataTypeData%BuoyWaveElev = SrcCalcStepIOdataTypeData%BuoyWaveElev +end subroutine + +subroutine WT_DestroyCalcStepIOdataType(CalcStepIOdataTypeData, ErrStat, ErrMsg) + type(CalcStepIOdataType), intent(inout) :: CalcStepIOdataTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroyCalcStepIOdataType' + ErrStat = ErrID_None + ErrMsg = '' + if (allocated(CalcStepIOdataTypeData%FrcMom_ADI_c)) then + deallocate(CalcStepIOdataTypeData%FrcMom_ADI_c) + end if +end subroutine + +subroutine WT_PackCalcStepIOdataType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(CalcStepIOdataType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackCalcStepIOdataType' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%Time_c) + call RegPack(RF, InData%PosAng_c) + call RegPack(RF, InData%Vel_c) + call RegPack(RF, InData%Acc_c) + call RegPack(RF, InData%FrcMom_c) + call RegPack(RF, InData%FrcMom_MD_c) + call RegPackAlloc(RF, InData%FrcMom_ADI_c) + call RegPack(RF, InData%HubVel_ADI_c) + call RegPack(RF, InData%BuoyWaveElev) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackCalcStepIOdataType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(CalcStepIOdataType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackCalcStepIOdataType' + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%Time_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%PosAng_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Vel_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Acc_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%FrcMom_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%FrcMom_MD_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%FrcMom_ADI_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HubVel_ADI_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%BuoyWaveElev); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_CopyWrOutputDataType(SrcWrOutputDataTypeData, DstWrOutputDataTypeData, CtrlCode, ErrStat, ErrMsg) + type(WrOutputDataType), intent(in) :: SrcWrOutputDataTypeData + type(WrOutputDataType), intent(inout) :: DstWrOutputDataTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(*), parameter :: RoutineName = 'WT_CopyWrOutputDataType' + ErrStat = ErrID_None + ErrMsg = '' + DstWrOutputDataTypeData%NumChans_cbind = SrcWrOutputDataTypeData%NumChans_cbind + DstWrOutputDataTypeData%NumChans_SS = SrcWrOutputDataTypeData%NumChans_SS + DstWrOutputDataTypeData%NumChans_MD = SrcWrOutputDataTypeData%NumChans_MD + DstWrOutputDataTypeData%NumChans_ADI = SrcWrOutputDataTypeData%NumChans_ADI + DstWrOutputDataTypeData%NumChans_all = SrcWrOutputDataTypeData%NumChans_all + if (allocated(SrcWrOutputDataTypeData%WriteOutputHdr_SS)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%WriteOutputHdr_SS) + UB(1:1) = ubound(SrcWrOutputDataTypeData%WriteOutputHdr_SS) + if (.not. allocated(DstWrOutputDataTypeData%WriteOutputHdr_SS)) then + allocate(DstWrOutputDataTypeData%WriteOutputHdr_SS(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%WriteOutputHdr_SS.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%WriteOutputHdr_SS = SrcWrOutputDataTypeData%WriteOutputHdr_SS + end if + if (allocated(SrcWrOutputDataTypeData%WriteOutputUnt_SS)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%WriteOutputUnt_SS) + UB(1:1) = ubound(SrcWrOutputDataTypeData%WriteOutputUnt_SS) + if (.not. allocated(DstWrOutputDataTypeData%WriteOutputUnt_SS)) then + allocate(DstWrOutputDataTypeData%WriteOutputUnt_SS(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%WriteOutputUnt_SS.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%WriteOutputUnt_SS = SrcWrOutputDataTypeData%WriteOutputUnt_SS + end if + if (allocated(SrcWrOutputDataTypeData%WriteOutputHdr_MD)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%WriteOutputHdr_MD) + UB(1:1) = ubound(SrcWrOutputDataTypeData%WriteOutputHdr_MD) + if (.not. allocated(DstWrOutputDataTypeData%WriteOutputHdr_MD)) then + allocate(DstWrOutputDataTypeData%WriteOutputHdr_MD(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%WriteOutputHdr_MD.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%WriteOutputHdr_MD = SrcWrOutputDataTypeData%WriteOutputHdr_MD + end if + if (allocated(SrcWrOutputDataTypeData%WriteOutputUnt_MD)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%WriteOutputUnt_MD) + UB(1:1) = ubound(SrcWrOutputDataTypeData%WriteOutputUnt_MD) + if (.not. allocated(DstWrOutputDataTypeData%WriteOutputUnt_MD)) then + allocate(DstWrOutputDataTypeData%WriteOutputUnt_MD(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%WriteOutputUnt_MD.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%WriteOutputUnt_MD = SrcWrOutputDataTypeData%WriteOutputUnt_MD + end if + if (allocated(SrcWrOutputDataTypeData%WriteOutputHdr_ADI)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%WriteOutputHdr_ADI) + UB(1:1) = ubound(SrcWrOutputDataTypeData%WriteOutputHdr_ADI) + if (.not. allocated(DstWrOutputDataTypeData%WriteOutputHdr_ADI)) then + allocate(DstWrOutputDataTypeData%WriteOutputHdr_ADI(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%WriteOutputHdr_ADI.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%WriteOutputHdr_ADI = SrcWrOutputDataTypeData%WriteOutputHdr_ADI + end if + if (allocated(SrcWrOutputDataTypeData%WriteOutputUnt_ADI)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%WriteOutputUnt_ADI) + UB(1:1) = ubound(SrcWrOutputDataTypeData%WriteOutputUnt_ADI) + if (.not. allocated(DstWrOutputDataTypeData%WriteOutputUnt_ADI)) then + allocate(DstWrOutputDataTypeData%WriteOutputUnt_ADI(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%WriteOutputUnt_ADI.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%WriteOutputUnt_ADI = SrcWrOutputDataTypeData%WriteOutputUnt_ADI + end if + if (allocated(SrcWrOutputDataTypeData%OutData_SS_c)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%OutData_SS_c) + UB(1:1) = ubound(SrcWrOutputDataTypeData%OutData_SS_c) + if (.not. allocated(DstWrOutputDataTypeData%OutData_SS_c)) then + allocate(DstWrOutputDataTypeData%OutData_SS_c(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%OutData_SS_c.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%OutData_SS_c = SrcWrOutputDataTypeData%OutData_SS_c + end if + if (allocated(SrcWrOutputDataTypeData%OutData_MD_c)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%OutData_MD_c) + UB(1:1) = ubound(SrcWrOutputDataTypeData%OutData_MD_c) + if (.not. allocated(DstWrOutputDataTypeData%OutData_MD_c)) then + allocate(DstWrOutputDataTypeData%OutData_MD_c(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%OutData_MD_c.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%OutData_MD_c = SrcWrOutputDataTypeData%OutData_MD_c + end if + if (allocated(SrcWrOutputDataTypeData%OutData_ADI_c)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%OutData_ADI_c) + UB(1:1) = ubound(SrcWrOutputDataTypeData%OutData_ADI_c) + if (.not. allocated(DstWrOutputDataTypeData%OutData_ADI_c)) then + allocate(DstWrOutputDataTypeData%OutData_ADI_c(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%OutData_ADI_c.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%OutData_ADI_c = SrcWrOutputDataTypeData%OutData_ADI_c + end if + if (allocated(SrcWrOutputDataTypeData%OutData_SS)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%OutData_SS) + UB(1:1) = ubound(SrcWrOutputDataTypeData%OutData_SS) + if (.not. allocated(DstWrOutputDataTypeData%OutData_SS)) then + allocate(DstWrOutputDataTypeData%OutData_SS(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%OutData_SS.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%OutData_SS = SrcWrOutputDataTypeData%OutData_SS + end if + if (allocated(SrcWrOutputDataTypeData%OutData_MD)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%OutData_MD) + UB(1:1) = ubound(SrcWrOutputDataTypeData%OutData_MD) + if (.not. allocated(DstWrOutputDataTypeData%OutData_MD)) then + allocate(DstWrOutputDataTypeData%OutData_MD(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%OutData_MD.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%OutData_MD = SrcWrOutputDataTypeData%OutData_MD + end if + if (allocated(SrcWrOutputDataTypeData%OutData_ADI)) then + LB(1:1) = lbound(SrcWrOutputDataTypeData%OutData_ADI) + UB(1:1) = ubound(SrcWrOutputDataTypeData%OutData_ADI) + if (.not. allocated(DstWrOutputDataTypeData%OutData_ADI)) then + allocate(DstWrOutputDataTypeData%OutData_ADI(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstWrOutputDataTypeData%OutData_ADI.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstWrOutputDataTypeData%OutData_ADI = SrcWrOutputDataTypeData%OutData_ADI + end if + DstWrOutputDataTypeData%OutName = SrcWrOutputDataTypeData%OutName + DstWrOutputDataTypeData%OutUn = SrcWrOutputDataTypeData%OutUn +end subroutine + +subroutine WT_DestroyWrOutputDataType(WrOutputDataTypeData, ErrStat, ErrMsg) + type(WrOutputDataType), intent(inout) :: WrOutputDataTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroyWrOutputDataType' + ErrStat = ErrID_None + ErrMsg = '' + if (allocated(WrOutputDataTypeData%WriteOutputHdr_SS)) then + deallocate(WrOutputDataTypeData%WriteOutputHdr_SS) + end if + if (allocated(WrOutputDataTypeData%WriteOutputUnt_SS)) then + deallocate(WrOutputDataTypeData%WriteOutputUnt_SS) + end if + if (allocated(WrOutputDataTypeData%WriteOutputHdr_MD)) then + deallocate(WrOutputDataTypeData%WriteOutputHdr_MD) + end if + if (allocated(WrOutputDataTypeData%WriteOutputUnt_MD)) then + deallocate(WrOutputDataTypeData%WriteOutputUnt_MD) + end if + if (allocated(WrOutputDataTypeData%WriteOutputHdr_ADI)) then + deallocate(WrOutputDataTypeData%WriteOutputHdr_ADI) + end if + if (allocated(WrOutputDataTypeData%WriteOutputUnt_ADI)) then + deallocate(WrOutputDataTypeData%WriteOutputUnt_ADI) + end if + if (allocated(WrOutputDataTypeData%OutData_SS_c)) then + deallocate(WrOutputDataTypeData%OutData_SS_c) + end if + if (allocated(WrOutputDataTypeData%OutData_MD_c)) then + deallocate(WrOutputDataTypeData%OutData_MD_c) + end if + if (allocated(WrOutputDataTypeData%OutData_ADI_c)) then + deallocate(WrOutputDataTypeData%OutData_ADI_c) + end if + if (allocated(WrOutputDataTypeData%OutData_SS)) then + deallocate(WrOutputDataTypeData%OutData_SS) + end if + if (allocated(WrOutputDataTypeData%OutData_MD)) then + deallocate(WrOutputDataTypeData%OutData_MD) + end if + if (allocated(WrOutputDataTypeData%OutData_ADI)) then + deallocate(WrOutputDataTypeData%OutData_ADI) + end if +end subroutine + +subroutine WT_PackWrOutputDataType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(WrOutputDataType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackWrOutputDataType' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%NumChans_cbind) + call RegPack(RF, InData%NumChans_SS) + call RegPack(RF, InData%NumChans_MD) + call RegPack(RF, InData%NumChans_ADI) + call RegPack(RF, InData%NumChans_all) + call RegPackAlloc(RF, InData%WriteOutputHdr_SS) + call RegPackAlloc(RF, InData%WriteOutputUnt_SS) + call RegPackAlloc(RF, InData%WriteOutputHdr_MD) + call RegPackAlloc(RF, InData%WriteOutputUnt_MD) + call RegPackAlloc(RF, InData%WriteOutputHdr_ADI) + call RegPackAlloc(RF, InData%WriteOutputUnt_ADI) + call RegPackAlloc(RF, InData%OutData_SS_c) + call RegPackAlloc(RF, InData%OutData_MD_c) + call RegPackAlloc(RF, InData%OutData_ADI_c) + call RegPackAlloc(RF, InData%OutData_SS) + call RegPackAlloc(RF, InData%OutData_MD) + call RegPackAlloc(RF, InData%OutData_ADI) + call RegPack(RF, InData%OutName) + call RegPack(RF, InData%OutUn) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackWrOutputDataType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(WrOutputDataType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackWrOutputDataType' + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%NumChans_cbind); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NumChans_SS); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NumChans_MD); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NumChans_ADI); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NumChans_all); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%WriteOutputHdr_SS); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%WriteOutputUnt_SS); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%WriteOutputHdr_MD); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%WriteOutputUnt_MD); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%WriteOutputHdr_ADI); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%WriteOutputUnt_ADI); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%OutData_SS_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%OutData_MD_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%OutData_ADI_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%OutData_SS); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%OutData_MD); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%OutData_ADI); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%OutName); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%OutUn); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_CopyMeshesMotionType(SrcMeshesMotionTypeData, DstMeshesMotionTypeData, CtrlCode, ErrStat, ErrMsg) + type(MeshesMotionType), intent(inout) :: SrcMeshesMotionTypeData + type(MeshesMotionType), intent(inout) :: DstMeshesMotionTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WT_CopyMeshesMotionType' + ErrStat = ErrID_None + ErrMsg = '' + call MeshCopy(SrcMeshesMotionTypeData%PtfmPtMotion, DstMeshesMotionTypeData%PtfmPtMotion, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call MeshCopy(SrcMeshesMotionTypeData%TowerMotion, DstMeshesMotionTypeData%TowerMotion, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call MeshCopy(SrcMeshesMotionTypeData%HubMotion, DstMeshesMotionTypeData%HubMotion, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + if (allocated(SrcMeshesMotionTypeData%BladeRootMotion)) then + LB(1:1) = lbound(SrcMeshesMotionTypeData%BladeRootMotion) + UB(1:1) = ubound(SrcMeshesMotionTypeData%BladeRootMotion) + if (.not. allocated(DstMeshesMotionTypeData%BladeRootMotion)) then + allocate(DstMeshesMotionTypeData%BladeRootMotion(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstMeshesMotionTypeData%BladeRootMotion.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + do i1 = LB(1), UB(1) + call MeshCopy(SrcMeshesMotionTypeData%BladeRootMotion(i1), DstMeshesMotionTypeData%BladeRootMotion(i1), CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + end if + call MeshCopy(SrcMeshesMotionTypeData%WaveBuoyMotion, DstMeshesMotionTypeData%WaveBuoyMotion, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return +end subroutine + +subroutine WT_DestroyMeshesMotionType(MeshesMotionTypeData, ErrStat, ErrMsg) + type(MeshesMotionType), intent(inout) :: MeshesMotionTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WT_DestroyMeshesMotionType' + ErrStat = ErrID_None + ErrMsg = '' + call MeshDestroy( MeshesMotionTypeData%PtfmPtMotion, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call MeshDestroy( MeshesMotionTypeData%TowerMotion, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call MeshDestroy( MeshesMotionTypeData%HubMotion, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (allocated(MeshesMotionTypeData%BladeRootMotion)) then + LB(1:1) = lbound(MeshesMotionTypeData%BladeRootMotion) + UB(1:1) = ubound(MeshesMotionTypeData%BladeRootMotion) + do i1 = LB(1), UB(1) + call MeshDestroy( MeshesMotionTypeData%BladeRootMotion(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + deallocate(MeshesMotionTypeData%BladeRootMotion) + end if + call MeshDestroy( MeshesMotionTypeData%WaveBuoyMotion, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) +end subroutine + +subroutine WT_PackMeshesMotionType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(MeshesMotionType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackMeshesMotionType' + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + if (RF%ErrStat >= AbortErrLev) return + call MeshPack(RF, InData%PtfmPtMotion) + call MeshPack(RF, InData%TowerMotion) + call MeshPack(RF, InData%HubMotion) + call RegPack(RF, allocated(InData%BladeRootMotion)) + if (allocated(InData%BladeRootMotion)) then + call RegPackBounds(RF, 1, lbound(InData%BladeRootMotion), ubound(InData%BladeRootMotion)) + LB(1:1) = lbound(InData%BladeRootMotion) + UB(1:1) = ubound(InData%BladeRootMotion) + do i1 = LB(1), UB(1) + call MeshPack(RF, InData%BladeRootMotion(i1)) + end do + end if + call MeshPack(RF, InData%WaveBuoyMotion) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackMeshesMotionType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(MeshesMotionType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackMeshesMotionType' + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call MeshUnpack(RF, OutData%PtfmPtMotion) ! PtfmPtMotion + call MeshUnpack(RF, OutData%TowerMotion) ! TowerMotion + call MeshUnpack(RF, OutData%HubMotion) ! HubMotion + if (allocated(OutData%BladeRootMotion)) deallocate(OutData%BladeRootMotion) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackBounds(RF, 1, LB, UB); if (RegCheckErr(RF, RoutineName)) return + allocate(OutData%BladeRootMotion(LB(1):UB(1)),stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%BladeRootMotion.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + do i1 = LB(1), UB(1) + call MeshUnpack(RF, OutData%BladeRootMotion(i1)) ! BladeRootMotion + end do + end if + call MeshUnpack(RF, OutData%WaveBuoyMotion) ! WaveBuoyMotion +end subroutine + +subroutine WT_CopyMeshesLoadsType(SrcMeshesLoadsTypeData, DstMeshesLoadsTypeData, CtrlCode, ErrStat, ErrMsg) + type(MeshesLoadsType), intent(inout) :: SrcMeshesLoadsTypeData + type(MeshesLoadsType), intent(inout) :: DstMeshesLoadsTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WT_CopyMeshesLoadsType' + ErrStat = ErrID_None + ErrMsg = '' + call MeshCopy(SrcMeshesLoadsTypeData%PtfmPtLoads, DstMeshesLoadsTypeData%PtfmPtLoads, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call MeshCopy(SrcMeshesLoadsTypeData%PtfmPtLoadsTmp, DstMeshesLoadsTypeData%PtfmPtLoadsTmp, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call MeshCopy(SrcMeshesLoadsTypeData%MooringLoads, DstMeshesLoadsTypeData%MooringLoads, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call MeshCopy(SrcMeshesLoadsTypeData%TowerLoads, DstMeshesLoadsTypeData%TowerLoads, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call MeshCopy(SrcMeshesLoadsTypeData%HubLoads, DstMeshesLoadsTypeData%HubLoads, CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + if (allocated(SrcMeshesLoadsTypeData%BladeRootLoads)) then + LB(1:1) = lbound(SrcMeshesLoadsTypeData%BladeRootLoads) + UB(1:1) = ubound(SrcMeshesLoadsTypeData%BladeRootLoads) + if (.not. allocated(DstMeshesLoadsTypeData%BladeRootLoads)) then + allocate(DstMeshesLoadsTypeData%BladeRootLoads(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstMeshesLoadsTypeData%BladeRootLoads.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + do i1 = LB(1), UB(1) + call MeshCopy(SrcMeshesLoadsTypeData%BladeRootLoads(i1), DstMeshesLoadsTypeData%BladeRootLoads(i1), CtrlCode, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + end if +end subroutine + +subroutine WT_DestroyMeshesLoadsType(MeshesLoadsTypeData, ErrStat, ErrMsg) + type(MeshesLoadsType), intent(inout) :: MeshesLoadsTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WT_DestroyMeshesLoadsType' + ErrStat = ErrID_None + ErrMsg = '' + call MeshDestroy( MeshesLoadsTypeData%PtfmPtLoads, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call MeshDestroy( MeshesLoadsTypeData%PtfmPtLoadsTmp, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call MeshDestroy( MeshesLoadsTypeData%MooringLoads, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call MeshDestroy( MeshesLoadsTypeData%TowerLoads, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call MeshDestroy( MeshesLoadsTypeData%HubLoads, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (allocated(MeshesLoadsTypeData%BladeRootLoads)) then + LB(1:1) = lbound(MeshesLoadsTypeData%BladeRootLoads) + UB(1:1) = ubound(MeshesLoadsTypeData%BladeRootLoads) + do i1 = LB(1), UB(1) + call MeshDestroy( MeshesLoadsTypeData%BladeRootLoads(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + deallocate(MeshesLoadsTypeData%BladeRootLoads) + end if +end subroutine + +subroutine WT_PackMeshesLoadsType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(MeshesLoadsType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackMeshesLoadsType' + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + if (RF%ErrStat >= AbortErrLev) return + call MeshPack(RF, InData%PtfmPtLoads) + call MeshPack(RF, InData%PtfmPtLoadsTmp) + call MeshPack(RF, InData%MooringLoads) + call MeshPack(RF, InData%TowerLoads) + call MeshPack(RF, InData%HubLoads) + call RegPack(RF, allocated(InData%BladeRootLoads)) + if (allocated(InData%BladeRootLoads)) then + call RegPackBounds(RF, 1, lbound(InData%BladeRootLoads), ubound(InData%BladeRootLoads)) + LB(1:1) = lbound(InData%BladeRootLoads) + UB(1:1) = ubound(InData%BladeRootLoads) + do i1 = LB(1), UB(1) + call MeshPack(RF, InData%BladeRootLoads(i1)) + end do + end if + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackMeshesLoadsType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(MeshesLoadsType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackMeshesLoadsType' + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call MeshUnpack(RF, OutData%PtfmPtLoads) ! PtfmPtLoads + call MeshUnpack(RF, OutData%PtfmPtLoadsTmp) ! PtfmPtLoadsTmp + call MeshUnpack(RF, OutData%MooringLoads) ! MooringLoads + call MeshUnpack(RF, OutData%TowerLoads) ! TowerLoads + call MeshUnpack(RF, OutData%HubLoads) ! HubLoads + if (allocated(OutData%BladeRootLoads)) deallocate(OutData%BladeRootLoads) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackBounds(RF, 1, LB, UB); if (RegCheckErr(RF, RoutineName)) return + allocate(OutData%BladeRootLoads(LB(1):UB(1)),stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%BladeRootLoads.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + do i1 = LB(1), UB(1) + call MeshUnpack(RF, OutData%BladeRootLoads(i1)) ! BladeRootLoads + end do + end if +end subroutine + +subroutine WT_CopyMeshesMapsType(SrcMeshesMapsTypeData, DstMeshesMapsTypeData, CtrlCode, ErrStat, ErrMsg) + type(MeshesMapsType), intent(inout) :: SrcMeshesMapsTypeData + type(MeshesMapsType), intent(inout) :: DstMeshesMapsTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WT_CopyMeshesMapsType' + ErrStat = ErrID_None + ErrMsg = '' + call NWTC_Library_CopyMeshMapType(SrcMeshesMapsTypeData%Motion_PRP_2_Twr, DstMeshesMapsTypeData%Motion_PRP_2_Twr, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call NWTC_Library_CopyMeshMapType(SrcMeshesMapsTypeData%Motion_PRP_2_Hub, DstMeshesMapsTypeData%Motion_PRP_2_Hub, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + if (allocated(SrcMeshesMapsTypeData%Motion_Hub_2_BldRoot)) then + LB(1:1) = lbound(SrcMeshesMapsTypeData%Motion_Hub_2_BldRoot) + UB(1:1) = ubound(SrcMeshesMapsTypeData%Motion_Hub_2_BldRoot) + if (.not. allocated(DstMeshesMapsTypeData%Motion_Hub_2_BldRoot)) then + allocate(DstMeshesMapsTypeData%Motion_Hub_2_BldRoot(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstMeshesMapsTypeData%Motion_Hub_2_BldRoot.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + do i1 = LB(1), UB(1) + call NWTC_Library_CopyMeshMapType(SrcMeshesMapsTypeData%Motion_Hub_2_BldRoot(i1), DstMeshesMapsTypeData%Motion_Hub_2_BldRoot(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + end if + if (allocated(SrcMeshesMapsTypeData%Load_BldRoot_2_Hub)) then + LB(1:1) = lbound(SrcMeshesMapsTypeData%Load_BldRoot_2_Hub) + UB(1:1) = ubound(SrcMeshesMapsTypeData%Load_BldRoot_2_Hub) + if (.not. allocated(DstMeshesMapsTypeData%Load_BldRoot_2_Hub)) then + allocate(DstMeshesMapsTypeData%Load_BldRoot_2_Hub(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstMeshesMapsTypeData%Load_BldRoot_2_Hub.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + do i1 = LB(1), UB(1) + call NWTC_Library_CopyMeshMapType(SrcMeshesMapsTypeData%Load_BldRoot_2_Hub(i1), DstMeshesMapsTypeData%Load_BldRoot_2_Hub(i1), CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + end do + end if + call NWTC_Library_CopyMeshMapType(SrcMeshesMapsTypeData%Load_Hub_2_PRP, DstMeshesMapsTypeData%Load_Hub_2_PRP, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call NWTC_Library_CopyMeshMapType(SrcMeshesMapsTypeData%Load_Twr_2_PRP, DstMeshesMapsTypeData%Load_Twr_2_PRP, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call NWTC_Library_CopyMeshMapType(SrcMeshesMapsTypeData%Load_Moor_2_PRP, DstMeshesMapsTypeData%Load_Moor_2_PRP, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return +end subroutine + +subroutine WT_DestroyMeshesMapsType(MeshesMapsTypeData, ErrStat, ErrMsg) + type(MeshesMapsType), intent(inout) :: MeshesMapsTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'WT_DestroyMeshesMapsType' + ErrStat = ErrID_None + ErrMsg = '' + call NWTC_Library_DestroyMeshMapType(MeshesMapsTypeData%Motion_PRP_2_Twr, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call NWTC_Library_DestroyMeshMapType(MeshesMapsTypeData%Motion_PRP_2_Hub, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (allocated(MeshesMapsTypeData%Motion_Hub_2_BldRoot)) then + LB(1:1) = lbound(MeshesMapsTypeData%Motion_Hub_2_BldRoot) + UB(1:1) = ubound(MeshesMapsTypeData%Motion_Hub_2_BldRoot) + do i1 = LB(1), UB(1) + call NWTC_Library_DestroyMeshMapType(MeshesMapsTypeData%Motion_Hub_2_BldRoot(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + deallocate(MeshesMapsTypeData%Motion_Hub_2_BldRoot) + end if + if (allocated(MeshesMapsTypeData%Load_BldRoot_2_Hub)) then + LB(1:1) = lbound(MeshesMapsTypeData%Load_BldRoot_2_Hub) + UB(1:1) = ubound(MeshesMapsTypeData%Load_BldRoot_2_Hub) + do i1 = LB(1), UB(1) + call NWTC_Library_DestroyMeshMapType(MeshesMapsTypeData%Load_BldRoot_2_Hub(i1), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + end do + deallocate(MeshesMapsTypeData%Load_BldRoot_2_Hub) + end if + call NWTC_Library_DestroyMeshMapType(MeshesMapsTypeData%Load_Hub_2_PRP, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call NWTC_Library_DestroyMeshMapType(MeshesMapsTypeData%Load_Twr_2_PRP, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call NWTC_Library_DestroyMeshMapType(MeshesMapsTypeData%Load_Moor_2_PRP, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) +end subroutine + +subroutine WT_PackMeshesMapsType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(MeshesMapsType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackMeshesMapsType' + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + if (RF%ErrStat >= AbortErrLev) return + call NWTC_Library_PackMeshMapType(RF, InData%Motion_PRP_2_Twr) + call NWTC_Library_PackMeshMapType(RF, InData%Motion_PRP_2_Hub) + call RegPack(RF, allocated(InData%Motion_Hub_2_BldRoot)) + if (allocated(InData%Motion_Hub_2_BldRoot)) then + call RegPackBounds(RF, 1, lbound(InData%Motion_Hub_2_BldRoot), ubound(InData%Motion_Hub_2_BldRoot)) + LB(1:1) = lbound(InData%Motion_Hub_2_BldRoot) + UB(1:1) = ubound(InData%Motion_Hub_2_BldRoot) + do i1 = LB(1), UB(1) + call NWTC_Library_PackMeshMapType(RF, InData%Motion_Hub_2_BldRoot(i1)) + end do + end if + call RegPack(RF, allocated(InData%Load_BldRoot_2_Hub)) + if (allocated(InData%Load_BldRoot_2_Hub)) then + call RegPackBounds(RF, 1, lbound(InData%Load_BldRoot_2_Hub), ubound(InData%Load_BldRoot_2_Hub)) + LB(1:1) = lbound(InData%Load_BldRoot_2_Hub) + UB(1:1) = ubound(InData%Load_BldRoot_2_Hub) + do i1 = LB(1), UB(1) + call NWTC_Library_PackMeshMapType(RF, InData%Load_BldRoot_2_Hub(i1)) + end do + end if + call NWTC_Library_PackMeshMapType(RF, InData%Load_Hub_2_PRP) + call NWTC_Library_PackMeshMapType(RF, InData%Load_Twr_2_PRP) + call NWTC_Library_PackMeshMapType(RF, InData%Load_Moor_2_PRP) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackMeshesMapsType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(MeshesMapsType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackMeshesMapsType' + integer(B4Ki) :: i1 + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call NWTC_Library_UnpackMeshMapType(RF, OutData%Motion_PRP_2_Twr) ! Motion_PRP_2_Twr + call NWTC_Library_UnpackMeshMapType(RF, OutData%Motion_PRP_2_Hub) ! Motion_PRP_2_Hub + if (allocated(OutData%Motion_Hub_2_BldRoot)) deallocate(OutData%Motion_Hub_2_BldRoot) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackBounds(RF, 1, LB, UB); if (RegCheckErr(RF, RoutineName)) return + allocate(OutData%Motion_Hub_2_BldRoot(LB(1):UB(1)),stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%Motion_Hub_2_BldRoot.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + do i1 = LB(1), UB(1) + call NWTC_Library_UnpackMeshMapType(RF, OutData%Motion_Hub_2_BldRoot(i1)) ! Motion_Hub_2_BldRoot + end do + end if + if (allocated(OutData%Load_BldRoot_2_Hub)) deallocate(OutData%Load_BldRoot_2_Hub) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackBounds(RF, 1, LB, UB); if (RegCheckErr(RF, RoutineName)) return + allocate(OutData%Load_BldRoot_2_Hub(LB(1):UB(1)),stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%Load_BldRoot_2_Hub.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + do i1 = LB(1), UB(1) + call NWTC_Library_UnpackMeshMapType(RF, OutData%Load_BldRoot_2_Hub(i1)) ! Load_BldRoot_2_Hub + end do + end if + call NWTC_Library_UnpackMeshMapType(RF, OutData%Load_Hub_2_PRP) ! Load_Hub_2_PRP + call NWTC_Library_UnpackMeshMapType(RF, OutData%Load_Twr_2_PRP) ! Load_Twr_2_PRP + call NWTC_Library_UnpackMeshMapType(RF, OutData%Load_Moor_2_PRP) ! Load_Moor_2_PRP +end subroutine + +subroutine WT_CopyStructTmpType(SrcStructTmpTypeData, DstStructTmpTypeData, CtrlCode, ErrStat, ErrMsg) + type(StructTmpType), intent(in) :: SrcStructTmpTypeData + type(StructTmpType), intent(inout) :: DstStructTmpTypeData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: ErrStat2 + character(*), parameter :: RoutineName = 'WT_CopyStructTmpType' + ErrStat = ErrID_None + ErrMsg = '' + DstStructTmpTypeData%Azimuth = SrcStructTmpTypeData%Azimuth + DstStructTmpTypeData%RotSpeed = SrcStructTmpTypeData%RotSpeed + DstStructTmpTypeData%BldPitch = SrcStructTmpTypeData%BldPitch + DstStructTmpTypeData%NacYaw = SrcStructTmpTypeData%NacYaw + DstStructTmpTypeData%FrcMom_ADI_at_Ptfm = SrcStructTmpTypeData%FrcMom_ADI_at_Ptfm + DstStructTmpTypeData%FrcMom_MD_at_Ptfm = SrcStructTmpTypeData%FrcMom_MD_at_Ptfm + DstStructTmpTypeData%BuoyPos_c = SrcStructTmpTypeData%BuoyPos_c + DstStructTmpTypeData%PtfmPosAng_c = SrcStructTmpTypeData%PtfmPosAng_c + DstStructTmpTypeData%PtfmVel_c = SrcStructTmpTypeData%PtfmVel_c + DstStructTmpTypeData%PtfmAcc_c = SrcStructTmpTypeData%PtfmAcc_c + DstStructTmpTypeData%NacPos_c = SrcStructTmpTypeData%NacPos_c + DstStructTmpTypeData%NacDCM_c = SrcStructTmpTypeData%NacDCM_c + DstStructTmpTypeData%NacVel_c = SrcStructTmpTypeData%NacVel_c + DstStructTmpTypeData%NacAcc_c = SrcStructTmpTypeData%NacAcc_c + DstStructTmpTypeData%HubPos_c = SrcStructTmpTypeData%HubPos_c + DstStructTmpTypeData%HubDCM_c = SrcStructTmpTypeData%HubDCM_c + DstStructTmpTypeData%HubVel_c = SrcStructTmpTypeData%HubVel_c + DstStructTmpTypeData%HubAcc_c = SrcStructTmpTypeData%HubAcc_c + if (allocated(SrcStructTmpTypeData%BldPos_c)) then + LB(1:1) = lbound(SrcStructTmpTypeData%BldPos_c) + UB(1:1) = ubound(SrcStructTmpTypeData%BldPos_c) + if (.not. allocated(DstStructTmpTypeData%BldPos_c)) then + allocate(DstStructTmpTypeData%BldPos_c(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstStructTmpTypeData%BldPos_c.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstStructTmpTypeData%BldPos_c = SrcStructTmpTypeData%BldPos_c + end if + if (allocated(SrcStructTmpTypeData%BldDCM_c)) then + LB(1:1) = lbound(SrcStructTmpTypeData%BldDCM_c) + UB(1:1) = ubound(SrcStructTmpTypeData%BldDCM_c) + if (.not. allocated(DstStructTmpTypeData%BldDCM_c)) then + allocate(DstStructTmpTypeData%BldDCM_c(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstStructTmpTypeData%BldDCM_c.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstStructTmpTypeData%BldDCM_c = SrcStructTmpTypeData%BldDCM_c + end if + if (allocated(SrcStructTmpTypeData%BldVel_c)) then + LB(1:1) = lbound(SrcStructTmpTypeData%BldVel_c) + UB(1:1) = ubound(SrcStructTmpTypeData%BldVel_c) + if (.not. allocated(DstStructTmpTypeData%BldVel_c)) then + allocate(DstStructTmpTypeData%BldVel_c(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstStructTmpTypeData%BldVel_c.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstStructTmpTypeData%BldVel_c = SrcStructTmpTypeData%BldVel_c + end if + if (allocated(SrcStructTmpTypeData%BldAcc_c)) then + LB(1:1) = lbound(SrcStructTmpTypeData%BldAcc_c) + UB(1:1) = ubound(SrcStructTmpTypeData%BldAcc_c) + if (.not. allocated(DstStructTmpTypeData%BldAcc_c)) then + allocate(DstStructTmpTypeData%BldAcc_c(LB(1):UB(1)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstStructTmpTypeData%BldAcc_c.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstStructTmpTypeData%BldAcc_c = SrcStructTmpTypeData%BldAcc_c + end if +end subroutine + +subroutine WT_DestroyStructTmpType(StructTmpTypeData, ErrStat, ErrMsg) + type(StructTmpType), intent(inout) :: StructTmpTypeData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'WT_DestroyStructTmpType' + ErrStat = ErrID_None + ErrMsg = '' + if (allocated(StructTmpTypeData%BldPos_c)) then + deallocate(StructTmpTypeData%BldPos_c) + end if + if (allocated(StructTmpTypeData%BldDCM_c)) then + deallocate(StructTmpTypeData%BldDCM_c) + end if + if (allocated(StructTmpTypeData%BldVel_c)) then + deallocate(StructTmpTypeData%BldVel_c) + end if + if (allocated(StructTmpTypeData%BldAcc_c)) then + deallocate(StructTmpTypeData%BldAcc_c) + end if +end subroutine + +subroutine WT_PackStructTmpType(RF, Indata) + type(RegFile), intent(inout) :: RF + type(StructTmpType), intent(in) :: InData + character(*), parameter :: RoutineName = 'WT_PackStructTmpType' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%Azimuth) + call RegPack(RF, InData%RotSpeed) + call RegPack(RF, InData%BldPitch) + call RegPack(RF, InData%NacYaw) + call RegPack(RF, InData%FrcMom_ADI_at_Ptfm) + call RegPack(RF, InData%FrcMom_MD_at_Ptfm) + call RegPack(RF, InData%BuoyPos_c) + call RegPack(RF, InData%PtfmPosAng_c) + call RegPack(RF, InData%PtfmVel_c) + call RegPack(RF, InData%PtfmAcc_c) + call RegPack(RF, InData%NacPos_c) + call RegPack(RF, InData%NacDCM_c) + call RegPack(RF, InData%NacVel_c) + call RegPack(RF, InData%NacAcc_c) + call RegPack(RF, InData%HubPos_c) + call RegPack(RF, InData%HubDCM_c) + call RegPack(RF, InData%HubVel_c) + call RegPack(RF, InData%HubAcc_c) + call RegPackAlloc(RF, InData%BldPos_c) + call RegPackAlloc(RF, InData%BldDCM_c) + call RegPackAlloc(RF, InData%BldVel_c) + call RegPackAlloc(RF, InData%BldAcc_c) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine WT_UnPackStructTmpType(RF, OutData) + type(RegFile), intent(inout) :: RF + type(StructTmpType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'WT_UnPackStructTmpType' + integer(B4Ki) :: LB(1), UB(1) + integer(IntKi) :: stat + logical :: IsAllocAssoc + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%Azimuth); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%RotSpeed); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%BldPitch); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NacYaw); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%FrcMom_ADI_at_Ptfm); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%FrcMom_MD_at_Ptfm); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%BuoyPos_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%PtfmPosAng_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%PtfmVel_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%PtfmAcc_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NacPos_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NacDCM_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NacVel_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NacAcc_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HubPos_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HubDCM_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HubVel_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HubAcc_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%BldPos_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%BldDCM_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%BldVel_c); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%BldAcc_c); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +END MODULE WaveTank_Types + +!ENDOFREGISTRYGENERATEDFILE diff --git a/glue-codes/labview/src/libwavetanktestinglib.h b/glue-codes/labview/src/libwavetanktestinglib.h new file mode 100644 index 0000000000..00ce5f13a6 --- /dev/null +++ b/glue-codes/labview/src/libwavetanktestinglib.h @@ -0,0 +1,14 @@ +#ifndef WAVETANKTESTING_H +#define WAVETANKTESTING_H + +#ifdef __cplusplus +extern "C" { +#endif + +void WaveTank_NoOp(); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/glue-codes/openfast/CMakeLists.txt b/glue-codes/openfast/CMakeLists.txt index a742b5972a..08fde158b7 100644 --- a/glue-codes/openfast/CMakeLists.txt +++ b/glue-codes/openfast/CMakeLists.txt @@ -40,5 +40,3 @@ if(BUILD_OPENFAST_LIB_DRIVER) install(TARGETS openfast_lib_driver RUNTIME DESTINATION bin) endif() - - diff --git a/glue-codes/python/examples/OpenFAST.py b/glue-codes/python/examples/OpenFAST.py index 6a953d70c8..d7b7e25784 100644 --- a/glue-codes/python/examples/OpenFAST.py +++ b/glue-codes/python/examples/OpenFAST.py @@ -1,4 +1,6 @@ - +# NOTE: this file is not complete, but can serve as a starting point for +# calling OpenFAST through the library interface. Modification will be +# necessary from pyOpenFAST import fast project_root = '/Users/rmudafor/Development/openfast' diff --git a/glue-codes/python/examples/SeaState.py b/glue-codes/python/examples/SeaState.py deleted file mode 100644 index fff806b8ea..0000000000 --- a/glue-codes/python/examples/SeaState.py +++ /dev/null @@ -1,38 +0,0 @@ - - -from pyOpenFAST.seastate import SeaStateLib -import matplotlib.pyplot as plt -import numpy as np - -project_root = '/Users/rmudafor/Development/openfast' -library_path = project_root + '/build/modules/seastate/libseastate_c_binding.dylib' - -dt = 1.0 -time_steps = 10 - -seastatelib = SeaStateLib( - library_path, - "NRELOffshrBsline5MW_OC4DeepCwindSemi_SeaState_WaveMod5.dat" -) - -seastatelib.init( - time_interval=dt, - n_steps=time_steps, -) - -seastate_outputs = np.zeros((time_steps, seastatelib.num_outs)) -for i in range(time_steps): - seastatelib.calc_output(i) - seastate_outputs[i] = seastatelib.output_values - print(i, [f"{value:3.4f} - " for value in seastate_outputs[i]]) -seastatelib.end() - -# Plot the results -# plt.figure(figsize=(10, 6)) -# plt.plot(seastate_outputs[:, 0]) -# plt.plot(seastate_outputs[:, 1]) -# plt.xlabel('Time Step') -# plt.ylabel('Value') -# plt.title('Sea State Outputs') -# plt.legend() -# plt.show() diff --git a/glue-codes/python/examples/WaveTankDriver.py b/glue-codes/python/examples/WaveTankDriver.py deleted file mode 100644 index 3a8ec07db8..0000000000 --- a/glue-codes/python/examples/WaveTankDriver.py +++ /dev/null @@ -1,198 +0,0 @@ - -from ctypes import ( - CDLL, - POINTER, - create_string_buffer, - byref, - c_byte, - c_int, - c_double, - c_float, - c_char, - c_char_p, - c_bool -) -import numpy as np -from pathlib import Path - -from OpynFAST.interface_abc import OpenFASTInterfaceType - -project_root = '/Users/rmudafor/Development/openfast' -library_path = project_root + '/build/glue-codes/labview/libwavetanktestinglib.dylib' - -class WaveTankLib(OpenFASTInterfaceType): - - def __init__(self, library_path: str, input_file_names: dict): - """ - _summary_ - - Args: - library_path (str): Path to the compile wavetank interface shared library - input_file_names (dict): Map of file names for each included module: - - MD_InputFile - - SS_InputFile - - AD_InputFile - - IfW_InputFile - """ - super().__init__(library_path) - - self.input_file_names = { - k: create_string_buffer(str(Path(v).absolute() ).encode('utf-8')) - for k,v in input_file_names.items() - } - - self._initialize_routines() - - # Create buffers for class data - self.ended = False # For error handling at end - - # This buffer for the channel names and units is set arbitrarily large - # to start. Channel name and unit lengths are currently hard - # coded to 20 (this must match ChanLen in NWTC_Base.f90). - # self._channel_names_c = create_string_buffer(20 * 4000 + 1) - # self._channel_units_c = create_string_buffer(20 * 4000 + 1) - - self.dt = c_double(0) - self.total_time = c_double(0) - self.numTimeSteps = c_int(0) - - def _initialize_routines(self): - self.WaveTank_Init.argtypes = [ - POINTER(c_char), # intent(in ) :: MD_InputFile_c(IntfStrLen) - POINTER(c_char), # intent(in ) :: SS_InputFile_c(IntfStrLen) - POINTER(c_char), # intent(in ) :: AD_InputFile_c(IntfStrLen) - POINTER(c_char), # intent(in ) :: IfW_InputFile_c(IntfStrLen) - POINTER(c_int), # intent(in ) :: IfW_InputFile_c(IntfStrLen) - POINTER(c_int), # intent(in ) :: n_camera_points_c - POINTER(c_char), # intent( out) :: ErrMsg_C(ErrMsgLen_C) - ] - self.WaveTank_Init.restype = c_int - - self.WaveTank_CalcOutput.argtypes = [ - POINTER(c_int), # integer(c_int) :: frame_number - POINTER(c_float), # real(c_float), intent(in ) :: positions_x(N_CAMERA_POINTS) - POINTER(c_float), # real(c_float), intent(in ) :: positions_y(N_CAMERA_POINTS) - POINTER(c_float), # real(c_float), intent(in ) :: positions_z(N_CAMERA_POINTS) - POINTER(c_float), # real(c_float), intent(in ) :: rotation_matrix(9) - POINTER(c_float), # real(c_float), intent( out) :: loads(N_CAMERA_POINTS) - POINTER(c_int), # integer(c_int), intent( out) :: ErrStat_C - POINTER(c_char), # character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) - ] - self.WaveTank_CalcOutput.restype = c_int - - - def init(self, n_camera_points): - _error_status = c_int(0) - _error_message = create_string_buffer(self.ERROR_MSG_C_LEN) - - # Convert the string into a c_char byte array - # input_string = '\x00'.join(input_string_array) - # input_string = input_string.encode('utf-8') - # input_string_length = len(input_string) - - # # Convert the initial positions array into c_float array - # init_positions_c = (c_float * 6)(0.0, ) - # for i, p in enumerate(platform_init_pos): - # init_positions_c[i] = c_float(p) - - # self._numChannels = c_int(0) - - # gravity = c_float(9.80665) - # water_density = c_float(1025) - # water_depth = c_float(200) - # msl2swl = c_float(0) - # outrootname = "./seastate.SeaSt".encode('utf-8') - # wave_kinematics_mode = c_int(0) - # n_steps = c_int(801) - # time_interval = c_float(0.125) - # wave_elevation_series_flag = c_int(0) - self.WaveTank_Init( - self.input_file_names["MoorDyn"], - self.input_file_names["SeaState"], - self.input_file_names["AeroDyn"], - self.input_file_names["InflowWind"], - byref(c_int(n_camera_points)), - # create_string_buffer(outrootname), - # byref(gravity), - # byref(water_density), - # byref(water_depth), - # byref(msl2swl), - # byref(n_steps), - # byref(time_interval), - # byref(wave_elevation_series_flag), - # byref(wave_kinematics_mode), - byref(_error_status), - _error_message, - ) - if self.fatal_error(_error_status): - raise RuntimeError(f"Error {_error_status.value}: {_error_message.value}") - - def calc_output( - self, - frame_number: int, - positions_x: np.ndarray, - positions_y: np.ndarray, - positions_z: np.ndarray, - rotation_matrix: np.ndarray, - loads: np.ndarray, - ): - _error_status = c_int(0) - _error_message = create_string_buffer(self.ERROR_MSG_C_LEN) - - self.WaveTank_CalcOutput( - byref(c_int(frame_number)), - positions_x.ctypes.data_as(POINTER(c_float)), - positions_y.ctypes.data_as(POINTER(c_float)), - positions_z.ctypes.data_as(POINTER(c_float)), - rotation_matrix.ctypes.data_as(POINTER(c_float)), - loads.ctypes.data_as(POINTER(c_float)), - byref(_error_status), - _error_message, - ) - if self.fatal_error(_error_status): - raise RuntimeError(f"Error {_error_status.value}: {_error_message.value}") - - @property - def output_channel_names(self): - if len(self._channel_names.value.split()) == 0: - return [] - output_channel_names = self._channel_names.value.split() - output_channel_names = [n.decode('UTF-8') for n in output_channel_names] - return output_channel_names - - @property - def output_channel_units(self): - if len(self._channel_units.value.split()) == 0: - return [] - output_channel_units = self._channel_units.value.split() - output_channel_units = [n.decode('UTF-8') for n in output_channel_units] - return output_channel_units - - -if __name__=="__main__": - wavetanklib = WaveTankLib( - library_path, - { - "MoorDyn": "/Users/rmudafor/Development/openfast/reg_tests/r-test/modules/moordyn/py_md_5MW_OC4Semi/md_primary.inp", - "SeaState": "/Users/rmudafor/Development/openfast/reg_tests/r-test/modules/seastate/seastate_1/NRELOffshrBsline5MW_OC4DeepCwindSemi_SeaState.dat", - "AeroDyn": "/Users/rmudafor/Development/openfast/reg_tests/r-test/modules/aerodyn/ad_MHK_RM1_Floating/MHK_RM1_Floating_AeroDyn.dat", - "InflowWind": "/Users/rmudafor/Development/openfast/reg_tests/r-test/modules/inflowwind/py_ifw_turbsimff/ifw_primary.inp", - }, - ) - wavetanklib.init(n_camera_points=3) - - positions_x = np.zeros(1, dtype=np.float32) - positions_y = np.zeros(1, dtype=np.float32) - positions_z = np.zeros(1, dtype=np.float32) - rotation_matrix = np.zeros(9, dtype=np.float32) - loads = np.zeros(6, dtype=np.float32) - - for i in range(50): - wavetanklib.calc_output( - frame_number=i, - positions_x=positions_x, - positions_y=positions_y, - positions_z=positions_z, - rotation_matrix=rotation_matrix, - loads=loads, - ) diff --git a/glue-codes/python/pyOpenFAST/aerodyn_inflow.py b/glue-codes/python/pyOpenFAST/aerodyn_inflow.py index da0afd7aa7..62b332137e 100644 --- a/glue-codes/python/pyOpenFAST/aerodyn_inflow.py +++ b/glue-codes/python/pyOpenFAST/aerodyn_inflow.py @@ -60,6 +60,8 @@ import numpy as np import numpy.typing as npt +from .interface_abc import OpenFASTInterfaceType + #------------------------------------------------------------------------------- # Helper functions and classes #------------------------------------------------------------------------------- @@ -144,7 +146,7 @@ class MotionData: #------------------------------------------------------------------------------- # C-interface library class for AeroDyn x InflowWind #------------------------------------------------------------------------------- -class AeroDynInflowLib(CDLL): +class AeroDynInflowLib(OpenFASTInterfaceType): """A Python interface to the AeroDyn/InflowWind library. This class provides a modern Python interface for calling and running AeroDyn @@ -152,28 +154,6 @@ class AeroDynInflowLib(CDLL): of the underlying Fortran library. """ - #-------------------------------------- - # Error levels (from IfW) - #-------------------------------------- - error_levels: Dict[int, str] = { - 0: "None", - 1: "Info", - 2: "Warning", - 3: "Severe Error", - 4: "Fatal Error" - } - - #-------------------------------------- - # Constants - #-------------------------------------- - # NOTE: The length of the error message in Fortran is determined by the - # ErrMsgLen variable in the NWTC_Base.f90 file. If ErrMsgLen is modified, - # the corresponding size here must also be updated to match. - ERROR_MESSAGE_LENGTH: int = 8197 - DEFAULT_STRING_LENGTH: int = 1025 - CHANNEL_NAME_LENGTH: int = 20 - MAX_CHANNELS: int = 8000 - def __init__(self, library_path: Union[str, Path]) -> None: """Initializes the AeroDyn/InflowWind interface. @@ -199,11 +179,6 @@ def __init__(self, library_path: Union[str, Path]) -> None: self.aerodyn_inputs_passed_as_string: bool = True # Pass input file as string self.inflow_inputs_passed_as_string: bool = True # Pass input file as string - # Error handling setup - self.abort_error_level = 4 - self.error_status_c = c_int(0) - self.error_message_c = create_string_buffer(self.ERROR_MESSAGE_LENGTH) - # Channel information buffers self._channel_names_c = create_string_buffer( self.CHANNEL_NAME_LENGTH * self.MAX_CHANNELS @@ -223,6 +198,10 @@ def __init__(self, library_path: Union[str, Path]) -> None: # MHK flag: 0->not MHK, 1->fixed bottom, 2->floating self.mhk = 0 + # External IfW data: 0->internal, 1->external IfW instance + # NOTE: if external, must call set pointer routine + self.externIfW = 0 + # 0->None, 1->Info, 2->Warning, 3->Severe Error, 4->Fatal Error self.debug_level = 0 @@ -316,7 +295,7 @@ def check_error(self) -> None: message = f"AeroDyn/InflowWind {error_level}: {error_msg}" # If the error level is fatal, call adi_end() and raise an error - if self.error_status_c.value >= self.abort_error_level: + if self.error_status_c.value >= self.abort_error_level.value: try: self.adi_end() except Exception as e: @@ -333,22 +312,37 @@ def adi_preinit(self) -> None: Raises: RuntimeError: If pre-initialization fails """ + # Prepare output file paths + vtk_output_dir_c = create_string_buffer( + self.output_vtk_dir.ljust(self.default_str_c_len).encode('utf-8') + ) + + # Convert VTK nacelle dimensions to C array + vtk_nac_dimension_c = to_c_array(self.vtk_nacelle_dimension, c_float) + self.ADI_C_PreInit( - byref(c_int(self.num_turbines)), # IN -> number of turbines - byref(c_int(self.transpose_dcm)), # IN -> transpose_dcm flag (0=false, 1=true) - byref(c_int(self.point_load_output)), # IN -> point_load_output flag (0=false, 1=true) - byref(c_float(self.gravity)), # IN -> gravity - byref(c_float(self.fluid_density)), # IN -> fluid density - byref(c_float(self.kinematic_viscosity)), # IN -> kinematic viscosity - byref(c_float(self.sound_speed)), # IN -> speed of sound - byref(c_float(self.atmospheric_pressure)), # IN -> atmospheric pressure - byref(c_float(self.vapor_pressure)), # IN -> vapor pressure - byref(c_float(self.water_depth)), # IN -> water depth - byref(c_float(self.mean_sea_level_offset)), # IN -> MSL to SWL offset - byref(c_int(self.mhk)), # IN -> mhk flag (0=not MHK, 1=fixed bottom, 2=floating) - byref(c_int(self.debug_level)), # IN -> debug level (0=None to 4=Fatal) - byref(self.error_status_c), # OUT <- error status code - self.error_message_c # OUT <- error message buffer + byref(c_int(self.num_turbines)), # IN -> number of turbines + byref(c_int(self.transpose_dcm)), # IN -> transpose_dcm flag (0=false, 1=true) + byref(c_int(self.point_load_output)), # IN -> point_load_output flag (0=false, 1=true) + byref(c_float(self.gravity)), # IN -> gravity + byref(c_float(self.fluid_density)), # IN -> fluid density + byref(c_float(self.kinematic_viscosity)), # IN -> kinematic viscosity + byref(c_float(self.sound_speed)), # IN -> speed of sound + byref(c_float(self.atmospheric_pressure)), # IN -> atmospheric pressure + byref(c_float(self.vapor_pressure)), # IN -> vapor pressure + byref(c_float(self.water_depth)), # IN -> water depth + byref(c_float(self.mean_sea_level_offset)), # IN -> MSL to SWL offset + byref(c_int(self.mhk)), # IN -> mhk flag (0=not MHK, 1=fixed bottom, 2=floating) + byref(c_int(self.externIfW)), # IN -> external IfW instance (0=internal IfW, 1=external IfW with pointer to data (setpointer call required)) + vtk_output_dir_c, # IN -> directory for vtk output files + byref(c_int(self.write_vtk)), # IN -> write VTK flag + byref(c_int(self.vtk_type)), # IN -> VTK write type + byref(c_double(self.vtk_dt)), # IN -> VTK output time step + vtk_nac_dimension_c, # IN -> VTK nacelle dimensions + byref(c_float(self.vtk_hub_radius)), # IN -> VTK hub radius + byref(c_int(self.debug_level)), # IN -> debug level (0=None to 4=all meshes) + byref(self.error_status_c), # OUT <- error status code + self.error_message_c # OUT <- error message buffer ) self.check_error() @@ -423,14 +417,8 @@ def adi_init( # Prepare output file paths output_file_root_name_c = create_string_buffer( - self.output_root_name.ljust(self.DEFAULT_STRING_LENGTH).encode('utf-8') + self.output_root_name.ljust(self.default_str_c_len).encode('utf-8') ) - vtk_output_dir_c = create_string_buffer( - self.output_vtk_dir.ljust(self.DEFAULT_STRING_LENGTH).encode('utf-8') - ) - - # Convert VTK nacelle dimensions to C array - vtk_nac_dimension_c = to_c_array(self.vtk_nacelle_dimension, c_float) self.ADI_C_Init( byref(c_int(self.aerodyn_inputs_passed_as_string)), # IN -> AD input file is passed as string @@ -440,16 +428,10 @@ def adi_init( c_char_p(ifw_input_string), # IN -> IfW input file as string byref(c_int(ifw_input_string_length)), # IN -> IfW input file string length output_file_root_name_c, # IN -> rootname for ADI file writing - vtk_output_dir_c, # IN -> directory for vtk output files byref(c_int(self.interpolation_order)), # IN -> interpolation order (1: linear, 2: quadratic) byref(c_double(self.dt)), # IN -> time step byref(c_double(self.t_max)), # IN -> maximum simulation time byref(c_int(self.store_hub_height_velocity)), # IN -> store hub height velocity flag - byref(c_int(self.write_vtk)), # IN -> write VTK flag - byref(c_int(self.vtk_type)), # IN -> VTK write type - byref(c_double(self.vtk_dt)), # IN -> VTK output time step - vtk_nac_dimension_c, # IN -> VTK nacelle dimensions - byref(c_float(self.vtk_hub_radius)), # IN -> VTK hub radius byref(c_int(self.write_outputs)), # IN -> write outputs flag byref(c_double(self.output_timestep)), # IN -> output time step byref(self._num_channels_c), # OUT <- number of channels @@ -708,6 +690,13 @@ def _initialize_routines(self) -> None: POINTER(c_float), # WtrDpth POINTER(c_float), # MSL2SWL POINTER(c_int), # MHK + POINTER(c_int), # externIfW + POINTER(c_char), # OutVTKdir + POINTER(c_int), # WrVTK + POINTER(c_int), # WrVTK_Type + POINTER(c_double), # WrVTK_DT -- 0 or negative to do every step + POINTER(c_float), # VTKNacDim + POINTER(c_float), # VTKHubRad POINTER(c_int), # debuglevel POINTER(c_int), # ErrStat_C POINTER(c_char) # ErrMsg_C @@ -748,16 +737,10 @@ def _initialize_routines(self) -> None: POINTER(c_char_p), # IfW input file as string POINTER(c_int), # IfW input file string length POINTER(c_char), # OutRootName - POINTER(c_char), # OutVTKdir POINTER(c_int), # InterpOrder POINTER(c_double), # dt POINTER(c_double), # tmax POINTER(c_int), # storeHHVel - POINTER(c_int), # WrVTK - POINTER(c_int), # WrVTK_Type - POINTER(c_double), # WrVTK_DT -- 0 or negative to do every step - POINTER(c_float), # VTKNacDim - POINTER(c_float), # VTKHubRad POINTER(c_int), # wrOuts -- file format for writing outputs POINTER(c_double), # DT_Outs -- timestep for outputs to file POINTER(c_int), # number of channels diff --git a/glue-codes/python/pyOpenFAST/fast.py b/glue-codes/python/pyOpenFAST/fast.py index 14fb2271f1..de29fd23e6 100644 --- a/glue-codes/python/pyOpenFAST/fast.py +++ b/glue-codes/python/pyOpenFAST/fast.py @@ -17,7 +17,6 @@ from .interface_abc import OpenFASTInterfaceType -IntfStrLen = 1025 # FAST_Library global NumFixedInputs = 51 # FAST_Library global @@ -127,7 +126,7 @@ def _initialize_routines(self) -> None: def init(self) -> None: _error_status = c_int(0) - _error_message = create_string_buffer(IntfStrLen) + _error_message = create_string_buffer(self.IntfStrLen) self.FAST_AllocateTurbines( byref(self.n_turbines), @@ -172,7 +171,7 @@ def init(self) -> None: def sim(self) -> None: _error_status = c_int(0) - _error_message = create_string_buffer(IntfStrLen) + _error_message = create_string_buffer(self.IntfStrLen) self.FAST_Start( byref(self.i_turb), @@ -213,7 +212,7 @@ def sim(self) -> None: def deinit(self) -> None: _error_status = c_int(0) - _error_message = create_string_buffer(IntfStrLen) + _error_message = create_string_buffer(self.IntfStrLen) if not self.ended: self.ended = True @@ -265,7 +264,7 @@ def total_output_steps(self) -> int: def get_hub_position(self) -> Tuple: _error_status = c_int(0) - _error_message = create_string_buffer(IntfStrLen) + _error_message = create_string_buffer(self.IntfStrLen) # Data buffers absolute_position = (c_float * 3)(0.0, ) diff --git a/glue-codes/python/pyOpenFAST/hydrodyn.py b/glue-codes/python/pyOpenFAST/hydrodyn.py index df9da2334a..507554a9fc 100644 --- a/glue-codes/python/pyOpenFAST/hydrodyn.py +++ b/glue-codes/python/pyOpenFAST/hydrodyn.py @@ -76,25 +76,9 @@ import numpy as np import datetime -class HydroDynLib(CDLL): - # Human readable error levels from IfW. - error_levels = { - 0: "None", - 1: "Info", - 2: "Warning", - 3: "Severe Error", - 4: "Fatal Error" - } - - # NOTE: the error message length in Fortran is controlled by the - # ErrMsgLen variable in the NWTC_Base.f90 file. If that ever - # changes, it may be necessary to update the corresponding size - # here. - error_msg_c_len = 1025 - - # NOTE: the length of the name used for any output file written by the - # HD Fortran code is 1025. - default_str_c_len = 1025 +from .interface_abc import OpenFASTInterfaceType + +class HydroDynLib(OpenFASTInterfaceType): def __init__(self, library_path): super().__init__(library_path) @@ -107,11 +91,6 @@ def __init__(self, library_path): self.seastate_inputs_passed_as_string: bool = True # Pass input file as string self.hydrodyn_inputs_passed_as_string: bool = True # Pass input file as string - # Create buffers for class data - self.abort_error_level = 4 - self.error_status_c = c_int(0) - self.error_message_c = create_string_buffer(self.error_msg_c_len) - # This is not sufficient for HD #FIXME: ChanLen may not always be 20 -- could be as much as 256 # Possible fix is to pass this length over to Fortran side. diff --git a/glue-codes/python/pyOpenFAST/inflowwind.py b/glue-codes/python/pyOpenFAST/inflowwind.py index bc741443e8..f2bcd94002 100644 --- a/glue-codes/python/pyOpenFAST/inflowwind.py +++ b/glue-codes/python/pyOpenFAST/inflowwind.py @@ -35,27 +35,9 @@ import datetime import os +from .interface_abc import OpenFASTInterfaceType -class InflowWindLib(CDLL): - # Human readable error levels from IfW. - error_levels = { - 0: "None", - 1: "Info", - 2: "Warning", - 3: "Severe Error", - 4: "Fatal Error" - } - - # NOTE: the error message length in Fortran is controlled by the - # ErrMsgLen variable in the NWTC_Base.f90 file. If that ever - # changes, it may be necessary to update the corresponding size - # here. - error_msg_c_len = 1025 - - # NOTE: the length of the name used for any output file written by the - # IfW Fortran code is 1025. - default_str_c_len = 1025 - +class InflowWindLib(OpenFASTInterfaceType): def __init__(self, library_path): super().__init__(library_path) self.library_path = library_path @@ -69,7 +51,7 @@ def __init__(self, library_path): # Create buffers for class data self.abort_error_level = 4 self.error_status_c = c_int(0) - self.error_message_c = create_string_buffer(self.error_msg_c_len) + self.error_message_c = create_string_buffer(self.ERROR_MESSAGE_LENGTH) # This buffer for the channel names and units is set arbitrarily large # to start. InflowWind only has a maximum of 9 outputs at present, but diff --git a/glue-codes/python/pyOpenFAST/interface_abc.py b/glue-codes/python/pyOpenFAST/interface_abc.py index 3758deff92..ad69e8b18b 100644 --- a/glue-codes/python/pyOpenFAST/interface_abc.py +++ b/glue-codes/python/pyOpenFAST/interface_abc.py @@ -17,6 +17,17 @@ class OpenFASTInterfaceType(CDLL): + #-------------------------------------- + # Constants + #-------------------------------------- + # NOTE: The length of the error message in Fortran is determined by the + # ErrMsgLen variable in the NWTC_Base.f90 file. If ErrMsgLen is modified, + # the corresponding size here must also be updated to match. + ERROR_MESSAGE_LENGTH: int = 8197 + DEFAULT_STRING_LENGTH: int = 1025 + CHANNEL_NAME_LENGTH: int = 20 + MAX_CHANNELS: int = 8000 + # Human readable error levels error_levels = { 0: "None", @@ -26,6 +37,9 @@ class OpenFASTInterfaceType(CDLL): 4: "Fatal Error" } + # NWTC Library sets the length of file names passed through the interfaces + IntfStrLen = 1025 + # NOTE: the error message length in Fortran is controlled by the # ErrMsgLen variable in the NWTC_Base.f90 file. If that ever # changes, it may be necessary to update the corresponding size @@ -33,10 +47,15 @@ class OpenFASTInterfaceType(CDLL): ERROR_MSG_C_LEN = 8197 # NOTE: the length of the name used for any output file written by the - # HD Fortran code is 1025. + # Fortran code is 1025. default_str_c_len = 1025 + # error handling abort_error_level = c_int(4) + error_status_c = c_int(0) + error_message_c = create_string_buffer(ERROR_MESSAGE_LENGTH) + + def __init__(self, library_path: str): super().__init__(library_path) diff --git a/glue-codes/python/pyOpenFAST/moordyn.py b/glue-codes/python/pyOpenFAST/moordyn.py index 01272a2d40..63136970d5 100644 --- a/glue-codes/python/pyOpenFAST/moordyn.py +++ b/glue-codes/python/pyOpenFAST/moordyn.py @@ -46,11 +46,6 @@ def __init__(self, library_path): super().__init__(library_path) self._initialize_routines() - - # Create buffers for class data - self.error_status_c = c_int(0) - self.error_message_c = create_string_buffer(self.ERROR_MSG_C_LEN) - self.error_message = create_string_buffer(1025) self.ended = False # For error handling at end self._channel_names = create_string_buffer(256*1000) diff --git a/glue-codes/python/pyOpenFAST/seastate.py b/glue-codes/python/pyOpenFAST/seastate.py index d10cb0126f..944fd79cb7 100644 --- a/glue-codes/python/pyOpenFAST/seastate.py +++ b/glue-codes/python/pyOpenFAST/seastate.py @@ -27,13 +27,27 @@ c_float, c_char, c_char_p, - c_bool + c_bool, + c_void_p ) import numpy as np +import numpy.typing as npt from pathlib import Path +import datetime +import os +from dataclasses import dataclass from .interface_abc import OpenFASTInterfaceType +@dataclass +class MotionData: + """ + POD-style container for motion-related data i.e. state of a node. Only + position information for SeaState + """ + position: npt.NDArray[np.float32] + + class SeaStateLib(OpenFASTInterfaceType): """ This is the Python interface to the OpenFAST SeaState module. @@ -46,41 +60,70 @@ class SeaStateLib(OpenFASTInterfaceType): from the last call to calc_output. """ - def __init__(self, library_path: str, input_file_name: str): + def __init__(self, library_path): super().__init__(library_path) - - self.input_file_name = str( Path(input_file_name).absolute() ).encode('utf-8') + self.library_path = library_path self._initialize_routines() - - self.ended = False # For error handling at end + self.ended = False # For error handling at end # Create buffers for class data - # These will generally be overwritten by the Fortran code - self.num_outs_c = c_int(0) - self.output_channel_names = [] - self.output_channel_units = [] - self.output_values = None + self.error_status_c = c_int(0) + self.error_message_c = create_string_buffer(self.ERROR_MSG_C_LEN) + + + # This buffer for the channel names and units is set arbitrarily large + # to start. Channel name and unit lengths are currently hard + # coded to 20 (this must match ChanLen in NWTC_Base.f90). + self._channel_names_c = create_string_buffer(20 * 4000 + 1) + self._channel_units_c = create_string_buffer(20 * 4000 + 1) + + self.numResPts = 0 # Number of wind points we will + # request information from + # non-CalcOutput routines. + + self.WaveTimeShift = 0 # shift wave time (positive only) + + self.numChannels = 0 # Number of channels returned + + # flags + self.debuglevel = 0 # 0-4 levels + + #-------------------------------------- + # VTK settings + #-------------------------------------- + self.vtk_write = 0 # Default -> no vtk output, 0 none, 1 init, 2 animation + self.vtk_dt = 0. # Default -> all + self.vtk_output_dir = "" # Set to specify a directory relative to input files def _initialize_routines(self): - self.SeaSt_C_Init.argtypes = [ - POINTER(c_char_p), # intent(in ) :: InputFile_c(IntfStrLen) - POINTER(c_char_p), # intent(in ) :: OutRootName_c(IntfStrLen) + self.SeaSt_C_PreInit.argtypes = [ POINTER(c_float), # intent(in ) :: Gravity_c POINTER(c_float), # intent(in ) :: WtrDens_c POINTER(c_float), # intent(in ) :: WtrDpth_c POINTER(c_float), # intent(in ) :: MSL2SWL_c - POINTER(c_int), # intent(in ) :: NSteps_c - POINTER(c_float), # intent(in ) :: TimeInterval_c - POINTER(c_int), # intent(in ) :: WaveElevSeriesFlag_c - POINTER(c_int), # intent(in ) :: WrWvKinMod_c + POINTER(c_int), # intent(in ) :: debuglevel + POINTER(c_char), # intent(in ) :: vtk_output_dir_c + POINTER(c_int), # intent(in ) :: vtk_write + POINTER(c_double), # intent(in ) :: vtk_dt + POINTER(c_int), # intent( out) :: ErrStat_C + POINTER(c_char), # intent( out) :: ErrMsg_C(ErrMsgLen_C) + ] + self.SeaSt_C_PreInit.restype = None + + self.SeaSt_C_Init.argtypes = [ + POINTER(c_char), # intent(in ) :: InputFile_c(IntfStrLen) + POINTER(c_char), # intent(in ) :: OutRootName_c(IntfStrLen) + POINTER(c_double), # intent(in ) :: TimeInterval_c + POINTER(c_double), # intent(in ) :: TMax_c + POINTER(c_double), # intent(in ) :: WaveTimeShift (positive only) POINTER(c_int), # intent( out) :: NumChannels_c POINTER(c_char), # intent( out) :: OutputChannelNames_C POINTER(c_char), # intent( out) :: OutputChannelUnits_C POINTER(c_int), # intent( out) :: ErrStat_C POINTER(c_char), # intent( out) :: ErrMsg_C(ErrMsgLen_C) ] - self.SeaSt_C_Init.restype = c_int + self.SeaSt_C_Init.restype = None self.SeaSt_C_CalcOutput.argtypes = [ POINTER(c_double), # intent(in ) :: Time_C @@ -88,54 +131,176 @@ def _initialize_routines(self): POINTER(c_int), # intent( out) :: ErrStat_C POINTER(c_char), # intent( out) :: ErrMsg_C(ErrMsgLen_C) ] - self.SeaSt_C_CalcOutput.restype = c_int + self.SeaSt_C_CalcOutput.restype = None self.SeaSt_C_End.argtypes = [ POINTER(c_int), # intent( out) :: ErrStat_C POINTER(c_char) # intent( out) :: ErrMsg_C(ErrMsgLen_C) ] - self.SeaSt_C_End.restype = c_int + self.SeaSt_C_End.restype = None + + self.SeaSt_C_GetWaveFieldPointer.argtypes = [ + POINTER(c_void_p), # intent( out) :: pointer to the WaveField data + POINTER(c_int), # intent( out) :: ErrStat_C + POINTER(c_char), # intent( out) :: ErrMsg_C(ErrMsgLen_C) + ] + self.SeaSt_C_GetWaveFieldPointer.restype = None + + self.SeaSt_C_SetWaveFieldPointer.argtypes = [ + POINTER(c_void_p), # intent(in ) :: pointer to the WaveField data + POINTER(c_int), # intent( out) :: ErrStat_C + POINTER(c_char), # intent( out) :: ErrMsg_C(ErrMsgLen_C) + ] + self.SeaSt_C_SetWaveFieldPointer.restype = None + + + self.SeaSt_C_GetFluidVelAcc.argtypes = [ + POINTER(c_double), # intent(in ) :: Time_C + POINTER(c_float), # intent(in ) :: Pos_c(3) + POINTER(c_float), # intent( out) :: Vel_c(3) + POINTER(c_float), # intent( out) :: Acc_c(3) + POINTER(c_int), # intent( out) :: NodeInWater_C + POINTER(c_int), # intent( out) :: ErrStat_C + POINTER(c_char) # intent( out) :: ErrMsg_C(ErrMsgLen_C) + + ] + self.SeaSt_C_GetFluidVelAcc.restype = None + + self.SeaSt_C_GetSurfElev.argtypes = [ + POINTER(c_double), # intent(in ) :: Time_C + POINTER(c_float), # intent(in ) :: Pos_c(3) + POINTER(c_float), # intent( out) :: Elev_C + POINTER(c_int), # intent( out) :: ErrStat_C + POINTER(c_char) # intent( out) :: ErrMsg_C(ErrMsgLen_C) + ] + self.SeaSt_C_GetSurfElev.restype = None + + self.SeaSt_C_GetSurfNorm.argtypes = [ + POINTER(c_double), # intent(in ) :: Time_C + POINTER(c_float), # intent(in ) :: Pos_c(3) + POINTER(c_float), # intent( out) :: norm(3) + POINTER(c_int), # intent( out) :: ErrStat_C + POINTER(c_char) # intent( out) :: ErrMsg_C(ErrMsgLen_C) + ] + self.SeaSt_C_GetSurfNorm.restype = None + + self.SeaSt_C_GetElevMinMaxEstimate.argtypes = [ + POINTER(c_float), # intent( out) :: elevMin_c + POINTER(c_float), # intent( out) :: elevMax_c + POINTER(c_int), # intent( out) :: ErrStat_C + POINTER(c_char) # intent( out) :: ErrMsg_C(ErrMsgLen_C) + ] + self.SeaSt_C_GetElevMinMaxEstimate.restype = None + + + + def check_error(self) -> None: + """Checks for and handles any errors from the Fortran library. - def init( + Raises: + RuntimeError: If a fatal error occurs in the Fortran code + """ + # If the error status is 0, return + if self.error_status_c.value == 0: + return + + # Get the error level and error message + error_level = self.error_levels.get( + self.error_status_c.value, + f"Unknown Error Level: {self.error_status_c.value}" + ) + error_msg = self.error_message_c.raw.decode('utf-8').strip() + message = f"WaveTank library {error_level}: {error_msg}" + # If the error level is fatal, call WaveTank_End() and raise an error + if self.error_status_c.value >= self.abort_error_level.value: + try: + self.SeaSt_C_End( + byref(self.error_status_c), # OUT <- error status code + self.error_message_c # OUT <- error message buffer + ) + if self.error_status_c.value == 4: + error_msg = self.error_message_c.raw.decode('utf-8').strip() + print(f'WaveTank_End error: {error_msg}') + except Exception as e: + message += f"\nAdditional error during cleanup: {e}" + raise RuntimeError(message) + else: + print(message) + + + #FIXME: store these elsewhere + def seastate_preinit( self, gravity: float = 9.80665, water_density: float = 1025, water_depth: float = 200, msl2swl: float = 0, + ): + """Set environment variables and general setup + + Args: + + Raises: + ValueError: If values are outside reasonable bounds + RuntimeError: If preinit fails + """ + vtk_output_dir_c = create_string_buffer( + self.vtk_output_dir.ljust(self.default_str_c_len).encode('utf-8') + ) + self.SeaSt_C_PreInit( + byref(c_float(gravity)), + byref(c_float(water_density)), + byref(c_float(water_depth)), + byref(c_float(msl2swl)), + byref(c_int(self.debug_level)), # IN -> debug level (0=None to 4=all meshes) + vtk_output_dir_c, # IN -> directory for vtk output files + byref(c_int(self.vtk_write)), # IN -> write VTK flag + byref(c_double(self.vtk_dt)), # IN -> VTK output time step + byref(self.error_status_c), # OUT <- error status code + self.error_message_c # OUT <- error message buffer + ) + self.check_error() + + + def seastate_init( + self, + primary_ss_file, outrootname: str = "./seastate.SeaSt", - wave_kinematics_mode: int = 0, - n_steps: int = 801, + time_max: float = 60, time_interval: float = 0.125, - wave_elevation_series_flag: int = 0, ): - _error_status = c_int(0) - _error_message = create_string_buffer(self.ERROR_MSG_C_LEN) + + ss_file_c = create_string_buffer( + primary_ss_file.ljust(self.default_str_c_len).encode('utf-8') + ) + outrootname_c = create_string_buffer( + outrootname.ljust(self.default_str_c_len).encode('utf-8') + ) # This buffer for the channel names and units is set arbitrarily large # to start. Channel name and unit lengths are currently hard # coded to 20 (this must match ChanLen in NWTC_Base.f90). _channel_names = create_string_buffer(20 * 4000 + 1) _channel_units = create_string_buffer(20 * 4000 + 1) + self._numChannels = c_int(0) + self.SeaSt_C_Init( - c_char_p(self.input_file_name), - c_char_p(outrootname.encode('utf-8')), - byref(c_float(gravity)), - byref(c_float(water_density)), - byref(c_float(water_depth)), - byref(c_float(msl2swl)), - byref(c_int(n_steps)), - byref(c_float(time_interval)), - byref(c_int(wave_elevation_series_flag)), - byref(c_int(wave_kinematics_mode)), - byref(self.num_outs_c), + ss_file_c, + outrootname_c, + byref(c_double(time_interval)), + byref(c_double(time_max)), + byref(c_double(self.WaveTimeShift)), + byref(self._numChannels), _channel_names, _channel_units, - byref(_error_status), - _error_message, + byref(self.error_status_c), # OUT <- error status code + self.error_message_c # OUT <- error message buffer ) - if self.fatal_error(_error_status): - raise RuntimeError(f"Error {_error_status.value}: {_error_message.value}") + self.check_error() + + # Initialize output channels + self.numChannels = self._numChannels.value # if len(_channel_names.value.split()) == 0: # self.output_channel_names = [] @@ -150,38 +315,281 @@ def init( self.output_channel_units = [n.decode('UTF-8') for n in _channel_units.value.split()] # Allocate the data for the outputs - self.output_values = np.zeros( self.num_outs_c.value, dtype=c_float, order='C' ) + self.output_values = np.zeros( self._numChannels.value, dtype=c_float, order='C' ) + + + def seastate_calcOutput(self, time: float, output_channel_values: npt.NDArray[np.float32]) -> None: + """Calculate output values at the given time. + + Args: + time: Current simulation time + output_channel_values: Array to store calculated output values + + Raises: + ValueError: If output_channel_values array has wrong size + RuntimeError: If calculation fails + """ + if output_channel_values.size != self.numChannels: + raise ValueError( + f"Output array must have size {self.numChannels}, " + f"got {output_channel_values.size}" + ) - def calc_output(self, t): - _error_status = c_int(0) - _error_message = create_string_buffer(self.ERROR_MSG_C_LEN) + output_channel_values_c = (c_float * self.numChannels)(0.) self.SeaSt_C_CalcOutput( - byref(c_double(t)), # IN: time + byref(c_double(time)), # IN -> current simulation time self.output_values.ctypes.data_as(POINTER(c_float)), # OUT: output channel values - byref(_error_status), # OUT: ErrStat_C - _error_message # OUT: ErrMsg_C + byref(self.error_status_c), # OUT <- error status + self.error_message_c # OUT <- error message ) + self.check_error() - if self.fatal_error(_error_status): - self.end() - raise RuntimeError(f"Error {_error_status.value}: {_error_message.value}") + # Copy results back to numpy array + output_channel_values[:] = np.reshape(self.output_values, (self.numChannels)) - def end(self): - _error_status = c_int(0) - _error_message = create_string_buffer(self.ERROR_MSG_C_LEN) + def seastate_end(self): if not self.ended: self.ended = True self.SeaSt_C_End( - byref(_error_status), - _error_message, + byref(self.error_status_c), # OUT <- error status code + self.error_message_c # OUT <- error message buffer ) - if self.fatal_error(_error_status): - raise RuntimeError(f"Error {_error_status.value}: {_error_message.value}") + self.check_error() + + + def seastate_getWaveFieldPointer(self,ss_pointer: c_void_p) -> None: + self.SeaSt_C_GetWaveFieldPointer( + byref(ss_pointer), # IN -> pointer to the WaveField data + byref(self.error_status_c), # OUT <- error status code + self.error_message_c # OUT <- error message buffer + ) + self.check_error() + + def seastate_setWaveFieldPointer(self,ss_pointer: c_void_p) -> None: + self.SeaSt_C_SetWaveFieldPointer( + byref(ss_pointer), # IN -> pointer to the WaveField data + byref(self.error_status_c), # OUT <- error status code + self.error_message_c # OUT <- error message buffer + ) + self.check_error() + + + def get_fluidVelAcc(self, + time: float, + position: npt.NDArray[np.float32], + vel: npt.NDArray[np.float32], + acc: npt.NDArray[np.float32], + nodeInWater: int, + ) -> None: + """ + Get fluid velocity, acceleration, and if node is in water values at the given time. + Args: + time: Current simulation time + position: position in 3D to get info from + vel: velocity at position + acc: acceleration at position + nodeInWater: 1 if position is in the water, 0 if not. Note that + this is relative to SWL unless stretching is used + Raises: + RuntimeError: If calculation fails + """ + # I don't know why I have to convert the position, but I get garbage + # across the inteface if I don't (IANAPP: I am not a python programmer) + pos = np.zeros( 3, dtype=c_float ) + pos[0] = position[0] + pos[1] = position[1] + pos[2] = position[2] + vel = np.zeros( 3, dtype=c_float ) + acc = np.zeros( 3, dtype=c_float ) + nodeInWater_c = c_int(0) + self.SeaSt_C_GetFluidVelAcc( + byref(c_double(time)), # IN -> current simulation time + pos.ctypes.data_as(POINTER(c_float)), # IN -> position (3 vector) + vel.ctypes.data_as(POINTER(c_float)), # OUT <- velocity (3 vector) + acc.ctypes.data_as(POINTER(c_float)), # OUT <- acceleration (3 vector) + nodeInWater_c, # OUT <- node is in water (0=false, 1=true) + byref(self.error_status_c), # OUT <- error status + self.error_message_c # OUT <- error message + ) + self.check_error() + nodeInWater = nodeInWater_c.value + return vel,acc,nodeInWater + + + def get_surfElev(self, + time: float, + position: npt.NDArray[np.float32], + elev: float, + ) -> None: + """ + Get the surface elevation at an X,Y point. + Args: + time: Current simulation time + position: position in 2D to get info from (3D could be passed in) + elev: elevation in meters + Raises: + RuntimeError: If calculation fails + """ + # I don't know why I have to convert the position, but I get garbage + # across the inteface if I don't (IANAPP: I am not a python programmer) + pos = np.array(position).astype(c_float)[:3] + elev_c = c_float(0.0) + self.SeaSt_C_GetSurfElev( + byref(c_double(time)), # IN -> current simulation time + pos.ctypes.data_as(POINTER(c_float)), # IN -> position (3 vector) + elev_c, # OUT <- total wave elevation + byref(self.error_status_c), # OUT <- error status + self.error_message_c # OUT <- error message + ) + self.check_error() + elev = elev_c.value + return elev + + + def get_surfNorm(self, + time: float, + position: npt.NDArray[np.float32], + norm: npt.NDArray[np.float32], + ) -> None: + """ + Get the normal to the surface at an X,Y point. + Args: + time: Current simulation time + position: position in 2D to get info from (3D could be passed in) + norm: normal vector + Raises: + RuntimeError: If calculation fails + """ + # I don't know why I have to convert the position, but I get garbage + # across the inteface if I don't (IANAPP: I am not a python programmer) + pos = np.zeros( 2, dtype=c_float ) + pos[0] = position[0] + pos[1] = position[1] + norm = np.zeros( 3, dtype=c_float ) + self.SeaSt_C_GetSurfNorm( + byref(c_double(time)), # IN -> current simulation time + pos.ctypes.data_as(POINTER(c_float)), # IN -> position (3 vector) + norm.ctypes.data_as(POINTER(c_float)), # OUT <- normal vector to surface + byref(self.error_status_c), # OUT <- error status + self.error_message_c # OUT <- error message + ) + self.check_error() + return norm + + def get_elevMinMax(self) -> tuple[float, float]: + """ + Get estimate of the min and max total wave elevation. Will over + estimate range when 2nd order waves used + + Returns: + tuple[float, float]: A tuple containing (elevMin, elevMax) where: + - elevMin: minimum elevation estimate in meters + - elevMax: maximum elevation estimate in meters + + Raises: + RuntimeError: If calculation fails + """ + elevMin_c = c_float(0.0) + elevMax_c = c_float(0.0) + print("Calling SeaSt_C_GetElevMinMaxEstimate") + self.SeaSt_C_GetElevMinMaxEstimate( + elevMin_c, # out <- min elev + elevMax_c, # out <- max elev + byref(self.error_status_c), # OUT <- error status + self.error_message_c # OUT <- error message + ) + self.check_error() + elevMin = elevMin_c.value + elevMax = elevMax_c.value + return elevMin,elevMax + @property def num_outs(self): - return self.num_outs_c.value \ No newline at end of file + return self._numChannels.value + + +#=============================================================================== +# Helper classes for writing output channels to file. +# For the regression testing to mirror the output from the InfowWind Fortran +# driver. This may also have value for debugging the interfacing to SS. + +class ResultsOut(): + """ + This is only for testing purposes. Since we are not returning the + velocities to anything, we will write them to file as we go for + comparison in the regression test. When coupled to another code, the + velocities array would be passed back to the calling code for use in + the aerodynamic solver. + """ + def __init__(self, filename, NumResPts): + + self.results_file = open(filename, 'w') # open output file and write header info + + # write file header + t_string=datetime.datetime.now() + dt_string=datetime.date.today() + self.results_file.write(f"## This file was generated by SeaState called from Python on {dt_string.strftime('%b-%d-%Y')} at {t_string.strftime('%H:%M:%S')}{os.linesep}") + self.results_file.write(f"## This file contains outputs from calls to SeaState routines (not the CalcOutput) at the {NumResPts} points specified in the file {filename}{os.linesep}") + self.results_file.write(f"# {os.linesep}") + self.results_file.write(f"# {os.linesep}") + self.results_file.write(f"# {os.linesep}") + self.results_file.write(f"# {os.linesep}") + self.results_file.write(f" T x y z V_x V_y V_z A_x A_Y A_Z nodeInWater elev norm_x norm_y norm_z{os.linesep}") + self.results_file.write(f" (s) (m) (m) (m) (m/s) (m/s) (m/s) (m/s) (m/s) (m/s) (-) (m) (m/s) (m/s) (m/s) {os.linesep}") + self.opened = True + + def write(self,t,p,v,a,nodeInWater,elev,n): + self.results_file.write(' %11.3f %11.3f %11.3f %11.3f %11.3f %11.3f %11.3f %11.3f %11.3f %11.3f %11d %11.3f %11.3f %11.3f %11.3f\n' % (t,p[0],p[1],p[2],v[0],v[1],v[2],a[0],a[1],a[2],nodeInWater,elev,n[0],n[1],n[2])) + + def end(self): + if self.opened: + self.results_file.close() + self.opened = False + + + + +class WriteOutChans(): + """ + This is only for testing purposes. Since we are not returning the + output channels to anything, we will write them to file. When coupled to + another code, this data would be passed back for inclusion the any output + file there. + """ + def __init__(self,filename,chan_names,chan_units): + chan_names.insert(0,'Time') # add time index header + chan_units.insert(0,'(s)') # add time index unit + self.OutFile=open(filename,'wt') # open output file and write header info + # write file header + t_string=datetime.datetime.now() + dt_string=datetime.date.today() + self.OutFile.write(f"## This file was generated by SeaState c-bindings library on {dt_string.strftime('%b-%d-%Y')} at {t_string.strftime('%H:%M:%S')}\n") + self.OutFile.write(f"## This file contains output channels requested from the OutList section of the input file") + self.OutFile.write(f"{filename}\n") + self.OutFile.write("#\n") + self.OutFile.write("#\n") + self.OutFile.write("#\n") + self.OutFile.write("#\n") + l = len(chan_names) + f_string = "{:^15s}"+" {:^20s} "*(l-1) + self.OutFile.write(f_string.format(*chan_names) + '\n') + self.OutFile.write(f_string.format(*chan_units) + '\n') + self.opened = True + + def write(self,chan_data): + l = chan_data.shape[1] + f_string = "{:10.4f}"+"{:25.7f}"*(l-1) + for i in range(0,chan_data.shape[0]): + self.OutFile.write(f_string.format(*chan_data[i,:]) + '\n') + #if i==0: + # print(f"{chan_data[i,:]}") + + def end(self): + if self.opened: + self.OutFile.close() + self.opened = False diff --git a/glue-codes/python/pyOpenFAST/tdmslib.py b/glue-codes/python/pyOpenFAST/tdmslib.py new file mode 100644 index 0000000000..b7f6d6a063 --- /dev/null +++ b/glue-codes/python/pyOpenFAST/tdmslib.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +""" +Created on Wed Jun 5 11:26:52 2024 + +@author: schamot + +Description: + Code takes a .tdms file path as input and outputs the data as a dictionary. + + Parameters + ---------- + path : str + path to the .tdms file. + + Returns + ------- + pyDict : dict + Python dictionary of the tdms file. + +""" + +import nptdms + +def main(): + fileName = "Oscilloscope Data/UF HASEL tests/UF2down_3kVcycleForce.tdms" + output = TdmsToDict(fileName) + print(output) + +def TdmsToDict(path): + ''' + Code takes a .tdms file path as input and outputs the data as a dictionary. + + Parameters + ---------- + path : str + path to the .tdms file. + + Returns + ------- + pyDict : dict + Python dictionary of the tdms file. + + ''' + # Get the files requested + tdmsFile = path + + # open/read the tdms file + tdms_file = nptdms.TdmsFile.read(tdmsFile) + + # Set up python dictionary + pyDict = {} + # Get group names + for group in tdms_file.groups(): + #print(f'''Group: {group.name}''') + # Create group dictionary key + pyDict[group.name] = {} + # Get channel names per group + for channel in group.channels(): + #print(f'''\tChannel: {channel.name}''') + pyDict[group.name][channel.name] = channel[:] + # if the data has properties add them + if channel.properties != {}: + pyDict[group.name][channel.name + "_properties"] = channel.properties + + return pyDict + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/glue-codes/python/pyOpenFAST/wavetanktesting.py b/glue-codes/python/pyOpenFAST/wavetanktesting.py new file mode 100644 index 0000000000..625d25c732 --- /dev/null +++ b/glue-codes/python/pyOpenFAST/wavetanktesting.py @@ -0,0 +1,445 @@ +# 2025.12.23 +# This is a work in progress. It is used for testing of the +# wavetanktestinglib that can be coupled to labview. It is not complete at +# this point +from ctypes import ( + CDLL, + POINTER, + create_string_buffer, + byref, + c_byte, + c_int, + c_double, + c_float, + c_char, + c_char_p, + c_bool +) + +import numpy as np +import numpy.typing as npt +from dataclasses import dataclass +from datetime import datetime +from pathlib import Path +from typing import Any,Dict, List, Optional, Tuple, Union + +from pyOpenFAST.interface_abc import OpenFASTInterfaceType + +def to_c_array(array: npt.NDArray, c_type: Any = c_float) -> Any: + """Converts numpy array to C array of specified type. + + Args: + array: Input numpy array + c_type: C type to convert to (default: c_float) + + Returns: + C-compatible array of the specified type + """ + try: + if isinstance(array, np.ndarray): + flat_array = array.flatten() + return (c_type * len(flat_array))(*flat_array) + # If list/tuple, convert directly to C array + return (c_type * len(array))(*array) + except Exception as e: + raise TypeError(f"Failed to convert to C array: {e}") + +def to_c_string(input_array: List[str]) -> Tuple[bytes, int]: + """Converts input string array into a null-separated byte string for use in C. + + Args: + input_array: List of strings to join with null characters + + Returns: + Tuple containing: + - The encoded byte string + - Length of the encoded string + """ + encoded_string = '\x00'.join(input_array).encode('utf-8') + return encoded_string, len(encoded_string) + + +@dataclass +class MotionData: + """POD-style container for motion-related data i.e. state of a node.""" + pos: npt.NDArray[np.float32] # [x,y,z,roll,pitch,yaw] + vel: npt.NDArray[np.float32] # [x_dot,y_dot,z_dot,roll_dot,pitch_dot,yaw_dot] + acc: npt.NDArray[np.float32] # [x_ddot,y_ddot,z_ddot,roll_ddot,pitch_ddot,yaw_ddot] + +@dataclass +class LoadsData: + """POD-style container for motion-related data i.e. state of a node.""" + loads: npt.NDArray[np.float32] # [Fx,Fy,Fz,Mx,My,Mz] + +#------------------------------------------------------------------------------- +# Generate a debug file +#------------------------------------------------------------------------------- +class DriverDbg: + """ + A helper class for debugging the wavetankinterface. This class writes out all the + input positions/orientations, velocities, accelerations, and the resulting + forces and moments at the platform mesh point. If functioning correctly, this + will be identical to the corresponding values in the wavetank output + channels. + + NOTE: This may not output everything in the interface as updates have been made + since writing this, but this routine was not updated accordingly. + """ + + def __init__(self, filename: str) -> None: + """Initializes the debugging class and open the output file.""" + self.filename = filename + self.opened = True + + with open(filename, 'wt') as self.debug_file: + self._write_header() + + self.debug_file = open(filename, 'at') # switch to append mode + + def _write_header(self) -> None: + """Writes the header information to the debug file.""" + # Build header components + timestamp = datetime.now().strftime('%b-%d-%Y %H:%M:%S') + header_lines = [ + f"## This file was generated by wavetank_c_lib on {timestamp}", + f"## This file contains the resulting forces/moments at the referenc mesh point passed into the adi_c_lib", + "#", + "#", + "#", + "#" + ] + + # Write column headers + column_names = ["Time"] + column_units = ["(s)"] + # Position columns + for suffix in ["x", "y", "z"]: + column_names.append(f"{suffix}") + column_units.append("(m)") + # orientation columns + for suffix in ["phi", "theta", "psi"]: + column_names.append(f"{suffix}") + column_units.append("(rad)") + # Velocity columns + for suffix in ["Vx", "Vy", "Vz"]: + column_names.append(f"{suffix}") + column_units.append("(m/s)") + # Angular velocity columns + for suffix in ["RVx", "RVy", "RVz"]: + column_names.append(f"{suffix}") + column_units.append("(rad/s)") + # Acceleration columns + for suffix in ["Ax", "Ay", "Az"]: + column_names.append(f"{suffix}") + column_units.append("(m/s^2)") + # Angular acceleration columns + for suffix in ["RAx", "RAy", "RAz"]: + column_names.append(f"{suffix}") + column_units.append("(rad/s^2)") + # Force columns + for suffix in ["Fx", "Fy", "Fz"]: + column_names.append(f"{suffix}") + column_units.append("(N)") + # Moment columns + for suffix in ["Mx", "My", "Mz"]: + column_names.append(f"{suffix}") + column_units.append("(N-m)") + + f_string = "{:^25s}" + header_lines.append("".join([f_string.format(name) for name in column_names])) + header_lines.append("".join([f_string.format(unit) for name, unit in zip(column_names, column_units)])) + + self.debug_file.write("\n".join(header_lines) + "\n") + + def write( + self, + t: float, + body_motion: MotionData, + body_loads: LoadsData, + ) -> None: + """Writes the current state to the debug file.""" + row_data = [f"{t:10.4f}"] + + row_data.extend([f"{val:25.7e}" for val in body_motion.pos[:]]) + row_data.extend([f"{val:25.7e}" for val in body_motion.vel[:]]) + row_data.extend([f"{val:25.7e}" for val in body_motion.acc[:]]) + row_data.extend([f"{val:25.7e}" for val in body_loads.loads[:]]) + + self.debug_file.write("".join(row_data) + "\n") + self.debug_file.flush() + + def end(self) -> None: + """Closes the debug file.""" + if self.opened: + self.debug_file.close() + self.opened = False + + + +class WaveTankLib(OpenFASTInterfaceType): + + #-------------------------------------- + # Error levels + #-------------------------------------- + error_levels: Dict[int, str] = { + 0: "None", + 1: "Info", + 2: "Warning", + 3: "Severe Error", + 4: "Fatal Error" + } + + # Debug output file: When coupled into another code, an array of position/orientation, + # velocities, and accelerations are passed in, and an array of Forces + Moments is + # returned. For debugging, it may be useful to dump all off this to a file. + debug_output_file: str = "DbgOutputs.out" + debug_outputs: int = 1 # For checking the interface, set this to 1 + + #-------------------------------------- + # Constants + #-------------------------------------- + # NOTE: The length of the error message in Fortran is determined by the + # ErrMsgLen variable in the NWTC_Base.f90 file. If ErrMsgLen is modified, + # the corresponding size here must also be updated to match. + ERROR_MESSAGE_LENGTH: int = 8197 + DEFAULT_STRING_LENGTH: int = 1025 + + def __init__(self, library_path: str): + """ + + Args: + library_path (str): Path to the compile wavetank interface shared library + input_file_names (dict): Map of file names for each included module: + - WT_InputFile + """ + super().__init__(library_path) + + self._initialize_routines() + + self.ended = False # For error handling at end + self.print_error_level = 1 + + + # Error handling setup + self.abort_error_level = 4 + self.error_status_c = c_int(0) + self.error_message_c = create_string_buffer(self.ERROR_MESSAGE_LENGTH) + + # returned values + self.rootname_c = create_string_buffer(self.IntfStrLen) + self.vtkdir_c = create_string_buffer(self.IntfStrLen) + self.buoyWaveElev_c = c_float(0) # wave elevation at buoy + + def _initialize_routines(self): + self.WaveTank_Init.argtypes = [ + POINTER(c_char), # intent(in ) :: WT_InputFile_c(IntfStrLen) + POINTER(c_char), # intent( out) :: RootName_C(IntfStrLen) + POINTER(c_char), # intent( out) :: VTKdir_C(IntfStrLen) + POINTER(c_int), # intent( out) :: ErrStat_C + POINTER(c_char), # intent( out) :: ErrMsg_C(ErrMsgLen_C) + ] + self.WaveTank_Init.restype = None + + self.WaveTank_CalcStep.argtypes = [ + POINTER(c_double), # real(c_double) :: time + POINTER(c_float), # intent(in ) :: pos(6) + POINTER(c_float), # intent(in ) :: vel(6) + POINTER(c_float), # intent(in ) :: acc(6) + POINTER(c_float), # intent( out) :: FrcMom(6) + POINTER(c_float), # intent( out) :: buoyWaveElev + POINTER(c_int), # integer(c_int), intent( out) :: ErrStat_C + POINTER(c_char), # character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + ] + self.WaveTank_CalcStep.restype = None + + self.WaveTank_End.argtypes = [ + POINTER(c_int), # integer(c_int), intent( out) :: ErrStat_C + POINTER(c_char), # character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + ] + self.WaveTank_End.restype = c_int + + self.WaveTank_SetWaveFieldPointer.argtypes = [ + POINTER(c_int), # integer(c_int), intent( out) :: ErrStat_C + POINTER(c_char), # character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + ] + self.WaveTank_SetWaveFieldPointer.restype = None + + + def check_error(self) -> None: + """Checks for and handles any errors from the Fortran library. + + Raises: + RuntimeError: If a fatal error occurs in the Fortran code + """ + # If the error status is 0, return + if self.error_status_c.value == 0: + return + + # Get the error level and error message + error_level = self.error_levels.get( + self.error_status_c.value, + f"Unknown Error Level: {self.error_status_c.value}" + ) + error_msg = self.error_message_c.raw.decode('utf-8').strip() + message = f"WaveTank library {error_level}: {error_msg}" + # If the error level is fatal, call WaveTank_End() and raise an error + if self.error_status_c.value >= self.abort_error_level: + try: + self.WaveTank_End( + byref(self.error_status_c), # OUT <- error status code + self.error_message_c # OUT <- error message buffer + ) + if self.error_status_c.value == 4: + error_msg = self.error_message_c.raw.decode('utf-8').strip() + print(f'WaveTank_End error: {error_msg}') + except Exception as e: + message += f"\nAdditional error during cleanup: {e}" + raise RuntimeError(message) + else: + print(message) + + + def _validate_loads_data( + self, + loads: LoadsData, + name: str, + ) -> None: + """Validates motion data dimensions. + + Args: + motion: Motion data to validate + name: Name of the component for error messages + + Raises: + ValueError: If dimensions are incorrect + """ + expected_shape = 6 + + if loads.loads.shape[0] != expected_shape: + raise ValueError( + f"{name} loads must have shape {expected_shape}, " + f"got {loads.loads.shape}" + ) + + + def _validate_motion_data( + self, + motion: MotionData, + name: str, + ) -> None: + """Validates motion data dimensions. + + Args: + motion: Motion data to validate + name: Name of the component for error messages + + Raises: + ValueError: If dimensions are incorrect + """ + expected_shape = 6 + + if motion.pos.shape[0] != expected_shape: + raise ValueError( + f"{name} position must have shape {expected_shape}, " + f"got {motion.pos.shape}" + ) + + if motion.vel.shape[0] != expected_shape: + raise ValueError( + f"{name} velocity must have shape {expected_shape}, " + f"got {motion.vel.shape}" + ) + + if motion.acc.shape[0] != expected_shape: + raise ValueError( + f"{name} acceleration must have shape {expected_shape}, " + f"got {motion.acc.shape}" + ) + + def _prepare_motion_arrays( + self, + body: MotionData, + ) -> Dict[str, Any]: + """Prepares C-compatible arrays for motion data. + + Args: + body: body motion data + + Returns: + Dictionary containing all prepared C arrays + """ + return { + # body data + 'body_pos_c': to_c_array(body.pos, c_float), + 'body_vel_c': to_c_array(body.vel, c_float), + 'body_acc_c': to_c_array(body.acc, c_float), + } + + + + def init(self, input_file_names: dict): + _error_message = create_string_buffer(self.ERROR_MSG_C_LEN) + + # Create C-compatible string buffers for input file names + self.input_file_names = { + k: create_string_buffer(str(Path(v).absolute()).encode('utf-8'), self.IntfStrLen) + for k,v in input_file_names.items() + } + + # # Convert the initial positions array into c_float array + # init_positions_c = (c_float * 6)(0.0, ) + # for i, p in enumerate(platform_init_pos): + # init_positions_c[i] = c_float(p) + + self.WaveTank_Init( + self.input_file_names["WaveTankConfig"], + self.rootname_c, # OUT <- rootname of output files + self.vtkdir_c, # OUT <- directory for vtk output + byref(self.error_status_c), # OUT <- error status code + self.error_message_c # OUT <- error message buffer + ) + self.check_error() + # tmp = self.rootname_c.raw.decode('utf-8').strip() + # print(f'RootName_c: {tmp}') + # tmp = self.vtkdir_c.raw.decode('utf-8').strip() + # print(f'VTKdir_c: {tmp}') + + def calc_step( + self, + time: float, + body_motion: MotionData, + body_loads: LoadsData, + ): + self._validate_motion_data(body_motion, "body") + self._validate_loads_data(body_loads, "body") + + # loads storage + #tmp_loads_c=np.array([0, 0, 0, 0, 0, 0], dtype=c_float) + tmp_loads_c = (c_float * (6))(0.) + + # Convert data to C arrays + motion_arrays = self._prepare_motion_arrays(body_motion) + + self.WaveTank_CalcStep( + byref(c_double(time)), + motion_arrays['body_pos_c'], # IN -> body pos [x,y,z,roll,pitch,yaw] + motion_arrays['body_vel_c'], # IN -> body vel [TVx, TVy, TVz, RVx, RVy, RVz] + motion_arrays['body_acc_c'], # IN -> body acc [TAx, TAy, TAz, RAx, RAy, RAz] + tmp_loads_c, # OUT <- body forces and moments [Fx,Fy,Fz,Mx,My,Mz] + byref(self.buoyWaveElev_c), # OUT <- buoy wave elevation + byref(self.error_status_c), # OUT <- error status code + self.error_message_c # OUT <- error message buffer + ) + self.check_error() + + # Copy over loads + body_loads.loads = np.ctypeslib.as_array(tmp_loads_c).astype(np.float32).copy() + #print(f"body_loads.loads {body_loads.loads}") + + + def end(self) -> None: + self.WaveTank_End( + byref(self.error_status_c), # OUT <- error status code + self.error_message_c # OUT <- error message buffer + ) + self.check_error() diff --git a/glue-codes/python/pyproject.toml b/glue-codes/python/pyproject.toml index 133d56967b..5d43e8b265 100644 --- a/glue-codes/python/pyproject.toml +++ b/glue-codes/python/pyproject.toml @@ -10,6 +10,7 @@ readme = "README.md" requires-python = ">=3.9" authors = [ { name = "Rafael Mudafort", email = "Rafael.Mudafort@nrel.gov" }, + { name = "Andy Platt", email = "Andy.Platt@nrel.gov" }, ] license = { file = "LICENSE.txt" } keywords = ["openfast"] @@ -22,20 +23,21 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy" ] dependencies = [ - "numpy~=2.0", - "matplotlib~=3.0", + "numpy>=1.26", + "matplotlib>=3.0", ] [project.optional-dependencies] windio = [ - "windio~=1.0", + "windio", ] develop = [ - "pytest~=8.0", + "pytest>=8.0", ] [tool.setuptools.packages.find] diff --git a/glue-codes/simulink/CMakeLists.txt b/glue-codes/simulink/CMakeLists.txt index a19e992659..1d358ad812 100644 --- a/glue-codes/simulink/CMakeLists.txt +++ b/glue-codes/simulink/CMakeLists.txt @@ -19,6 +19,7 @@ set(MEX_LIBS $ $ + $ $ $ $ diff --git a/modules/aerodyn/CMakeLists.txt b/modules/aerodyn/CMakeLists.txt index cf4eecaa12..19c407f8b0 100644 --- a/modules/aerodyn/CMakeLists.txt +++ b/modules/aerodyn/CMakeLists.txt @@ -39,6 +39,15 @@ add_library(basicaerolib STATIC ) target_link_libraries(basicaerolib ifwlib nwtclibs) +# AeroAcoustics library +add_library(aeroacousticslib STATIC + src/AeroAcoustics_TNO.f90 + src/AeroAcoustics.f90 + src/AeroAcoustics_IO.f90 + src/AeroAcoustics_Types.f90 +) +target_link_libraries(aeroacousticslib basicaerolib nwtclibs) + # AeroDyn Library add_library(aerodynlib STATIC src/AeroDyn.f90 @@ -53,12 +62,6 @@ add_library(aerodynlib STATIC src/BEMT_Types.f90 src/DBEMT_Types.f90 - # AeroAcoustics - Main - src/AeroAcoustics_TNO.f90 - src/AeroAcoustics.f90 - src/AeroAcoustics_IO.f90 - src/AeroAcoustics_Types.f90 - # FVW lib src/FVW.f90 src/FVW_IO.f90 @@ -69,7 +72,7 @@ add_library(aerodynlib STATIC src/FVW_Tests.f90 src/FVW_Types.f90 ) -target_link_libraries(aerodynlib basicaerolib nwtclibs seastlib) +target_link_libraries(aerodynlib basicaerolib aeroacousticslib seastlib nwtclibs) # ADI lib add_library(adilib STATIC @@ -91,6 +94,13 @@ add_executable(aerodyn_driver ) target_link_libraries(aerodyn_driver aerodyn_driver_subs) +# AeroAcoustics driver +add_executable(aeroacoustics_driver + src/AeroAcoustics_Driver_Subs.f90 + src/AeroAcoustics_Driver.f90 +) +target_link_libraries(aeroacoustics_driver aeroacousticslib versioninfolib) + # UnsteadyAero Driver add_executable(unsteadyaero_driver src/UnsteadyAero_Driver.f90 @@ -98,17 +108,36 @@ add_executable(unsteadyaero_driver ) target_link_libraries(unsteadyaero_driver basicaerolib lindynlib versioninfolib) + # AeroDyn-InflowWind c-bindings interface library -add_library(aerodyn_inflow_c_binding SHARED +# create object instead of directly linking into shared and static -- causes issues in parallel builds +# This is only required because we are static linking the library for wavetank +# NOTE: target linking at the object, static, and shared libraries. Different CMake versions handle this +# slightly differently with unpredictable results if I don't. +add_library(aerodyn_inflow_c_binding_object OBJECT src/AeroDyn_Inflow_C_Binding_Types.f90 src/AeroDyn_Inflow_C_Binding.f90 ) -target_link_libraries(aerodyn_inflow_c_binding aerodyn_driver_subs versioninfolib) +target_link_libraries(aerodyn_inflow_c_binding_object adilib aerodyn_driver_subs nwtclibs versioninfolib) +set_property(TARGET aerodyn_inflow_c_binding_object PROPERTY POSITION_INDEPENDENT_CODE 1) # required for shared libs + +# shared +add_library(aerodyn_inflow_c_binding SHARED $) +target_link_libraries(aerodyn_inflow_c_binding adilib aerodyn_driver_subs nwtclibs versioninfolib) if(APPLE OR UNIX) target_compile_definitions(aerodyn_inflow_c_binding PRIVATE IMPLICIT_DLLEXPORT) endif() -install(TARGETS aerodynlib basicaerolib aerodyn_driver_subs aerodyn_driver unsteadyaero_driver aerodyn_inflow_c_binding adilib +# C-bindings non-shared interface +# This is a workaround for building wavetank into a single DLL (also allows setting CU globaly for sending screen to file for labview integration) +add_library(aerodyn_inflow_c_bind_static STATIC $) +target_link_libraries(aerodyn_inflow_c_bind_static adilib aerodyn_driver_subs nwtclibs versioninfolib) +if(APPLE OR UNIX) + target_compile_definitions(aerodyn_inflow_c_bind_static PRIVATE IMPLICIT_DLLEXPORT) +endif() + + +install(TARGETS aerodynlib aeroacousticslib basicaerolib aerodyn_driver_subs aerodyn_driver aeroacoustics_driver unsteadyaero_driver aerodyn_inflow_c_binding adilib aerodyn_inflow_c_bind_static EXPORT "${CMAKE_PROJECT_NAME}Libraries" RUNTIME DESTINATION bin LIBRARY DESTINATION lib diff --git a/modules/aerodyn/src/AeroAcoustics.f90 b/modules/aerodyn/src/AeroAcoustics.f90 index 212ae66d8f..9142a8eea4 100644 --- a/modules/aerodyn/src/AeroAcoustics.f90 +++ b/modules/aerodyn/src/AeroAcoustics.f90 @@ -49,19 +49,23 @@ module AeroAcoustics REAL(ReKi), parameter :: AA_u_min = 0.1_ReKi REAL(ReKi), parameter :: AA_EPSILON = 1.E-16 ! EPSILON(AA_EPSILON) + + REAL(ReKi), parameter :: RotorRegionAlph_delta = 60.0_ReKi ! degrees : size of bin, must be a number that evenly divides 360 degrees + REAL(ReKi), parameter :: RotorRegionRad_delta = 5.0_ReKi ! meters : size of bin along blade span (rotor radius) + REAL(ReKi), parameter :: RotorRegionTimeSampling = 5.0_ReKi ! seconds (for Num_total_sampleTI) contains !---------------------------------------------------------------------------------------------------------------------------------- !> This routine is called at the start of the simulation to perform initialization steps. !! The parameters are set here and not changed during the simulation. !! The initial states and initial guess for the input are defined. -subroutine AA_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut, ErrStat, ErrMsg ) - type(AA_InitInputType), intent(in ) :: InitInp !< Input data for initialization routine +subroutine AA_Init( InitInp, u, p, xd, OtherState, y, m, Interval, AFInfo, InitOut, ErrStat, ErrMsg ) + type(AA_InitInputType), intent(inout) :: InitInp !< Input data for initialization routine; out because we move allocated array type(AA_InputType), intent( out) :: u !< An initial guess for the input; input mesh must be defined type(AA_ParameterType), intent( out) :: p !< Parameters - type(AA_ContinuousStateType), intent( out) :: x !< Initial continuous states + !type(AA_ContinuousStateType), intent( out) :: x !< Initial continuous states type(AA_DiscreteStateType), intent( out) :: xd !< Initial discrete states - type(AA_ConstraintStateType), intent( out) :: z !< Initial guess of the constraint states + !type(AA_ConstraintStateType), intent( out) :: z !< Initial guess of the constraint states type(AA_OtherStateType), intent( out) :: OtherState !< Initial other states type(AA_OutputType), intent( out) :: y !< Initial system outputs (outputs are not calculated; !! only the output mesh is initialized) @@ -75,6 +79,9 @@ subroutine AA_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut type(AA_InitOutputType), intent( out) :: InitOut !< Output for initialization routine integer(IntKi), intent( out) :: errStat !< Error status of the operation character(*), intent( out) :: errMsg !< Error message if ErrStat /= ErrID_None + type(AFI_ParameterType), intent(in ) :: AFInfo(:) !< The airfoil parameter data +! integer(IntKi), intent(in ) :: AFIndx(:,:) + ! Local variables integer(IntKi) :: errStat2 ! temporary error status of the operation character(ErrMsgLen) :: errMsg2 ! temporary error message @@ -90,15 +97,15 @@ subroutine AA_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut call DispNVD( AA_Ver ) ! To get rid of a compiler warning. - x%DummyContState = 0.0_SiKi - z%DummyConstrState = 0.0_SiKi + !x%DummyContState = 0.0_SiKi + !z%DummyConstrState = 0.0_SiKi !bjj: note that we haven't validated p%NumBlades before using it below! p%NumBlades = InitInp%NumBlades ! need this before reading the AD input file so that we know how many blade files to read p%RootName = TRIM(InitInp%RootName)//'.'//trim(AA_Nickname) ! Read the primary AeroAcoustics input file in AeroAcoustics_IO - call ReadInputFiles( InitInp%InputFile, InitInp%AFInfo, InputFileData, interval, p%RootName, ErrStat2, ErrMsg2 ) + call ReadInputFiles( InitInp%InputFile, AFInfo, InputFileData, interval, p%RootName, ErrStat2, ErrMsg2 ) if (Failed()) return ! Validate the inputs @@ -108,19 +115,20 @@ subroutine AA_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut if (InitInp%AirDens <= 0.0) call SetErrStat ( ErrID_Fatal, 'The air density (AirDens) must be greater than zero.', ErrStat, ErrMsg, RoutineName ) if (InitInp%KinVisc <= 0.0) call SetErrStat ( ErrID_Fatal, 'The kinesmatic viscosity (KinVisc) must be greater than zero.', ErrStat, ErrMsg, RoutineName ) if (InitInp%SpdSound <= 0.0) call SetErrStat ( ErrID_Fatal, 'The speed of sound (SpdSound) must be greater than zero.', ErrStat, ErrMsg, RoutineName ) + if (InitInp%NumBlNds < 1) call SetErrStat ( ErrID_Fatal, 'AeroAcoustics requires at least 1 node.', ErrStat, ErrMsg, RoutineName ) if (Failed()) return ! Define parameters - call SetParameters( InitInp, InputFileData, p, ErrStat2, ErrMsg2 ); if(Failed()) return + call SetParameters( InitInp, InputFileData, p, AFInfo, ErrStat2, ErrMsg2 ); if(Failed()) return ! Define and initialize inputs call Init_u( u, p, errStat2, errMsg2 ); if(Failed()) return ! Initialize states and misc vars - call Init_MiscVars(m, p, u, errStat2, errMsg2); if(Failed()) return + call Init_MiscVars(m, p, errStat2, errMsg2); if(Failed()) return call Init_States(xd, OtherState, p, errStat2, errMsg2); if(Failed()) return ! Define write outputs here (must initialize AFTER Init_MiscVars) - call Init_y(y, m, u, p, errStat2, errMsg2); if(Failed()) return + call Init_y(y, m, p, errStat2, errMsg2); if(Failed()) return ! Define initialization output here call AA_SetInitOut(p, InitOut, errStat2, errMsg2); if(Failed()) return @@ -142,10 +150,11 @@ end subroutine Cleanup end subroutine AA_Init !---------------------------------------------------------------------------------------------------------------------------------- !> This routine sets AeroAcoustics parameters for use during the simulation; these variables are not changed after AA_Init. -subroutine SetParameters( InitInp, InputFileData, p, ErrStat, ErrMsg ) - TYPE(AA_InitInputType), INTENT(IN ) :: InitInp !< Input data for initialization routine, out is needed because of copy below +subroutine SetParameters( InitInp, InputFileData, p, AFInfo, ErrStat, ErrMsg ) + TYPE(AA_InitInputType), INTENT(INOUT) :: InitInp !< Input data for initialization routine, out is needed because of copy below TYPE(AA_InputFile), INTENT(INOUT) :: InputFileData !< Data stored in the module's input file -- intent(out) only for move_alloc statements TYPE(AA_ParameterType), INTENT(INOUT) :: p !< Parameters + type(AFI_ParameterType), intent(in ) :: AFInfo(:) !< The airfoil parameter data INTEGER(IntKi), INTENT( OUT) :: ErrStat !< Error status of the operation CHARACTER(*), INTENT( OUT) :: ErrMsg ! Error message if ErrStat /= ErrID_None ! Local variables @@ -154,14 +163,15 @@ subroutine SetParameters( InitInp, InputFileData, p, ErrStat, ErrMsg ) ! INTEGER(IntKi) :: simcou,coun ! simple loop counter INTEGER(IntKi) :: I,J,whichairfoil,K,i1_1,i10_1,i1_2,i10_2,iLE character(*), parameter :: RoutineName = 'SetParameters' - REAL(ReKi) :: val1,val10,f2,f4,lefttip,rightip,jumpreg, dist1, dist10 + REAL(ReKi) :: val1,val10,f2,f4, dist1, dist10 + REAL(ReKi) :: BladeSpanUsedForNoise + ! Initialize variables for this routine ErrStat = ErrID_None ErrMsg = "" - !!Assign input fiel data to parameters + !!Assign input file data to parameters p%DT = InputFileData%DT_AA ! seconds - p%AA_Bl_Prcntge = InputFileData%AA_Bl_Prcntge ! % - p%total_sampleTI = 5/p%DT ! 10 seconds for TI sampling + p%Num_total_sampleTI = max( NINT(RotorRegionTimeSampling / InputFileData%DT_AA), 1 ) p%AAStart = InputFileData%AAStart p%IBLUNT = InputFileData%IBLUNT p%ILAM = InputFileData%ILAM @@ -177,7 +187,7 @@ subroutine SetParameters( InitInp, InputFileData, p, ErrStat, ErrMsg ) p%NrOutFile = InputFileData%NrOutFile p%outFmt = "ES15.6E3" p%NumBlNds = InitInp%NumBlNds - p%AirDens = InitInp%AirDens + p%AirDens = InitInp%AirDens p%KinVisc = InitInp%KinVisc p%SpdSound = InitInp%SpdSound p%HubHeight = InitInp%HubHeight @@ -187,23 +197,12 @@ subroutine SetParameters( InitInp, InputFileData, p, ErrStat, ErrMsg ) p%TI = InputFileData%TI p%avgV = InputFileData%avgV - ! Copy AFInfo into AA module - ! TODO Allocate AFInfo and AFindx variables (DONE AND DONE) - ALLOCATE(p%AFInfo( size(InitInp%AFInfo) ), STAT=ErrStat2) - IF ( ErrStat2 /= 0 ) THEN - CALL SetErrStat(ErrID_Fatal, 'Error allocating memory for the InitInp%AFInfo array.', ErrStat2, ErrMsg2, RoutineName) - RETURN - ENDIF - - do i=1,size(InitInp%AFInfo) - call AFI_CopyParam(InitInp%AFInfo(i), p%AFInfo(i), MESH_NEWCOPY, errStat2, errMsg2); if(Failed()) return - end do ! Check 1 IF( (p%ITURB.eq.ITURB_TNO) .or. p%IInflow == IInflow_FullGuidati .OR. p%IInflow == IInflow_SimpleGuidati )then ! if tno is on or one of the guidati models is on, check if we have airfoil coordinates - DO k=1,size(p%AFInfo) ! if any of the airfoil coordinates are missing change calculation method - IF( p%AFInfo(k)%NumCoords .lt. 5 )then + DO k=1,size(AFInfo) ! if any of the airfoil coordinates are missing change calculation method + IF( AFInfo(k)%NumCoords .lt. 5 )then CALL WrScr( 'Airfoil coordinates are missing: If Full or Simplified Guidati or Bl Calculation is on coordinates are needed ' ) CALL WrScr( 'Calculation methods enforced as BPM for TBLTE and only Amiet for inflow ' ) p%ITURB = ITURB_BPM @@ -243,8 +242,7 @@ subroutine SetParameters( InitInp, InputFileData, p, ErrStat, ErrMsg ) call MOVE_ALLOC(InputFileData%ObsXYZ,p%ObsXYZ) ! - call AllocAry(p%BlAFID, p%NumBlNds, p%numBlades, 'p%BlAFID' , ErrStat2, ErrMsg2); if(Failed()) return - p%BlAFID=InitInp%BlAFID + call MOVE_ALLOC(InitInp%BlAFID,p%BlAFID) ! Blade Characteristics chord,span,trailing edge angle and thickness,airfoil ID for each segment call AllocAry(p%TEThick ,p%NumBlNds,p%NumBlades,'p%TEThick' ,ErrStat2,ErrMsg2); if(Failed()) return @@ -252,36 +250,49 @@ subroutine SetParameters( InitInp, InputFileData, p, ErrStat, ErrMsg ) call AllocAry(p%StallStart,p%NumBlNds,p%NumBlades,'p%StallStart',ErrStat2,ErrMsg2); if(Failed()) return p%StallStart = 0.0_ReKi - do i=1,p%NumBlades + do i=1,p%NumBlades do j=1,p%NumBlNds whichairfoil = p%BlAFID(j,i) p%TEThick(j,i) = InputFileData%BladeProps(whichairfoil)%TEThick p%TEAngle(j,i) = InputFileData%BladeProps(whichairfoil)%TEAngle - if(p%AFInfo(whichairfoil)%NumTabs /=1 ) then + if(AFInfo(whichairfoil)%NumTabs /=1 ) then call SetErrStat(ErrID_Fatal, 'Number of airfoil tables within airfoil file different than 1, which is not supported.', ErrStat2, ErrMsg2, RoutineName ) if(Failed()) return endif - p%StallStart(j,i) = p%AFInfo(whichairfoil)%Table(1)%UA_BL%alpha1*180/PI ! approximate stall angle of attack [deg] (alpha1 in [rad]) + p%StallStart(j,i) = AFInfo(whichairfoil)%Table(1)%UA_BL%alpha1*180/PI ! approximate stall angle of attack [deg] (alpha1 in [rad]) enddo enddo - call AllocAry(p%BlSpn, p%NumBlNds, p%NumBlades, 'p%BlSpn' , ErrStat2, ErrMsg2); if(Failed()) return - call AllocAry(p%BlChord, p%NumBlNds, p%NumBlades, 'p%BlChord', ErrStat2, ErrMsg2); if(Failed()) return - call AllocAry(p%AerCent, 2, p%NumBlNds, p%NumBlades, 'p%AerCent', ErrStat2, ErrMsg2); if(Failed()) return + call AllocAry(p%BlSpn, p%NumBlNds, p%NumBlades, 'p%BlSpn' , ErrStat2, ErrMsg2); if(Failed()) return + call AllocAry(p%BlElemSpn, p%NumBlNds, p%NumBlades, 'p%BlElemSpn', ErrStat2, ErrMsg2); if(Failed()) return + call AllocAry(p%BlChord, p%NumBlNds, p%NumBlades, 'p%BlChord' , ErrStat2, ErrMsg2); if(Failed()) return + call AllocAry(p%AerCent, 2, p%NumBlNds, p%NumBlades, 'p%AerCent' , ErrStat2, ErrMsg2); if(Failed()) return p%BlSpn = InitInp%BlSpn p%BlChord = InitInp%BlChord - do j=p%NumBlNds,2,-1 - IF ( p%BlSpn(j,1) .lt. p%BlSpn(p%NumBlNds,1)*(100-p%AA_Bl_Prcntge)/100 )THEN ! assuming + p%startnode = max(1, p%NumBlNds - 1) + BladeSpanUsedForNoise = p%BlSpn(p%NumBlNds,1)*(1.0 - InputFileData%AA_Bl_Prcntge/100.0) + do j=p%NumBlNds-1,2,-1 + IF ( p%BlSpn(j,1) .lt. BladeSpanUsedForNoise )THEN p%startnode=j exit ! exit the loop endif enddo - - IF (p%startnode.lt.2) THEN - p%startnode=2 - ENDIF + p%startnode = max(min(p%NumBlNds,2),p%startnode) + + p%BlElemSpn = 0; + DO I = 1,p%numBlades + DO J = p%startnode,p%NumBlNds ! starts loop from startnode. + IF (J < 2) THEN + p%BlElemSpn(J,I) = p%BlSpn(J,I) !assume this is the innermost node + ELSEIF (J .EQ. p%NumBlNds) THEN + p%BlElemSpn(J,I) = p%BlSpn(J,I)-p%BlSpn(J-1,I) + ELSE + p%BlElemSpn(J,I) = (p%BlSpn(J,I)-p%BlSpn(J-1,I))/2 + (p%BlSpn(J+1,I)-p%BlSpn(J,I))/2 ! this is the average element size around this node, equivalent to (p%BlSpn(J+1,I) - p%BlSpn(J-1,I))/2 + ENDIF + end do + end do !print*, 'AeroAcoustics Module is using the blade nodes starting from ' ,p%startnode,' Radius in meter ',p%BlSpn(p%startnode,1) !AerodYnamic center extraction for each segment @@ -289,9 +300,9 @@ subroutine SetParameters( InitInp, InputFileData, p, ErrStat, ErrMsg ) do j=1,p%NumBlNds whichairfoil = p%BlAFID(j,i) ! just a temporary variable for clear coding ! airfoil coordinates read by AeroDyn. First value is the aerodynamic center - if (p%AFInfo(whichairfoil)%NumCoords > 0) then - p%AerCent(1,J,I) = p%AFInfo(whichairfoil)%X_Coord(1) ! assigned here corresponding airfoil. - p%AerCent(2,J,I) = p%AFInfo(whichairfoil)%Y_Coord(1) ! assigned here corresponding airfoil. + if (AFInfo(whichairfoil)%NumCoords > 0) then + p%AerCent(1,J,I) = AFInfo(whichairfoil)%X_Coord(1) ! assigned here corresponding airfoil. + p%AerCent(2,J,I) = AFInfo(whichairfoil)%Y_Coord(1) ! assigned here corresponding airfoil. else p%AerCent(1,J,I) = 0.0_ReKi p%AerCent(2,J,I) = 0.0_ReKi @@ -336,34 +347,30 @@ subroutine SetParameters( InitInp, InputFileData, p, ErrStat, ErrMsg ) ! If guidati is on, calculate the airfoil thickness at 1% and at 10% chord from input airfoil coordinates IF (p%IInflow .EQ. IInflow_FullGuidati) THEN - call AllocAry(p%AFThickGuida,2,size(p%AFInfo), 'p%AFThickGuida', errStat2, errMsg2); if(Failed()) return + call AllocAry(p%AFThickGuida,2,size(AFInfo), 'p%AFThickGuida', errStat2, errMsg2); if(Failed()) return p%AFThickGuida=0.0_Reki - DO k=1,size(p%AFInfo) ! for each airfoil interpolation + DO k=1,size(AFInfo) ! for each airfoil interpolation - ! IF ((MIN(p%AFInfo(k)%X_Coord) < 0.) .or. (MAX(p%AFInfo(k)%X_Coord) > 0.)) THEN - ! call SetErrStat ( ErrID_Fatal,'The coordinates of airfoil '//trim(num2lstr(k))//' are mot defined between x=0 and x=1. Code stops.' ,ErrStat, ErrMsg, RoutineName ) - ! ENDIF - ! find index where LE is found - DO i=3,size(p%AFInfo(k)%X_Coord) - IF (p%AFInfo(k)%X_Coord(i) - p%AFInfo(k)%X_Coord(i-1) > 0.) THEN + DO i=3,size(AFInfo(k)%X_Coord) + IF (AFInfo(k)%X_Coord(i) - AFInfo(k)%X_Coord(i-1) > 0.) THEN iLE = i exit ! end the innermost do loop (i) ENDIF ENDDO ! From LE toward TE - dist1 = ABS( p%AFInfo(k)%X_Coord(iLE) - 0.01) - dist10 = ABS( p%AFInfo(k)%X_Coord(iLE) - 0.10) - DO i=iLE+1,size(p%AFInfo(k)%X_Coord) - IF (ABS(p%AFInfo(k)%X_Coord(i) - 0.01) < dist1) THEN + dist1 = ABS( AFInfo(k)%X_Coord(iLE) - 0.01) + dist10 = ABS( AFInfo(k)%X_Coord(iLE) - 0.10) + DO i=iLE+1,size(AFInfo(k)%X_Coord) + IF (ABS(AFInfo(k)%X_Coord(i) - 0.01) < dist1) THEN i1_1 = i - dist1 = ABS(p%AFInfo(k)%X_Coord(i) - 0.01) + dist1 = ABS(AFInfo(k)%X_Coord(i) - 0.01) ENDIF - IF (ABS(p%AFInfo(k)%X_Coord(i) - 0.1) < dist10) THEN + IF (ABS(AFInfo(k)%X_Coord(i) - 0.1) < dist10) THEN i10_1 = i - dist10 = ABS(p%AFInfo(k)%X_Coord(i) - 0.1) + dist10 = ABS(AFInfo(k)%X_Coord(i) - 0.1) ENDIF ENDDO @@ -371,52 +378,35 @@ subroutine SetParameters( InitInp, InputFileData, p, ErrStat, ErrMsg ) dist1 = 0.99 dist10 = 0.90 DO i=1,iLE-1 - IF (ABS(p%AFInfo(k)%X_Coord(i) - 0.01) < dist1) THEN + IF (ABS(AFInfo(k)%X_Coord(i) - 0.01) < dist1) THEN i1_2 = i - dist1 = ABS(p%AFInfo(k)%X_Coord(i) - 0.01) + dist1 = ABS(AFInfo(k)%X_Coord(i) - 0.01) ENDIF - IF (ABS(p%AFInfo(k)%X_Coord(i) - 0.1) < dist10) THEN + IF (ABS(AFInfo(k)%X_Coord(i) - 0.1) < dist10) THEN i10_2 = i - dist10 = ABS(p%AFInfo(k)%X_Coord(i) - 0.1) + dist10 = ABS(AFInfo(k)%X_Coord(i) - 0.1) ENDIF ENDDO - val1 = p%AFInfo(k)%Y_Coord(i1_1) - p%AFInfo(k)%Y_Coord(i1_2) - val10 = p%AFInfo(k)%Y_Coord(i10_1) - p%AFInfo(k)%Y_Coord(i10_2) + val1 = AFInfo(k)%Y_Coord(i1_1 ) - AFInfo(k)%Y_Coord(i1_2) + val10 = AFInfo(k)%Y_Coord(i10_1) - AFInfo(k)%Y_Coord(i10_2) p%AFThickGuida(1,k)=val1 ! 1 % chord thickness p%AFThickGuida(2,k)=val10 ! 10 % chord thickness ENDDO ENDIF - !! for turbulence intensity calculations on the fly every 5 meter the whole rotor area is divided vertically to store flow fields in each region - jumpreg=7 - p%toptip = CEILING(p%HubHeight+maxval(p%BlSpn(:,1)))+2 !Top Tip Height = Hub height plus radius - p%bottip = FLOOR(p%HubHeight-maxval(p%BlSpn(:,1)))-2 !Bottom Tip Height = Hub height minus radius - call AllocAry(p%rotorregionlimitsVert,ceiling(((p%toptip)-(p%bottip))/jumpreg), 'p%rotorregionlimitsVert', errStat2, errMsg2); if(Failed()) return - do i=0,size(p%rotorregionlimitsVert)-1 - p%rotorregionlimitsVert(i+1)=(p%bottip)+jumpreg*i - enddo - !! for turbulence intensity calculations on the fly every 5 meter the whole rotor area is divided horizontally to store flow fields in each region - jumpreg=7 - lefttip = 2*maxval(p%BlSpn(:,1))+5 ! - rightip = 0 ! - call AllocAry( p%rotorregionlimitsHorz,ceiling(((lefttip)-(rightip))/jumpreg), 'p%rotorregionlimitsHorz', errStat2, errMsg2); if(Failed()) return - do i=0,size(p%rotorregionlimitsHorz)-1 - p%rotorregionlimitsHorz(i+1)=rightip+jumpreg*i - enddo - jumpreg=60 ! 10 ! must be divisable to 360 - call AllocAry(p%rotorregionlimitsalph,INT((360/jumpreg)+1), 'p%rotorregionlimitsalph', errStat2, errMsg2); if(Failed()) return - do i=0,size(p%rotorregionlimitsalph)-1 - p%rotorregionlimitsalph(i+1)=jumpreg*i - enddo - jumpreg=5 - call AllocAry( p%rotorregionlimitsrad, (CEILING( maxval(p%BlSpn(:,1))/jumpreg )+2), 'p%rotorregionlimitsrad', errStat2, errMsg2); if(Failed()) return - do i=1,size(p%rotorregionlimitsrad)-1 - p%rotorregionlimitsrad(i+1)=jumpreg*i - enddo - p%rotorregionlimitsrad(1)=0.0_reki - p%rotorregionlimitsrad(size(p%rotorregionlimitsrad)-1)=p%rotorregionlimitsrad(size(p%rotorregionlimitsrad)-1)+3 + p%NumRotorRegionLimitsAlph = NINT(360./RotorRegionAlph_delta) + 1 + p%NumRotorRegionLimitsRad = CEILING( maxval(p%BlSpn)/RotorRegionRad_delta )+2 + + call AllocAry( p%RotorRegion_k_minus1, p%NumBlNds, p%NumBlades, 'p%RotorRegion_k_minus1', errStat2, errMsg2); if(Failed()) return + p%RotorRegion_k_minus1 = 0 + do i=1,p%NumBlades + do j=1,p%NumBlNds + p%RotorRegion_k_minus1(j,i) = CEILING( p%BlSpn(j,i) / RotorRegionRad_delta ) + p%RotorRegion_k_minus1(j,i) = MIN( p%NumRotorRegionLimitsRad - 1, MAX( 1, p%RotorRegion_k_minus1(j,i) ) ) !safety + end do + enddo contains logical function Failed() @@ -454,10 +444,9 @@ end function Failed end subroutine Init_u !---------------------------------------------------------------------------------------------------------------------------------- !> This routine initializes AeroAcoustics output array variables for use during the simulation. -subroutine Init_y(y, m, u, p, errStat, errMsg) +subroutine Init_y(y, m, p, errStat, errMsg) type(AA_OutputType), intent( out) :: y !< Module outputs type(AA_MiscVarType), intent(in ) :: m !< misc/optimization data - type(AA_InputType), intent(inout) :: u !< Module inputs -- intent(out) because of mesh sibling copy type(AA_ParameterType), intent(inout) :: p !< Parameters integer(IntKi), intent( out) :: errStat !< Error status of the operation character(*), intent( out) :: errMsg !< Error message if ErrStat /= ErrID_None @@ -502,10 +491,9 @@ end function Failed end subroutine Init_y !---------------------------------------------------------------------------------------------------------------------------------- !> This routine initializes (allocates) the misc variables for use during the simulation. -subroutine Init_MiscVars(m, p, u, errStat, errMsg) +subroutine Init_MiscVars(m, p, errStat, errMsg) type(AA_MiscVarType), intent(inout) :: m !< misc/optimization data (not defined in submodules) type(AA_ParameterType), intent(in ) :: p !< Parameters - type(AA_InputType), intent(inout) :: u !< input for HubMotion mesh (create sibling mesh here) integer(IntKi), intent( out) :: errStat !< Error status of the operation character(*), intent( out) :: errMsg !< Error message if ErrStat /= ErrID_None ! Local variables @@ -529,10 +517,7 @@ subroutine Init_MiscVars(m, p, u, errStat, errMsg) call AllocAry(m%SPLTIP , size(p%FreqList), 'SPLTIP' , errStat2, errMsg2); if(Failed()) return call AllocAry(m%SPLTI , size(p%FreqList), 'SPLTI' , errStat2, errMsg2); if(Failed()) return call AllocAry(m%SPLTIGui , size(p%FreqList), 'SPLTIGui' , errStat2, errMsg2); if(Failed()) return - call AllocAry(m%CfVar , 2 , 'CfVar' , errStat2, errMsg2); if(Failed()) return - call AllocAry(m%d99Var , 2 , 'd99Var' , errStat2, errMsg2); if(Failed()) return - call AllocAry(m%dstarVar , 2 , 'dstarVar' , errStat2, errMsg2); if(Failed()) return - call AllocAry(m%EdgeVelVar , 2 , 'EdgeVelVar', errStat2, errMsg2); if(Failed()) return + call AllocAry(m%LE_Location, 3, p%NumBlNds, p%numBlades, 'LE_Location', ErrStat2, ErrMsg2); if(Failed()) return ! arrays for computing WriteOutput values @@ -561,33 +546,30 @@ end subroutine Init_MiscVars !---------------------------------------------------------------------------------------------------------------------------------- !> This routine initializes (allocates) the misc variables for use during the simulation. subroutine Init_states(xd, OtherState, p, errStat, errMsg) - type(AA_DiscreteStateType), intent(inout) :: xd ! - type(AA_OtherStateType), intent(inout) :: OtherState !< Initial other states - type(AA_ParameterType), intent(in ) :: p !< Parameters - integer(IntKi), intent( out) :: errStat !< Error status of the operation - character(*), intent( out) :: errMsg !< Error message if ErrStat /= ErrID_None - ! Local variables - integer(intKi) :: ErrStat2 ! temporary Error status - character(ErrMsgLen) :: ErrMsg2 ! temporary Error message - character(*), parameter :: RoutineName = 'Init_DiscrStates' + type(AA_DiscreteStateType), intent(inout) :: xd ! + type(AA_OtherStateType), intent(inout) :: OtherState !< Initial other states + type(AA_ParameterType), intent(in ) :: p !< Parameters + integer(IntKi), intent( out) :: errStat !< Error status of the operation + character(*), intent( out) :: errMsg !< Error message if ErrStat /= ErrID_None + ! Local variables + integer(intKi) :: ErrStat2 ! temporary Error status + character(ErrMsgLen) :: ErrMsg2 ! temporary Error message + character(*), parameter :: RoutineName = 'Init_states' - ! Initialize variables for this routine - errStat = ErrID_None - errMsg = "" + ! Initialize variables for this routine + errStat = ErrID_None + errMsg = "" - call AllocAry(xd%MeanVxVyVz, p%NumBlNds, p%numBlades, 'xd%MeanVxVyVz', ErrStat2, ErrMsg2); if(Failed()) return - call AllocAry(xd%TIVx, p%NumBlNds, p%numBlades, 'xd%TIVx' , ErrStat2, ErrMsg2); if(Failed()) return + call AllocAry(xd%TIVx, p%NumBlNds, p%numBlades, 'xd%TIVx' , ErrStat2, ErrMsg2); if(Failed()) return + xd%TIVx = 0.0_ReKi - call AllocAry(xd%RegVxStor, p%total_sampleTI, size(p%rotorregionlimitsrad)-1,size(p%rotorregionlimitsalph)-1,'xd%Vxst', ErrStat2,ErrMsg2); if(Failed()) return - call AllocAry(xd%RegionTIDelete, size(p%rotorregionlimitsrad)-1,size(p%rotorregionlimitsalph)-1,'xd%RegionTIDelete', ErrStat2,ErrMsg2); if(Failed()) return - call AllocAry(OtherState%allregcounter , size(p%rotorregionlimitsrad)-1,size(p%rotorregionlimitsalph)-1,'OtherState%allregcounter', ErrStat2,ErrMsg2); if(Failed()) return + if (p%TICalcMeth == TICalc_Every) then + call AllocAry(xd%RegVxStor, p%Num_total_sampleTI, p%NumRotorRegionLimitsRad-1,p%NumRotorRegionLimitsAlph-1,'xd%Vxst', ErrStat2,ErrMsg2); if(Failed()) return + call AllocAry(OtherState%allregcounter , p%NumRotorRegionLimitsRad-1,p%NumRotorRegionLimitsAlph-1,'OtherState%allregcounter', ErrStat2,ErrMsg2); if(Failed()) return - xd%MeanVxVyVz = 0.0_ReKi - xd%TIVx = 0.0_ReKi - xd%RegionTIDelete = 0.0_ReKi - xd%RegVxStor = 0.0_reki - - OtherState%allregcounter = 2 + xd%RegVxStor = 0.0_reki + OtherState%allregcounter = 0 + endif contains logical function Failed() @@ -610,22 +592,23 @@ subroutine AA_UpdateStates( t, n, m, u, p, xd, OtherState, errStat, errMsg ) ! integer(intKi) :: ErrStat2 ! temporary Error status ! character(ErrMsgLen) :: ErrMsg2 ! temporary Error message character(*), parameter :: RoutineName = 'AA_UpdateStates' - REAL(ReKi),DIMENSION(p%NumBlNds,p%numBlades) :: TEMPSTD ! temporary standard deviation variable - REAL(ReKi) :: tempsingle,tempmean,angletemp,abs_le_x ! temporary standard deviation variable - integer(intKi) :: i,j,k,rco +! REAL(ReKi),DIMENSION(p%NumBlNds,p%numBlades) :: TEMPSTD ! temporary standard deviation variable + REAL(ReKi) :: InflowNorm,meanInflow,angletemp,abs_le_x ! temporary standard deviation variable + integer(intKi) :: i,j integer(intKi) :: k_minus1,rco_minus1 ErrStat = ErrID_None ErrMsg = "" - ! Cumulative mean and standard deviation, states are updated as Vx Vy Vz changes at each time step - TEMPSTD = sqrt( u%Inflow(1,:,:)**2+u%Inflow(2,:,:)**2+u%Inflow(3,:,:)**2 ) - xd%MeanVxVyVz = (TEMPSTD + xd%MeanVxVyVz*n) / (n+1) - ! xd%VxSq = TEMPSTD**2 + xd%VxSq - ! TEMPSTD = sqrt( (xd%VxSq/(n+1)) - (xd%MeanVxVyVz**2) ) - ! xd%TIVx = (TEMPSTD / xd%MeanVxVyVz ) ! check inflow noise input for multiplication with 100 or not + !! Cumulative mean and standard deviation, states are updated as Vx Vy Vz changes at each time step + !TEMPSTD = sqrt( u%Inflow(1,:,:)**2+u%Inflow(2,:,:)**2+u%Inflow(3,:,:)**2 ) + !xd%MeanVxVyVz = (TEMPSTD + xd%MeanVxVyVz*n) / (n+1) + !! xd%VxSq = TEMPSTD**2 + xd%VxSq + !! TEMPSTD = sqrt( (xd%VxSq/(n+1)) - (xd%MeanVxVyVz**2) ) + !! xd%TIVx = (TEMPSTD / xd%MeanVxVyVz ) ! check inflow noise input for multiplication with 100 or not - IF( (p%TICalcMeth.eq.2) ) THEN + + IF( p%TICalcMeth == TICalc_Every ) THEN call Calc_LE_Location_Array(p,m,u) ! sets m%LE_Location(:,:,:) do i=1,p%NumBlades @@ -633,44 +616,34 @@ subroutine AA_UpdateStates( t, n, m, u, p, xd, OtherState, errStat, errMsg ) abs_le_x=m%LE_Location(3,j,i)-p%hubheight if (EqualRealNos(abs_le_x, 0.0_ReKi)) then - angletemp = 0.0_ReKi + rco_minus1 = 1 else - angletemp = ATAN2(m%LE_Location(2,j,i), abs_le_x) * R2D_D + angletemp = ATAN2(m%LE_Location(2,j,i), abs_le_x) * R2D ! returns angles in the range [-180, 180] degrees + if (angletemp<0.) angletemp = angletemp + 360. ! in calculation for rco_minus1 below, we compare angles in the range [0, 360] degrees + rco_minus1 = ceiling(angletemp / RotorRegionAlph_delta) + rco_minus1 = MIN( p%NumRotorRegionLimitsAlph-1, MAX(1, rco_minus1) ) ! safety end if - k_minus1 = 0 - do k=1,size(p%rotorregionlimitsrad) - IF (p%BlSpn(j,i)-p%rotorregionlimitsrad(k).lt.0) THEN ! it means location is in the k-1 region - !print*, abs_le_x,p%rotorregionlimitsrad(k),k-1 - k_minus1 = k - 1 - exit ! exit "k" do loop - ENDIF - enddo - k_minus1 = MAX(1,k_minus1) + k_minus1 = p%RotorRegion_k_minus1(j,i) - rco_minus1 = 0 - do rco=1,size(p%rotorregionlimitsalph) - IF (angletemp-p%rotorregionlimitsalph(rco).lt.0) THEN ! it means location is in the k-1 region - rco_minus1 = rco - 1 - exit ! exit "rco" do loop - ENDIF - enddo - rco_minus1 = MAX(1,rco_minus1) ! make sure it didn't + OtherState%allregcounter(k_minus1,rco_minus1) = OtherState%allregcounter(k_minus1,rco_minus1) + 1 ! increase the sample amount in that specific bin - OtherState%allregcounter(k_minus1,rco_minus1) = OtherState%allregcounter(k_minus1,rco_minus1) + 1 ! increase the sample amount in that specific 5 meter height vertical region - - tempsingle = sqrt( u%Inflow(1,j,i)**2+u%Inflow(2,j,i)**2+u%Inflow(3,j,i)**2 ) ! + InflowNorm = TwoNorm( u%Inflow(:,j,i) ) + !note: p%Num_total_sampleTI = size(xd%RegVxStor,1) ! with storage region dependent moving average and TI - IF ( OtherState%allregcounter(k_minus1,rco_minus1) .lt. size(xd%RegVxStor,1)+1 ) THEN - xd%RegVxStor(OtherState%allregcounter(k_minus1,rco_minus1),k_minus1,rco_minus1)=tempsingle - xd%TIVx(j,i) = 0 - xd%RegionTIDelete(k_minus1,rco_minus1)=0 + IF ( OtherState%allregcounter(k_minus1,rco_minus1) <= p%Num_total_sampleTI ) THEN + xd%RegVxStor(OtherState%allregcounter(k_minus1,rco_minus1),k_minus1,rco_minus1) = InflowNorm + xd%TIVx(j,i) = 0 ELSE - xd%RegVxStor((mod( OtherState%allregcounter(k_minus1,rco_minus1) - size(xd%RegVxStor,1), size(xd%RegVxStor,1)))+1,k_minus1,rco_minus1)=tempsingle - tempmean=SUM(xd%RegVxStor(:,k_minus1,rco_minus1)) - tempmean=tempmean/size(xd%RegVxStor,1) - xd%RegionTIDelete(k_minus1,rco_minus1)=SQRT((SUM((xd%RegVxStor(:,k_minus1,rco_minus1)-tempmean)**2)) / size(xd%RegVxStor,1) ) - xd%TIVx(j,i) = xd%RegionTIDelete(k_minus1,rco_minus1) ! only the fluctuation + xd%RegVxStor( mod( OtherState%allregcounter(k_minus1,rco_minus1), p%Num_total_sampleTI )+1, k_minus1, rco_minus1)=InflowNorm + meanInflow = SUM( xd%RegVxStor(:,k_minus1,rco_minus1) ) /p%Num_total_sampleTI + + if ( EqualRealNos(meanInflow,0.0_ReKi)) then + xd%TIVx(j,i) = 0.0_ReKi + else + xd%TIVx(j,i) = SQRT( SUM((xd%RegVxStor(:,k_minus1,rco_minus1)-meanInflow)**2) / p%Num_total_sampleTI ) ! only the fluctuation (this is the population standard deviation, not TI) + xd%TIVx(j,i) = xd%TIVx(j,i) / meanInflow ! this is TI as a fraction (std(U)/mean(U)) + end if ENDIF enddo enddo @@ -680,7 +653,7 @@ subroutine AA_UpdateStates( t, n, m, u, p, xd, OtherState, errStat, errMsg ) do j=1,p%NumBlNds ! We scale the incident turbulence intensity by the ratio of average to incident wind speed ! The scaled TI is used by the Amiet model - xd%TIVx(j,i)=p%TI*p%avgV/u%Vrel(J,I) + xd%TIVx(j,i)=p%TI * p%avgV/u%Vrel(J,I) enddo enddo endif @@ -688,12 +661,12 @@ subroutine AA_UpdateStates( t, n, m, u, p, xd, OtherState, errStat, errMsg ) end subroutine AA_UpdateStates !---------------------------------------------------------------------------------------------------------------------------------- !> This routine is called at the end of the simulation. -subroutine AA_End( u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) +subroutine AA_End( u, p, xd, OtherState, y, m, ErrStat, ErrMsg ) TYPE(AA_InputType), INTENT(INOUT) :: u !< System inputs TYPE(AA_ParameterType), INTENT(INOUT) :: p !< Parameters - TYPE(AA_ContinuousStateType), INTENT(INOUT) :: x !< Continuous states + !TYPE(AA_ContinuousStateType), INTENT(INOUT) :: x !< Continuous states TYPE(AA_DiscreteStateType), INTENT(INOUT) :: xd !< Discrete states - TYPE(AA_ConstraintStateType), INTENT(INOUT) :: z !< Constraint states + !TYPE(AA_ConstraintStateType), INTENT(INOUT) :: z !< Constraint states TYPE(AA_OtherStateType), INTENT(INOUT) :: OtherState !< Other states TYPE(AA_OutputType), INTENT(INOUT) :: y !< System outputs TYPE(AA_MiscVarType), INTENT(INOUT) :: m !< Misc/optimization variables @@ -736,7 +709,7 @@ END SUBROUTINE AA_End !! This subroutine is used to compute the output channels (motions and loads) and place them in the WriteOutput() array. !! The descriptions of the output channels are not given here. Please see the included OutListParameters.xlsx sheet for !! for a complete description of each output parameter. -subroutine AA_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg) +subroutine AA_CalcOutput( t, u, p, xd, OtherState, y, m, ErrStat, ErrMsg) ! NOTE: no matter how many channels are selected for output, all of the outputs are calcalated ! All of the calculated output channels are placed into the m%AllOuts(:), while the channels selected for outputs are ! placed in the y%WriteOutput(:) array. @@ -744,9 +717,9 @@ subroutine AA_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg) REAL(DbKi), INTENT(IN ) :: t !< Current simulation time in seconds TYPE(AA_InputType), INTENT(IN ) :: u !< Inputs at Time t TYPE(AA_ParameterType), INTENT(IN ) :: p !< Parameters - TYPE(AA_ContinuousStateType), INTENT(IN ) :: x !< Continuous states at t + !TYPE(AA_ContinuousStateType), INTENT(IN ) :: x !< Continuous states at t TYPE(AA_DiscreteStateType), INTENT(IN ) :: xd !< Discrete states at t - TYPE(AA_ConstraintStateType), INTENT(IN ) :: z !< Constraint states at t + !TYPE(AA_ConstraintStateType), INTENT(IN ) :: z !< Constraint states at t TYPE(AA_OtherStateType), INTENT(IN ) :: OtherState !< Other states at t TYPE(AA_OutputType), INTENT(INOUT) :: y !< Outputs computed at t (Input only so that mesh con- type(AA_MiscVarType), INTENT(INOUT) :: m !< Misc/optimization variables @@ -765,13 +738,13 @@ subroutine AA_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg) IF (t >= p%AAStart) THEN IF (.NOT. AA_OutputToSeparateFile .or. mod(t + 1E-10,p%DT) .lt. 1E-6) THEN !bjj: should check NINT(t/p%DT)? - call CalcObserve(p,m,u,xd,errStat2, errMsg2) + call CalcObserve(p,m,u,errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName); if (ErrStat >= AbortErrLev) return - call CalcAeroAcousticsOutput(u,p,m,xd,y,errStat2,errMsg2) + call CalcAeroAcousticsOutput(u,p,m,xd,errStat2,errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName); if (ErrStat >= AbortErrLev) return - call Calc_WriteOutput( p, u, m, y, ErrStat2, ErrMsg2 ) + call Calc_WriteOutput( p, m, y, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName); if (ErrStat >= AbortErrLev) return if (AA_OutputToSeparateFile) then @@ -818,8 +791,7 @@ SUBROUTINE Calc_LE_Location_Array(p,m,u) END SUBROUTINE Calc_LE_Location_Array !----------------------------------------------------------------------------------------------------------------------------------! -SUBROUTINE CalcObserve(p,m,u,xd,errStat,errMsg) - TYPE(AA_DiscreteStateType), INTENT(IN ) :: xd !< discrete state type +SUBROUTINE CalcObserve(p,m,u,errStat,errMsg) TYPE(AA_ParameterType), intent(in ) :: p !< Parameters TYPE(AA_InputType), intent(in ) :: u !< NN Inputs at Time TYPE(AA_MiscVarType), intent(inout) :: m !< misc/optimization data (not defined in submodules) @@ -851,7 +823,7 @@ SUBROUTINE CalcObserve(p,m,u,xd,errStat,errMsg) DO J = 1,p%NumBlNds ! Rotate the coordinates of leading and trailing edge from the local reference system to the global. Then add the coordinates of the aerodynamic center in the global coordinate system ! The global coordinate system is located on the ground, has x pointing downwind, y pointing laterally, and z pointing vertically upwards - RTEObservereal = MATMUL(p%AFTeCo(:,J,I), u%RotGtoL(:,:,J,I)) + u%AeroCent_G(:,J,I) ! Note that with the vector math, this is equivalent to MATMUL(TRANSPOSE(p%AFTeCo(:,J,I)), p%AFTeCo(:,J,I)) + u%AeroCent_G(:,J,I) + RTEObservereal = MATMUL(p%AFTeCo(:,J,I), u%RotGtoL(:,:,J,I)) + u%AeroCent_G(:,J,I) ! Note that with the vector math, this is equivalent to MATMUL(TRANSPOSE(p%RotGtoL(:,:,J,I)), p%AFTeCo(:,J,I)) + u%AeroCent_G(:,J,I) ! Loop through the observers DO K = 1,p%NrObsLoc @@ -897,9 +869,8 @@ SUBROUTINE CalcObserve(p,m,u,xd,errStat,errMsg) END SUBROUTINE CalcObserve !----------------------------------------------------------------------------------------------------------------------------------! -SUBROUTINE CalcAeroAcousticsOutput(u,p,m,xd,y,errStat,errMsg) +SUBROUTINE CalcAeroAcousticsOutput(u,p,m,xd,errStat,errMsg) TYPE(AA_InputType), INTENT(IN ) :: u !< Inputs at Time t - TYPE(AA_OutputType), INTENT(INOUT) :: y !< TYPE(AA_ParameterType), INTENT(IN ) :: p !< Parameters TYPE(AA_MiscVarType), INTENT(INOUT) :: m !< misc/optimization data (not defined in submodules) TYPE(AA_DiscreteStateType), INTENT(IN ) :: xd !< discrete state type @@ -914,7 +885,6 @@ SUBROUTINE CalcAeroAcousticsOutput(u,p,m,xd,y,errStat,errMsg) REAL(ReKi) :: AlphaNoise REAL(ReKi) :: AlphaNoise_Deg ! REAL(ReKi) :: UNoise ! - REAL(ReKi) :: elementspan ! real(ReKi) :: Ptotal character(*), parameter :: RoutineName = 'CalcAeroAcousticsOutput' @@ -946,11 +916,6 @@ SUBROUTINE CalcAeroAcousticsOutput(u,p,m,xd,y,errStat,errMsg) Unoise = SIGN(AA_u_min, Unoise) ENDIF - IF (J .EQ. p%NumBlNds) THEN - elementspan = (p%BlSpn(J,I)-p%BlSpn(J-1,I))/2 - ELSE - elementspan = (p%BlSpn(J,I)-p%BlSpn(J-1,I))/2 + (p%BlSpn(J+1,I)-p%BlSpn(J,I))/2 - ENDIF AlphaNoise= u%AoANoise(J,I) call MPi2Pi(AlphaNoise) ! make sure this is in an appropriate range [-pi,pi] AlphaNoise_Deg = AlphaNoise * R2D_D ! convert to degrees since that is how this code is set up. @@ -972,7 +937,7 @@ SUBROUTINE CalcAeroAcousticsOutput(u,p,m,xd,y,errStat,errMsg) !--------Laminar Boundary Layer Vortex Shedding Noise----------------------------! IF ( (p%ILAM .EQ. ILAM_BPM) .AND. (p%ITRIP .EQ. ITRIP_None) ) THEN CALL LBLVS(AlphaNoise_Deg,p%BlChord(J,I),UNoise,m%ChordAngleTE(K,J,I),m%SpanAngleTE(K,J,I), & - elementspan,m%rTEtoObserve(K,J,I), p,m%d99Var(2),m%dstarVar(1),m%dstarVar(2),m%SPLLBL,p%StallStart(J,I)) + p%BlElemSpn(J,I),m%rTEtoObserve(K,J,I), p,m%d99Var(2),m%dstarVar(1),m%dstarVar(2),m%SPLLBL,p%StallStart(J,I)) call TotalContributionFromType(m%SPLLBL,Ptotal,NoiseMech=1) ENDIF @@ -981,14 +946,14 @@ SUBROUTINE CalcAeroAcousticsOutput(u,p,m,xd,y,errStat,errMsg) IF ( p%ITURB /= ITURB_None ) THEN !returns m%SPLP, m%SPLS, m%SPLALPH CALL TBLTE(AlphaNoise_Deg,p%BlChord(J,I),UNoise,m%ChordAngleTE(K,J,I),m%SpanAngleTE(K,J,I), & - elementspan,m%rTEtoObserve(K,J,I), p, m%d99Var(2),m%dstarVar(1),m%dstarVar(2),p%StallStart(J,I), & + p%BlElemSpn(J,I),m%rTEtoObserve(K,J,I), p, m%d99Var(2),m%dstarVar(1),m%dstarVar(2),p%StallStart(J,I), & m%SPLP,m%SPLS,m%SPLALPH ) IF (p%ITURB .EQ. ITURB_TNO) THEN m%EdgeVelVar=1.0_ReKi !returns m%SPLP, m%SPLS from TBLTE CALL TBLTE_TNO(UNoise,m%ChordAngleTE(K,J,I),m%SpanAngleTE(K,J,I), & - elementspan,m%rTEtoObserve(K,J,I),m%CfVar,m%d99var,m%EdgeVelVar ,p, & + p%BlElemSpn(J,I),m%rTEtoObserve(K,J,I),m%CfVar,m%d99var,m%EdgeVelVar ,p, & m%SPLP,m%SPLS) ENDIF @@ -1002,7 +967,7 @@ SUBROUTINE CalcAeroAcousticsOutput(u,p,m,xd,y,errStat,errMsg) !--------Blunt Trailing Edge Noise----------------------------------------------! IF ( p%IBLUNT == IBLUNT_BPM ) THEN ! calculate m%SPLBLUNT(1:nFreq) CALL BLUNT(AlphaNoise_Deg,p%BlChord(J,I),UNoise,m%ChordAngleTE(K,J,I),m%SpanAngleTE(K,J,I), & - elementspan,m%rTEtoObserve(K,J,I),p%TEThick(J,I),p%TEAngle(J,I), & + p%BlElemSpn(J,I),m%rTEtoObserve(K,J,I),p%TEThick(J,I),p%TEAngle(J,I), & p, m%d99Var(2),m%dstarVar(1),m%dstarVar(2),m%SPLBLUNT,p%StallStart(J,I) ) call TotalContributionFromType(m%SPLBLUNT,Ptotal,NoiseMech=5) @@ -1025,7 +990,7 @@ SUBROUTINE CalcAeroAcousticsOutput(u,p,m,xd,y,errStat,errMsg) ! Amiet's Inflow Noise Model is Calculated as long as InflowNoise is On CALL InflowNoise(AlphaNoise,p%BlChord(J,I),Unoise,m%ChordAngleLE(K,J,I),m%SpanAngleLE(K,J,I),& - elementspan,m%rLEtoObserve(K,J,I),xd%TIVx(J,I),p,m%SPLti ) + p%BlElemSpn(J,I),m%rLEtoObserve(K,J,I),xd%TIVx(J,I),p,m%SPLti ) ! If Guidati model (simplified or full version) is also on then the 'SPL correction' to Amiet's model will be added IF ( p%IInflow .EQ. IInflow_FullGuidati ) THEN @@ -1529,7 +1494,7 @@ SUBROUTINE InflowNoise(AlphaNoise,Chord,U,THETA,PHI,d,RObs,TINoise,p,SPLti) REAL(ReKi), INTENT(IN ) :: d ! element span REAL(ReKi), INTENT(IN ) :: RObs ! distance to observer ! REAL(ReKi), INTENT(IN ) :: MeanVNoise ! - REAL(ReKi), INTENT(IN ) :: TINoise ! + REAL(ReKi), INTENT(IN ) :: TINoise ! turbulence intensity (NOT in percent) ! REAL(ReKi), INTENT(IN ) :: LE_Location ! ! REAL(ReKi), INTENT(IN ) :: dissip ! @@ -1598,9 +1563,15 @@ SUBROUTINE InflowNoise(AlphaNoise,Chord,U,THETA,PHI,d,RObs,TINoise,p,SPLti) ! mu = Mach*WaveNumber*Chord/2.0/Beta2 !Note: when we set RObs in CalcObserve(), we make sure it is >= AA_EPSILON ! avoid divide-by-zero - ! tinooisess could be 0, especially on the first step, so we need to check that we don't get a + ! tinooisess could be 0, especially on the first step, so we need to check (use LOG10AA instead of LOG10) SPLhigh = 10.*LOG10AA(p%AirDens**2 * p%SpdSound**4 * p%Lturb * (d/2.) / (RObs**2) *(Mach**5) * & tinooisess**2 *(Khat**3)* (1+Khat**2)**(-7./3.) * Directivity) + 78.4 ! ref a; [2] ) + !bjj 01-13-2026: comparing with Eq 8 in ref [2], + ! (1) The paper uses "Kbar" instead of Khat (which the code uses). + ! (2) In the paper, "I" is in percent and it adds the constant 58.4. In the code, we have "I" as a fraction and I is squared, so + ! 10*log10(x*100^2)+58.4 = 10*(log10(x)+log10(100^2)) + 58.4 = 10*log10(x) + 10*log10(100^2) + 58.4 = 10*log10(x) + 40 + 58.4 + ! Seems like we should be adding 98.4 instead of 78.4 in this code. However, I also haven't found documentation for the "component due to angles of attack" below, + ! so maybe this isn't wrong. !!! SPLhigh = 10.*LOG10(p%Lturb*(d/2.)/ & !!! (RObs*RObs)*(Mach**5)*tinooisess*tinooisess*(WaveNumber**3) & @@ -1608,7 +1579,7 @@ SUBROUTINE InflowNoise(AlphaNoise,Chord,U,THETA,PHI,d,RObs,TINoise,p,SPLti) SPLhigh = SPLhigh + 10.*LOG10(1+ 9.0*AlphaNoise**2) ! Component due to angles of attack, ref a [2]) - Sears = 1/(2.*PI*Kbar/Beta2+1/(1+2.4*Kbar/Beta2)) ! ref a [2]) + Sears = 1./(TwoPi*Kbar/Beta2 + 1./(1.+2.4*Kbar/Beta2)) ! ref a [2]) !!! Sears = 1/(2.*PI*WaveNumber/Beta2+1/(1+2.4*WaveNumber/Beta2)) ! ref b [3]) @@ -2003,9 +1974,9 @@ SUBROUTINE THICK(C,RC,ALPSTAR,p,DELTAP,DSTRS,DSTRP,StallVal) REAL(ReKi), INTENT(IN ) :: C !< Chord Length REAL(ReKi), INTENT(IN ) :: RC !< RC= U*C/KinViscosity TYPE(AA_ParameterType), INTENT(IN ) :: p !< Parameters - REAL(ReKi), INTENT( OUT) :: DELTAP !< - REAL(ReKi), INTENT( OUT) :: DSTRS !< - REAL(ReKi), INTENT( OUT) :: DSTRP !< + REAL(ReKi), INTENT( OUT) :: DELTAP !< Pressure side boundary layer thickness + REAL(ReKi), INTENT( OUT) :: DSTRS !< Suction side displacement thickness + REAL(ReKi), INTENT( OUT) :: DSTRP !< Pressure side displacement thickness REAL(ReKi), INTENT(IN ) :: StallVal !< Stall angle at station i ! Local variables @@ -2032,7 +2003,7 @@ SUBROUTINE THICK(C,RC,ALPSTAR,p,DELTAP,DSTRS,DSTRP,StallVal) IF (RC .LE. .3E+06) THEN DSTR0 = .0601 * RC **(-.114)*C ELSE - DSTR0=10.**(3.411-1.5397*LogRC+.1059*LogRC**2)*C + DSTR0=10.**(3.411-1.5397*LogRC+.1059*LogRC**2)*C END IF ! Lightly tripped IF (p%ITRIP .EQ. ITRIP_Light) DSTR0 = DSTR0 * .6 @@ -2042,22 +2013,30 @@ SUBROUTINE THICK(C,RC,ALPSTAR,p,DELTAP,DSTRS,DSTRP,StallVal) ENDIF ! Pressure side displacement thickness, Eq. (9) of [1] - DSTRP = 10.**(-.0432*ALPSTAR+.00113*ALPSTAR**2)*DSTR0 + DSTRP = 10.**(-.0432*ALPSTAR+.00113*ALPSTAR**2)*DSTR0 ! IF (p%ITRIP .EQ. 3) DSTRP = DSTRP * 1.48 ! commented since itrip is never 3 check if meant 2.(EB_DTU) + ! Suction side displacement thickness - IF (p%ITRIP .EQ. ITRIP_Heavy) THEN - ! Heavily tripped, Eq. (12) of [1] - IF (ALPSTAR .LE. 5.) DSTRS=10.**(.0679*ALPSTAR)*DSTR0 - IF((ALPSTAR .GT. 5.).AND.(ALPSTAR .LE. StallVal)) & - DSTRS = .381*10.**(.1516*ALPSTAR)*DSTR0 - IF (ALPSTAR .GT. StallVal)DSTRS=14.296*10.**(.0258*ALPSTAR)*DSTR0 - ELSE + IF (p%ITRIP .EQ. ITRIP_Heavy) THEN + ! Heavily tripped, Eq. (12) of [1] + IF (ALPSTAR .LE. 5.) THEN + DSTRS=10.**(.0679*ALPSTAR)*DSTR0 + ELSEIF (ALPSTAR .LE. StallVal) THEN + DSTRS = 0.381 * 10.**(.1516*ALPSTAR)*DSTR0 + ELSE + DSTRS = 14.296 * 10.**(.0258*ALPSTAR)*DSTR0 + ENDIF + ELSE ! Untripped or lightly tripped, Eq. (15) of [1] - IF (ALPSTAR .LE. 7.5)DSTRS =10.**(.0679*ALPSTAR)*DSTR0 - IF((ALPSTAR .GT. 7.5).AND.(ALPSTAR .LE. StallVal)) & - DSTRS = .0162*10.**(.3066*ALPSTAR)*DSTR0 - IF (ALPSTAR .GT. StallVal) DSTRS = 52.42*10.**(.0258*ALPSTAR)*DSTR0 - ENDIF + IF (ALPSTAR .LE. 7.5) THEN + DSTRS =10.**(.0679*ALPSTAR)*DSTR0 + ELSEIF(ALPSTAR .LE. StallVal) THEN + DSTRS = .0162*10.**(.3066*ALPSTAR)*DSTR0 + ELSE + DSTRS = 52.42*10.**(.0258*ALPSTAR)*DSTR0 + ENDIF + ENDIF + END SUBROUTINE Thick !==================================================================================================== !> This subroutine computes the high frequency directivity function for the trailing edge diff --git a/modules/aerodyn/src/AeroAcoustics_Driver.f90 b/modules/aerodyn/src/AeroAcoustics_Driver.f90 new file mode 100644 index 0000000000..ea5bdbf6bd --- /dev/null +++ b/modules/aerodyn/src/AeroAcoustics_Driver.f90 @@ -0,0 +1,176 @@ +!********************************************************************************************************************************** +! LICENSING +! Copyright (C) 2015-2016 National Renewable Energy Laboratory +! +! This file is part of AeroDyn. +! +! Licensed under the Apache License, Version 2.0 (the "License"); +! you may not use this file except in compliance with the License. +! You may obtain a copy of the License at +! +! http://www.apache.org/licenses/LICENSE-2.0 +! +! Unless required by applicable law or agreed to in writing, software +! distributed under the License is distributed on an "AS IS" BASIS, +! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +! See the License for the specific language governing permissions and +! limitations under the License. +! + +!there will also be various control flags... this may be updated as needed: +!TBLflag = {'BPM','TNO'} +!bluntnessFlag = {'DTU','BPM'} +!BPMBLflag = {'true','false'} +!useOrigModelAtSepOnset = {'true','false'} + + + + + +!********************************************************************************************************************************** +program AeroAcoustics_Driver + use AeroAcoustics_Driver_Subs + use VersionInfo + implicit none + + ! Program variables + REAL(ReKi) :: PrevClockTime ! Clock time at start of simulation in seconds [(s)] + REAL(ReKi) :: UsrTime1 ! User CPU time for simulation initialization [(s)] + REAL(ReKi) :: UsrTime2 ! User CPU time for simulation (without initialization) [(s)] + INTEGER(IntKi) , DIMENSION(1:8) :: StrtTime ! Start time of simulation (including initialization) [-] + INTEGER(IntKi) , DIMENSION(1:8) :: SimStrtTime ! Start time of simulation (after initialization) [-] + REAL(DbKi) :: t_global ! global-loop time marker + REAL(DbKi) :: TiLstPrn ! The simulation time of the last print (to file) [(s)] + + TYPE(Dvr_Data) :: DriverData + + character(1024) :: InputFile + integer :: nt !< loop counter (for time step) + character(20) :: FlagArg ! flag argument from command line + integer(IntKi) :: ErrStat ! status of error message + character(ErrMsgLen) :: ErrMsg !local error message if ErrStat /= ErrID_None + + + CALL DATE_AND_TIME ( Values=StrtTime ) ! Let's time the whole simulation + CALL CPU_TIME ( UsrTime1 ) ! Initial time (this zeros the start time when used as a MATLAB function) + UsrTime1 = MAX( 0.0_ReKi, UsrTime1 ) ! CPU_TIME: If a meaningful time cannot be returned, a processor-dependent negative value is returned + UsrTime2 = UsrTime1 ! CPU_TIME: Initialize in case of error before getting real data + SimStrtTime = StrtTime ! CPU_TIME: Initialize in case of error before getting real data + nt = 0 + + ! --- Driver initialization + CALL NWTC_Init( ProgNameIN=version%Name ) + + InputFile = "" ! initialize to empty string to make sure it's input from the command line + CALL CheckArgs( InputFile, Flag=FlagArg ) + IF ( LEN( TRIM(FlagArg) ) > 0 ) CALL NormStop() + + ! Display the copyright notice and compile info: + CALL DispCopyrightLicense( version%Name ) + CALL DispCompileRuntimeInfo( version%Name ) + + + ! Initialize modules + call ReadDriverInputFile( InputFile, DriverData, ErrStat, ErrMsg ); call CheckError() + call Init_AFI(DriverData%Airfoil_FileName, DriverData%AFInfo, ErrStat, ErrMsg); call CheckError() + call Init_AAmodule(DriverData, ErrStat, ErrMsg); call CheckError() + + ! Init of time estimator + t_global=0.0_DbKi + call SimStatus_FirstTime( TiLstPrn, PrevClockTime, SimStrtTime, UsrTime2, t_global, DriverData%TMax ) + + ! Time loop + do nt = 1, DriverData%numSteps + ! Time update to screen + t_global=nt * DriverData%dt + + if (mod( nt + 1, 10 )==0) call SimStatus(TiLstPrn, PrevClockTime, t_global, DriverData%TMax) + + ! update states and calculate output + call SetInputsForAA(DriverData) + + call AA_CalcOutput(t_global, DriverData%u, DriverData%p, DriverData%xd, DriverData%OtherState, DriverData%y, DriverData%m, errStat, errMsg); call CheckError() + call Dvr_WriteOutputs(t_global, nt, DriverData) ! write to file at this step + + ! Get state variables at next step: INPUT at step nt - 1, OUTPUT at step nt + call AA_UpdateStates(t_global, nt, DriverData%m, DriverData%u, DriverData%p, DriverData%xd, DriverData%OtherState, errStat, errMsg); call CheckError() + + end do !nt=1,numSteps + + + call Dvr_End() +contains +!................................ + subroutine CheckError() + if (ErrStat /= ErrID_None) then + call WrScr(TRIM(errMsg)) + if (errStat >= AbortErrLev) then + call Dvr_End() + end if + ErrStat = ErrID_None + end if + end subroutine CheckError +!................................ + subroutine Dvr_End() + integer(IntKi) :: errStat2 ! local status of error message + character(ErrMsgLen) :: errMsg2 ! local error message if ErrStat /= ErrID_None + + call Dvr_EndOutput(DriverData, nt, errStat2, errMsg2) + if (ErrStat2 /= ErrID_None) call WrScr(TRIM(errMsg2)) + + call RunTimes(StrtTime, UsrTime1, SimStrtTime, UsrTime2, t_global) + + if (ErrStat >= AbortErrLev) then + call WrScr('') + CALL ProgAbort( 'AeroAcoustics Driver encountered simulation error level: '& + //TRIM(GetErrStr(ErrStat)), TrapErrors=.FALSE., TimeWait=3._ReKi ) ! wait 3 seconds (in case they double-clicked and got an error) + else + call NormStop() + end if + end subroutine Dvr_End +!................................ +end program AeroAcoustics_Driver + + + +!Inputs that will be supplied externally: +!driver%DT +! +!Need to set in InitInput: +! rho [InitInputType%airDens] +! c0 or co [InitInputType%SpdSound] +! L [InitInputType%BlSpn] +! chord [InitInputType%BlChord] +! [driver%DT = Interval] +! visc [InitInputType%KinVisc] +! [InitInputType%HubHeight] +! Airfoil info: +! BlAFID +! AFInfo +! +!Set in AA Input File: +! Lturb (already in AA input file) [InputFileData%Lturb] +! dStarS [m%dstarVar(1), dstarVar1, DSTRS -> interpolated from p%dstarall1 = InputFileData%Suct_DispThick using AoA and Re] meters +! dStarP [m%dstarVar(2), dstarVar2, DSTRP -> interpolated from p%dstarall2 = InputFileData%Pres_DispThick using AoA and Re] meters +! TI [InputFileData%TI] +! cfS [m%CfVar(1), Cfall(1) -> interpolated p%Cfall1=InputFileData%Suct_Cf with Re and AoA] +! cfP [m%CfVar(2), Cfall(2) -> interpolated p%Cfall2=InputFileData%Pres_Cf with Re and AoA] +! deltaS [m%d99Var(1) -> interpolated p%d99all1=InputFileData%Suct_BLThick with Re and AoA] +! deltaP [m%d99Var(2), d99Var2 -> interpolated p%d99all2=InputFileData%Pres_BLThick with Re and AoA ] PRESSURE SIDE BOUNDARY LAYER THICKNESS METERS +! uEdgeS [m%EdgeVelVar(1), EdgeVelAll(2) -> interpolated p%EdgeVelRat1=InputFileData%Suct_EdgeVelRat with Re and AoA] +! uEdgeP [m%EdgeVelVar(2), EdgeVelAll(2) -> interpolated p%EdgeVelRat2=InputFileData%Pres_EdgeVelRat with Re and AoA] +! +!Inputs caluculated in AeroDyn (now set in driver input file?): +! meanWindspeed [u%Inflow] +! AoA [u%AoANoise] +! [u%vRel] +! [AeroCent_G] = u%BladeMotion(j)%Position(:,i) + u%BladeMotion(j)%TranslationDisp(:,i) (global position of the blade node) -> fixed value??? +! [RotGtoL] -> set to identity +! +!Inputs calculated +! Ma [M or Mach] : calculated M = U / p%SpdSound ! MACH NUMBER +! Re [RC] : calculated RC = U * C/p%KinVisc ! Reynolds number; C = chord; U=UNoise=sign( max(abs(u%Vrel(J,I)),0.1), u%Vrel(J,I) ) +! +!fSep1p0_alpha (new to BPM) +!fSpe0p7_alpha (new to BPM) + \ No newline at end of file diff --git a/modules/aerodyn/src/AeroAcoustics_Driver_Subs.f90 b/modules/aerodyn/src/AeroAcoustics_Driver_Subs.f90 new file mode 100644 index 0000000000..15e2ef4205 --- /dev/null +++ b/modules/aerodyn/src/AeroAcoustics_Driver_Subs.f90 @@ -0,0 +1,523 @@ +module AeroAcoustics_Driver_Subs + + use NWTC_Library + use AirfoilInfo + use AirfoilInfo_Types + use AeroAcoustics + use AeroAcoustics_Types + + implicit none + + integer, parameter :: NumAFfiles = 1 + integer, parameter :: NumBlades = 1 + integer, parameter :: NumBlNds = 1 + logical, parameter :: UseCm = .false. + + integer(IntKi), parameter :: idFmt_Ascii = 1 + integer(IntKi), parameter :: idFmt_Binary = 2 + integer(IntKi), parameter :: idFmt_Both = 3 + integer(IntKi), parameter :: idFmt_Valid(3) = (/idFmt_Ascii, idFmt_Binary, idFmt_Both/) + real(ReKi), parameter :: myNaN = -9999.9 + character(1), parameter :: delim = TAB + + real(DbKi), parameter :: RotGtoL(3,3) = reshape( [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0], SHAPE=[3,3] ) + TYPE(ProgDesc), PARAMETER :: version = ProgDesc( 'AeroAcoustics_driver', '', '' ) ! The version number of this program. + + + type Dvr_Data + ! Environmental Conditions + real(ReKi) :: KinVisc !< Kinematic viscosity of working fluid (m^2/s) + real(ReKi) :: AirDens !< AirDens | Air density (kg/m^3) + real(ReKi) :: SpdSound !< Speed of sound in working fluid (m/s) + + ! Output data + character(1024) :: OutRootName = '' !< output file rootname [-] + integer(IntKi) :: unOutFile = -1 !< unit number for writing text output file + character(256) :: OutFmt !< Format used for text tabular output, excluding the time channel. Resulting field should be 10 characters. (quoted string) + integer(IntKi) :: OutFileFmt = idFmt_Binary !< Format for tabular (time-marching) output file (switch) {1: text file [.out], 2: binary file [.outb], 3: both} + logical :: WrBinaryOutput=.false. + logical :: WrTextOutput=.false. + integer(IntKi) :: NumOuts= 0 !< number of output channels, including time + integer(IntKi) :: NumSteps= 0 !< number of steps in output + character(ChanLen) , dimension(:), allocatable :: WriteOutputHdr !< channel headers [-] + character(ChanLen) , dimension(:), allocatable :: WriteOutputUnt !< channel units [-] + real(ReKi) , dimension(:,:), allocatable :: storage !< nchannel x ntime [-] + real(ReKi) , dimension(:), allocatable :: outline !< output line to be written to disk [-] + integer(IntKi) :: FmtWidth + + ! AeroAcoustics Input data + REAL(DbKi) :: AeroCent_G(3) !< Global position of the blade node + REAL(ReKi) :: vRel !< Relative velocity (m/s) + REAL(ReKi) :: AoA !< Angle of attack (rad) + REAL(ReKi) :: WindSpeed !< Atmospheric undisturbed flow on blade [Inflow] (m/s) + REAL(ReKi) :: HubHeight !< hub height (m) + REAL(ReKi) :: BladeLength !< effectively the element span (m) since we are running this with only one element + + ! Time control + real(DbKi) :: DT !< Simulation time step [used only when AnalysisType/=3] (s) + real(DbKi) :: TMax !< Total run time [used only when AnalysisType/=3] (s) + + ! AFI data + character(1024) :: AirFoil_FileName + real(ReKi) :: Chord = 1.0 + type(AFI_ParameterType) :: AFInfo(NumAFfiles) +! integer, allocatable :: AFIndx(:,:) + + ! AeroAcoustics data + character(1024) :: AA_InputFileName !< name of the AA input file + type(AA_InitInputType) :: InitInp !< Input data for initialization routine + type(AA_InputType) :: u !< An initial guess for the input; input mesh must be defined + type(AA_ParameterType) :: p !< Parameters + !type(AA_ContinuousStateType) :: x !< Initial continuous states + type(AA_DiscreteStateType) :: xd !< Initial discrete states + !type(AA_ConstraintStateType) :: z !< Initial guess of the constraint states + type(AA_OtherStateType) :: OtherState !< Initial other states + type(AA_OutputType) :: y !< Initial system outputs (outputs are not calculated) + type(AA_MiscVarType) :: m !< Initial misc/optimization variables + end type Dvr_Data + +contains + +!-------------------------------------------------------------------------------------------------------------- +subroutine ReadDriverInputFile( FileName, DriverData, ErrStat, ErrMsg ) + character(1024), intent(in ) :: FileName + type(Dvr_Data), intent(inout) :: DriverData ! driver data + integer, intent( out) :: ErrStat ! returns a non-zero value when an error occurs + character(*), intent( out) :: ErrMsg ! Error message if ErrStat /= ErrID_None + + ! Local variables + integer :: UnEcho ! The local unit number for this module's echo file + integer :: iLine, i + character(1024) :: EchoFile ! Name of driver's echo file + character(1024) :: PriPath ! the path to the primary input file + character(1024) :: Line ! the path to the primary input file + type(FileInfoType) :: FI !< The derived type for holding the file information. + integer(IntKi) :: errStat2 ! Status of error message + character(1024) :: errMsg2 ! Error message if ErrStat /= ErrID_None + character(*), parameter :: RoutineName = 'ReadDriverInputFile' + integer, parameter :: NumHeaderLines = 3 + logical :: Echo + + ! Initialize the echo file unit to -1 which is the default to prevent echoing, we will alter this based on user input + UnEcho = -1 + Echo = .false. + ErrStat = ErrID_None + ErrMsg = '' + + ! Read all input file lines into fileinfo + call WrScr(' Opening AeroAcoustics Driver input file: '//trim(FileName) ) + call ProcessComFile(FileName, FI, errStat2, errMsg2); if (Failed()) return + CALL GetPath( FileName, PriPath ) ! Input files will be relative to the path where the primary input file is located. + !call GetRoot(FileName, dvr%root) + + ! --- Header and echo + iLine = NumHeaderLines ! Skip the first NumHeaderLines lines as they are known to be header lines and separators + call ParseVar(FI, iLine, 'Echo', Echo, errStat2, errMsg2); if (Failed()) return; + if ( Echo ) then + EchoFile = trim(FileName)//'.ech' + call OpenEcho (UnEcho, EchoFile, errStat2, errMsg2 ); if(Failed()) return + do i = 1,iLine-1 + write(UnEcho, '(A)') trim(FI%Lines(i)) + enddo + end if + + call ParseVar(FI, iLine, 'TMax', DriverData%TMax , errStat2, errMsg2, UnEcho); if(Failed()) return + call ParseVar(FI, iLine, 'DT', DriverData%DT, errStat2, errMsg2, UnEcho); if(Failed()) return + call ParseVar(FI, iLine, 'AA_InputFile', DriverData%AA_InputFileName , errStat2, errMsg2, UnEcho); if(Failed()) return + call ParseVar(FI, iLine, 'AirFoil_FileName' , DriverData%AirFoil_FileName, errStat2, errMsg2, UnEcho); if(Failed()) return + + ! --- Environmental conditions section + call ParseCom(FI, iLine, Line , errStat2, errMsg2, UnEcho); if(Failed()) return + call ParseVar(FI, iLine, 'AirDens', DriverData%AirDens , errStat2, errMsg2, UnEcho); if(Failed()) return + call ParseVar(FI, iLine, 'KinVisc', DriverData%KinVisc , errStat2, errMsg2, UnEcho); if(Failed()) return + call ParseVar(FI, iLine, 'SpdSound', DriverData%SpdSound, errStat2, errMsg2, UnEcho); if(Failed()) return + + ! --- SIMULATION INPUTS section + call ParseCom(FI, iLine, Line , errStat2, errMsg2, UnEcho); if(Failed()) return + call ParseVar(FI, iLine, 'WindSpeed' , DriverData%WindSpeed , errStat2, errMsg2, UnEcho); if(Failed()) return + call ParseVar(FI, iLine, 'AoA' , DriverData%AoA , errStat2, errMsg2, UnEcho); if(Failed()) return + call ParseVar(FI, iLine, 'vRel' , DriverData%vRel , errStat2, errMsg2, UnEcho); if(Failed()) return + call ParseAry(FI, iLine, 'AeroCent_G' , DriverData%AeroCent_G , 3, errStat2, errMsg2, UnEcho); if(Failed()) return + call ParseVar(FI, iLine, 'HubHeight' , DriverData%HubHeight , errStat2, errMsg2, UnEcho); if(Failed()) return +! call ParseVar(FI, iLine, 'Chord' , DriverData%Chord , errStat2, errMsg2, UnEcho); if(Failed()) return + call ParseVar(FI, iLine, 'Span' , DriverData%BladeLength, errStat2, errMsg2, UnEcho); if(Failed()) return + + ! --- OUTPUT section + call ParseCom(FI, iLine, Line , errStat2, errMsg2, UnEcho); if(Failed()) return + call ParseVar(FI, iLine, 'OutFmt' , DriverData%OutFmt , errStat2, errMsg2, UnEcho); if(Failed()) return + call ParseVar(FI, iLine, 'OutFileFmt', DriverData%OutFileFmt, errStat2, errMsg2, UnEcho); if(Failed()) return + + ! convert units: + DriverData%AoA = DriverData%AoA * D2R + + + ! --- Get relative path names + call GetRoot(FileName, DriverData%OutRootName) ! OutRootName is inferred from current filename. + !if (PathIsRelative(DriverData%OutRootName)) DriverData%OutRootName = TRIM(PriPath)//TRIM(DriverData%OutRootName) + if (PathIsRelative(DriverData%AA_InputFileName)) DriverData%AA_InputFileName = TRIM(PriPath)//TRIM(DriverData%AA_InputFileName) + if (PathIsRelative(DriverData%AirFoil_FileName)) DriverData%AirFoil_FileName = TRIM(PriPath)//TRIM(DriverData%AirFoil_FileName ) + + ! --- Checks + if (DriverData%OutFileFmt == idFmt_Both) then + DriverData%WrBinaryOutput = .true. + DriverData%WrTextOutput = .true. + elseif (DriverData%OutFileFmt == idFmt_Ascii) then + DriverData%WrBinaryOutput = .false. + DriverData%WrTextOutput = .true. + elseif (DriverData%OutFileFmt == idFmt_Binary) then + DriverData%WrBinaryOutput = .true. + DriverData%WrTextOutput = .false. + else + DriverData%WrBinaryOutput = .false. + DriverData%WrTextOutput = .false. + end if + + if (DriverData%WrTextOutput) then + CALL ChkRealFmtStr( DriverData%OutFmt, 'OutFmt', DriverData%FmtWidth, ErrStat2, ErrMsg2 ) + IF ( DriverData%FmtWidth < 10 ) CALL SetErrStat( ErrID_Warn, 'OutFmt produces a column width of '// & + TRIM(Num2LStr(DriverData%FmtWidth))//'), which may be too small.', ErrStat, ErrMsg, RoutineName ) + end if + + call Cleanup() +contains + logical function Failed() + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + Failed = ErrStat >= AbortErrLev + if (Failed) call Cleanup() + end function Failed + + subroutine Cleanup() + ! Close this module's echo file + if ( Echo ) then + close(UnEcho) + end if + Call NWTC_Library_Destroyfileinfotype(FI, errStat2, errMsg2) + end subroutine Cleanup + + +end subroutine ReadDriverInputFile +!-------------------------------------------------------------------------------------------------------------- + +subroutine Dvr_EndOutput(DriverData, nt, errStat, errMsg) + type(Dvr_Data), intent(inout) :: DriverData ! driver data + integer(IntKi), intent(in ) :: nt ! number of time steps written + integer(IntKi) , intent(out) :: errStat ! Status of error message + character(*) , intent(out) :: errMsg ! Error message if errStat /= ErrID_None + + character(ErrMsgLen) :: errMsg2 ! temporary Error message if errStat /= ErrID_None + integer(IntKi) :: errStat2 ! temporary Error status of the operation + character(*), parameter :: RoutineName = 'Dvr_EndOutput' + + errStat = ErrID_None + errMsg = '' + + ! Close the output file + if (DriverData%WrTextOutput) then + if (DriverData%unOutFile > 0) close(DriverData%unOutFile) + DriverData%unOutFile = -1 + endif + if (DriverData%WrBinaryOutput .and. allocated(DriverData%storage)) then + call WrScr(' Writing output file: '//trim(DriverData%OutRootName)//'.outb') + call WrBinFAST(trim(DriverData%OutRootName)//'.outb', FileFmtID_ChanLen_In, version%Name, DriverData%WriteOutputHdr, DriverData%WriteOutputUnt, (/0.0_DbKi, DriverData%dt/), DriverData%storage(:,1:nt), errStat2, errMsg2) + call SetErrStat(errStat2, errMsg2, errStat, errMsg, RoutineName) + endif +end subroutine Dvr_EndOutput + + +!---------------------------------------------------------------------------------------------------------------------------------- +!> Initialize outputs to file for driver +subroutine Dvr_InitializeOutputs(DriverData, AA_InitOut, errStat, errMsg) + TYPE(Dvr_Data) , intent(inout) :: DriverData + TYPE(AA_InitOutputTYpe), intent(in ) :: AA_InitOut + integer(IntKi) , intent( out) :: errStat ! Status of error message + character(*) , intent( out) :: errMsg ! Error message if errStat /= ErrID_None + ! locals + integer(IntKi) :: errStat2 ! Status of error message + character(ErrMsgLen) :: errMsg2 ! Error message + integer :: i, j + integer :: ActualChanLen + + errStat = ErrID_None + errMsg = '' + + DriverData%numSteps = ceiling(DriverData%TMax / DriverData%dt) + DriverData%numOuts = sum(DriverData%p%numOutsAll) + 1 ! includes time channel + if (DriverData%numOuts < 2) then + ErrStat2=ErrID_Fatal + ErrMsg2='AeroAcoustics module is not printing any outputs. Simulation will end.' + if (Failed()) return + end if + + ! --- Allocate driver-level outputs + call AllocAry(DriverData%WriteOutputHdr, DriverData%numOuts, 'WriteOutputHdr', errStat2, errMsg2); if(Failed()) return + call AllocAry(DriverData%WriteOutputUnt, DriverData%numOuts, 'WriteOutputUnt', errStat2, errMsg2); if(Failed()) return + + i=1 + DriverData%WriteOutputHdr(i) = 'Time' + DriverData%WriteOutputUnt(i) = '(s)' + + if (DriverData%numOuts > 0) then + do j=1,DriverData%p%numOutsAll(1) + i = i + 1 + DriverData%WriteOutputHdr(i) = AA_InitOut%WriteOutputHdr(j) + DriverData%WriteOutputUnt(i) = AA_InitOut%WriteOutputUnt(j) + end do + + do j=1,DriverData%p%numOutsAll(2) + i = i + 1 + DriverData%WriteOutputHdr(i) = AA_InitOut%WriteOutputHdrforPE(j) + DriverData%WriteOutputUnt(i) = AA_InitOut%WriteOutputUntforPE(j) + end do + + do j=1,DriverData%p%numOutsAll(3) + i = i + 1 + DriverData%WriteOutputHdr(i) = AA_InitOut%WriteOutputHdrSep(j) + DriverData%WriteOutputUnt(i) = AA_InitOut%WriteOutputUntSep(j) + end do + + do j=1,DriverData%p%numOutsAll(4) + i = i + 1 + DriverData%WriteOutputHdr(i) = AA_InitOut%WriteOutputHdrNodes(j) + DriverData%WriteOutputUnt(i) = AA_InitOut%WriteOutputUntNodes(j) + end do + end if + + if (DriverData%WrTextOutput .or. DriverData%WrBinaryOutput) then + call AllocAry(DriverData%outLine, DriverData%numOuts-1, 'outLine', errStat2, errMsg2); if(Failed()) return + DriverData%outLine=0.0_ReKi + end if + + if (DriverData%WrTextOutput) then + ActualChanLen = min(ChanLen, max(10, DriverData%FmtWidth)) + do i=1,DriverData%NumOuts + ActualChanLen = max( ActualChanLen, LEN_TRIM(DriverData%WriteOutputHdr(I)) ) + ActualChanLen = max( ActualChanLen, LEN_TRIM(DriverData%WriteOutputUnt(I)) ) + enddo ! I + + call GetNewUnit( DriverData%unOutFile, ErrStat2, ErrMsg2 ) + if (Failed()) return + + call OpenFOutFile ( DriverData%unOutFile, trim(DriverData%OutRootName)//'.out', ErrStat2, ErrMsg2 ) + if (Failed()) return + + write (DriverData%unOutFile,'(A)') '' + write (DriverData%unOutFile,'(A)') 'Predictions were generated on '//CurDate()//' at '//CurTime()//' using '//trim(GetNVD(version)) + write (DriverData%unOutFile,'(A)') '' + write (DriverData%unOutFile,'(A)') '' + write (DriverData%unOutFile,'(A)') 'Output from AeroAcoustics driver' + write (DriverData%unOutFile,'(A)') '' + + !...................................................... + ! Write the names of the output parameters on one line: line 7 + !...................................................... + call WrFileNR ( DriverData%unOutFile, DriverData%WriteOutputHdr(1)(1:min(15,ChanLen)) ) + do i=2,DriverData%NumOuts + call WrFileNR ( DriverData%unOutFile, delim//DriverData%WriteOutputHdr(i)(1:ActualChanLen) ) + end do ! i + write (DriverData%unOutFile,'()') + + !...................................................... + ! Write the units of the output parameters on one line: line 8 + !...................................................... + call WrFileNR ( DriverData%unOutFile, DriverData%WriteOutputUnt(1)(1:min(15,ChanLen)) ) + do i=2,DriverData%NumOuts + call WrFileNR ( DriverData%unOutFile, delim//DriverData%WriteOutputUnt(i)(1:ActualChanLen) ) + end do ! i + write (DriverData%unOutFile,'()') + + end if + + ! --- Binary + if (DriverData%WrBinaryOutput) then + ! we aren't storing time here + call AllocAry(DriverData%storage, DriverData%numOuts-1, DriverData%numSteps, 'storage', errStat2, errMsg2); if(Failed()) return + DriverData%storage= myNaN !0.0_ReKi ! Alternative: myNaN + endif + +contains + logical function Failed() + CALL SetErrStat(errStat2, errMsg2, errStat, errMsg, 'Dvr_InitializeOutputs' ) + Failed = errStat >= AbortErrLev + end function Failed +end subroutine Dvr_InitializeOutputs +!---------------------------------------------------------------------------------------------------------------------------------- +subroutine Dvr_WriteOutputs(t, nt, DriverData) + Integer(IntKi) , intent(in ) :: nt ! time step number + real(DbKi) , intent(in ) :: t ! simulation time (s) + type(Dvr_Data), intent(inout) :: DriverData ! driver data + + ! ! Local variables. + integer :: i, j + + if (DriverData%WrTextOutput .or. DriverData%WrBinaryOutput) then + i = 0 + + ! Driver outputs + if (DriverData%numOuts > 0) then + do j=1,DriverData%p%numOutsAll(1) + i = i + 1 + DriverData%outLine(i) = DriverData%y%WriteOutput(j) + end do + + do j=1,DriverData%p%numOutsAll(2) + i = i + 1 + DriverData%outLine(i) = DriverData%y%WriteOutputforPE(j) + end do + + do j=1,DriverData%p%numOutsAll(3) + i = i + 1 + DriverData%outLine(i) = DriverData%y%WriteOutputSep(j) + end do + + do j=1,DriverData%p%numOutsAll(4) + i = i + 1 + DriverData%outLine(i) = DriverData%y%WriteOutputNodes(j) + end do + end if + + if (DriverData%WrBinaryOutput) DriverData%storage(:,nt) = DriverData%outLine + if (DriverData%WrTextOutput) then + write(DriverData%unOutFile,'(F15.4,'//trim(num2lstr(DriverData%numOuts-1))//'("'//delim//'"'//trim(DriverData%outFmt)//'))') t, DriverData%outLine(:) + end if + + + end if + + +end subroutine Dvr_WriteOutputs +!---------------------------------------------------------------------------------------------------------------------------------- +subroutine Init_AFI(afName, AFInfo, ErrStat, ErrMsg) + + CHARACTER(1024), intent(in ) :: afName + type(AFI_ParameterType), intent( out) :: AFInfo(NumAFfiles) + integer(IntKi), intent( out) :: ErrStat ! Error status. + character(*), intent( out) :: ErrMsg ! Error message. + + type(AFI_InitInputType) :: AFI_InitInputs + integer :: UnEc + + ErrStat = ErrID_None + ErrMsg = "" + + AFI_InitInputs%InCol_Alfa = 1 + AFI_InitInputs%InCol_Cl = 2 + AFI_InitInputs%InCol_Cd = 3 + AFI_InitInputs%InCol_Cm = 0 + AFI_InitInputs%InCol_Cpmin = 0 + AFI_InitInputs%AFTabMod = AFITable_1 ! 1D-interpolation (on AoA only) + AFI_InitInputs%UAMod = 3 ! We calculate some of the UA coefficients based on UA Model, but AA doesn't care which + AFI_InitInputs%FileName = afName !InitInp%AF_File(i) + + UnEc = 0 + + ! Read in and process the airfoil file. + ! This includes creating the spline coefficients to be used for interpolation. + + call AFI_Init ( AFI_InitInputs, AFInfo(1), errStat, errMsg, UnEc ) + if (ErrStat >= AbortErrLev) return + +end subroutine Init_AFI +!---------------------------------------------------------------------------------------------------------------------------------- +!> This routine initializes the Airfoil Noise module from within AeroDyn. +SUBROUTINE Init_AAmodule( DriverData, ErrStat, ErrMsg ) +!.................................................................................................................................. + type(Dvr_Data), intent(inout) :: DriverData !< AeroDyn-level initialization inputs + + integer(IntKi), intent( out) :: errStat !< Error status of the operation + character(*), intent( out) :: errMsg !< Error message if ErrStat /= ErrID_None + + ! Local variables + real(DbKi) :: Interval ! DT + type(AA_InitInputType) :: InitInp ! Input data for initialization routine + type(AA_InitOutputType) :: InitOut ! Output for initialization routine + integer(intKi) :: j ! node index + integer(intKi) :: k ! blade index + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'Init_AAmodule' + + ErrStat = ErrID_None + ErrMsg = "" + + ! Transfer from parameters and input file to init input + InitInp%InputFile = DriverData%AA_InputFileName + InitInp%NumBlades = NumBlades + InitInp%NumBlNds = NumBlNds + InitInp%RootName = DriverData%OutRootName + +! read from input file or set default value + Interval = DriverData%DT + InitInp%airDens = DriverData%airDens !(rho) + InitInp%kinVisc = DriverData%kinVisc !(nu) + InitInp%SpdSound = DriverData%SpdSound !(co) + InitInp%HubHeight = DriverData%HubHeight + + ! --- Allocate and set AirfoilID, chord and Span for each blades + ! note here that each blade is required to have the same number of nodes + call AllocAry( InitInp%BlAFID, NumBlNds, NumBlades,'InitInp%BlAFID', errStat2, ErrMsg2 ); call SetErrStat( errStat2, errMsg2, errStat, errMsg, RoutineName ) + call AllocAry( InitInp%BlChord, NumBlNds, NumBlades, 'BlChord', errStat2, ErrMsg2 ); call SetErrStat( errStat2, errMsg2, errStat, errMsg, RoutineName ) + call AllocAry( InitInp%BlSpn, NumBlNds, NumBlades, 'BlSpn', errStat2, ErrMsg2 ); call SetErrStat( errStat2, errMsg2, errStat, errMsg, RoutineName ) + if (ErrStat >= AbortErrLev) then + call cleanup() + return + end if + + do k = 1, NumBlades + do j=1, NumBlNds + InitInp%BlChord(j,k) = DriverData%Chord !RotInputFileData%BladeProps(k)%BlChord(j) + InitInp%BlSpn (j,k) = real(j,ReKi)/real(NumBlNds,ReKi) * DriverData%BladeLength + InitInp%BlAFID(j,k) = NumAFfiles !RotInputFileData%BladeProps(k)%BlAFID(j) + end do + end do + + ! --- AeroAcoustics initialization call + call AA_Init(InitInp, DriverData%u, DriverData%p, DriverData%xd, DriverData%OtherState,DriverData%y, DriverData%m, Interval, DriverData%AFInfo, InitOut, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2,ErrMsg2, ErrStat, ErrMsg, RoutineName) + + if (ErrStat < AbortErrLev) then + call Dvr_InitializeOutputs(DriverData, InitOut, errStat2, errMsg2) + call SetErrStat(ErrStat2,ErrMsg2, ErrStat, ErrMsg, RoutineName) + end if + + call Cleanup() + +contains + + subroutine Cleanup() + call AA_DestroyInitInput ( InitInp, ErrStat2, ErrMsg2 ) + call AA_DestroyInitOutput ( InitOut, ErrStat2, ErrMsg2 ) + end subroutine Cleanup + +END SUBROUTINE Init_AAmodule +!---------------------------------------------------------------------------------------------------------------------------------- +!> This subroutine sets m%AA_u. +subroutine SetInputsForAA(DriverData) + type(Dvr_Data), intent(inout) :: DriverData !< AeroDyn-level initialization inputs + + ! local variables + integer(intKi) :: i ! loop counter for nodes + integer(intKi) :: j ! loop counter for blades + + do j=1,NumBlades + do i = 1,NumBlNds + ! Get local orientation matrix to transform from blade element coordinates to global coordinates + DriverData%u%RotGtoL(:,:,i,j) = RotGtoL ! default to identitiy orientation + + ! Get blade element aerodynamic center in global coordinates + DriverData%u%AeroCent_G(:,i,j) = DriverData%AeroCent_G !BJJ: does this need to change with time? probably + + ! Set the blade element relative velocity (including induction) + DriverData%u%Vrel(i,j) = DriverData%VRel + + ! Set the blade element angle of attack + DriverData%u%AoANoise(i,j) = DriverData%AoA + + ! Set the blade element undisturbed flow + DriverData%u%Inflow(:,i,j) = [DriverData%WindSpeed, 0.0_ReKi, 0.0_ReKi] + end do + end do +end subroutine SetInputsForAA +!---------------------------------------------------------------------------------------------------------------------------------- + +end module AeroAcoustics_Driver_Subs + diff --git a/modules/aerodyn/src/AeroAcoustics_IO.f90 b/modules/aerodyn/src/AeroAcoustics_IO.f90 index 655717568f..24049bb023 100644 --- a/modules/aerodyn/src/AeroAcoustics_IO.f90 +++ b/modules/aerodyn/src/AeroAcoustics_IO.f90 @@ -41,28 +41,28 @@ MODULE AeroAcoustics_IO integer(intKi), parameter :: X_BLMethod_BPM = 1 ! integer(intKi), parameter :: X_BLMethod_Tables = 2 ! - integer(intKi), parameter :: TICalc_Interp = 1 ! interpolate from pretabulated - integer(intKi), parameter :: TICalc_Every = 2 ! calculate ti automatically + integer(intKi), parameter :: TICalc_Interp = 1 ! interpolate from pretabulated (TICalcMethod) + integer(intKi), parameter :: TICalc_Every = 2 ! calculate ti automatically (TICalcMethod) integer(intKi), parameter :: ITURB_None = 0 ! TBLTE noise is not calculated integer(intKi), parameter :: ITURB_BPM = 1 ! TBLTE noise is calculated with BPM integer(intKi), parameter :: ITURB_TNO = 2 ! TBLTE noise is calculated with TNO - integer(intKi), parameter :: IInflow_None = 0 ! IInflow noise is not calculated - integer(intKi), parameter :: IInflow_BPM = 1 ! IInflow noise is calculated with BPM - integer(intKi), parameter :: IInflow_FullGuidati = 2 ! IInflow noise is calculated with FullGuidati + integer(intKi), parameter :: IInflow_None = 0 ! IInflow noise is not calculated + integer(intKi), parameter :: IInflow_BPM = 1 ! IInflow noise is calculated with BPM + integer(intKi), parameter :: IInflow_FullGuidati = 2 ! IInflow noise is calculated with FullGuidati integer(intKi), parameter :: IInflow_SimpleGuidati = 3 ! IInflow noise is calculated with SimpleGuidati contains !---------------------------------------------------------------------------------------------------------------------------------- -SUBROUTINE ReadInputFiles( InputFileName, AFI, InputFileData, Default_DT, OutFileRoot, ErrStat, ErrMsg ) +SUBROUTINE ReadInputFiles( InputFileName, AFInfo, InputFileData, Default_DT, OutFileRoot, ErrStat, ErrMsg ) ! This subroutine reads the input file and stores all the data in the AA_InputFile structure. ! It does not perform data validation. !.................................................................................................................................. ! Passed variables REAL(DbKi), INTENT(IN) :: Default_DT ! The default DT (from glue code) CHARACTER(*), INTENT(IN) :: InputFileName ! Name of the aeroacoustics input file - TYPE(AFI_ParameterType), INTENT(IN) :: AFI(:) ! airfoil array: contains names of the BL input file + TYPE(AFI_ParameterType), INTENT(IN) :: AFInfo(:) ! airfoil array: contains names of the BL input file CHARACTER(*), INTENT(IN) :: OutFileRoot ! The rootname of all the output files written by this routine. TYPE(AA_InputFile), INTENT(OUT) :: InputFileData ! Data stored in the module's input file INTEGER(IntKi), INTENT(OUT) :: ErrStat ! The error status code @@ -83,7 +83,7 @@ SUBROUTINE ReadInputFiles( InputFileName, AFI, InputFileData, Default_DT, OutFil if(Failed()) return ! get the blade input-file data - ALLOCATE( InputFileData%BladeProps( size(AFI) ), STAT = ErrStat2 ) + ALLOCATE( InputFileData%BladeProps( size(AFInfo) ), STAT = ErrStat2 ) IF (ErrStat2 /= 0) THEN CALL SetErrStat(ErrID_Fatal,"Error allocating memory for BladeProps.", ErrStat, ErrMsg, RoutineName) call cleanup() @@ -92,7 +92,7 @@ SUBROUTINE ReadInputFiles( InputFileName, AFI, InputFileData, Default_DT, OutFil if (InputFileData%ITURB==ITURB_TNO .or. InputFileData%X_BLMethod==X_BLMethod_Tables .or. InputFileData%IBLUNT==IBLUNT_BPM) then ! We need to read the BL tables - CALL ReadBLTables( InputFileName, AFI, InputFileData, UnEcho, ErrStat2, ErrMsg2 ) + CALL ReadBLTables( InputFileName, AFInfo, InputFileData, UnEcho, ErrStat2, ErrMsg2 ) if (Failed()) return endif @@ -122,7 +122,7 @@ SUBROUTINE ReadPrimaryFile( InputFile, InputFileData, Default_DT, OutFileRoot, U integer(IntKi) :: I ! loop counter integer(IntKi) :: UnIn,UnIn2 ! Unit number for reading file character(1024) :: ObserverFile ! name of the files containing obesever location - integer(IntKi) :: ErrStat2, IOS,cou ! Temporary Error status + integer(IntKi) :: ErrStat2, cou ! Temporary Error status logical :: Echo ! Determines if an echo file should be written character(ErrMsgLen) :: ErrMsg2 ! Temporary Error message character(1024) :: PriPath ! Path name of the primary file @@ -279,10 +279,10 @@ END SUBROUTINE ReadPrimaryFile !---------------------------------------------------------------------------------------------------------------------------------- ! ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SUBROUTINE ReadBLTables( InputFile, AFI, InputFileData, UnEc, ErrStat, ErrMsg ) +SUBROUTINE ReadBLTables( InputFile, AFInfo, InputFileData, UnEc, ErrStat, ErrMsg ) ! Passed variables character(*), intent(in) :: InputFile ! Name of the file containing the primary input data - TYPE(AFI_ParameterType), INTENT(IN) :: AFI(:) ! airfoil array: contains names of the BL input file + TYPE(AFI_ParameterType), INTENT(IN) :: AFInfo(:) ! airfoil array: contains names of the BL input file type(AA_InputFile), intent(inout) :: InputFileData ! All the data in the Noise input file integer(IntKi), intent(in) :: UnEc ! I/O unit for echo file. If > 0, file is open for writing. integer(IntKi), intent(out) :: ErrStat ! Error status @@ -306,10 +306,10 @@ SUBROUTINE ReadBLTables( InputFile, AFI, InputFileData, UnEc, ErrStat, ErrMsg ) UnIn = -1 CALL GetPath( InputFile, PriPath ) ! Input files will be relative to the path where the primary input file is located. - nAirfoils = size(AFI) + nAirfoils = size(AFInfo) do iAF=1,nAirfoils - FileName = trim(AFI(iAF)%BL_file) + FileName = trim(AFInfo(iAF)%BL_file) call WrScr('AeroAcoustics_IO: reading BL table:'//trim(Filename)) @@ -442,48 +442,58 @@ SUBROUTINE ValidateInputData( InputFileData, NumBl, ErrStat, ErrMsg ) character(*), intent(out) :: ErrMsg !< Error message ! local variables character(*), parameter :: RoutineName = 'ValidateInputData' + ErrStat = ErrID_None ErrMsg = "" + if (NumBl > MaxBl .or. NumBl < 1) call SetErrStat( ErrID_Fatal, 'Number of blades must be between 1 and '//trim(num2lstr(MaxBl))//'.', ErrSTat, ErrMsg, RoutineName ) + if (InputFileData%DT_AA <= 0.0) call SetErrStat ( ErrID_Fatal, 'DT_AA must be greater than zero.', ErrStat, ErrMsg, RoutineName ) + if (InputFileData%IBLUNT /= IBLUNT_None .and. InputFileData%IBLUNT /= IBLUNT_BPM) then call SetErrStat ( ErrID_Fatal, & 'IBLUNT must '//trim(num2lstr(IBLUNT_None))//' (none) or '//trim(num2lstr(IBLUNT_BPM))//' (Bluntness noise calculated).', ErrStat, ErrMsg, RoutineName ) endif if (InputFileData%ILAM /= ILAM_None .and. InputFileData%ilam /= ILAM_BPM) then - call SetErrStat ( ErrID_Fatal, 'ILAM must be '//trim(num2lstr(ILAM_None))//' No calculation '//& + call SetErrStat ( ErrID_Fatal, 'ILAM must be '//trim(num2lstr(ILAM_None))//' No calculation '//& trim(num2lstr(ILAM_BPM))//' (ILAM Calculated).', ErrStat, ErrMsg, RoutineName ) end if if (InputFileData%ITIP /= ITIP_None .and. InputFileData%ITIP /= ITIP_ON) then - call SetErrStat ( ErrID_Fatal, 'ITIP must be '//trim(num2lstr(ITIP_None))//' (Off) or '//& + call SetErrStat ( ErrID_Fatal, 'ITIP must be '//trim(num2lstr(ITIP_None))//' (Off) or '//& trim(num2lstr(ITIP_On))//' (ITIP On).', ErrStat, ErrMsg, RoutineName ) end if if (InputFileData%ITRIP /= ITRIP_None .and. InputFileData%ITRIP /= ITRIP_Heavy .and. InputFileData%ITRIP /= ITRIP_Light) then - call SetErrStat ( ErrID_Fatal,'ITRIP must be '//trim(num2lstr(ITRIP_None))//' (none) or '//trim(num2lstr(ITRIP_Heavy))//& + call SetErrStat ( ErrID_Fatal,'ITRIP must be '//trim(num2lstr(ITRIP_None))//' (none) or '//trim(num2lstr(ITRIP_Heavy))//& ' (heavily tripped BL Calculation) or '//trim(num2lstr(ITRIP_Light))//' (lightly tripped BL)' ,ErrStat, ErrMsg, RoutineName ) end if if (InputFileData%ITURB /= ITURB_None .and. InputFileData%ITURB /= ITURB_BPM .and. InputFileData%ITURB /= ITURB_TNO) then - call SetErrStat ( ErrID_Fatal, 'ITURB must be 0 (off) or 1 (BPM) or 2 (TNO) .', ErrStat, ErrMsg, RoutineName ) + call SetErrStat ( ErrID_Fatal, 'ITURB must be 0 (off) or 1 (BPM) or 2 (TNO) .', ErrStat, ErrMsg, RoutineName ) end if if (InputFileData%IInflow /= IInflow_None .and. InputFileData%IInflow /= IInflow_BPM & .and. InputFileData%IInflow /= IInflow_FullGuidati .and. InputFileData%IInflow /= IInflow_SimpleGuidati ) then - call SetErrStat ( ErrID_Fatal, 'IInflow must be 0 (off) or 1 (only Amiet) or 2 (Full Guidati)'//& + call SetErrStat ( ErrID_Fatal, 'IInflow must be 0 (off) or 1 (only Amiet) or 2 (Full Guidati)'//& 'or 3 (Simple Guidati).', ErrStat, ErrMsg, RoutineName ) end if if (InputFileData%TICalcMeth /= TICalc_Every .and. InputFileData%TICalcMeth /= TICalc_Interp ) then - call SetErrStat ( ErrID_Fatal, 'TICalcMeth must be '//trim(num2lstr(TICalc_Every))//' TICalc automatic or '//& + call SetErrStat ( ErrID_Fatal, 'TICalcMeth must be '//trim(num2lstr(TICalc_Every))//' (TICalc automatic) or '//& trim(num2lstr(TICalc_Interp))//' (TICalcMeth interp).', ErrStat, ErrMsg, RoutineName ) end if if (InputFileData%X_BLMethod /= X_BLMethod_BPM .and. InputFileData%X_BLMethod /= X_BLMethod_Tables) then - call SetErrStat ( ErrID_Fatal, 'X_BLMethod must be '//trim(num2lstr(X_BLMethod_BPM))//' X_BLMethod_ with BPM or '//& + call SetErrStat ( ErrID_Fatal, 'X_BLMethod must be '//trim(num2lstr(X_BLMethod_BPM))//' X_BLMethod_ with BPM or '//& trim(num2lstr(X_BLMethod_Tables))//' (X_BLMethod with BL tables).', ErrStat, ErrMsg, RoutineName ) end if + if (InputFileData%NrObsLoc <= 0.0) call SetErrStat ( ErrID_Fatal, 'Number of Observer Locations should be greater than zero', ErrStat, ErrMsg, RoutineName ) - if (InputFileData%NrOutFile /= 1 .and. InputFileData%NrOutFile /= 2 .and. InputFileData%NrOutFile /= 3 & - .and. InputFileData%NrOutFile /= 4) then - call SetErrStat ( ErrID_Fatal, ' NrOutFile must be 1 or 2 or 3 or 4', ErrStat, ErrMsg, RoutineName ) + + if (InputFileData%NrOutFile /= 1 .and. InputFileData%NrOutFile /= 2 .and. InputFileData%NrOutFile /= 3 .and. InputFileData%NrOutFile /= 4) then + call SetErrStat ( ErrID_Fatal, ' NrOutFile must be 1 or 2 or 3 or 4', ErrStat, ErrMsg, RoutineName ) + end if + + if (InputFileData%AA_Bl_Prcntge > 100.0 .or. InputFileData%AA_Bl_Prcntge < 0.0) then + call SetErrStat ( ErrID_Fatal, ' AA_Bl_Prcntge must be between 0 and 100%', ErrStat, ErrMsg, RoutineName ) end if + END SUBROUTINE ValidateInputData !---------------------------------------------------------------------------------------------------------------------------------- @@ -747,9 +757,8 @@ subroutine AA_WriteOutputLine(y, t, p, errStat, errMsg) ENDIF end subroutine AA_WriteOutputLine !---------------------------------------------------------------------------------------------------------------------------------- -SUBROUTINE Calc_WriteOutput( p, u, m, y, ErrStat, ErrMsg ) +SUBROUTINE Calc_WriteOutput( p, m, y, ErrStat, ErrMsg ) TYPE(AA_ParameterType), INTENT(IN ) :: p ! The module parameters - TYPE(AA_InputType), INTENT(IN ) :: u ! inputs TYPE(AA_MiscVarType), INTENT(INOUT) :: m ! misc variables TYPE(AA_OutputType), INTENT(INOUT) :: y ! outputs INTEGER(IntKi), INTENT( OUT) :: ErrStat ! The error status code diff --git a/modules/aerodyn/src/AeroAcoustics_Registry.txt b/modules/aerodyn/src/AeroAcoustics_Registry.txt index b2d9e98b58..e858c0e1de 100644 --- a/modules/aerodyn/src/AeroAcoustics_Registry.txt +++ b/modules/aerodyn/src/AeroAcoustics_Registry.txt @@ -29,14 +29,13 @@ typedef AeroAcoustics/AA InitInputType CHARACTER(1024) InputFi typedef ^ InitInputType IntKi NumBlades - - - "Number of blades on the turbine" typedef ^ InitInputType IntKi NumBlNds - - - "Number of blade nodes" typedef ^ InitInputType CHARACTER(1024) RootName - - - "RootName for writing output files" - -typedef ^ InitInputType ReKi BlSpn {:}{:} - - "Span at blade node" m -typedef ^ InitInputType ReKi BlChord {:}{:} - - "Chord at blade node" m +typedef ^ InitInputType ReKi BlSpn {:}{:} - - "Span at blade node" m +typedef ^ InitInputType ReKi BlChord {:}{:} - - "Chord at blade node" m typedef ^ InitInputType ReKi AirDens - - - "Air density" kg/m^3 typedef ^ InitInputType ReKi KinVisc - - - "Kinematic air viscosity" m^2/s typedef ^ InitInputType ReKi SpdSound - - - "Speed of sound" m/s -typedef ^ InitInputType ReKi HubHeight - - - "Hub Height" m +typedef ^ InitInputType ReKi HubHeight - - - "Hub Height" m typedef ^ InitInputType IntKi BlAFID {:}{:} - - "Index of airfoil data file for blade node location [array of numBladeNodes by numBlades]" - -typedef ^ InitInputType AFI_ParameterType AFInfo {:} - - "Airfoil information structure containing the aerodynamic center and airfoil shape coordinates" # # Define outputs from the initialization routine here: typedef ^ InitOutputType CHARACTER(20) WriteOutputHdr {:} - - "Names of the output-to-file channels" - @@ -50,54 +49,52 @@ typedef ^ InitOutputType CHARACTER(25) WriteOutputU # # ..... Primary Input file data ................................................................................................... -typedef ^ AA_InputFile DbKi DT_AA - - - "Time interval for aerodynamic calculations {or \"default\"}" s -typedef ^ AA_InputFile IntKi IBLUNT - - - "FLAG TO COMPUTE BLUNTNESS NOISE" - -typedef ^ AA_InputFile IntKi ILAM - - - "FLAG TO COMPUTE LBL NOISE {0=off, 1=BPM calculation}" - -typedef ^ AA_InputFile IntKi ITIP - - - "FLAG TO COMPUTE TIP NOISE {0=off, 1=on}" - -typedef ^ AA_InputFile IntKi ITRIP - - - "FLAG TO TRIP BOUNDARY LAYER {0=none, 1 (heavily tripped BL Calculation), 2 (lightly tripped BL)}" - -typedef ^ AA_InputFile IntKi ITURB - - - "FLAG TO COMPUTE TBLTE NOISE {0=none, 1 (BPM), 2 (TNO)}" - -typedef ^ AA_InputFile IntKi IInflow - - - "FLAG TO COMPUTE Turbulent Inflow NOISE {0=none, 1 (only Amiet), 2 (Full Guidati), 3 (Simplified Guidati)}" - -typedef ^ AA_InputFile IntKi X_BLMethod - - - "Integer describing calculation method for boundary layer properties, = 1 BPM = 2 Pretabulated" - -typedef ^ AA_InputFile IntKi TICalcMeth - - - "TICalcMeth" - -typedef ^ AA_InputFile IntKi NReListBL - - - "Number of values of ReListBL" - -typedef ^ AA_InputFile Logical aweightflag - - - "Integer a weighting call" - -typedef ^ AA_InputFile Logical ROUND - - - "LOGICAL INDICATING ROUNDED TIP" - -typedef ^ AA_InputFile ReKi ALPRAT - - - "TIP LIFT CURVE SLOPE" - -typedef ^ AA_InputFile IntKi AA_Bl_Prcntge - - - "see the AeroAcoustics input file for description " - -typedef ^ AA_InputFile IntKi NrObsLoc - - - "Number of observer locations " - -typedef ^ AA_InputFile ReKi ObsXYZ {:}{:} - - "Observer location in tower-base coordinate (X-Y-Z)" m -typedef ^ AA_InputFile AA_BladePropsType BladeProps {:} - - "blade property information from blade input files" - -typedef ^ AA_InputFile IntKi NrOutFile - - - "Nr of output files" - -typedef ^ AA_InputFile CHARACTER(1024) AAoutfile {4} - - "AAoutfile for writing output files" - -typedef ^ AA_InputFile CHARACTER(1024) FTitle - - - "File Title: the 2nd line of the input file, which contains a description of its contents" - -typedef ^ AA_InputFile DBKi AAStart - - - "Time after which to calculate AA" s -typedef ^ AA_InputFile ReKi TI - - - "Average rotor incident turbulence intensity" - -typedef ^ AA_InputFile ReKi avgV - - - "Average wind speed" - -typedef ^ AA_InputFile ReKi Lturb - - - "Turbulent lengthscale in Amiet model" - -typedef ^ AA_InputFile ReKi ReListBL {:} - - "" - -typedef ^ AA_InputFile ReKi AoAListBL {:} - - "" deg -typedef ^ AA_InputFile ReKi Pres_DispThick {:}{:}{:} - - "" -typedef ^ AA_InputFile ReKi Suct_DispThick {:}{:}{:} - - "" -typedef ^ AA_InputFile ReKi Pres_BLThick {:}{:}{:} - - "" -typedef ^ AA_InputFile ReKi Suct_BLThick {:}{:}{:} - - "" -typedef ^ AA_InputFile ReKi Pres_Cf {:}{:}{:} - - "" -typedef ^ AA_InputFile ReKi Suct_Cf {:}{:}{:} - - "" -typedef ^ AA_InputFile ReKi Pres_EdgeVelRat {:}{:}{:} - - "" -typedef ^ AA_InputFile ReKi Suct_EdgeVelRat {:}{:}{:} - - "" +typedef ^ AA_InputFile DbKi DT_AA - - - "Time interval for aerodynamic calculations {or \"default\"}" s +typedef ^ AA_InputFile IntKi IBLUNT - - - "FLAG TO COMPUTE BLUNTNESS NOISE" - +typedef ^ AA_InputFile IntKi ILAM - - - "FLAG TO COMPUTE LBL NOISE {0=off, 1=BPM calculation}" - +typedef ^ AA_InputFile IntKi ITIP - - - "FLAG TO COMPUTE TIP NOISE {0=off, 1=on}" - +typedef ^ AA_InputFile IntKi ITRIP - - - "FLAG TO TRIP BOUNDARY LAYER {0=none, 1 (heavily tripped BL Calculation), 2 (lightly tripped BL)}" - +typedef ^ AA_InputFile IntKi ITURB - - - "FLAG TO COMPUTE TBLTE NOISE {0=none, 1 (BPM), 2 (TNO)}" - +typedef ^ AA_InputFile IntKi IInflow - - - "FLAG TO COMPUTE Turbulent Inflow NOISE {0=none, 1 (only Amiet), 2 (Full Guidati), 3 (Simplified Guidati)}" - +typedef ^ AA_InputFile IntKi X_BLMethod - - - "Integer describing calculation method for boundary layer properties, = 1 BPM = 2 Pretabulated" - +typedef ^ AA_InputFile IntKi TICalcMeth - - - "TICalcMeth" - +typedef ^ AA_InputFile IntKi NReListBL - - - "Number of values of ReListBL" - +typedef ^ AA_InputFile Logical aweightflag - - - "Integer a weighting call" - +typedef ^ AA_InputFile Logical ROUND - - - "LOGICAL INDICATING ROUNDED TIP" - +typedef ^ AA_InputFile ReKi ALPRAT - - - "TIP LIFT CURVE SLOPE" - +typedef ^ AA_InputFile IntKi AA_Bl_Prcntge - - - "see the AeroAcoustics input file for description " - +typedef ^ AA_InputFile IntKi NrObsLoc - - - "Number of observer locations " - +typedef ^ AA_InputFile ReKi ObsXYZ {:}{:} - - "Observer location in tower-base coordinate (X-Y-Z)" m +typedef ^ AA_InputFile AA_BladePropsType BladeProps {:} - - "blade property information from blade input files" - +typedef ^ AA_InputFile IntKi NrOutFile - - - "Nr of output files" - +typedef ^ AA_InputFile CHARACTER(1024) AAoutfile {4} - - "AAoutfile for writing output files" - +typedef ^ AA_InputFile CHARACTER(1024) FTitle - - - "File Title: the 2nd line of the input file, which contains a description of its contents" - +typedef ^ AA_InputFile DBKi AAStart - - - "Time after which to calculate AA" s +typedef ^ AA_InputFile ReKi TI - - - "Average rotor incident turbulence intensity" - +typedef ^ AA_InputFile ReKi avgV - - - "Average wind speed" - +typedef ^ AA_InputFile ReKi Lturb - - - "Turbulent lengthscale in Amiet model" - +typedef ^ AA_InputFile ReKi ReListBL {:} - - "" - +typedef ^ AA_InputFile ReKi AoAListBL {:} - - "" deg +typedef ^ AA_InputFile ReKi Pres_DispThick {:}{:}{:} - - "" +typedef ^ AA_InputFile ReKi Suct_DispThick {:}{:}{:} - - "" +typedef ^ AA_InputFile ReKi Pres_BLThick {:}{:}{:} - - "" +typedef ^ AA_InputFile ReKi Suct_BLThick {:}{:}{:} - - "" +typedef ^ AA_InputFile ReKi Pres_Cf {:}{:}{:} - - "" +typedef ^ AA_InputFile ReKi Suct_Cf {:}{:}{:} - - "" +typedef ^ AA_InputFile ReKi Pres_EdgeVelRat {:}{:}{:} - - "" +typedef ^ AA_InputFile ReKi Suct_EdgeVelRat {:}{:}{:} - - "" # ..... States .................................................................................................................... # Define continuous (differentiable) states here: # -typedef ^ ContinuousStateType SiKi DummyContState - - - "Remove this variable if you have continuous states" - +#typedef ^ ContinuousStateType SiKi DummyContState - - - "Remove this variable if you have continuous states" - # # Define discrete (nondifferentiable) states here: -typedef ^ DiscreteStateType ReKi TIVx {:}{:} - - "Vx St. deviat" - -typedef ^ DiscreteStateType ReKi MeanVxVyVz {:}{:} - - "Vrel Cumu. Mean" - -typedef ^ DiscreteStateType ReKi RegVxStor {:}{:}{:} - - "VxVyVz Store for fft or TI - dissipation" - -typedef ^ DiscreteStateType ReKi RegionTIDelete {:}{:} - - "" - +typedef ^ DiscreteStateType ReKi TIVx {:}{:} - - "Vx St. deviat" - +typedef ^ DiscreteStateType ReKi RegVxStor {:}{:}{:} - - "VxVyVz Store for fft or TI - dissipation" - # # Define constraint states here: -typedef ^ ConstraintStateType SiKi DummyConstrState - - - "Remove this variable if you have states" - +#typedef ^ ConstraintStateType SiKi DummyConstrState - - - "Remove this variable if you have states" - # # Define "other" states here: typedef ^ OtherStateType IntKi allregcounter {:}{:} - - "" - @@ -111,7 +108,7 @@ typedef ^ MiscVarType ReKi ChordAn typedef ^ MiscVarType ReKi SpanAngleLE {:}{:}{:} - - "C" - typedef ^ MiscVarType ReKi rTEtoObserve {:}{:}{:} - - "C" - typedef ^ MiscVarType ReKi rLEtoObserve {:}{:}{:} - - "C" - -typedef ^ MiscVarType ReKi LE_Location {:}{:}{:} - - "Height of Leading Edge for calculation of TI and Scales if needed" - +typedef ^ MiscVarType ReKi LE_Location {:}{:}{:} - - "Height of Leading Edge for calculation of TI and Scales if needed" - typedef ^ MiscVarType ReKi RotSpeedAoA - - - "C" - typedef ^ MiscVarType ReKi SPLLBL {:} - - "C" - typedef ^ MiscVarType ReKi SPLP {:} - - "C" - @@ -121,16 +118,16 @@ typedef ^ MiscVarType ReKi SPLTIP typedef ^ MiscVarType ReKi SPLTI {:} - - "C" - typedef ^ MiscVarType ReKi SPLTIGui {:} - - "C" - typedef ^ MiscVarType ReKi SPLBLUNT {:} - - "C" - -typedef ^ MiscVarType ReKi CfVar {:} - - "Output Skin friction coef Pressure Side" - -typedef ^ MiscVarType ReKi d99Var {:} - - "BL Output " - -typedef ^ MiscVarType ReKi dStarVar {:} - - "BL Output " - -typedef ^ MiscVarType ReKi EdgeVelVar {:} - - "BL Output " - -typedef ^ MiscVarType IntKi LastIndex {2} - - "index for BL param interpolation" - +typedef ^ MiscVarType ReKi CfVar {2} - - "Output Skin friction coef Pressure Side" - +typedef ^ MiscVarType ReKi d99Var {2} - - "BL Output " - +typedef ^ MiscVarType ReKi dStarVar {2} - - "BL Output " - +typedef ^ MiscVarType ReKi EdgeVelVar {2} - - "BL Output " - +typedef ^ MiscVarType IntKi LastIndex {2} - - "index for BL param interpolation" - # arrays for calculating WriteOutput values -typedef ^ MiscVarType ReKi SumSpecNoiseSep {:}{:}{:} - - "Spectra of summed noise level of all blades and blade nodes for each receiver and frequency" SPL -typedef ^ MiscVarType ReKi OASPL {:}{:}{:} - - "summed noise level for each blade and blade nodes and receiver " SPL -typedef ^ MiscVarType ReKi DirectiviOutput {:} - - " " SPL -typedef ^ MiscVarType ReKi PtotalFreq {:}{:} - - "SPL for each observer and frequency" +typedef ^ MiscVarType ReKi SumSpecNoiseSep {:}{:}{:} - - "Spectra of summed noise level of all blades and blade nodes for each receiver and frequency" SPL +typedef ^ MiscVarType ReKi OASPL {:}{:}{:} - - "summed noise level for each blade and blade nodes and receiver " SPL +typedef ^ MiscVarType ReKi DirectiviOutput {:} - - " " SPL +typedef ^ MiscVarType ReKi PtotalFreq {:}{:} - - "SPL for each observer and frequency" # ..... Parameters ................................................................................................................ @@ -152,69 +149,65 @@ typedef ^ ParameterType IntKi NumBlNd typedef ^ ParameterType ReKi AirDens - - - "Air density" kg/m^3 typedef ^ ParameterType ReKi KinVisc - - - "Kinematic air viscosity" m^2/s typedef ^ ParameterType ReKi SpdSound - - - "Speed of sound" m/s -typedef ^ ParameterType ReKi HubHeight - - - "Hub height" m -typedef ^ ParameterType ReKi toptip - - - "Top Tip Height = Hub height plus radius" m -typedef ^ ParameterType ReKi bottip - - - "Bottom Tip Height = Hub height minus radius" m -typedef ^ ParameterType ReKi rotorregionlimitsVert {:} - - "" -typedef ^ ParameterType ReKi rotorregionlimitsHorz {:} - - "" -typedef ^ ParameterType ReKi rotorregionlimitsalph {:} - - "" -typedef ^ ParameterType ReKi rotorregionlimitsrad {:} - - "" +typedef ^ ParameterType ReKi HubHeight - - - "Hub height" m +typedef ^ ParameterType IntKi RotorRegion_k_minus1 {:}{:} - - "index array for RotorRegion blade span location" - +typedef ^ ParameterType IntKi NumRotorRegionLimitsAlph - - - "size of RotorRegionLimitsAlph array" - +typedef ^ ParameterType IntKi NumRotorRegionLimitsRad - - - "size of RotorRegionLimitsRad array" - typedef ^ ParameterType IntKi NrObsLoc - - - "Number of observer locations " - typedef ^ ParameterType Logical aweightflag - - - " " - typedef ^ ParameterType Logical TxtFileOutput - - - " " - -typedef ^ ParameterType DBKi AAStart - - - "Time after which to calculate AA" s +typedef ^ ParameterType DBKi AAStart - - - "Time after which to calculate AA" s typedef ^ ParameterType ReKi ObsXYZ {:}{:} - - "Observer location in tower-base coordinate (X-Y-Z)" m typedef ^ ParameterType ReKi FreqList {:} - - "List of Acoustic Frequencies to Calculate" Hz -typedef ^ ParameterType ReKi Aweight {:} - - "List of Acoustic Frequencies a weighting" dB -typedef ^ ParameterType IntKi total_sampleTI - - - "Total FFT Sample amount for dissipation calculation" - -typedef ^ ParameterType IntKi AA_Bl_Prcntge - - - "The Percentage of the Blade which the noise is calculated" % +typedef ^ ParameterType ReKi Aweight {:} - - "List of Acoustic Frequencies a weighting" dB +typedef ^ ParameterType IntKi Num_total_sampleTI - - - "Total FFT Sample amount for dissipation calculation" - typedef ^ ParameterType IntKi startnode - - - "Corersponding node to the noise calculation percentage of the blade" - -typedef ^ ParameterType ReKi Lturb - - - "Turbulent lengthscale in Amiet model" m -typedef ^ ParameterType ReKi avgV - - - "Average wind speed to compute incident turbulence intensity" m -typedef ^ ParameterType ReKi TI - - - "Rotor incident turbulent intensity" -typedef ^ ParameterType CHARACTER(1024) FTitle - - - "File Title: the 2nd line of the input file, which contains a description of its contents" - +typedef ^ ParameterType ReKi Lturb - - - "Turbulent lengthscale in Amiet model" m +typedef ^ ParameterType ReKi avgV - - - "Average wind speed to compute incident turbulence intensity" m +typedef ^ ParameterType ReKi TI - - - "Rotor incident turbulent intensity" +typedef ^ ParameterType CHARACTER(1024) FTitle - - - "File Title: the 2nd line of the input file, which contains a description of its contents" - # parameters for output -typedef ^ ParameterType character(20) outFmt - - - "Format specifier" "-" -typedef ^ ParameterType IntKi NrOutFile - - - "Nr of output files" - -typedef ^ ParameterType IntKi NumOuts - - - "Number of parameters in the output list (number of outputs requested)" - -typedef ^ ParameterType IntKi NumOutsAll {4} - - "Number of parameters in the output list (number of outputs requested)" - -typedef ^ ParameterType IntKi unOutFile {4} - - "unit number for writing output file" "-" -typedef ^ ParameterType CHARACTER(1024) RootName - - - "RootName for writing output files" - -typedef ^ ParameterType ReKi StallStart {:}{:} - - "ation" - -typedef ^ ParameterType ReKi TEThick {:}{:} - - "ation" - -typedef ^ ParameterType ReKi TEAngle {:}{:} - - "ation" - -typedef ^ ParameterType ReKi AerCent {:}{:}{:} - - "ation" - -typedef ^ ParameterType IntKi BlAFID {:}{:} - - "Index of airfoil data file for blade node location [array of numBladeNodes by numBlades]" - -typedef ^ ParameterType AFI_ParameterType AFInfo {:} - - "Airfoil information structure containing the aerodynamic center and airfoil shape coordinates" -typedef ^ ParameterType ReKi AFLECo {:}{:}{:} - - "Dimensionalized " -typedef ^ ParameterType ReKi AFTECo {:}{:}{:} - - -typedef ^ ParameterType ReKi BlSpn {:}{:} - - "Span at blade node" m -typedef ^ ParameterType ReKi BlChord {:}{:} - - "Chord at blade node" m -typedef ^ ParameterType ReKi ReListBL {:} - - "BL list of Reynolds" - -typedef ^ ParameterType ReKi AOAListBL {:} - - "BL list of Angle Of Attack " deg -typedef ^ ParameterType ReKi dStarAll1 {:}{:}{:} - - "Output Disp Thickness Suction Side" m -typedef ^ ParameterType ReKi dStarAll2 {:}{:}{:} - - "Output Disp Thickness Pressure Side" m -typedef ^ ParameterType ReKi d99All1 {:}{:}{:} - - "Output B.L. Thickness Suction Side" m -typedef ^ ParameterType ReKi d99All2 {:}{:}{:} - - "Output B.L. Thickness Pressure Side" m -typedef ^ ParameterType ReKi CfAll1 {:}{:}{:} - - "Output Skin friction coef Suction Side" - -typedef ^ ParameterType ReKi CfAll2 {:}{:}{:} - - "Output Skin friction coef Pressure Side" - -typedef ^ ParameterType ReKi EdgeVelRat1 {:}{:}{:} - - "Output Edge Velocity Ratio Suction" - -typedef ^ ParameterType ReKi EdgeVelRat2 {:}{:}{:} - - "Output Edge Velocity Ratio Pressure Side" - -typedef ^ ParameterType ReKi AFThickGuida {:}{:} - - "1 and 10 percent thickness t/c used for Simplified Guidati" +typedef ^ ParameterType character(20) outFmt - - - "Format specifier" "-" +typedef ^ ParameterType IntKi NrOutFile - - - "Nr of output files" - +typedef ^ ParameterType IntKi NumOuts - - - "Number of parameters in the output list (number of outputs requested)" - +typedef ^ ParameterType IntKi NumOutsAll {4} - - "Number of parameters in the output list (number of outputs requested)" - +typedef ^ ParameterType IntKi unOutFile {4} - - "unit number for writing output file" "-" +typedef ^ ParameterType CHARACTER(1024) RootName - - - "RootName for writing output files" - +typedef ^ ParameterType ReKi StallStart {:}{:} - - "ation" - +typedef ^ ParameterType ReKi TEThick {:}{:} - - "ation" - +typedef ^ ParameterType ReKi TEAngle {:}{:} - - "ation" - +typedef ^ ParameterType ReKi AerCent {:}{:}{:} - - "ation" - +typedef ^ ParameterType IntKi BlAFID {:}{:} - - "Index of airfoil data file for blade node location [array of numBladeNodes by numBlades]" - +typedef ^ ParameterType ReKi AFLECo {:}{:}{:} - - "Dimensionalized " +typedef ^ ParameterType ReKi AFTECo {:}{:}{:} - - +typedef ^ ParameterType ReKi BlSpn {:}{:} - - "Span at blade node" m +typedef ^ ParameterType ReKi BlElemSpn {:}{:} - - "Element span at blade node" m +typedef ^ ParameterType ReKi BlChord {:}{:} - - "Chord at blade node" m +typedef ^ ParameterType ReKi ReListBL {:} - - "BL list of Reynolds" - +typedef ^ ParameterType ReKi AOAListBL {:} - - "BL list of Angle Of Attack " deg +typedef ^ ParameterType ReKi dStarAll1 {:}{:}{:} - - "Output Disp Thickness Suction Side" m +typedef ^ ParameterType ReKi dStarAll2 {:}{:}{:} - - "Output Disp Thickness Pressure Side" m +typedef ^ ParameterType ReKi d99All1 {:}{:}{:} - - "Output B.L. Thickness Suction Side" m +typedef ^ ParameterType ReKi d99All2 {:}{:}{:} - - "Output B.L. Thickness Pressure Side" m +typedef ^ ParameterType ReKi CfAll1 {:}{:}{:} - - "Output Skin friction coef Suction Side" - +typedef ^ ParameterType ReKi CfAll2 {:}{:}{:} - - "Output Skin friction coef Pressure Side" - +typedef ^ ParameterType ReKi EdgeVelRat1 {:}{:}{:} - - "Output Edge Velocity Ratio Suction" - +typedef ^ ParameterType ReKi EdgeVelRat2 {:}{:}{:} - - "Output Edge Velocity Ratio Pressure Side" - +typedef ^ ParameterType ReKi AFThickGuida {:}{:} - - "1 and 10 percent thickness t/c used for Simplified Guidati" # ..... Inputs .................................................................................................................... # Define inputs that are contained on the mesh here: -typedef ^ InputType ReKi RotGtoL {:}{:}{:}{:} - - "3x3 rotation matrix transform a vector from the local airfoil coordinate system to the global inertial coordinate system" - -typedef ^ InputType ReKi AeroCent_G {:}{:}{:} - - "location in global coordinates of the blade element aerodynamic center. 1st index = vector components, 2nd index = blade node, 3rd index = blade" - -typedef ^ InputType ReKi Vrel {:}{:} - - "Vrel" - -typedef ^ InputType ReKi AoANoise {:}{:} - - "Angle of attack" rad -typedef ^ InputType ReKi Inflow {:}{:}{:} - - "atmospheric undisturbed flow on blade" +typedef ^ InputType ReKi RotGtoL {:}{:}{:}{:} - - "3x3 rotation matrix transform a vector from the local airfoil coordinate system to the global inertial coordinate system" - +typedef ^ InputType ReKi AeroCent_G {:}{:}{:} - - "location in global coordinates of the blade element aerodynamic center. 1st index = vector components, 2nd index = blade node, 3rd index = blade" - +typedef ^ InputType ReKi Vrel {:}{:} - - "Vrel" - +typedef ^ InputType ReKi AoANoise {:}{:} - - "Angle of attack" rad +typedef ^ InputType ReKi Inflow {:}{:}{:} - - "atmospheric undisturbed flow on blade" # ..... Outputs ................................................................................................................... # Define outputs that are contained on the mesh here: # Define outputs that are not on this mesh here: -typedef ^ OutputType ReKi WriteOutputForPE {:} - - "Data to be written to an output file: see WriteOutputHdr for names of each variable" "see WriteOutputUnt" -typedef ^ OutputType ReKi WriteOutput {:} - - "Data to be written to an output file: see WriteOutputHdr for names of each variable" "see WriteOutputUnt" -typedef ^ OutputType ReKi WriteOutputSep {:} - - "Data to be written to an output file: see WriteOutputHdr for names of each variable" "see WriteOutputUnt" -typedef ^ OutputType ReKi WriteOutputNodes {:} - - "Data to be written to an output file: see WriteOutputHdr for names of each variable" "see WriteOutputUnt" +typedef ^ OutputType ReKi WriteOutputForPE {:} - - "Data to be written to an output file: see WriteOutputHdr for names of each variable" "see WriteOutputUnt" +typedef ^ OutputType ReKi WriteOutput {:} - - "Data to be written to an output file: see WriteOutputHdr for names of each variable" "see WriteOutputUnt" +typedef ^ OutputType ReKi WriteOutputSep {:} - - "Data to be written to an output file: see WriteOutputHdr for names of each variable" "see WriteOutputUnt" +typedef ^ OutputType ReKi WriteOutputNodes {:} - - "Data to be written to an output file: see WriteOutputHdr for names of each variable" "see WriteOutputUnt" diff --git a/modules/aerodyn/src/AeroAcoustics_TNO.f90 b/modules/aerodyn/src/AeroAcoustics_TNO.f90 index 7cf5941b0b..00053096fb 100644 --- a/modules/aerodyn/src/AeroAcoustics_TNO.f90 +++ b/modules/aerodyn/src/AeroAcoustics_TNO.f90 @@ -72,6 +72,7 @@ function SPL_integrate(Omega,limits,ISSUCTION, & co = real(SpdSound, TNOKi) rho = real(AirDens, TNOKi) nu = real(KinVisc, TNOKi) + ! Blade node values Cf = real(Cfall, TNOKi) d99 = real(d99all, TNOKi) diff --git a/modules/aerodyn/src/AeroAcoustics_Types.f90 b/modules/aerodyn/src/AeroAcoustics_Types.f90 index d3676a39d0..78edce7d4c 100644 --- a/modules/aerodyn/src/AeroAcoustics_Types.f90 +++ b/modules/aerodyn/src/AeroAcoustics_Types.f90 @@ -53,7 +53,6 @@ MODULE AeroAcoustics_Types REAL(ReKi) :: SpdSound = 0.0_ReKi !< Speed of sound [m/s] REAL(ReKi) :: HubHeight = 0.0_ReKi !< Hub Height [m] INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: BlAFID !< Index of airfoil data file for blade node location [array of numBladeNodes by numBlades] [-] - TYPE(AFI_ParameterType) , DIMENSION(:), ALLOCATABLE :: AFInfo !< Airfoil information structure containing the aerodynamic center and airfoil shape coordinates [-] END TYPE AA_InitInputType ! ======================= ! ========= AA_InitOutputType ======= @@ -106,24 +105,12 @@ MODULE AeroAcoustics_Types REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: Suct_EdgeVelRat !< [-] END TYPE AA_InputFile ! ======================= -! ========= AA_ContinuousStateType ======= - TYPE, PUBLIC :: AA_ContinuousStateType - REAL(SiKi) :: DummyContState = 0.0_R4Ki !< Remove this variable if you have continuous states [-] - END TYPE AA_ContinuousStateType -! ======================= ! ========= AA_DiscreteStateType ======= TYPE, PUBLIC :: AA_DiscreteStateType REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: TIVx !< Vx St. deviat [-] - REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: MeanVxVyVz !< Vrel Cumu. Mean [-] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: RegVxStor !< VxVyVz Store for fft or TI - dissipation [-] - REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: RegionTIDelete !< [-] END TYPE AA_DiscreteStateType ! ======================= -! ========= AA_ConstraintStateType ======= - TYPE, PUBLIC :: AA_ConstraintStateType - REAL(SiKi) :: DummyConstrState = 0.0_R4Ki !< Remove this variable if you have states [-] - END TYPE AA_ConstraintStateType -! ======================= ! ========= AA_OtherStateType ======= TYPE, PUBLIC :: AA_OtherStateType INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: allregcounter !< [-] @@ -148,10 +135,10 @@ MODULE AeroAcoustics_Types REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: SPLTI !< C [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: SPLTIGui !< C [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: SPLBLUNT !< C [-] - REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: CfVar !< Output Skin friction coef Pressure Side [-] - REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: d99Var !< BL Output [-] - REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: dStarVar !< BL Output [-] - REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: EdgeVelVar !< BL Output [-] + REAL(ReKi) , DIMENSION(1:2) :: CfVar = 0.0_ReKi !< Output Skin friction coef Pressure Side [-] + REAL(ReKi) , DIMENSION(1:2) :: d99Var = 0.0_ReKi !< BL Output [-] + REAL(ReKi) , DIMENSION(1:2) :: dStarVar = 0.0_ReKi !< BL Output [-] + REAL(ReKi) , DIMENSION(1:2) :: EdgeVelVar = 0.0_ReKi !< BL Output [-] INTEGER(IntKi) , DIMENSION(1:2) :: LastIndex = 0_IntKi !< index for BL param interpolation [-] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: SumSpecNoiseSep !< Spectra of summed noise level of all blades and blade nodes for each receiver and frequency [SPL] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: OASPL !< summed noise level for each blade and blade nodes and receiver [SPL] @@ -178,12 +165,9 @@ MODULE AeroAcoustics_Types REAL(ReKi) :: KinVisc = 0.0_ReKi !< Kinematic air viscosity [m^2/s] REAL(ReKi) :: SpdSound = 0.0_ReKi !< Speed of sound [m/s] REAL(ReKi) :: HubHeight = 0.0_ReKi !< Hub height [m] - REAL(ReKi) :: toptip = 0.0_ReKi !< Top Tip Height = Hub height plus radius [m] - REAL(ReKi) :: bottip = 0.0_ReKi !< Bottom Tip Height = Hub height minus radius [m] - REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: rotorregionlimitsVert !< [-] - REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: rotorregionlimitsHorz !< [-] - REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: rotorregionlimitsalph !< [-] - REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: rotorregionlimitsrad !< [-] + INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: RotorRegion_k_minus1 !< index array for RotorRegion blade span location [-] + INTEGER(IntKi) :: NumRotorRegionLimitsAlph = 0_IntKi !< size of RotorRegionLimitsAlph array [-] + INTEGER(IntKi) :: NumRotorRegionLimitsRad = 0_IntKi !< size of RotorRegionLimitsRad array [-] INTEGER(IntKi) :: NrObsLoc = 0_IntKi !< Number of observer locations [-] LOGICAL :: aweightflag = .false. !< [-] LOGICAL :: TxtFileOutput = .false. !< [-] @@ -191,8 +175,7 @@ MODULE AeroAcoustics_Types REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: ObsXYZ !< Observer location in tower-base coordinate (X-Y-Z) [m] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: FreqList !< List of Acoustic Frequencies to Calculate [Hz] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: Aweight !< List of Acoustic Frequencies a weighting [dB] - INTEGER(IntKi) :: total_sampleTI = 0_IntKi !< Total FFT Sample amount for dissipation calculation [-] - INTEGER(IntKi) :: AA_Bl_Prcntge = 0_IntKi !< The Percentage of the Blade which the noise is calculated [%] + INTEGER(IntKi) :: Num_total_sampleTI = 0_IntKi !< Total FFT Sample amount for dissipation calculation [-] INTEGER(IntKi) :: startnode = 0_IntKi !< Corersponding node to the noise calculation percentage of the blade [-] REAL(ReKi) :: Lturb = 0.0_ReKi !< Turbulent lengthscale in Amiet model [m] REAL(ReKi) :: avgV = 0.0_ReKi !< Average wind speed to compute incident turbulence intensity [m] @@ -209,10 +192,10 @@ MODULE AeroAcoustics_Types REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: TEAngle !< ation [-] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: AerCent !< ation [-] INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: BlAFID !< Index of airfoil data file for blade node location [array of numBladeNodes by numBlades] [-] - TYPE(AFI_ParameterType) , DIMENSION(:), ALLOCATABLE :: AFInfo !< Airfoil information structure containing the aerodynamic center and airfoil shape coordinates [-] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: AFLECo !< Dimensionalized [-] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: AFTECo REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: BlSpn !< Span at blade node [m] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: BlElemSpn !< Element span at blade node [m] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: BlChord !< Chord at blade node [m] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: ReListBL !< BL list of Reynolds [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: AOAListBL !< BL list of Angle Of Attack [deg] @@ -244,16 +227,15 @@ MODULE AeroAcoustics_Types REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: WriteOutputNodes !< Data to be written to an output file: see WriteOutputHdr for names of each variable [see WriteOutputUnt] END TYPE AA_OutputType ! ======================= - integer(IntKi), public, parameter :: AA_x_DummyContState = 1 ! AA%DummyContState - integer(IntKi), public, parameter :: AA_u_RotGtoL = 2 ! AA%RotGtoL - integer(IntKi), public, parameter :: AA_u_AeroCent_G = 3 ! AA%AeroCent_G - integer(IntKi), public, parameter :: AA_u_Vrel = 4 ! AA%Vrel - integer(IntKi), public, parameter :: AA_u_AoANoise = 5 ! AA%AoANoise - integer(IntKi), public, parameter :: AA_u_Inflow = 6 ! AA%Inflow - integer(IntKi), public, parameter :: AA_y_WriteOutputForPE = 7 ! AA%WriteOutputForPE - integer(IntKi), public, parameter :: AA_y_WriteOutput = 8 ! AA%WriteOutput - integer(IntKi), public, parameter :: AA_y_WriteOutputSep = 9 ! AA%WriteOutputSep - integer(IntKi), public, parameter :: AA_y_WriteOutputNodes = 10 ! AA%WriteOutputNodes + integer(IntKi), public, parameter :: AA_u_RotGtoL = 1 ! AA%RotGtoL + integer(IntKi), public, parameter :: AA_u_AeroCent_G = 2 ! AA%AeroCent_G + integer(IntKi), public, parameter :: AA_u_Vrel = 3 ! AA%Vrel + integer(IntKi), public, parameter :: AA_u_AoANoise = 4 ! AA%AoANoise + integer(IntKi), public, parameter :: AA_u_Inflow = 5 ! AA%Inflow + integer(IntKi), public, parameter :: AA_y_WriteOutputForPE = 6 ! AA%WriteOutputForPE + integer(IntKi), public, parameter :: AA_y_WriteOutput = 7 ! AA%WriteOutput + integer(IntKi), public, parameter :: AA_y_WriteOutputSep = 8 ! AA%WriteOutputSep + integer(IntKi), public, parameter :: AA_y_WriteOutputNodes = 9 ! AA%WriteOutputNodes contains @@ -304,10 +286,8 @@ subroutine AA_CopyInitInput(SrcInitInputData, DstInitInputData, CtrlCode, ErrSta integer(IntKi), intent(in ) :: CtrlCode integer(IntKi), intent( out) :: ErrStat character(*), intent( out) :: ErrMsg - integer(B4Ki) :: i1, i2 integer(B4Ki) :: LB(2), UB(2) integer(IntKi) :: ErrStat2 - character(ErrMsgLen) :: ErrMsg2 character(*), parameter :: RoutineName = 'AA_CopyInitInput' ErrStat = ErrID_None ErrMsg = '' @@ -355,32 +335,12 @@ subroutine AA_CopyInitInput(SrcInitInputData, DstInitInputData, CtrlCode, ErrSta end if DstInitInputData%BlAFID = SrcInitInputData%BlAFID end if - if (allocated(SrcInitInputData%AFInfo)) then - LB(1:1) = lbound(SrcInitInputData%AFInfo) - UB(1:1) = ubound(SrcInitInputData%AFInfo) - if (.not. allocated(DstInitInputData%AFInfo)) then - allocate(DstInitInputData%AFInfo(LB(1):UB(1)), stat=ErrStat2) - if (ErrStat2 /= 0) then - call SetErrStat(ErrID_Fatal, 'Error allocating DstInitInputData%AFInfo.', ErrStat, ErrMsg, RoutineName) - return - end if - end if - do i1 = LB(1), UB(1) - call AFI_CopyParam(SrcInitInputData%AFInfo(i1), DstInitInputData%AFInfo(i1), CtrlCode, ErrStat2, ErrMsg2) - call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - if (ErrStat >= AbortErrLev) return - end do - end if end subroutine subroutine AA_DestroyInitInput(InitInputData, ErrStat, ErrMsg) type(AA_InitInputType), intent(inout) :: InitInputData integer(IntKi), intent( out) :: ErrStat character(*), intent( out) :: ErrMsg - integer(B4Ki) :: i1, i2 - integer(B4Ki) :: LB(2), UB(2) - integer(IntKi) :: ErrStat2 - character(ErrMsgLen) :: ErrMsg2 character(*), parameter :: RoutineName = 'AA_DestroyInitInput' ErrStat = ErrID_None ErrMsg = '' @@ -393,23 +353,12 @@ subroutine AA_DestroyInitInput(InitInputData, ErrStat, ErrMsg) if (allocated(InitInputData%BlAFID)) then deallocate(InitInputData%BlAFID) end if - if (allocated(InitInputData%AFInfo)) then - LB(1:1) = lbound(InitInputData%AFInfo) - UB(1:1) = ubound(InitInputData%AFInfo) - do i1 = LB(1), UB(1) - call AFI_DestroyParam(InitInputData%AFInfo(i1), ErrStat2, ErrMsg2) - call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - end do - deallocate(InitInputData%AFInfo) - end if end subroutine subroutine AA_PackInitInput(RF, Indata) type(RegFile), intent(inout) :: RF type(AA_InitInputType), intent(in) :: InData character(*), parameter :: RoutineName = 'AA_PackInitInput' - integer(B4Ki) :: i1, i2 - integer(B4Ki) :: LB(2), UB(2) if (RF%ErrStat >= AbortErrLev) return call RegPack(RF, InData%InputFile) call RegPack(RF, InData%NumBlades) @@ -422,15 +371,6 @@ subroutine AA_PackInitInput(RF, Indata) call RegPack(RF, InData%SpdSound) call RegPack(RF, InData%HubHeight) call RegPackAlloc(RF, InData%BlAFID) - call RegPack(RF, allocated(InData%AFInfo)) - if (allocated(InData%AFInfo)) then - call RegPackBounds(RF, 1, lbound(InData%AFInfo), ubound(InData%AFInfo)) - LB(1:1) = lbound(InData%AFInfo) - UB(1:1) = ubound(InData%AFInfo) - do i1 = LB(1), UB(1) - call AFI_PackParam(RF, InData%AFInfo(i1)) - end do - end if if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -438,7 +378,6 @@ subroutine AA_UnPackInitInput(RF, OutData) type(RegFile), intent(inout) :: RF type(AA_InitInputType), intent(inout) :: OutData character(*), parameter :: RoutineName = 'AA_UnPackInitInput' - integer(B4Ki) :: i1, i2 integer(B4Ki) :: LB(2), UB(2) integer(IntKi) :: stat logical :: IsAllocAssoc @@ -454,19 +393,6 @@ subroutine AA_UnPackInitInput(RF, OutData) call RegUnpack(RF, OutData%SpdSound); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%HubHeight); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%BlAFID); if (RegCheckErr(RF, RoutineName)) return - if (allocated(OutData%AFInfo)) deallocate(OutData%AFInfo) - call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return - if (IsAllocAssoc) then - call RegUnpackBounds(RF, 1, LB, UB); if (RegCheckErr(RF, RoutineName)) return - allocate(OutData%AFInfo(LB(1):UB(1)),stat=stat) - if (stat /= 0) then - call SetErrStat(ErrID_Fatal, 'Error allocating OutData%AFInfo.', RF%ErrStat, RF%ErrMsg, RoutineName) - return - end if - do i1 = LB(1), UB(1) - call AFI_UnpackParam(RF, OutData%AFInfo(i1)) ! AFInfo - end do - end if end subroutine subroutine AA_CopyInitOutput(SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg) @@ -994,44 +920,6 @@ subroutine AA_UnPackInputFile(RF, OutData) call RegUnpackAlloc(RF, OutData%Suct_EdgeVelRat); if (RegCheckErr(RF, RoutineName)) return end subroutine -subroutine AA_CopyContState(SrcContStateData, DstContStateData, CtrlCode, ErrStat, ErrMsg) - type(AA_ContinuousStateType), intent(in) :: SrcContStateData - type(AA_ContinuousStateType), intent(inout) :: DstContStateData - integer(IntKi), intent(in ) :: CtrlCode - integer(IntKi), intent( out) :: ErrStat - character(*), intent( out) :: ErrMsg - character(*), parameter :: RoutineName = 'AA_CopyContState' - ErrStat = ErrID_None - ErrMsg = '' - DstContStateData%DummyContState = SrcContStateData%DummyContState -end subroutine - -subroutine AA_DestroyContState(ContStateData, ErrStat, ErrMsg) - type(AA_ContinuousStateType), intent(inout) :: ContStateData - integer(IntKi), intent( out) :: ErrStat - character(*), intent( out) :: ErrMsg - character(*), parameter :: RoutineName = 'AA_DestroyContState' - ErrStat = ErrID_None - ErrMsg = '' -end subroutine - -subroutine AA_PackContState(RF, Indata) - type(RegFile), intent(inout) :: RF - type(AA_ContinuousStateType), intent(in) :: InData - character(*), parameter :: RoutineName = 'AA_PackContState' - if (RF%ErrStat >= AbortErrLev) return - call RegPack(RF, InData%DummyContState) - if (RegCheckErr(RF, RoutineName)) return -end subroutine - -subroutine AA_UnPackContState(RF, OutData) - type(RegFile), intent(inout) :: RF - type(AA_ContinuousStateType), intent(inout) :: OutData - character(*), parameter :: RoutineName = 'AA_UnPackContState' - if (RF%ErrStat /= ErrID_None) return - call RegUnpack(RF, OutData%DummyContState); if (RegCheckErr(RF, RoutineName)) return -end subroutine - subroutine AA_CopyDiscState(SrcDiscStateData, DstDiscStateData, CtrlCode, ErrStat, ErrMsg) type(AA_DiscreteStateType), intent(in) :: SrcDiscStateData type(AA_DiscreteStateType), intent(inout) :: DstDiscStateData @@ -1055,18 +943,6 @@ subroutine AA_CopyDiscState(SrcDiscStateData, DstDiscStateData, CtrlCode, ErrSta end if DstDiscStateData%TIVx = SrcDiscStateData%TIVx end if - if (allocated(SrcDiscStateData%MeanVxVyVz)) then - LB(1:2) = lbound(SrcDiscStateData%MeanVxVyVz) - UB(1:2) = ubound(SrcDiscStateData%MeanVxVyVz) - if (.not. allocated(DstDiscStateData%MeanVxVyVz)) then - allocate(DstDiscStateData%MeanVxVyVz(LB(1):UB(1),LB(2):UB(2)), stat=ErrStat2) - if (ErrStat2 /= 0) then - call SetErrStat(ErrID_Fatal, 'Error allocating DstDiscStateData%MeanVxVyVz.', ErrStat, ErrMsg, RoutineName) - return - end if - end if - DstDiscStateData%MeanVxVyVz = SrcDiscStateData%MeanVxVyVz - end if if (allocated(SrcDiscStateData%RegVxStor)) then LB(1:3) = lbound(SrcDiscStateData%RegVxStor) UB(1:3) = ubound(SrcDiscStateData%RegVxStor) @@ -1079,18 +955,6 @@ subroutine AA_CopyDiscState(SrcDiscStateData, DstDiscStateData, CtrlCode, ErrSta end if DstDiscStateData%RegVxStor = SrcDiscStateData%RegVxStor end if - if (allocated(SrcDiscStateData%RegionTIDelete)) then - LB(1:2) = lbound(SrcDiscStateData%RegionTIDelete) - UB(1:2) = ubound(SrcDiscStateData%RegionTIDelete) - if (.not. allocated(DstDiscStateData%RegionTIDelete)) then - allocate(DstDiscStateData%RegionTIDelete(LB(1):UB(1),LB(2):UB(2)), stat=ErrStat2) - if (ErrStat2 /= 0) then - call SetErrStat(ErrID_Fatal, 'Error allocating DstDiscStateData%RegionTIDelete.', ErrStat, ErrMsg, RoutineName) - return - end if - end if - DstDiscStateData%RegionTIDelete = SrcDiscStateData%RegionTIDelete - end if end subroutine subroutine AA_DestroyDiscState(DiscStateData, ErrStat, ErrMsg) @@ -1103,15 +967,9 @@ subroutine AA_DestroyDiscState(DiscStateData, ErrStat, ErrMsg) if (allocated(DiscStateData%TIVx)) then deallocate(DiscStateData%TIVx) end if - if (allocated(DiscStateData%MeanVxVyVz)) then - deallocate(DiscStateData%MeanVxVyVz) - end if if (allocated(DiscStateData%RegVxStor)) then deallocate(DiscStateData%RegVxStor) end if - if (allocated(DiscStateData%RegionTIDelete)) then - deallocate(DiscStateData%RegionTIDelete) - end if end subroutine subroutine AA_PackDiscState(RF, Indata) @@ -1120,9 +978,7 @@ subroutine AA_PackDiscState(RF, Indata) character(*), parameter :: RoutineName = 'AA_PackDiscState' if (RF%ErrStat >= AbortErrLev) return call RegPackAlloc(RF, InData%TIVx) - call RegPackAlloc(RF, InData%MeanVxVyVz) call RegPackAlloc(RF, InData%RegVxStor) - call RegPackAlloc(RF, InData%RegionTIDelete) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -1135,47 +991,7 @@ subroutine AA_UnPackDiscState(RF, OutData) logical :: IsAllocAssoc if (RF%ErrStat /= ErrID_None) return call RegUnpackAlloc(RF, OutData%TIVx); if (RegCheckErr(RF, RoutineName)) return - call RegUnpackAlloc(RF, OutData%MeanVxVyVz); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%RegVxStor); if (RegCheckErr(RF, RoutineName)) return - call RegUnpackAlloc(RF, OutData%RegionTIDelete); if (RegCheckErr(RF, RoutineName)) return -end subroutine - -subroutine AA_CopyConstrState(SrcConstrStateData, DstConstrStateData, CtrlCode, ErrStat, ErrMsg) - type(AA_ConstraintStateType), intent(in) :: SrcConstrStateData - type(AA_ConstraintStateType), intent(inout) :: DstConstrStateData - integer(IntKi), intent(in ) :: CtrlCode - integer(IntKi), intent( out) :: ErrStat - character(*), intent( out) :: ErrMsg - character(*), parameter :: RoutineName = 'AA_CopyConstrState' - ErrStat = ErrID_None - ErrMsg = '' - DstConstrStateData%DummyConstrState = SrcConstrStateData%DummyConstrState -end subroutine - -subroutine AA_DestroyConstrState(ConstrStateData, ErrStat, ErrMsg) - type(AA_ConstraintStateType), intent(inout) :: ConstrStateData - integer(IntKi), intent( out) :: ErrStat - character(*), intent( out) :: ErrMsg - character(*), parameter :: RoutineName = 'AA_DestroyConstrState' - ErrStat = ErrID_None - ErrMsg = '' -end subroutine - -subroutine AA_PackConstrState(RF, Indata) - type(RegFile), intent(inout) :: RF - type(AA_ConstraintStateType), intent(in) :: InData - character(*), parameter :: RoutineName = 'AA_PackConstrState' - if (RF%ErrStat >= AbortErrLev) return - call RegPack(RF, InData%DummyConstrState) - if (RegCheckErr(RF, RoutineName)) return -end subroutine - -subroutine AA_UnPackConstrState(RF, OutData) - type(RegFile), intent(inout) :: RF - type(AA_ConstraintStateType), intent(inout) :: OutData - character(*), parameter :: RoutineName = 'AA_UnPackConstrState' - if (RF%ErrStat /= ErrID_None) return - call RegUnpack(RF, OutData%DummyConstrState); if (RegCheckErr(RF, RoutineName)) return end subroutine subroutine AA_CopyOtherState(SrcOtherStateData, DstOtherStateData, CtrlCode, ErrStat, ErrMsg) @@ -1439,54 +1255,10 @@ subroutine AA_CopyMisc(SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg) end if DstMiscData%SPLBLUNT = SrcMiscData%SPLBLUNT end if - if (allocated(SrcMiscData%CfVar)) then - LB(1:1) = lbound(SrcMiscData%CfVar) - UB(1:1) = ubound(SrcMiscData%CfVar) - if (.not. allocated(DstMiscData%CfVar)) then - allocate(DstMiscData%CfVar(LB(1):UB(1)), stat=ErrStat2) - if (ErrStat2 /= 0) then - call SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%CfVar.', ErrStat, ErrMsg, RoutineName) - return - end if - end if - DstMiscData%CfVar = SrcMiscData%CfVar - end if - if (allocated(SrcMiscData%d99Var)) then - LB(1:1) = lbound(SrcMiscData%d99Var) - UB(1:1) = ubound(SrcMiscData%d99Var) - if (.not. allocated(DstMiscData%d99Var)) then - allocate(DstMiscData%d99Var(LB(1):UB(1)), stat=ErrStat2) - if (ErrStat2 /= 0) then - call SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%d99Var.', ErrStat, ErrMsg, RoutineName) - return - end if - end if - DstMiscData%d99Var = SrcMiscData%d99Var - end if - if (allocated(SrcMiscData%dStarVar)) then - LB(1:1) = lbound(SrcMiscData%dStarVar) - UB(1:1) = ubound(SrcMiscData%dStarVar) - if (.not. allocated(DstMiscData%dStarVar)) then - allocate(DstMiscData%dStarVar(LB(1):UB(1)), stat=ErrStat2) - if (ErrStat2 /= 0) then - call SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%dStarVar.', ErrStat, ErrMsg, RoutineName) - return - end if - end if - DstMiscData%dStarVar = SrcMiscData%dStarVar - end if - if (allocated(SrcMiscData%EdgeVelVar)) then - LB(1:1) = lbound(SrcMiscData%EdgeVelVar) - UB(1:1) = ubound(SrcMiscData%EdgeVelVar) - if (.not. allocated(DstMiscData%EdgeVelVar)) then - allocate(DstMiscData%EdgeVelVar(LB(1):UB(1)), stat=ErrStat2) - if (ErrStat2 /= 0) then - call SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%EdgeVelVar.', ErrStat, ErrMsg, RoutineName) - return - end if - end if - DstMiscData%EdgeVelVar = SrcMiscData%EdgeVelVar - end if + DstMiscData%CfVar = SrcMiscData%CfVar + DstMiscData%d99Var = SrcMiscData%d99Var + DstMiscData%dStarVar = SrcMiscData%dStarVar + DstMiscData%EdgeVelVar = SrcMiscData%EdgeVelVar DstMiscData%LastIndex = SrcMiscData%LastIndex if (allocated(SrcMiscData%SumSpecNoiseSep)) then LB(1:3) = lbound(SrcMiscData%SumSpecNoiseSep) @@ -1593,18 +1365,6 @@ subroutine AA_DestroyMisc(MiscData, ErrStat, ErrMsg) if (allocated(MiscData%SPLBLUNT)) then deallocate(MiscData%SPLBLUNT) end if - if (allocated(MiscData%CfVar)) then - deallocate(MiscData%CfVar) - end if - if (allocated(MiscData%d99Var)) then - deallocate(MiscData%d99Var) - end if - if (allocated(MiscData%dStarVar)) then - deallocate(MiscData%dStarVar) - end if - if (allocated(MiscData%EdgeVelVar)) then - deallocate(MiscData%EdgeVelVar) - end if if (allocated(MiscData%SumSpecNoiseSep)) then deallocate(MiscData%SumSpecNoiseSep) end if @@ -1641,10 +1401,10 @@ subroutine AA_PackMisc(RF, Indata) call RegPackAlloc(RF, InData%SPLTI) call RegPackAlloc(RF, InData%SPLTIGui) call RegPackAlloc(RF, InData%SPLBLUNT) - call RegPackAlloc(RF, InData%CfVar) - call RegPackAlloc(RF, InData%d99Var) - call RegPackAlloc(RF, InData%dStarVar) - call RegPackAlloc(RF, InData%EdgeVelVar) + call RegPack(RF, InData%CfVar) + call RegPack(RF, InData%d99Var) + call RegPack(RF, InData%dStarVar) + call RegPack(RF, InData%EdgeVelVar) call RegPack(RF, InData%LastIndex) call RegPackAlloc(RF, InData%SumSpecNoiseSep) call RegPackAlloc(RF, InData%OASPL) @@ -1678,10 +1438,10 @@ subroutine AA_UnPackMisc(RF, OutData) call RegUnpackAlloc(RF, OutData%SPLTI); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%SPLTIGui); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%SPLBLUNT); if (RegCheckErr(RF, RoutineName)) return - call RegUnpackAlloc(RF, OutData%CfVar); if (RegCheckErr(RF, RoutineName)) return - call RegUnpackAlloc(RF, OutData%d99Var); if (RegCheckErr(RF, RoutineName)) return - call RegUnpackAlloc(RF, OutData%dStarVar); if (RegCheckErr(RF, RoutineName)) return - call RegUnpackAlloc(RF, OutData%EdgeVelVar); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%CfVar); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%d99Var); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%dStarVar); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%EdgeVelVar); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%LastIndex); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%SumSpecNoiseSep); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%OASPL); if (RegCheckErr(RF, RoutineName)) return @@ -1695,10 +1455,8 @@ subroutine AA_CopyParam(SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg) integer(IntKi), intent(in ) :: CtrlCode integer(IntKi), intent( out) :: ErrStat character(*), intent( out) :: ErrMsg - integer(B4Ki) :: i1, i2, i3 integer(B4Ki) :: LB(3), UB(3) integer(IntKi) :: ErrStat2 - character(ErrMsgLen) :: ErrMsg2 character(*), parameter :: RoutineName = 'AA_CopyParam' ErrStat = ErrID_None ErrMsg = '' @@ -1719,56 +1477,20 @@ subroutine AA_CopyParam(SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg) DstParamData%KinVisc = SrcParamData%KinVisc DstParamData%SpdSound = SrcParamData%SpdSound DstParamData%HubHeight = SrcParamData%HubHeight - DstParamData%toptip = SrcParamData%toptip - DstParamData%bottip = SrcParamData%bottip - if (allocated(SrcParamData%rotorregionlimitsVert)) then - LB(1:1) = lbound(SrcParamData%rotorregionlimitsVert) - UB(1:1) = ubound(SrcParamData%rotorregionlimitsVert) - if (.not. allocated(DstParamData%rotorregionlimitsVert)) then - allocate(DstParamData%rotorregionlimitsVert(LB(1):UB(1)), stat=ErrStat2) - if (ErrStat2 /= 0) then - call SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%rotorregionlimitsVert.', ErrStat, ErrMsg, RoutineName) - return - end if - end if - DstParamData%rotorregionlimitsVert = SrcParamData%rotorregionlimitsVert - end if - if (allocated(SrcParamData%rotorregionlimitsHorz)) then - LB(1:1) = lbound(SrcParamData%rotorregionlimitsHorz) - UB(1:1) = ubound(SrcParamData%rotorregionlimitsHorz) - if (.not. allocated(DstParamData%rotorregionlimitsHorz)) then - allocate(DstParamData%rotorregionlimitsHorz(LB(1):UB(1)), stat=ErrStat2) + if (allocated(SrcParamData%RotorRegion_k_minus1)) then + LB(1:2) = lbound(SrcParamData%RotorRegion_k_minus1) + UB(1:2) = ubound(SrcParamData%RotorRegion_k_minus1) + if (.not. allocated(DstParamData%RotorRegion_k_minus1)) then + allocate(DstParamData%RotorRegion_k_minus1(LB(1):UB(1),LB(2):UB(2)), stat=ErrStat2) if (ErrStat2 /= 0) then - call SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%rotorregionlimitsHorz.', ErrStat, ErrMsg, RoutineName) + call SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%RotorRegion_k_minus1.', ErrStat, ErrMsg, RoutineName) return end if end if - DstParamData%rotorregionlimitsHorz = SrcParamData%rotorregionlimitsHorz - end if - if (allocated(SrcParamData%rotorregionlimitsalph)) then - LB(1:1) = lbound(SrcParamData%rotorregionlimitsalph) - UB(1:1) = ubound(SrcParamData%rotorregionlimitsalph) - if (.not. allocated(DstParamData%rotorregionlimitsalph)) then - allocate(DstParamData%rotorregionlimitsalph(LB(1):UB(1)), stat=ErrStat2) - if (ErrStat2 /= 0) then - call SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%rotorregionlimitsalph.', ErrStat, ErrMsg, RoutineName) - return - end if - end if - DstParamData%rotorregionlimitsalph = SrcParamData%rotorregionlimitsalph - end if - if (allocated(SrcParamData%rotorregionlimitsrad)) then - LB(1:1) = lbound(SrcParamData%rotorregionlimitsrad) - UB(1:1) = ubound(SrcParamData%rotorregionlimitsrad) - if (.not. allocated(DstParamData%rotorregionlimitsrad)) then - allocate(DstParamData%rotorregionlimitsrad(LB(1):UB(1)), stat=ErrStat2) - if (ErrStat2 /= 0) then - call SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%rotorregionlimitsrad.', ErrStat, ErrMsg, RoutineName) - return - end if - end if - DstParamData%rotorregionlimitsrad = SrcParamData%rotorregionlimitsrad + DstParamData%RotorRegion_k_minus1 = SrcParamData%RotorRegion_k_minus1 end if + DstParamData%NumRotorRegionLimitsAlph = SrcParamData%NumRotorRegionLimitsAlph + DstParamData%NumRotorRegionLimitsRad = SrcParamData%NumRotorRegionLimitsRad DstParamData%NrObsLoc = SrcParamData%NrObsLoc DstParamData%aweightflag = SrcParamData%aweightflag DstParamData%TxtFileOutput = SrcParamData%TxtFileOutput @@ -1809,8 +1531,7 @@ subroutine AA_CopyParam(SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg) end if DstParamData%Aweight = SrcParamData%Aweight end if - DstParamData%total_sampleTI = SrcParamData%total_sampleTI - DstParamData%AA_Bl_Prcntge = SrcParamData%AA_Bl_Prcntge + DstParamData%Num_total_sampleTI = SrcParamData%Num_total_sampleTI DstParamData%startnode = SrcParamData%startnode DstParamData%Lturb = SrcParamData%Lturb DstParamData%avgV = SrcParamData%avgV @@ -1882,22 +1603,6 @@ subroutine AA_CopyParam(SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg) end if DstParamData%BlAFID = SrcParamData%BlAFID end if - if (allocated(SrcParamData%AFInfo)) then - LB(1:1) = lbound(SrcParamData%AFInfo) - UB(1:1) = ubound(SrcParamData%AFInfo) - if (.not. allocated(DstParamData%AFInfo)) then - allocate(DstParamData%AFInfo(LB(1):UB(1)), stat=ErrStat2) - if (ErrStat2 /= 0) then - call SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%AFInfo.', ErrStat, ErrMsg, RoutineName) - return - end if - end if - do i1 = LB(1), UB(1) - call AFI_CopyParam(SrcParamData%AFInfo(i1), DstParamData%AFInfo(i1), CtrlCode, ErrStat2, ErrMsg2) - call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - if (ErrStat >= AbortErrLev) return - end do - end if if (allocated(SrcParamData%AFLECo)) then LB(1:3) = lbound(SrcParamData%AFLECo) UB(1:3) = ubound(SrcParamData%AFLECo) @@ -1934,6 +1639,18 @@ subroutine AA_CopyParam(SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg) end if DstParamData%BlSpn = SrcParamData%BlSpn end if + if (allocated(SrcParamData%BlElemSpn)) then + LB(1:2) = lbound(SrcParamData%BlElemSpn) + UB(1:2) = ubound(SrcParamData%BlElemSpn) + if (.not. allocated(DstParamData%BlElemSpn)) then + allocate(DstParamData%BlElemSpn(LB(1):UB(1),LB(2):UB(2)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstParamData%BlElemSpn.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstParamData%BlElemSpn = SrcParamData%BlElemSpn + end if if (allocated(SrcParamData%BlChord)) then LB(1:2) = lbound(SrcParamData%BlChord) UB(1:2) = ubound(SrcParamData%BlChord) @@ -2084,24 +1801,11 @@ subroutine AA_DestroyParam(ParamData, ErrStat, ErrMsg) type(AA_ParameterType), intent(inout) :: ParamData integer(IntKi), intent( out) :: ErrStat character(*), intent( out) :: ErrMsg - integer(B4Ki) :: i1, i2, i3 - integer(B4Ki) :: LB(3), UB(3) - integer(IntKi) :: ErrStat2 - character(ErrMsgLen) :: ErrMsg2 character(*), parameter :: RoutineName = 'AA_DestroyParam' ErrStat = ErrID_None ErrMsg = '' - if (allocated(ParamData%rotorregionlimitsVert)) then - deallocate(ParamData%rotorregionlimitsVert) - end if - if (allocated(ParamData%rotorregionlimitsHorz)) then - deallocate(ParamData%rotorregionlimitsHorz) - end if - if (allocated(ParamData%rotorregionlimitsalph)) then - deallocate(ParamData%rotorregionlimitsalph) - end if - if (allocated(ParamData%rotorregionlimitsrad)) then - deallocate(ParamData%rotorregionlimitsrad) + if (allocated(ParamData%RotorRegion_k_minus1)) then + deallocate(ParamData%RotorRegion_k_minus1) end if if (allocated(ParamData%ObsXYZ)) then deallocate(ParamData%ObsXYZ) @@ -2127,15 +1831,6 @@ subroutine AA_DestroyParam(ParamData, ErrStat, ErrMsg) if (allocated(ParamData%BlAFID)) then deallocate(ParamData%BlAFID) end if - if (allocated(ParamData%AFInfo)) then - LB(1:1) = lbound(ParamData%AFInfo) - UB(1:1) = ubound(ParamData%AFInfo) - do i1 = LB(1), UB(1) - call AFI_DestroyParam(ParamData%AFInfo(i1), ErrStat2, ErrMsg2) - call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - end do - deallocate(ParamData%AFInfo) - end if if (allocated(ParamData%AFLECo)) then deallocate(ParamData%AFLECo) end if @@ -2145,6 +1840,9 @@ subroutine AA_DestroyParam(ParamData, ErrStat, ErrMsg) if (allocated(ParamData%BlSpn)) then deallocate(ParamData%BlSpn) end if + if (allocated(ParamData%BlElemSpn)) then + deallocate(ParamData%BlElemSpn) + end if if (allocated(ParamData%BlChord)) then deallocate(ParamData%BlChord) end if @@ -2187,8 +1885,6 @@ subroutine AA_PackParam(RF, Indata) type(RegFile), intent(inout) :: RF type(AA_ParameterType), intent(in) :: InData character(*), parameter :: RoutineName = 'AA_PackParam' - integer(B4Ki) :: i1, i2, i3 - integer(B4Ki) :: LB(3), UB(3) if (RF%ErrStat >= AbortErrLev) return call RegPack(RF, InData%DT) call RegPack(RF, InData%IBLUNT) @@ -2207,12 +1903,9 @@ subroutine AA_PackParam(RF, Indata) call RegPack(RF, InData%KinVisc) call RegPack(RF, InData%SpdSound) call RegPack(RF, InData%HubHeight) - call RegPack(RF, InData%toptip) - call RegPack(RF, InData%bottip) - call RegPackAlloc(RF, InData%rotorregionlimitsVert) - call RegPackAlloc(RF, InData%rotorregionlimitsHorz) - call RegPackAlloc(RF, InData%rotorregionlimitsalph) - call RegPackAlloc(RF, InData%rotorregionlimitsrad) + call RegPackAlloc(RF, InData%RotorRegion_k_minus1) + call RegPack(RF, InData%NumRotorRegionLimitsAlph) + call RegPack(RF, InData%NumRotorRegionLimitsRad) call RegPack(RF, InData%NrObsLoc) call RegPack(RF, InData%aweightflag) call RegPack(RF, InData%TxtFileOutput) @@ -2220,8 +1913,7 @@ subroutine AA_PackParam(RF, Indata) call RegPackAlloc(RF, InData%ObsXYZ) call RegPackAlloc(RF, InData%FreqList) call RegPackAlloc(RF, InData%Aweight) - call RegPack(RF, InData%total_sampleTI) - call RegPack(RF, InData%AA_Bl_Prcntge) + call RegPack(RF, InData%Num_total_sampleTI) call RegPack(RF, InData%startnode) call RegPack(RF, InData%Lturb) call RegPack(RF, InData%avgV) @@ -2238,18 +1930,10 @@ subroutine AA_PackParam(RF, Indata) call RegPackAlloc(RF, InData%TEAngle) call RegPackAlloc(RF, InData%AerCent) call RegPackAlloc(RF, InData%BlAFID) - call RegPack(RF, allocated(InData%AFInfo)) - if (allocated(InData%AFInfo)) then - call RegPackBounds(RF, 1, lbound(InData%AFInfo), ubound(InData%AFInfo)) - LB(1:1) = lbound(InData%AFInfo) - UB(1:1) = ubound(InData%AFInfo) - do i1 = LB(1), UB(1) - call AFI_PackParam(RF, InData%AFInfo(i1)) - end do - end if call RegPackAlloc(RF, InData%AFLECo) call RegPackAlloc(RF, InData%AFTECo) call RegPackAlloc(RF, InData%BlSpn) + call RegPackAlloc(RF, InData%BlElemSpn) call RegPackAlloc(RF, InData%BlChord) call RegPackAlloc(RF, InData%ReListBL) call RegPackAlloc(RF, InData%AOAListBL) @@ -2269,7 +1953,6 @@ subroutine AA_UnPackParam(RF, OutData) type(RegFile), intent(inout) :: RF type(AA_ParameterType), intent(inout) :: OutData character(*), parameter :: RoutineName = 'AA_UnPackParam' - integer(B4Ki) :: i1, i2, i3 integer(B4Ki) :: LB(3), UB(3) integer(IntKi) :: stat logical :: IsAllocAssoc @@ -2291,12 +1974,9 @@ subroutine AA_UnPackParam(RF, OutData) call RegUnpack(RF, OutData%KinVisc); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%SpdSound); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%HubHeight); if (RegCheckErr(RF, RoutineName)) return - call RegUnpack(RF, OutData%toptip); if (RegCheckErr(RF, RoutineName)) return - call RegUnpack(RF, OutData%bottip); if (RegCheckErr(RF, RoutineName)) return - call RegUnpackAlloc(RF, OutData%rotorregionlimitsVert); if (RegCheckErr(RF, RoutineName)) return - call RegUnpackAlloc(RF, OutData%rotorregionlimitsHorz); if (RegCheckErr(RF, RoutineName)) return - call RegUnpackAlloc(RF, OutData%rotorregionlimitsalph); if (RegCheckErr(RF, RoutineName)) return - call RegUnpackAlloc(RF, OutData%rotorregionlimitsrad); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%RotorRegion_k_minus1); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NumRotorRegionLimitsAlph); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%NumRotorRegionLimitsRad); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%NrObsLoc); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%aweightflag); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%TxtFileOutput); if (RegCheckErr(RF, RoutineName)) return @@ -2304,8 +1984,7 @@ subroutine AA_UnPackParam(RF, OutData) call RegUnpackAlloc(RF, OutData%ObsXYZ); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%FreqList); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%Aweight); if (RegCheckErr(RF, RoutineName)) return - call RegUnpack(RF, OutData%total_sampleTI); if (RegCheckErr(RF, RoutineName)) return - call RegUnpack(RF, OutData%AA_Bl_Prcntge); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Num_total_sampleTI); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%startnode); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%Lturb); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%avgV); if (RegCheckErr(RF, RoutineName)) return @@ -2322,22 +2001,10 @@ subroutine AA_UnPackParam(RF, OutData) call RegUnpackAlloc(RF, OutData%TEAngle); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%AerCent); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%BlAFID); if (RegCheckErr(RF, RoutineName)) return - if (allocated(OutData%AFInfo)) deallocate(OutData%AFInfo) - call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return - if (IsAllocAssoc) then - call RegUnpackBounds(RF, 1, LB, UB); if (RegCheckErr(RF, RoutineName)) return - allocate(OutData%AFInfo(LB(1):UB(1)),stat=stat) - if (stat /= 0) then - call SetErrStat(ErrID_Fatal, 'Error allocating OutData%AFInfo.', RF%ErrStat, RF%ErrMsg, RoutineName) - return - end if - do i1 = LB(1), UB(1) - call AFI_UnpackParam(RF, OutData%AFInfo(i1)) ! AFInfo - end do - end if call RegUnpackAlloc(RF, OutData%AFLECo); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%AFTECo); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%BlSpn); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%BlElemSpn); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%BlChord); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%ReListBL); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%AOAListBL); if (RegCheckErr(RF, RoutineName)) return @@ -2603,87 +2270,6 @@ function AA_OutputMeshPointer(y, DL) result(Mesh) end select end function -subroutine AA_VarsPackContState(Vars, x, ValAry) - type(AA_ContinuousStateType), intent(in) :: x - type(ModVarsType), intent(in) :: Vars - real(R8Ki), intent(inout) :: ValAry(:) - integer(IntKi) :: i - do i = 1, size(Vars%x) - call AA_VarPackContState(Vars%x(i), x, ValAry) - end do -end subroutine - -subroutine AA_VarPackContState(V, x, ValAry) - type(ModVarType), intent(in) :: V - type(AA_ContinuousStateType), intent(in) :: x - real(R8Ki), intent(inout) :: ValAry(:) - associate (DL => V%DL, VarVals => ValAry(V%iLoc(1):V%iLoc(2))) - select case (DL%Num) - case (AA_x_DummyContState) - VarVals(1) = x%DummyContState ! Scalar - case default - VarVals = 0.0_R8Ki - end select - end associate -end subroutine - -subroutine AA_VarsUnpackContState(Vars, ValAry, x) - type(ModVarsType), intent(in) :: Vars - real(R8Ki), intent(in) :: ValAry(:) - type(AA_ContinuousStateType), intent(inout) :: x - integer(IntKi) :: i - do i = 1, size(Vars%x) - call AA_VarUnpackContState(Vars%x(i), ValAry, x) - end do -end subroutine - -subroutine AA_VarUnpackContState(V, ValAry, x) - type(ModVarType), intent(in) :: V - real(R8Ki), intent(in) :: ValAry(:) - type(AA_ContinuousStateType), intent(inout) :: x - associate (DL => V%DL, VarVals => ValAry(V%iLoc(1):V%iLoc(2))) - select case (DL%Num) - case (AA_x_DummyContState) - x%DummyContState = VarVals(1) ! Scalar - end select - end associate -end subroutine - -function AA_ContinuousStateFieldName(DL) result(Name) - type(DatLoc), intent(in) :: DL - character(32) :: Name - select case (DL%Num) - case (AA_x_DummyContState) - Name = "x%DummyContState" - case default - Name = "Unknown Field" - end select -end function - -subroutine AA_VarsPackContStateDeriv(Vars, x, ValAry) - type(AA_ContinuousStateType), intent(in) :: x - type(ModVarsType), intent(in) :: Vars - real(R8Ki), intent(inout) :: ValAry(:) - integer(IntKi) :: i - do i = 1, size(Vars%x) - call AA_VarPackContStateDeriv(Vars%x(i), x, ValAry) - end do -end subroutine - -subroutine AA_VarPackContStateDeriv(V, x, ValAry) - type(ModVarType), intent(in) :: V - type(AA_ContinuousStateType), intent(in) :: x - real(R8Ki), intent(inout) :: ValAry(:) - associate (DL => V%DL, VarVals => ValAry(V%iLoc(1):V%iLoc(2))) - select case (DL%Num) - case (AA_x_DummyContState) - VarVals(1) = x%DummyContState ! Scalar - case default - VarVals = 0.0_R8Ki - end select - end associate -end subroutine - subroutine AA_VarsPackInput(Vars, u, ValAry) type(AA_InputType), intent(in) :: u type(ModVarsType), intent(in) :: Vars diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index f5e468699a..ac3ed56d78 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -492,7 +492,7 @@ subroutine AD_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut ! Initialize the AeroAcoustics Module if the CompAA flag is set !............................................................................................ if (p%rotors(iR)%CompAA) then - call Init_AAmodule( InitInp%rotors(iR), InputFileData, InputFileData%rotors(iR), u%rotors(iR), m%rotors(iR)%AA_u, p%rotors(iR), p, x%rotors(iR)%AA, xd%rotors(iR)%AA, z%rotors(iR)%AA, OtherState%rotors(iR)%AA, m%rotors(iR)%AA_y, m%rotors(iR)%AA, AA_InitOut, ErrStat2, ErrMsg2 ) + call Init_AAmodule( InitInp%rotors(iR), InputFileData, InputFileData%rotors(iR), u%rotors(iR), m%rotors(iR)%AA_u, p%rotors(iR), p, xd%rotors(iR)%AA, OtherState%rotors(iR)%AA, m%rotors(iR)%AA_y, m%rotors(iR)%AA, AA_InitOut, ErrStat2, ErrMsg2 ) if (Failed()) return; end if enddo @@ -1784,7 +1784,7 @@ subroutine AD_End( u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg ) do iR = 1, SIZE(p%rotors) if (p%rotors(iR)%CompAA) then - call AA_End( m%rotors(iR)%AA_u, p%rotors(iR)%AA, x%rotors(iR)%AA, xd%rotors(iR)%AA, z%rotors(iR)%AA, OtherState%rotors(iR)%AA, m%rotors(iR)%AA_y, m%rotors(iR)%AA, ErrStat, ErrMsg ) + call AA_End( m%rotors(iR)%AA_u, p%rotors(iR)%AA, xd%rotors(iR)%AA, OtherState%rotors(iR)%AA, m%rotors(iR)%AA_y, m%rotors(iR)%AA, ErrStat, ErrMsg ) end if enddo @@ -2284,7 +2284,7 @@ subroutine RotCalcOutput( t, u, RotInflow, p, p_AD, x, xd, z, OtherState, y, m, ! Also, SetInputs() [called above] calls SetInputsForBEMT() which in turn establishes current versions of the Global to local transformations we need as inputs to AA call SetInputsForAA(p, u, RotInflow, m, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - call AA_CalcOutput(t, m%AA_u, p%AA, x%AA, xd%AA, z%AA, OtherState%AA, m%AA_y, m%AA, errStat2, errMsg2) + call AA_CalcOutput(t, m%AA_u, p%AA, xd%AA, OtherState%AA, m%AA_y, m%AA, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) ! end if @@ -4774,7 +4774,7 @@ SUBROUTINE Init_AFIparams( InputFileData, p_AFI, UnEc, RootName, ErrStat, ErrMsg END SUBROUTINE Init_AFIparams !---------------------------------------------------------------------------------------------------------------------------------- !> This routine initializes the Airfoil Noise module from within AeroDyn. -SUBROUTINE Init_AAmodule( DrvInitInp, AD_InputFileData, RotInputFileData, u_AD, u, p, p_AD, x, xd, z, OtherState, y, m, InitOut, ErrStat, ErrMsg ) +SUBROUTINE Init_AAmodule( DrvInitInp, AD_InputFileData, RotInputFileData, u_AD, u, p, p_AD, xd, OtherState, y, m, InitOut, ErrStat, ErrMsg ) !.................................................................................................................................. type(RotInitInputType), intent(in ) :: DrvInitInp !< AeroDyn-level initialization inputs type(AD_InputFile), intent(in ) :: AD_InputFileData !< All the data in the AeroDyn input file @@ -4783,9 +4783,9 @@ SUBROUTINE Init_AAmodule( DrvInitInp, AD_InputFileData, RotInputFileData, u_AD, type(AA_InputType), intent( out) :: u !< An initial guess for the input; input mesh must be defined type(RotParameterType), intent(inout) :: p !< Parameters ! intent out b/c we set the AA parameters here type(AD_ParameterType), intent(inout) :: p_AD !< Parameters ! intent out b/c we set the AA parameters here - type(AA_ContinuousStateType), intent( out) :: x !< Initial continuous states + !type(AA_ContinuousStateType), intent( out) :: x !< Initial continuous states type(AA_DiscreteStateType), intent( out) :: xd !< Initial discrete states - type(AA_ConstraintStateType), intent( out) :: z !< Initial guess of the constraint states + !type(AA_ConstraintStateType), intent( out) :: z !< Initial guess of the constraint states type(AA_OtherStateType), intent( out) :: OtherState !< Initial other states type(AA_OutputType), intent( out) :: y !< Initial system outputs (outputs are not calculated; !! only the output mesh is initialized) @@ -4821,17 +4821,6 @@ SUBROUTINE Init_AAmodule( DrvInitInp, AD_InputFileData, RotInputFileData, u_AD, InitInp%SpdSound = AD_InputFileData%SpdSound InitInp%HubHeight = DrvInitInp%HubPosition(3) - ! --- Transfer of airfoil info - ALLOCATE ( InitInp%AFInfo( size(p_AD%AFI) ), STAT=ErrStat2 ) - IF ( ErrStat2 /= 0 ) THEN - CALL SetErrStat ( ErrID_Fatal, 'Error allocating memory for the InitInp%AFInfo array.', ErrStat2, ErrMsg2, RoutineName ) - RETURN - ENDIF - do i=1,size(p_AD%AFI) - call AFI_CopyParam( p_AD%AFI(i), InitInp%AFInfo(i), MESH_NEWCOPY, errStat2, errMsg2 ) - call SetErrStat( errStat2, errMsg2, errStat, errMsg, RoutineName ) - end do - ! --- Allocate and set AirfoilID, chord and Span for each blades ! note here that each blade is required to have the same number of nodes call AllocAry( InitInp%BlAFID, p%NumBlNds, p%NumBlades,'InitInp%BlAFID', errStat2, ErrMsg2 ) @@ -4846,14 +4835,14 @@ SUBROUTINE Init_AAmodule( DrvInitInp, AD_InputFileData, RotInputFileData, u_AD, end if do k = 1, p%NumBlades do j=1, RotInputFileData%BladeProps(k)%NumBlNds - InitInp%BlChord(j,k) = RotInputFileData%BladeProps(k)%BlChord( j) + InitInp%BlChord(j,k) = RotInputFileData%BladeProps(k)%BlChord(j) InitInp%BlSpn (j,k) = RotInputFileData%BladeProps(k)%BlSpn(j) - InitInp%BlAFID(j,k) = RotInputFileData%BladeProps(k)%BlAFID(j) + InitInp%BlAFID(j,k) = RotInputFileData%BladeProps(k)%BlAFID(j) end do end do ! --- AeroAcoustics initialization call - call AA_Init(InitInp, u, p%AA, x, xd, z, OtherState, y, m, Interval, InitOut, ErrStat2, ErrMsg2 ) + call AA_Init(InitInp, u, p%AA, xd, OtherState, y, m, Interval, p_AD%AFI, InitOut, ErrStat2, ErrMsg2 ) call SetErrStat(ErrStat2,ErrMsg2, ErrStat, ErrMsg, RoutineName) call Cleanup() @@ -4861,7 +4850,7 @@ SUBROUTINE Init_AAmodule( DrvInitInp, AD_InputFileData, RotInputFileData, u_AD, contains subroutine Cleanup() - call AA_DestroyInitInput ( InitInp, ErrStat2, ErrMsg2 ) + call AA_DestroyInitInput ( InitInp, ErrStat2, ErrMsg2 ) end subroutine Cleanup END SUBROUTINE Init_AAmodule diff --git a/modules/aerodyn/src/AeroDyn_Driver_Subs.f90 b/modules/aerodyn/src/AeroDyn_Driver_Subs.f90 index f7c9a8371b..ab65a81387 100644 --- a/modules/aerodyn/src/AeroDyn_Driver_Subs.f90 +++ b/modules/aerodyn/src/AeroDyn_Driver_Subs.f90 @@ -101,8 +101,7 @@ subroutine Dvr_Init(dvr, ADI, FED, SeaSt, errStat, errMsg ) ! local variables integer(IntKi) :: errStat2 ! local status of error message character(ErrMsgLen) :: errMsg2 ! local error message if errStat /= ErrID_None - character(1000) :: inputFile ! String to hold the file name. - character(200) :: git_commit ! String containing the current git commit hash + character(1000) :: InputFile ! String to hold the file name. character(20) :: FlagArg ! flag argument from command line integer :: iWT ! Index on wind turbines/rotors errStat = ErrID_None @@ -113,7 +112,7 @@ subroutine Dvr_Init(dvr, ADI, FED, SeaSt, errStat, errMsg ) InputFile = "" ! initialize to empty string to make sure it's input from the command line CALL CheckArgs( InputFile, Flag=FlagArg ) - IF ( LEN( TRIM(FlagArg) ) > 0 ) CALL NormStop() + IF ( LEN( TRIM(FlagArg) ) > 0 ) CALL NormStop() ! stop if user set a flag argument (like '-h' or '-v') ! Display the copyright notice and compile info: CALL DispCopyrightLicense( version%Name ) diff --git a/modules/aerodyn/src/AeroDyn_Inflow.f90 b/modules/aerodyn/src/AeroDyn_Inflow.f90 index ad52c59a14..042870b073 100644 --- a/modules/aerodyn/src/AeroDyn_Inflow.f90 +++ b/modules/aerodyn/src/AeroDyn_Inflow.f90 @@ -73,14 +73,25 @@ subroutine ADI_Init(InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut p%MHK = InitInp%AD%MHK p%WtrDpth = InitInp%AD%WtrDpth - ! --- Initialize Inflow Wind + ! --- Initialize Inflow Wind call ADI_InitInflowWind(InitInp%RootName, InitInp%IW_InitInp, u%AD, OtherState%AD, m%IW, Interval, InitOut_IW, errStat2, errMsg2); if (Failed()) return ! Concatenate AD outputs to IW outputs call concatOutputHeaders(InitOut%WriteOutputHdr, InitOut%WriteOutputUnt, InitOut_IW%WriteOutputHdr, InitOut_IW%WriteOutputUnt, errStat2, errMsg2); if(Failed()) return ! --- Initialize AeroDyn ! Link InflowWind's FlowField to AeroDyn's FlowField - InitInp%AD%FlowField => InitOut_IW%FlowField + select case (m%IW%CompInflow) + case (0) ! steady wind - data stored as a flowfield dataset + InitInp%AD%FlowField => InitOut_IW%FlowField + case (1) ! IfW is used directly in ADI + InitInp%AD%FlowField => InitOut_IW%FlowField + case (2) ! FlowField pointer is passed in + InitInp%AD%FlowField => InitInp%FlowField + case default + ErrStat2 = ErrID_Fatal + ErrMsg2 = 'Invalid value for CompInflow' + if (Failed()) return + end select call AD_Init(InitInp%AD, u%AD, p%AD, x%AD, xd%AD, z%AD, OtherState%AD, y%AD, m%AD, Interval, InitOut_AD, errStat2, errMsg2); if (Failed()) return InitOut%Ver = InitOut_AD%ver @@ -257,7 +268,6 @@ end subroutine ADI_UpdateStates !---------------------------------------------------------------------------------------------------------------------------------- !> Routine for computing outputs, used in both loose and tight coupling. subroutine ADI_CalcOutput(t, u, p, x, xd, z, OtherState, y, m, errStat, errMsg) - use IfW_FlowField, only: IfW_FlowField_GetVelAcc real(DbKi), intent(in ) :: t !< Current simulation time in seconds type(ADI_InputType), intent(inout) :: u !< Inputs at Time t ! NOTE: set as in-out since "Inflow" needs to be set type(ADI_ParameterType), intent(in ) :: p !< Parameters @@ -279,7 +289,9 @@ subroutine ADI_CalcOutput(t, u, p, x, xd, z, OtherState, y, m, errStat, errMsg) errMsg = "" !---------------------------------------------------------------------------- - ! Calculate InflowWind outputs if module was initialized + ! Calculate InflowWind outputs if module is directly used within ADI. + ! NOTE: if IfW is external and only the FlowField pointer is used, we don't do + ! any IfW calcoutput here !---------------------------------------------------------------------------- if (m%IW%CompInflow == 1) then @@ -385,7 +397,7 @@ subroutine ADI_InitInflowWind(Root, i_IW, u_AD, o_AD, IW, dt, InitOutData, errSt if(Failed()) return IW%p%FlowField%AccFieldValid = .true. end if - else + elseif (i_IW%CompInflow == 1) then ! InflowWind loaded here ! Initialze InflowWind module InitInData%InputFileName = i_IW%InputFile InitInData%Linearize = i_IW%Linearize @@ -400,12 +412,12 @@ subroutine ADI_InitInflowWind(Root, i_IW, u_AD, o_AD, IW, dt, InitOutData, errSt endif InitInData%RootName = trim(Root)//'.IfW' InitInData%MHK = i_IW%MHK - InitInData%OutputAccel = InitInData%MHK /= MHK_None + InitInData%OutputAccel = i_IW%OutputAccel + ! OLAF might be used in AD, in which case we need to allow out of bounds for some calcs. To do that ! the average values for the entire wind profile must be calculated and stored (we don't know if OLAF ! is used until after AD_Init below). InitInData%BoxExceedAllow = .true. - InitInData%OutputAccel = i_IW%OutputAccel !FIXME: bjj: what about these initialization inputs? ! InitInData%HubPosition @@ -415,6 +427,7 @@ subroutine ADI_InitInflowWind(Root, i_IW, u_AD, o_AD, IW, dt, InitOutData, errSt IW%x, IW%xd, IW%z, IW%OtherSt, & IW%y, IW%m, dt, InitOutData, errStat2, errMsg2 ) if(Failed()) return + !elseif (i_IW%CompInflow == 2) then ! InflowWind is external, using FlowField pointer, no init call required endif ! --- Store main init input data (data that don't use InfloWind directly) diff --git a/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 b/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 index 4c6f4fed13..c553415aa7 100644 --- a/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 +++ b/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 @@ -34,20 +34,28 @@ MODULE AeroDyn_Inflow_C_BINDING SAVE PUBLIC :: ADI_C_Init - !PUBLIC :: ADI_C_ReInit PUBLIC :: ADI_C_CalcOutput PUBLIC :: ADI_C_UpdateStates PUBLIC :: ADI_C_End - PUBLIC :: ADI_C_PreInit ! Initial call to setup number of turbines - PUBLIC :: ADI_C_SetupRotor ! Initial node positions etc for a rotor - PUBLIC :: ADI_C_SetRotorMotion ! Set motions for a given rotor - PUBLIC :: ADI_C_GetRotorLoads ! Retrieve loads for a given rotor - PUBLIC :: ADI_C_GetDiskAvgVel ! Get the disk average velocity for the rotor + PUBLIC :: ADI_C_PreInit ! Initial call to setup number of turbines + PUBLIC :: ADI_C_SetupRotor ! Initial node positions etc for a rotor + PUBLIC :: ADI_C_SetRotorMotion ! Set motions for a given rotor + PUBLIC :: ADI_C_GetRotorLoads ! Retrieve loads for a given rotor + PUBLIC :: ADI_C_GetDiskAvgVel ! Get the disk average velocity for the rotor + PUBLIC :: ADI_C_SetFlowFieldPointer ! Set the pointer to the flowfield data (from external IfW instance) + PUBLIC :: ADI_C_GetFlowFieldPointer ! Get the pointer to the flowfield data (to pass to external IfW instance) !------------------------------------------------------------------------------------ ! Version info for display type(ProgDesc), parameter :: version = ProgDesc( 'AeroDyn-Inflow library', '', '' ) + !------------------------------------------------------------------------------------ + ! Wind + ! externFlowField - FlowField is handled externally to ADI, pointer must be set manually + ! FlowFieldPtrSet - is the FlowField pointer set from external? + logical :: externFlowField = .false. + logical :: FlowFieldPtrSet = .false. + !------------------------------------------------------------------------------------ ! Debugging: DebugLevel -- passed at PreInit ! 0 - none @@ -129,7 +137,9 @@ MODULE AeroDyn_Inflow_C_BINDING ! interface and therefore must store it here analogously to how it is handled ! in the OpenFAST glue code. integer(IntKi) :: n_Global ! global timestep - integer(IntKi) :: n_VTK ! VTK timestep + integer(IntKi) :: VTKn_Global ! global timestep for VTK + integer(IntKi) :: VTKn_last ! last global timestep for VTK + character(IntfStrLen) :: OutVTKDir !< Output directory for files (relative to current location) real(DbKi) :: InputTimePrev ! input time of last UpdateStates call real(DbKi) :: InputTimePrev_Calc ! input time of last CalcOutput call ! Note that we are including the previous state info here (not done in OF this way) @@ -190,6 +200,10 @@ subroutine ADI_C_PreInit( & defSpdSound_in, defPatm_in, defPvap_in, & WtrDpth_in, MSL2SWL_in, & MHK_in, & + externFlowField_in, & + OutVTKDir_C, & + WrVTK_in, WrVTK_inType, WrVTK_inDT, & + VTKNacDim_in, VTKHubRad_in, & DebugLevel_in, ErrStat_C, ErrMsg_C & ) BIND (C, NAME='ADI_C_PreInit') implicit none @@ -197,24 +211,34 @@ subroutine ADI_C_PreInit( & !DEC$ ATTRIBUTES DLLEXPORT :: ADI_C_PreInit !GCC$ ATTRIBUTES DLLEXPORT :: ADI_C_PreInit #endif - integer(c_int), intent(in ) :: NumTurbines_C - integer(c_int), intent(in ) :: TransposeDCM_in !< Transpose DCMs as they are passed i - integer(c_int), intent(in ) :: PointLoadOutput_in - real(c_float), intent(in ) :: gravity_in !< Gravitational acceleration (m/s^2) - real(c_float), intent(in ) :: defFldDens_in !< Air density (kg/m^3) - real(c_float), intent(in ) :: defKinVisc_in !< Kinematic viscosity of working fluid (m^2/s) - real(c_float), intent(in ) :: defSpdSound_in !< Speed of sound in working fluid (m/s) - real(c_float), intent(in ) :: defPatm_in !< Atmospheric pressure (Pa) [used only for an MHK turbine cavitation check] - real(c_float), intent(in ) :: defPvap_in !< Vapour pressure of working fluid (Pa) [used only for an MHK turbine cavitation check] - real(c_float), intent(in ) :: WtrDpth_in !< Water depth (m) [used only for an MHK turbine] - real(c_float), intent(in ) :: MSL2SWL_in !< Offset between still-water level and mean sea level (m) [positive upward, used only for an MHK turbine] - integer(c_int), intent(in ) :: MHK_in !< Marine hydrokinetic turbine [0: none; 1: fixed bottom MHK; 2: Floating MHK] - integer(c_int), intent(in ) :: DebugLevel_in - integer(c_int), intent( out) :: ErrStat_C - character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer(c_int), intent(in ) :: NumTurbines_C + integer(c_int), intent(in ) :: TransposeDCM_in !< Transpose DCMs as they are passed in + integer(c_int), intent(in ) :: PointLoadOutput_in + real(c_float), intent(in ) :: gravity_in !< Gravitational acceleration (m/s^2) + real(c_float), intent(in ) :: defFldDens_in !< Air density (kg/m^3) + real(c_float), intent(in ) :: defKinVisc_in !< Kinematic viscosity of working fluid (m^2/s) + real(c_float), intent(in ) :: defSpdSound_in !< Speed of sound in working fluid (m/s) + real(c_float), intent(in ) :: defPatm_in !< Atmospheric pressure (Pa) [used only for an MHK turbine cavitation check] + real(c_float), intent(in ) :: defPvap_in !< Vapour pressure of working fluid (Pa) [used only for an MHK turbine cavitation check] + real(c_float), intent(in ) :: WtrDpth_in !< Water depth (m) [used only for an MHK turbine] + real(c_float), intent(in ) :: MSL2SWL_in !< Offset between still-water level and mean sea level (m) [positive upward, used only for an MHK turbine] + integer(c_int), intent(in ) :: MHK_in !< Marine hydrokinetic turbine [0: none; 1: fixed bottom MHK; 2: Floating MHK] + ! inflow + integer(c_int), intent(in ) :: externFlowField_in !< skip IfW setup and use external module with pointer + ! VTK + character(kind=c_char), intent(in ) :: OutVTKDir_C(IntfStrLen) !< Directory to put all vtk output + integer(c_int), intent(in ) :: WrVTK_in !< Write VTK outputs [0: none, 1: init only, 2: animation] + integer(c_int), intent(in ) :: WrVTK_inType !< Write VTK outputs as [1: surface, 2: lines, 3: both] + real(c_double), intent(in ) :: WrVTK_inDT !< Timestep between VTK writes + real(c_float), intent(in ) :: VTKNacDim_in(6) !< Nacelle dimension passed in for VTK surface rendering [0,y0,z0,Lx,Ly,Lz] (m) + real(c_float), intent(in ) :: VTKHubrad_in !< Hub radius for VTK surface rendering + integer(c_int), intent(in ) :: DebugLevel_in + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) ! Local variables integer(IntKi) :: iWT !< current turbine + integer(IntKi) :: i !< generic index variables integer :: ErrStat_F !< aggregated error status character(ErrMsgLen) :: ErrMsg_F !< aggregated error message integer :: ErrStat_F2 !< temporary error status from a call @@ -229,6 +253,9 @@ subroutine ADI_C_PreInit( & CALL DispCopyrightLicense( version%Name ) CALL DispCompileRuntimeInfo( version%Name ) + ! clear out any memory that may be leftover from previous use of library without unloading + call ClearTmpStorage() + ! Save flag for outputting point or distributed loads PointLoadOutput = PointLoadOutput_in /= 0 @@ -320,7 +347,9 @@ subroutine ADI_C_PreInit( & ! Set whether these are MHK turbines InitInp%AD%MHK = MHK_in InitInp%IW_InitInp%MHK = MHK_in - InitInp%IW_InitInp%OutputAccel = MHK_in /= MHK_None + InitInp%IW_InitInp%OutputAccel = MHK_in /= MHK_None + InitInp%IW_InitInp%WtrDpth = REAL(WtrDpth_in, ReKi) + InitInp%IW_InitInp%MSL2SWL = REAL(MSL2SWL_in, ReKi) ! Offset the origin to account for water depth for MHK turbines do iWT=1,Sim%NumTurbines @@ -329,6 +358,31 @@ subroutine ADI_C_PreInit( & end if enddo + + !---------------------------------------------------- + ! ADI settings + !---------------------------------------------------- + ! FlowField - internal to ADI library, or external + ! if externally set, must call the ADI_C_SetFlowFieldPointer routine + if (externFlowField_in==1_c_int) then + externFlowField = .true. + endif + + ! Setup VTK + ! OutVTKDir -- output directory + OutVTKDir = TRANSFER( OutVTKDir_C, OutVTKDir ) + i = INDEX(OutVTKDir,C_NULL_CHAR) - 1 ! if this has a c null character at the end... + if ( i > 0 ) OutVTKDir = OutVTKDir(1:I) ! remove it + + ! VTK writing + WrOutputsData%WrVTK = int(WrVTK_in, IntKi) + WrOutputsData%WrVTK_Type = int(WrVTK_inType, IntKi) + WrOutputsData%VTK_dt = real(WrVTK_inDT, DbKi) + WrOutputsData%VTKNacDim = real(VTKNacDim_in, SiKi) + WrOutputsData%VTKHubrad = real(VTKHubrad_in, SiKi) + WrOutputsData%VTKRefPoint = (/ 0.0_ReKi, 0.0_ReKi, 0.0_ReKi /) !TODO: should this be an input? + WrOutputsData%n_VTKTime = 1 ! output every timestep + call SetErrStat_F2C(ErrStat_F,ErrMsg_F,ErrStat_C,ErrMsg_C) contains @@ -378,6 +432,14 @@ subroutine ShowPassedData() call WrScr(" defPvap_C "//trim(Num2LStr( defPvap_in )) ) call WrScr(" WtrDpth_C "//trim(Num2LStr( WtrDpth_in )) ) call WrScr(" MSL2SWL_C "//trim(Num2LStr( MSL2SWL_in )) ) + call WrScr(" Wind from external IfW instance") + TmpFlag="F"; if (externFlowField_in==1_c_int) TmpFlag="T" + call WrScr(" externFlowField_in "//TmpFlag ) + call WrScr(" VTK visualization variables") + call WrScr(" OutVTKDir "//trim(OutVTKDir) ) + call WrScr(" WrVTK_in "//trim(Num2LStr( WrVTK_in )) ) + call WrScr(" WrVTK_inType "//trim(Num2LStr( WrVTK_inType )) ) + call WrScr(" WrVTK_inDT "//trim(Num2LStr( WrVTK_inDT )) ) call WrScr("-----------------------------------------------------------") end subroutine ShowPassedData @@ -388,11 +450,8 @@ end subroutine ADI_C_PreInit !=============================================================================================================== SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileStringLength_C, & IfWinputFilePassed, IfWinputFileString_C, IfWinputFileStringLength_C, OutRootName_C, & - OutVTKDir_C, & InterpOrder_C, DT_C, TMax_C, & storeHHVel, & - WrVTK_in, WrVTK_inType, WrVTK_inDT, & - VTKNacDim_in, VTKHubRad_in, & wrOuts_C, DT_Outs_C, & NumChannels_C, OutputChannelNames_C, OutputChannelUnits_C, & ErrStat_C, ErrMsg_C) BIND (C, NAME='ADI_C_Init') @@ -409,7 +468,6 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString type(c_ptr), intent(in ) :: IfWinputFileString_C !< Input file as a single string with lines delineated by C_NULL_CHAR integer(c_int), intent(in ) :: IfWinputFileStringLength_C !< length of the input file string character(kind=c_char), intent(in ) :: OutRootName_C(IntfStrLen) !< Root name to use for echo files and other - character(kind=c_char), intent(in ) :: OutVTKDir_C(IntfStrLen) !< Directory to put all vtk output ! Interpolation integer(c_int), intent(in ) :: InterpOrder_C !< Interpolation order to use (must be 1 or 2) ! Time @@ -417,12 +475,6 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString real(c_double), intent(in ) :: TMax_C !< Maximum time for simulation ! Flags integer(c_int), intent(in ) :: storeHHVel !< Store hub height time series from IfW - ! VTK - integer(c_int), intent(in ) :: WrVTK_in !< Write VTK outputs [0: none, 1: init only, 2: animation] - integer(c_int), intent(in ) :: WrVTK_inType !< Write VTK outputs as [1: surface, 2: lines, 3: both] - real(c_double), intent(in ) :: WrVTK_inDT !< Timestep between VTK writes - real(c_float), intent(in ) :: VTKNacDim_in(6) !< Nacelle dimension passed in for VTK surface rendering [0,y0,z0,Lx,Ly,Lz] (m) - real(c_float), intent(in ) :: VTKHubrad_in !< Hub radius for VTK surface rendering integer(c_int), intent(in ) :: wrOuts_C !< Write ADI output file real(c_double), intent(in ) :: DT_Outs_C !< Timestep to write output file from ADI ! Output @@ -442,7 +494,6 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString character(ErrMsgLen) :: ErrMsg_F !< aggregated error message integer(IntKi) :: ErrStat_F2 !< temporary error status from a call character(ErrMsgLen) :: ErrMsg_F2 !< temporary error message from a call - character(IntfStrLen) :: OutVTKDir !< Output directory for files (relative to current location) integer(IntKi) :: i,j,k !< generic index variables integer(IntKi) :: iWT !< current turbine number (iterate through during setup for ADI_Init call) integer(IntKi) :: AeroProjMod !< for checking that all turbines use the same AeroProjMod @@ -465,6 +516,24 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString if (Failed()) return endif + ! if using an external IfW, check that the pointer was actually set and is valid + if (externFlowField) then + if (.not. FlowFieldPtrSet) then + ErrStat_F2 = ErrID_Fatal + ErrMsg_F2 = "FlowField data from an external InflowWind instance declared, but pointer to data not set. Call ADI_C_SetFlowFieldPointer after ADI_C_PreInit, but before ADI_C_Init" + if (Failed()) return + endif + if (associated(InitInp%FlowField)) then + ! basic sanity check + if (InitInp%FlowField%FieldType <= 0_IntKi) then + ErrStat_F2 = ErrID_Fatal + ErrMsg_F2 = "Invalid FlowField pointer passed in, or external InflowWind FlowField not initialized. Call ADI_C_SetFlowFieldPointer after ADI_C_PreInit, but before ADI_C_Init" + if (Failed()) return + endif + endif + endif + + do iWT=1,Sim%NumTurbines if (Sim%WT(iWT)%NumBlades < 0) call SetErrStat(ErrID_Fatal,"Rotor "//trim(Num2LStr(iWT))//" not initialized. Call ADI_C_SetupRotor prior to calling ADI_C_Init",ErrStat_F2,ErrMsg_F2,RoutineName) enddo @@ -481,9 +550,6 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString endif enddo - ! Setup temporary storage arrays for simpler transfers - call SetTempStorage(ErrStat_F2,ErrMsg_F2); if (Failed()) return - !-------------------------- ! Input files @@ -493,11 +559,6 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString i = INDEX(OutRootName,C_NULL_CHAR) - 1 ! if this has a c null character at the end... if ( i > 0 ) OutRootName = OutRootName(1:I) ! remove it - ! OutVTKDir -- output directory - OutVTKDir = TRANSFER( OutVTKDir_C, OutVTKDir ) - i = INDEX(OutVTKDir,C_NULL_CHAR) - 1 ! if this has a c null character at the end... - if ( i > 0 ) OutVTKDir = OutVTKDir(1:I) ! remove it - ! For debugging the interface: if (DebugLevel > 0) then call ShowPassedData() @@ -557,19 +618,13 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString Sim%root = trim(OutRootName) ! Timekeeping n_Global = 0_IntKi ! Assume we are on timestep 0 at start - n_VTK = -1_IntKi ! counter advance just before writing + VTKn_Global = 0_IntKi + VTKn_last = -1_IntKi ! Interpolation order InterpOrder = int(InterpOrder_C, IntKi) - ! VTK outputs - WrOutputsData%WrVTK = int(WrVTK_in, IntKi) - WrOutputsData%WrVTK_Type = int(WrVTK_inType, IntKi) - WrOutputsData%VTK_dt = real(WrVTK_inDT, DbKi) - WrOutputsData%VTKNacDim = real(VTKNacDim_in, SiKi) - WrOutputsData%VTKHubrad = real(VTKHubrad_in, SiKi) - WrOutputsData%VTKRefPoint = (/ 0.0_ReKi, 0.0_ReKi, 0.0_ReKi /) !TODO: should this be an input? + ! VTK output file WrOutputsData%root = trim(OutRootName) - WrOutputsData%n_VTKTime = 1 ! output every timestep ! Write outputs to file WrOutputsData%fileFmt = int(wrOuts_C, IntKi) @@ -577,6 +632,7 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString ! Validate and set some inputs (moved to subroutine to make cleaner to read call ValidateSetInputs(ErrStat_F2,ErrMsg_F2); if(Failed()) return + call ValidateSetVTK(ErrStat_F2,ErrMsg_F2); if(Failed()) return ! Linearization ! for now, set linearization to false. Pass this in later when interface supports it @@ -592,7 +648,11 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString InitInp%storeHHVel = storeHHVel==1_c_int InitInp%WrVTK = WrOutputsData%WrVTK InitInp%WrVTK_Type = WrOutputsData%WrVTK_Type - InitInp%IW_InitInp%CompInflow = 1 ! Use InflowWind + if (externFlowField) then + InitInp%IW_InitInp%CompInflow = 2 ! Use external instance of InflowWind + else + InitInp%IW_InitInp%CompInflow = 1 ! Use InflowWind + endif !---------------------------------------------------- ! Allocate input array u and corresponding InputTimes @@ -631,7 +691,7 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString ! Set the interface meshes for motion inputs and loads output !------------------------------------------------------------- call SetupMotionLoadsInterfaceMeshes(); if (Failed()) return - ! setup meshes + ! setup VTK params if (WrOutputsData%WrVTK > 0_IntKi) then if (len_trim(OutVTKDir) <= 0) then OutVTKDir = 'vtk-ADI' @@ -778,23 +838,6 @@ subroutine ValidateSetInputs(ErrStat3,ErrMsg3) return endif - ! VTK outputs - if ( WrOutputsData%WrVTK < 0_IntKi .or. WrOutputsData%WrVTK > 2_IntKi ) then - call SetErrStat(ErrID_Fatal,"WrVTK option for writing VTK visualization files must be [0: none, 1: init only, 2: animation]",ErrStat3,ErrMsg3,RoutineName) - return - endif - if ( WrOutputsData%WrVTK_Type > 0_IntKi ) then - if ( WrOutputsData%WrVTK_Type < 1_IntKi .or. WrOutputsData%WrVTK_Type > 3_IntKi ) then - call SetErrStat(ErrID_Fatal,"WrVTK_Type option for writing VTK visualization files must be [1: surface, 2: lines, 3: both]",ErrStat3,ErrMsg3,RoutineName) - return - endif - if (WrOutputsData%VTKHubRad < 0.0_SiKi) then - call SetErrStat(ErrID_Warn,"VTKHubRad for surface visualization of hub less than zero. Setting to zero.",ErrStat3,ErrMsg3,RoutineName) - WrOutputsData%VTKHubRad = 0.0_SiKi - endif - endif - - ! check fileFmt if ( WrOutputsData%fileFmt /= idFmtNone .and. WrOutputsData%fileFmt /= idFmtAscii .and. & WrOutputsData%fileFmt /= idFmtBinary .and. WrOutputsData%fileFmt /= idFmtBoth) then @@ -814,6 +857,31 @@ subroutine ValidateSetInputs(ErrStat3,ErrMsg3) call SetErrStat(ErrID_Warn,"Requested DT_Outs is not an integer multiple of DT. Changing DT_Outs to "//trim(Num2LStr(WrOutputsData%DT_Outs))//".",ErrStat3,ErrMsg3,RoutineName) endif endif + end subroutine ValidateSetInputs + !> Validate and set some of the outputs (values must be stored before here as some might be changed) + subroutine ValidateSetVTK(ErrStat3,ErrMsg3) + integer(IntKi), intent( out) :: ErrStat3 !< temporary error status + character(ErrMsgLen), intent( out) :: ErrMsg3 !< temporary error message + + ErrStat3 = ErrID_None + ErrMsg3 = "" + + ! VTK outputs + if ( WrOutputsData%WrVTK < 0_IntKi .or. WrOutputsData%WrVTK > 2_IntKi ) then + call SetErrStat(ErrID_Fatal,"WrVTK option for writing VTK visualization files must be [0: none, 1: init only, 2: animation]",ErrStat3,ErrMsg3,RoutineName) + return + endif + if ( WrOutputsData%WrVTK_Type > 0_IntKi ) then + if ( WrOutputsData%WrVTK_Type < 1_IntKi .or. WrOutputsData%WrVTK_Type > 3_IntKi ) then + call SetErrStat(ErrID_Fatal,"WrVTK_Type option for writing VTK visualization files must be [1: surface, 2: lines, 3: both]",ErrStat3,ErrMsg3,RoutineName) + return + endif + if (WrOutputsData%VTKHubRad < 0.0_SiKi) then + call SetErrStat(ErrID_Warn,"VTKHubRad for surface visualization of hub less than zero. Setting to zero.",ErrStat3,ErrMsg3,RoutineName) + WrOutputsData%VTKHubRad = 0.0_SiKi + endif + endif + if (WrOutputsData%WrVTK > 1_IntKi) then ! only if writing during simulation is requested (ignore init or no outputs) ! If a smaller timestep between outputs is requested than the simulation runs at, change to DT if (WrOutputsData%VTK_DT < Sim%dT) then @@ -827,7 +895,7 @@ subroutine ValidateSetInputs(ErrStat3,ErrMsg3) call SetErrStat(ErrID_Warn,"Requested VTK_DT is not an integer multiple of DT. Changing VTK_DT to "//trim(Num2LStr(WrOutputsData%VTK_DT))//".",ErrStat3,ErrMsg3,RoutineName) endif endif - end subroutine ValidateSetInputs + end subroutine ValidateSetVTK !> allocate data storage for file outputs subroutine SetupFileOutputs() @@ -859,6 +927,7 @@ end subroutine SetupFileOutputs subroutine ShowPassedData() character(1) :: TmpFlag integer :: i,j + character(IntfStrLen) :: tmpPath call WrSCr("") call WrScr("-----------------------------------------------------------") call WrScr("Interface debugging: Variables passed in through interface") @@ -869,12 +938,19 @@ subroutine ShowPassedData() call WrScr(" ADinputFilePassed_C "//TmpFlag ) call WrScr(" ADinputFileString_C (ptr addr) "//trim(Num2LStr(LOC(ADinputFileString_C))) ) call WrScr(" ADinputFileStringLength_C "//trim(Num2LStr( ADinputFileStringLength_C )) ) + if (ADinputFilePassed==0_c_int) then + i = index(ADinputFileString, char(0)) ! skip anything after c_null_char + call WrScr(" ADinputFileString_C "//ADinputFileString(1:i)) + endif TmpFlag="F"; if (IfWinputFilePassed==1_c_int) TmpFlag="T" call WrScr(" IfWinputFilePassed_C "//TmpFlag ) call WrScr(" IfWinputFileString_C (ptr addr)"//trim(Num2LStr(LOC(IfWinputFileString_C))) ) call WrScr(" IfWinputFileStringLength_C "//trim(Num2LStr( IfWinputFileStringLength_C )) ) + if (IfWinputFilePassed==0_c_int) then + i = index(IfWinputFileString, char(0)) ! skip anything after c_null_char + call WrScr(" IfWinputFileString_C "//trim(IfWinputFileString(1:i))) + endif call WrScr(" OutRootName "//trim(OutRootName) ) - call WrScr(" OutVTKDir "//trim(OutVTKDir) ) call WrScr(" Interpolation") call WrScr(" InterpOrder_C "//trim(Num2LStr( InterpOrder_C )) ) call WrScr(" Time variables") @@ -886,11 +962,10 @@ subroutine ShowPassedData() call WrScr(" Flags") TmpFlag="F"; if (storeHHVel==1_c_int) TmpFlag="T" call WrScr(" storeHHVel "//TmpFlag ) - call WrScr(" WrVTK_in "//trim(Num2LStr( WrVTK_in )) ) - call WrScr(" WrVTK_inType "//trim(Num2LStr( WrVTK_inType )) ) - call WrScr(" WrVTK_inDT "//trim(Num2LStr( WrVTK_inDT )) ) call WrScr("-----------------------------------------------------------") end subroutine ShowPassedData +!FIXME: add a ShowReturnData here! + !> This subroutine sets the interface meshes to map to the input motions to the AD !! meshes @@ -1048,13 +1123,12 @@ SUBROUTINE ADI_C_CalcOutput(Time_C, & !------------------------------------------------------- ! Write VTK if requested (animation=2) if (WrOutputsData%WrVTK > 1_IntKi) then - ! Check if writing this step (note this may overwrite if we rerun a step in a correction loop) - if ( mod( n_Global, WrOutputsData%n_VTKTime ) == 0 ) THEN - ! increment the current VTK output number if not a correction step, otherwise overwrite previous - if (.not. EqualRealNos( real(Time,DbKi), InputTimePrev_Calc ) ) then - n_VTK = n_VTK + 1_IntKi ! Increment for this write - endif + ! only write on desired time interval (same logic used in c-binding modules) + VTKn_Global = nint(Time_C / WrOutputsData%VTK_dt ) + if (VTKn_Global /= VTKn_last) then ! already wrote this one + VTKn_last = VTKn_Global ! store the current number to make sure we don't write it twice call WrVTK_Meshes(ADI%u(1)%AD%rotors(:),(/0.0_SiKi,0.0_SiKi,0.0_SiKi/),ErrStat_F2,ErrMsg_F2) + if (Failed()) return endif endif @@ -1078,6 +1152,7 @@ logical function Failed() call SetErrStat_F2C(ErrStat_F,ErrMsg_F,ErrStat_C,ErrMsg_C) endif end function Failed +!FIXME: add a showpassed/return routine END SUBROUTINE ADI_C_CalcOutput !=============================================================================================================== @@ -1288,19 +1363,7 @@ SUBROUTINE ADI_C_End(ErrStat_C,ErrMsg_C) BIND (C, NAME='ADI_C_End') if (allocated(ADI%u)) deallocate(ADI%u) endif - ! Destroy any other copies of states (rerun on (STATE_CURR) is ok) - call ADI_DestroyContState( ADI%x( STATE_LAST), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyContState( ADI%x( STATE_CURR), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyContState( ADI%x( STATE_PRED), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyDiscState( ADI%xd( STATE_LAST), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyDiscState( ADI%xd( STATE_CURR), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyDiscState( ADI%xd( STATE_PRED), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyConstrState( ADI%z( STATE_LAST), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyConstrState( ADI%z( STATE_CURR), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyConstrState( ADI%z( STATE_PRED), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyOtherState( ADI%OtherState(STATE_LAST), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyOtherState( ADI%OtherState(STATE_CURR), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - call ADI_DestroyOtherState( ADI%OtherState(STATE_PRED), ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) + call ADI_DestroyData(ADI, ErrStat_F2, ErrMsg_F2 ); call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) ! if deallocate other items now !if (allocated(InputTimes)) deallocate(InputTimes) @@ -1756,15 +1819,13 @@ subroutine ADI_C_SetRotorMotion( iWT_c, & enddo ! Transfer motions to input meshes - do iWT=1,Sim%NumTurbines - call Set_MotionMesh(iWT, ErrStat_F2, ErrMsg_F2); if (Failed()) return - call AD_SetInputMotion( iWT, ADI_u, & - HubPos_C, HubOri_C, HubVel_C, HubAcc_C, & - NacPos_C, NacOri_C, NacVel_C, NacAcc_C, & - BldRootPos_C, BldRootOri_C, BldRootVel_C, BldRootAcc_C, & - ErrStat_F2, ErrMsg_F2 ) ! transfer input motion mesh to u(1) meshes - if (Failed()) return - enddo + call Set_MotionMesh(iWT, ErrStat_F2, ErrMsg_F2); if (Failed()) return + call AD_SetInputMotion( iWT, ADI_u, & + HubPos_C, HubOri_C, HubVel_C, HubAcc_C, & + NacPos_C, NacOri_C, NacVel_C, NacAcc_C, & + BldRootPos_C, BldRootOri_C, BldRootVel_C, BldRootAcc_C, & + ErrStat_F2, ErrMsg_F2 ) ! transfer input motion mesh to u(1) meshes + if (Failed()) return ! Set error status call SetErrStat_F2C(ErrStat_F,ErrMsg_F,ErrStat_C,ErrMsg_C) @@ -2372,7 +2433,7 @@ subroutine WrVTK_Points(ErrStat3,ErrMsg3) ! Blade point motion (structural mesh from driver) do iBlade=1,Sim%WT(iWT)%NumBlades - call MeshWrVTK(RefPoint, BldStrMotionMesh(iWT)%BldMesh(iBlade), trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.BldStrMotionMesh'//trim(Num2LStr(iBlade)), n_VTK, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) + call MeshWrVTK(RefPoint, BldStrMotionMesh(iWT)%BldMesh(iBlade), trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.BldStrMotionMesh'//trim(Num2LStr(iBlade)), VTKn_Global, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) if (ErrStat3 >= AbortErrLev) return enddo @@ -2380,20 +2441,20 @@ subroutine WrVTK_Points(ErrStat3,ErrMsg3) if (allocated(rot_u(iWT)%BladeRootMotion)) then do iBlade=1,Sim%WT(iWT)%NumBlades if (rot_u(iWT)%BladeRootMotion(iBlade)%Committed) then - call MeshWrVTK(RefPoint, rot_u(iWT)%BladeRootMotion(iBlade), trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.BladeRootMotion'//trim(num2lstr(iBlade)), n_VTK, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) + call MeshWrVTK(RefPoint, rot_u(iWT)%BladeRootMotion(iBlade), trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.BladeRootMotion'//trim(num2lstr(iBlade)), VTKn_Global, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) if (ErrStat3 >= AbortErrLev) return endif enddo endif ! Nacelle (structural point input - if ( rot_u(iWT)%NacelleMotion%Committed ) call MeshWrVTK(RefPoint, rot_u(iWT)%NacelleMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.NacelleMotion', n_VTK, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) + if ( rot_u(iWT)%NacelleMotion%Committed ) call MeshWrVTK(RefPoint, rot_u(iWT)%NacelleMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.NacelleMotion', VTKn_Global, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) if (ErrStat3 >= AbortErrLev) return ! Free wake if (allocated(ADI%m%AD%FVW_u) .and. iWT==1) then if (allocated(ADI%m%AD%FVW_u(1)%WingsMesh)) then - call WrVTK_FVW(ADI%p%AD%FVW, ADI%x(STATE_CURR)%AD%FVW, ADI%z(STATE_CURR)%AD%FVW, ADI%m%AD%FVW, trim(WrOutputsData%VTK_OutFileRoot)//'.FVW', n_VTK, WrOutputsData%VTK_tWidth, bladeFrame=.FALSE.) ! bladeFrame==.FALSE. to output in global coords + call WrVTK_FVW(ADI%p%AD%FVW, ADI%x(STATE_CURR)%AD%FVW, ADI%z(STATE_CURR)%AD%FVW, ADI%m%AD%FVW, trim(WrOutputsData%VTK_OutFileRoot)//'.FVW', VTKn_Global, WrOutputsData%VTK_tWidth, bladeFrame=.FALSE.) ! bladeFrame==.FALSE. to output in global coords endif end if end subroutine WrVTK_Points @@ -2409,11 +2470,11 @@ subroutine WrVTK_Surfaces(ErrStat3,ErrMsg3) ErrMsg3 = '' ! TODO: use this routine when it is moved out of the driver and into ADI - ! call AD_WrVTK_Surfaces(ADI%u(1)%AD, ADI%y%AD, RefPoint, ADI%m%VTK_Surfaces, n_VTK, WrOutputsData%Root, WrOutputsData%VTK_tWidth, 25, WrOutputsData%VTKHubRad) + ! call AD_WrVTK_Surfaces(ADI%u(1)%AD, ADI%y%AD, RefPoint, ADI%m%VTK_Surfaces, VTKn_Global, WrOutputsData%Root, WrOutputsData%VTK_tWidth, 25, WrOutputsData%VTKHubRad) ! Nacelle if ( rot_u(iWT)%NacelleMotion%Committed ) then - call MeshWrVTK_PointSurface (RefPoint, rot_u(iWT)%NacelleMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.NacelleSurface', n_VTK, & + call MeshWrVTK_PointSurface (RefPoint, rot_u(iWT)%NacelleMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.NacelleSurface', VTKn_Global, & OutputFields, errStat3, errMsg3, WrOutputsData%VTK_tWidth, verts=WrOutputsData%VTK_Surface(iWT)%NacelleBox) if (ErrStat3 >= AbortErrLev) return endif @@ -2421,14 +2482,14 @@ subroutine WrVTK_Surfaces(ErrStat3,ErrMsg3) ! Tower if (rot_u(iWT)%TowerMotion%Committed) then call MeshWrVTK_Ln2Surface (RefPoint, rot_u(iWT)%TowerMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.TowerSurface', & - n_VTK, OutputFields, errStat3, errMsg3, WrOutputsData%VTK_tWidth, numSectors, ADI%m%VTK_Surfaces(iWT)%TowerRad ) + VTKn_Global, OutputFields, errStat3, errMsg3, WrOutputsData%VTK_tWidth, numSectors, ADI%m%VTK_Surfaces(iWT)%TowerRad ) if (ErrStat3 >= AbortErrLev) return endif ! Hub if (rot_u(iWT)%HubMotion%Committed) then call MeshWrVTK_PointSurface (RefPoint, rot_u(iWT)%HubMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.HubSurface', & - n_VTK, OutputFields, errStat3, errMsg3, WrOutputsData%VTK_tWidth, & + VTKn_Global, OutputFields, errStat3, errMsg3, WrOutputsData%VTK_tWidth, & NumSegments=numSectors, radius=WrOutputsData%VTKHubRad) if (ErrStat3 >= AbortErrLev) return endif @@ -2438,7 +2499,7 @@ subroutine WrVTK_Surfaces(ErrStat3,ErrMsg3) do iBlade=1,Sim%WT(iWT)%NumBlades if (rot_u(iWT)%BladeMotion(iBlade)%Committed) then call MeshWrVTK_Ln2Surface (RefPoint, rot_u(iWT)%BladeMotion(iBlade), trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Blade'//trim(num2lstr(iBlade))//'Surface', & - n_VTK, OutputFields, errStat3, errMsg3, WrOutputsData%VTK_tWidth , verts=ADI%m%VTK_Surfaces(iWT)%BladeShape(iBlade)%AirfoilCoords, & + VTKn_Global, OutputFields, errStat3, errMsg3, WrOutputsData%VTK_tWidth , verts=ADI%m%VTK_Surfaces(iWT)%BladeShape(iBlade)%AirfoilCoords, & Sib=ADI%y%AD%rotors(iWT)%BladeLoad(iBlade) ) if (ErrStat3 >= AbortErrLev) return endif @@ -2454,22 +2515,22 @@ subroutine WrVTK_Lines(ErrStat3,ErrMsg3) ErrMsg3 = '' ! Tower - if (rot_u(iWT)%TowerMotion%Committed) call MeshWrVTK(RefPoint, rot_u(iWT)%TowerMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Tower', n_VTK, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) + if (rot_u(iWT)%TowerMotion%Committed) call MeshWrVTK(RefPoint, rot_u(iWT)%TowerMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Tower', VTKn_Global, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) if (ErrStat3 >= AbortErrLev) return ! Nacelle meshes - if (rot_u(iWT)%NacelleMotion%Committed) call MeshWrVTK(RefPoint, rot_u(iWT)%NacelleMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Nacelle', n_VTK, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) + if (rot_u(iWT)%NacelleMotion%Committed) call MeshWrVTK(RefPoint, rot_u(iWT)%NacelleMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Nacelle', VTKn_Global, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) if (ErrStat3 >= AbortErrLev) return ! Hub - if (rot_u(iWT)%HubMotion%Committed) call MeshWrVTK(RefPoint, rot_u(iWT)%HubMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Hub', n_VTK, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) + if (rot_u(iWT)%HubMotion%Committed) call MeshWrVTK(RefPoint, rot_u(iWT)%HubMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Hub', VTKn_Global, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) if (ErrStat3 >= AbortErrLev) return ! Blades if (allocated(rot_u(iWT)%BladeMotion)) then do iBlade=1,Sim%WT(iWT)%NumBlades if (rot_u(iWT)%BladeMotion(iBlade)%Committed) then - call MeshWrVTK(RefPoint, rot_u(iWT)%BladeMotion(iBlade), trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Blade'//trim(num2lstr(iBlade)), n_VTK, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) + call MeshWrVTK(RefPoint, rot_u(iWT)%BladeMotion(iBlade), trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Blade'//trim(num2lstr(iBlade)), VTKn_Global, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) if (ErrStat3 >= AbortErrLev) return endif enddo @@ -2522,34 +2583,6 @@ subroutine WrVTK_Ground (RefPoint, HalfLengths, FileRootName, errStat, errMsg) end subroutine WrVTK_Ground -!-------------------------------------------------------------------- -!> Set some temporary data storage arrays to simplify data conversion -subroutine SetTempStorage(ErrStat,ErrMsg) - INTEGER(IntKi), intent(out) :: errStat !< Indicates whether an error occurred (see NWTC_Library) - character(*), intent(out) :: errMsg !< Error message associated with the errStat - INTEGER(IntKi) :: ErrStat_F2 - CHARACTER(ErrMsgLen) :: ErrMsg_F2 - character(*), parameter :: RoutineName = 'SetTempStorage' !< for error handling - ErrStat = ErrID_None - ErrMsg = "" - if (.not. allocated(NumMeshPts)) then - ErrStat = ErrID_Fatal - ErrMSg = "Pre-Init has not been called yet" - return - endif - if (minval(NumMeshPts) < 0) then - ErrStat = ErrID_Fatal - ErrMSg = "ADI_C_SetupRotor haven't been called for all rotors" - return - endif - -contains - logical function Failed() - CALL SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat, ErrMsg, RoutineName ) - Failed = ErrStat >= AbortErrLev - end function Failed -end subroutine SetTempStorage - !-------------------------------------------------------------------- !> Don't leave junk in memory. So destroy meshes and mappings. subroutine ClearTmpStorage() @@ -2575,7 +2608,6 @@ subroutine ClearMeshArr1(MeshName) enddo deallocate(MeshName) end subroutine ClearMeshArr1 - subroutine ClearMeshMapArr2(MapName) type(MeshMapType), allocatable :: MapName(:,:) integer :: i,j @@ -2586,8 +2618,85 @@ subroutine ClearMeshMapArr2(MapName) enddo deallocate(MapName) end subroutine ClearMeshMapArr2 - end subroutine ClearTmpStorage +!> return the pointer to the WaveField data +subroutine ADI_C_GetFlowFieldPointer(FlowFieldPointer_C,ErrStat_C,ErrMsg_C) BIND (C, NAME='ADI_C_GetFlowFieldPointer') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: ADI_C_GetFlowFieldPointer +!GCC$ ATTRIBUTES DLLEXPORT :: ADI_C_GetFlowFieldPointer +#endif + type(c_ptr), intent( out) :: FlowFieldPointer_C + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer :: ErrStat + character(ErrMsgLen) :: ErrMsg + character(*), parameter :: RoutineName = 'ADI_C_GetFlowFieldPointer' + ErrStat = ErrID_None + ErrMSg = "" + if (associated(ADI%m%IW%p%FlowField)) then + FlowFieldPointer_C = C_LOC(ADI%m%IW%p%FlowField) + else + FlowFieldPointer_C = C_NULL_PTR + call SetErrStat(ErrID_Fatal,"Pointer to FlowField data not valid: data not initialized",ErrStat,ErrMsg,RoutineName) + endif + call SetErrStat_F2C( ErrStat, ErrMsg, ErrStat_C, ErrMsg_C ) + if (DebugLevel > 1) call ShowPassedData() + return +contains + subroutine ShowPassedData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: ADI_C_GetFlowFieldPointer") + call WrScr(" --------------------------------------------------------") + call WrScr(" FlowFieldPointer_C -> "//trim(Num2LStr(loc(ADI%m%IW%p%FlowField)))) + call WrScr("-----------------------------------------------------------") + end subroutine ShowPassedData +end subroutine + + +!> set the pointer to the FlowField data +subroutine ADI_C_SetFlowFieldPointer(FlowFieldPointer_C,ErrStat_C,ErrMsg_C) BIND (C, NAME='ADI_C_SetFlowFieldPointer') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: ADI_C_SetFlowFieldPointer +!GCC$ ATTRIBUTES DLLEXPORT :: ADI_C_SetFlowFieldPointer +#endif + type(c_ptr), intent(in ) :: FlowFieldPointer_C + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer :: ErrStat + character(ErrMsgLen) :: ErrMsg + character(*), parameter :: RoutineName = 'ADI_C_SetFlowFieldPointer' + ErrStat = ErrID_None + ErrMSg = "" + ! check if externFlowField expected + if (.not. externFlowField) then + call SetErrStat(ErrID_Severe,"External flowfield not expected. Set externFlowField_in=1 in call to ADI_C_PreInit prior to calling ADI_C_SetFlowFieldPointer.",ErrStat,ErrMsg,RoutineName) + endif + ! set pointer + call C_F_POINTER(FlowFieldPointer_C, InitInp%FlowField) + if (associated(InitInp%FlowField)) then + ! basic sanity check + if (InitInp%FlowField%FieldType <= 0_IntKi) then + call SetErrStat(ErrID_Fatal,"Invalid pointer passed in, or FlowField not initialized",ErrStat,ErrMsg,RoutineName) + else + FlowFieldPtrSet = .true. + endif + else + call SetErrStat(ErrID_Fatal,"Invalid pointer passed in, or FlowField not initialized.",ErrStat,ErrMsg,RoutineName) + endif + call SetErrStat_F2C( ErrStat, ErrMsg, ErrStat_C, ErrMsg_C ) + if (DebugLevel > 1) call ShowPassedData() + return +contains + subroutine ShowPassedData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: ADI_C_SetFlowFieldPointer") + call WrScr(" --------------------------------------------------------") + call WrScr(" FlowFieldPointer_C <- "//trim(Num2LStr(loc(InitInp%FlowField)))) + call WrScr("-----------------------------------------------------------") + end subroutine ShowPassedData +end subroutine + + END MODULE AeroDyn_Inflow_C_BINDING diff --git a/modules/aerodyn/src/AeroDyn_Inflow_C_Binding_Registry.txt b/modules/aerodyn/src/AeroDyn_Inflow_C_Binding_Registry.txt index 11c880895c..81d602087d 100644 --- a/modules/aerodyn/src/AeroDyn_Inflow_C_Binding_Registry.txt +++ b/modules/aerodyn/src/AeroDyn_Inflow_C_Binding_Registry.txt @@ -11,8 +11,7 @@ ################################################################################################################################### # ...... Include files (definitions from NWTC Library) ............................................................................ include Registry_NWTC_Library.txt -# -# ..... Table of combined cases to run ....................................................................................................... +# ADI c-bind types param AeroDyn_Inflow_C_Binding/ADI_cbind - IntKi NumPtsDiskAvg - 144 - "Number of points for disk average velocity calculations" - typedef ^ DiskAvgVelData ReKi DiskWindPosRel {3}{NumPtsDiskAvg} - - "Position points for disk average sampling, relative to hub" - typedef ^ ^ ReKi DiskWindPosAbs {3}{NumPtsDiskAvg} - - "Position points for disk average sampling, absolute/global/interial" - diff --git a/modules/aerodyn/src/AeroDyn_Inflow_Registry.txt b/modules/aerodyn/src/AeroDyn_Inflow_Registry.txt index 3054e4bc18..3dfe392fe6 100644 --- a/modules/aerodyn/src/AeroDyn_Inflow_Registry.txt +++ b/modules/aerodyn/src/AeroDyn_Inflow_Registry.txt @@ -27,23 +27,23 @@ typedef ^ ^ InflowWind_ParameterType p typedef ^ ^ InflowWind_MiscVarType m - - - "Misc/optimization variables" typedef ^ ^ InflowWind_InputType u - - - "Array of inputs associated with InputTimes" typedef ^ ^ InflowWind_OutputType y - - - "System outputs" -typedef ^ ^ IntKi CompInflow - - - "0=Steady Wind, 1=InflowWind" "-" +typedef ^ ^ IntKi CompInflow - - - "0=Steady Wind, 1=InflowWind, 2=External IfW (ADI c-bind only)" "-" typedef ^ ^ ReKi HWindSpeed - - - "RefHeight Wind speed" typedef ^ ^ ReKi RefHt - - - "RefHeight" typedef ^ ^ ReKi PLExp - - - "PLExp" # ..... InflowWind Input data ..................................................................................................... typedef ^ ADI_IW_InputData Character(1024) InputFile - - - "Name of InfloWind input file" - -typedef ^ ^ IntKi CompInflow - - - "0=Steady Wind, 1=InflowWind" "-" +typedef ^ ^ IntKi CompInflow - - - "0=Steady Wind, 1=InflowWind, 2=External IfW (ADI c-bind only)" "-" typedef ^ ^ ReKi HWindSpeed - - - "RefHeight Wind speed" typedef ^ ^ ReKi RefHt - - - "RefHeight" typedef ^ ^ ReKi PLExp - - - "PLExp" typedef ^ ^ IntKi MHK - - - "MHK turbine type switch" - +typedef ^ ^ ReKi WtrDpth - - - "Water depth" m +typedef ^ ^ ReKi MSL2SWL - - - "Offset between still-water level and mean sea level" m typedef ^ ^ IntKi FilePassingMethod - 0 - "Should we read everthing from an input file (0), passed in as a FileInfoType structure (1), or passed as the IfW_InputFile structure (2)" - typedef ^ ^ FileInfoType PassedFileInfo - - - "If we don't use the input file, pass everything through this as a FileInfo structure" - typedef ^ ^ InflowWind_InputFile PassedFileData - - - "If we don't use the input file, pass everything through this as an IfW InputFile structure" - typedef ^ ^ LOGICAL Linearize - .FALSE. - "Flag that tells this module if the glue code wants to linearize." - -typedef ^ ^ ReKi WtrDpth - - - "Water depth" m -typedef ^ ^ ReKi MSL2SWL - - - "Offset between still-water level and mean sea level" m typedef ^ ^ Character(1024) RootName - - - "RootName for writing output files" - typedef ^ ^ LOGICAL OutputAccel - .FALSE. - "Flag to output wind acceleration" - @@ -57,6 +57,8 @@ typedef ^ ^ Logical storeHHV typedef ^ ^ IntKi WrVTK - 0 - "0= no vtk, 1=init only, 2=animation" "-" typedef ^ ^ IntKi WrVTK_Type - 1 - "Flag for VTK output type (1=surface, 2=line, 3=both)" - typedef ^ ^ ReKi WtrDpth - - - "Water depth" m +typedef ^ ^ FlowFieldType *FlowField - - - "Pointer of InflowWinds flow field data type (from external IfW instance -- used with c-binding)" - + # ..... InitOut ................................................................................................................... typedef ^ InitOutputType ProgDesc Ver - - - "This module's name, version, and date" - diff --git a/modules/aerodyn/src/AeroDyn_Inflow_Types.f90 b/modules/aerodyn/src/AeroDyn_Inflow_Types.f90 index feafdc0262..470536defc 100644 --- a/modules/aerodyn/src/AeroDyn_Inflow_Types.f90 +++ b/modules/aerodyn/src/AeroDyn_Inflow_Types.f90 @@ -46,7 +46,7 @@ MODULE AeroDyn_Inflow_Types TYPE(InflowWind_MiscVarType) :: m !< Misc/optimization variables [-] TYPE(InflowWind_InputType) :: u !< Array of inputs associated with InputTimes [-] TYPE(InflowWind_OutputType) :: y !< System outputs [-] - INTEGER(IntKi) :: CompInflow = 0_IntKi !< 0=Steady Wind, 1=InflowWind [-] + INTEGER(IntKi) :: CompInflow = 0_IntKi !< 0=Steady Wind, 1=InflowWind, 2=External IfW (ADI c-bind only) [-] REAL(ReKi) :: HWindSpeed = 0.0_ReKi !< RefHeight Wind speed [-] REAL(ReKi) :: RefHt = 0.0_ReKi !< RefHeight [-] REAL(ReKi) :: PLExp = 0.0_ReKi !< PLExp [-] @@ -55,17 +55,17 @@ MODULE AeroDyn_Inflow_Types ! ========= ADI_IW_InputData ======= TYPE, PUBLIC :: ADI_IW_InputData Character(1024) :: InputFile !< Name of InfloWind input file [-] - INTEGER(IntKi) :: CompInflow = 0_IntKi !< 0=Steady Wind, 1=InflowWind [-] + INTEGER(IntKi) :: CompInflow = 0_IntKi !< 0=Steady Wind, 1=InflowWind, 2=External IfW (ADI c-bind only) [-] REAL(ReKi) :: HWindSpeed = 0.0_ReKi !< RefHeight Wind speed [-] REAL(ReKi) :: RefHt = 0.0_ReKi !< RefHeight [-] REAL(ReKi) :: PLExp = 0.0_ReKi !< PLExp [-] INTEGER(IntKi) :: MHK = 0_IntKi !< MHK turbine type switch [-] + REAL(ReKi) :: WtrDpth = 0.0_ReKi !< Water depth [m] + REAL(ReKi) :: MSL2SWL = 0.0_ReKi !< Offset between still-water level and mean sea level [m] INTEGER(IntKi) :: FilePassingMethod = 0 !< Should we read everthing from an input file (0), passed in as a FileInfoType structure (1), or passed as the IfW_InputFile structure (2) [-] TYPE(FileInfoType) :: PassedFileInfo !< If we don't use the input file, pass everything through this as a FileInfo structure [-] TYPE(InflowWind_InputFile) :: PassedFileData !< If we don't use the input file, pass everything through this as an IfW InputFile structure [-] LOGICAL :: Linearize = .FALSE. !< Flag that tells this module if the glue code wants to linearize. [-] - REAL(ReKi) :: WtrDpth = 0.0_ReKi !< Water depth [m] - REAL(ReKi) :: MSL2SWL = 0.0_ReKi !< Offset between still-water level and mean sea level [m] Character(1024) :: RootName !< RootName for writing output files [-] LOGICAL :: OutputAccel = .FALSE. !< Flag to output wind acceleration [-] END TYPE ADI_IW_InputData @@ -79,6 +79,7 @@ MODULE AeroDyn_Inflow_Types INTEGER(IntKi) :: WrVTK = 0 !< 0= no vtk, 1=init only, 2=animation [-] INTEGER(IntKi) :: WrVTK_Type = 1 !< Flag for VTK output type (1=surface, 2=line, 3=both) [-] REAL(ReKi) :: WtrDpth = 0.0_ReKi !< Water depth [m] + TYPE(FlowFieldType) , POINTER :: FlowField => NULL() !< Pointer of InflowWinds flow field data type (from external IfW instance -- used with c-binding) [-] END TYPE ADI_InitInputType ! ======================= ! ========= ADI_InitOutputType ======= @@ -198,31 +199,30 @@ MODULE AeroDyn_Inflow_Types integer(IntKi), public, parameter :: ADI_x_AD_rotors_BEMT_DBEMT_element_vind = 2 ! ADI%AD%rotors(DL%i1)%BEMT%DBEMT%element(DL%i2, DL%i3)%vind integer(IntKi), public, parameter :: ADI_x_AD_rotors_BEMT_DBEMT_element_vind_1 = 3 ! ADI%AD%rotors(DL%i1)%BEMT%DBEMT%element(DL%i2, DL%i3)%vind_1 integer(IntKi), public, parameter :: ADI_x_AD_rotors_BEMT_V_w = 4 ! ADI%AD%rotors(DL%i1)%BEMT%V_w - integer(IntKi), public, parameter :: ADI_x_AD_rotors_AA_DummyContState = 5 ! ADI%AD%rotors(DL%i1)%AA%DummyContState - integer(IntKi), public, parameter :: ADI_x_AD_FVW_W_Gamma_NW = 6 ! ADI%AD%FVW%W(DL%i1)%Gamma_NW - integer(IntKi), public, parameter :: ADI_x_AD_FVW_W_Gamma_FW = 7 ! ADI%AD%FVW%W(DL%i1)%Gamma_FW - integer(IntKi), public, parameter :: ADI_x_AD_FVW_W_Eps_NW = 8 ! ADI%AD%FVW%W(DL%i1)%Eps_NW - integer(IntKi), public, parameter :: ADI_x_AD_FVW_W_Eps_FW = 9 ! ADI%AD%FVW%W(DL%i1)%Eps_FW - integer(IntKi), public, parameter :: ADI_x_AD_FVW_W_r_NW = 10 ! ADI%AD%FVW%W(DL%i1)%r_NW - integer(IntKi), public, parameter :: ADI_x_AD_FVW_W_r_FW = 11 ! ADI%AD%FVW%W(DL%i1)%r_FW - integer(IntKi), public, parameter :: ADI_x_AD_FVW_UA_element_x = 12 ! ADI%AD%FVW%UA(DL%i1)%element(DL%i2, DL%i3)%x - integer(IntKi), public, parameter :: ADI_u_AD_rotors_NacelleMotion = 13 ! ADI%AD%rotors(DL%i1)%NacelleMotion - integer(IntKi), public, parameter :: ADI_u_AD_rotors_TowerMotion = 14 ! ADI%AD%rotors(DL%i1)%TowerMotion - integer(IntKi), public, parameter :: ADI_u_AD_rotors_HubMotion = 15 ! ADI%AD%rotors(DL%i1)%HubMotion - integer(IntKi), public, parameter :: ADI_u_AD_rotors_BladeRootMotion = 16 ! ADI%AD%rotors(DL%i1)%BladeRootMotion(DL%i2) - integer(IntKi), public, parameter :: ADI_u_AD_rotors_BladeMotion = 17 ! ADI%AD%rotors(DL%i1)%BladeMotion(DL%i2) - integer(IntKi), public, parameter :: ADI_u_AD_rotors_TFinMotion = 18 ! ADI%AD%rotors(DL%i1)%TFinMotion - integer(IntKi), public, parameter :: ADI_u_AD_rotors_UserProp = 19 ! ADI%AD%rotors(DL%i1)%UserProp - integer(IntKi), public, parameter :: ADI_y_AD_rotors_NacelleLoad = 20 ! ADI%AD%rotors(DL%i1)%NacelleLoad - integer(IntKi), public, parameter :: ADI_y_AD_rotors_HubLoad = 21 ! ADI%AD%rotors(DL%i1)%HubLoad - integer(IntKi), public, parameter :: ADI_y_AD_rotors_TowerLoad = 22 ! ADI%AD%rotors(DL%i1)%TowerLoad - integer(IntKi), public, parameter :: ADI_y_AD_rotors_BladeLoad = 23 ! ADI%AD%rotors(DL%i1)%BladeLoad(DL%i2) - integer(IntKi), public, parameter :: ADI_y_AD_rotors_TFinLoad = 24 ! ADI%AD%rotors(DL%i1)%TFinLoad - integer(IntKi), public, parameter :: ADI_y_AD_rotors_WriteOutput = 25 ! ADI%AD%rotors(DL%i1)%WriteOutput - integer(IntKi), public, parameter :: ADI_y_HHVel = 26 ! ADI%HHVel - integer(IntKi), public, parameter :: ADI_y_PLExp = 27 ! ADI%PLExp - integer(IntKi), public, parameter :: ADI_y_IW_WriteOutput = 28 ! ADI%IW_WriteOutput - integer(IntKi), public, parameter :: ADI_y_WriteOutput = 29 ! ADI%WriteOutput + integer(IntKi), public, parameter :: ADI_x_AD_FVW_W_Gamma_NW = 5 ! ADI%AD%FVW%W(DL%i1)%Gamma_NW + integer(IntKi), public, parameter :: ADI_x_AD_FVW_W_Gamma_FW = 6 ! ADI%AD%FVW%W(DL%i1)%Gamma_FW + integer(IntKi), public, parameter :: ADI_x_AD_FVW_W_Eps_NW = 7 ! ADI%AD%FVW%W(DL%i1)%Eps_NW + integer(IntKi), public, parameter :: ADI_x_AD_FVW_W_Eps_FW = 8 ! ADI%AD%FVW%W(DL%i1)%Eps_FW + integer(IntKi), public, parameter :: ADI_x_AD_FVW_W_r_NW = 9 ! ADI%AD%FVW%W(DL%i1)%r_NW + integer(IntKi), public, parameter :: ADI_x_AD_FVW_W_r_FW = 10 ! ADI%AD%FVW%W(DL%i1)%r_FW + integer(IntKi), public, parameter :: ADI_x_AD_FVW_UA_element_x = 11 ! ADI%AD%FVW%UA(DL%i1)%element(DL%i2, DL%i3)%x + integer(IntKi), public, parameter :: ADI_u_AD_rotors_NacelleMotion = 12 ! ADI%AD%rotors(DL%i1)%NacelleMotion + integer(IntKi), public, parameter :: ADI_u_AD_rotors_TowerMotion = 13 ! ADI%AD%rotors(DL%i1)%TowerMotion + integer(IntKi), public, parameter :: ADI_u_AD_rotors_HubMotion = 14 ! ADI%AD%rotors(DL%i1)%HubMotion + integer(IntKi), public, parameter :: ADI_u_AD_rotors_BladeRootMotion = 15 ! ADI%AD%rotors(DL%i1)%BladeRootMotion(DL%i2) + integer(IntKi), public, parameter :: ADI_u_AD_rotors_BladeMotion = 16 ! ADI%AD%rotors(DL%i1)%BladeMotion(DL%i2) + integer(IntKi), public, parameter :: ADI_u_AD_rotors_TFinMotion = 17 ! ADI%AD%rotors(DL%i1)%TFinMotion + integer(IntKi), public, parameter :: ADI_u_AD_rotors_UserProp = 18 ! ADI%AD%rotors(DL%i1)%UserProp + integer(IntKi), public, parameter :: ADI_y_AD_rotors_NacelleLoad = 19 ! ADI%AD%rotors(DL%i1)%NacelleLoad + integer(IntKi), public, parameter :: ADI_y_AD_rotors_HubLoad = 20 ! ADI%AD%rotors(DL%i1)%HubLoad + integer(IntKi), public, parameter :: ADI_y_AD_rotors_TowerLoad = 21 ! ADI%AD%rotors(DL%i1)%TowerLoad + integer(IntKi), public, parameter :: ADI_y_AD_rotors_BladeLoad = 22 ! ADI%AD%rotors(DL%i1)%BladeLoad(DL%i2) + integer(IntKi), public, parameter :: ADI_y_AD_rotors_TFinLoad = 23 ! ADI%AD%rotors(DL%i1)%TFinLoad + integer(IntKi), public, parameter :: ADI_y_AD_rotors_WriteOutput = 24 ! ADI%AD%rotors(DL%i1)%WriteOutput + integer(IntKi), public, parameter :: ADI_y_HHVel = 25 ! ADI%HHVel + integer(IntKi), public, parameter :: ADI_y_PLExp = 26 ! ADI%PLExp + integer(IntKi), public, parameter :: ADI_y_IW_WriteOutput = 27 ! ADI%IW_WriteOutput + integer(IntKi), public, parameter :: ADI_y_WriteOutput = 28 ! ADI%WriteOutput contains @@ -350,6 +350,8 @@ subroutine ADI_CopyIW_InputData(SrcIW_InputDataData, DstIW_InputDataData, CtrlCo DstIW_InputDataData%RefHt = SrcIW_InputDataData%RefHt DstIW_InputDataData%PLExp = SrcIW_InputDataData%PLExp DstIW_InputDataData%MHK = SrcIW_InputDataData%MHK + DstIW_InputDataData%WtrDpth = SrcIW_InputDataData%WtrDpth + DstIW_InputDataData%MSL2SWL = SrcIW_InputDataData%MSL2SWL DstIW_InputDataData%FilePassingMethod = SrcIW_InputDataData%FilePassingMethod call NWTC_Library_CopyFileInfoType(SrcIW_InputDataData%PassedFileInfo, DstIW_InputDataData%PassedFileInfo, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) @@ -358,8 +360,6 @@ subroutine ADI_CopyIW_InputData(SrcIW_InputDataData, DstIW_InputDataData, CtrlCo call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return DstIW_InputDataData%Linearize = SrcIW_InputDataData%Linearize - DstIW_InputDataData%WtrDpth = SrcIW_InputDataData%WtrDpth - DstIW_InputDataData%MSL2SWL = SrcIW_InputDataData%MSL2SWL DstIW_InputDataData%RootName = SrcIW_InputDataData%RootName DstIW_InputDataData%OutputAccel = SrcIW_InputDataData%OutputAccel end subroutine @@ -390,12 +390,12 @@ subroutine ADI_PackIW_InputData(RF, Indata) call RegPack(RF, InData%RefHt) call RegPack(RF, InData%PLExp) call RegPack(RF, InData%MHK) + call RegPack(RF, InData%WtrDpth) + call RegPack(RF, InData%MSL2SWL) call RegPack(RF, InData%FilePassingMethod) call NWTC_Library_PackFileInfoType(RF, InData%PassedFileInfo) call InflowWind_PackInputFile(RF, InData%PassedFileData) call RegPack(RF, InData%Linearize) - call RegPack(RF, InData%WtrDpth) - call RegPack(RF, InData%MSL2SWL) call RegPack(RF, InData%RootName) call RegPack(RF, InData%OutputAccel) if (RegCheckErr(RF, RoutineName)) return @@ -412,12 +412,12 @@ subroutine ADI_UnPackIW_InputData(RF, OutData) call RegUnpack(RF, OutData%RefHt); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%PLExp); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%MHK); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%WtrDpth); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%MSL2SWL); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%FilePassingMethod); if (RegCheckErr(RF, RoutineName)) return call NWTC_Library_UnpackFileInfoType(RF, OutData%PassedFileInfo) ! PassedFileInfo call InflowWind_UnpackInputFile(RF, OutData%PassedFileData) ! PassedFileData call RegUnpack(RF, OutData%Linearize); if (RegCheckErr(RF, RoutineName)) return - call RegUnpack(RF, OutData%WtrDpth); if (RegCheckErr(RF, RoutineName)) return - call RegUnpack(RF, OutData%MSL2SWL); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%RootName); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%OutputAccel); if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -428,6 +428,7 @@ subroutine ADI_CopyInitInput(SrcInitInputData, DstInitInputData, CtrlCode, ErrSt integer(IntKi), intent(in ) :: CtrlCode integer(IntKi), intent( out) :: ErrStat character(*), intent( out) :: ErrMsg + integer(B4Ki) :: LB(0), UB(0) integer(IntKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 character(*), parameter :: RoutineName = 'ADI_CopyInitInput' @@ -444,6 +445,7 @@ subroutine ADI_CopyInitInput(SrcInitInputData, DstInitInputData, CtrlCode, ErrSt DstInitInputData%WrVTK = SrcInitInputData%WrVTK DstInitInputData%WrVTK_Type = SrcInitInputData%WrVTK_Type DstInitInputData%WtrDpth = SrcInitInputData%WtrDpth + DstInitInputData%FlowField => SrcInitInputData%FlowField end subroutine subroutine ADI_DestroyInitInput(InitInputData, ErrStat, ErrMsg) @@ -459,12 +461,14 @@ subroutine ADI_DestroyInitInput(InitInputData, ErrStat, ErrMsg) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) call ADI_DestroyIW_InputData(InitInputData%IW_InitInp, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + nullify(InitInputData%FlowField) end subroutine subroutine ADI_PackInitInput(RF, Indata) type(RegFile), intent(inout) :: RF type(ADI_InitInputType), intent(in) :: InData character(*), parameter :: RoutineName = 'ADI_PackInitInput' + logical :: PtrInIndex if (RF%ErrStat >= AbortErrLev) return call AD_PackInitInput(RF, InData%AD) call ADI_PackIW_InputData(RF, InData%IW_InitInp) @@ -473,6 +477,13 @@ subroutine ADI_PackInitInput(RF, Indata) call RegPack(RF, InData%WrVTK) call RegPack(RF, InData%WrVTK_Type) call RegPack(RF, InData%WtrDpth) + call RegPack(RF, associated(InData%FlowField)) + if (associated(InData%FlowField)) then + call RegPackPointer(RF, c_loc(InData%FlowField), PtrInIndex) + if (.not. PtrInIndex) then + call IfW_FlowField_PackFlowFieldType(RF, InData%FlowField) + end if + end if if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -480,6 +491,11 @@ subroutine ADI_UnPackInitInput(RF, OutData) type(RegFile), intent(inout) :: RF type(ADI_InitInputType), intent(inout) :: OutData character(*), parameter :: RoutineName = 'ADI_UnPackInitInput' + integer(B4Ki) :: LB(0), UB(0) + integer(IntKi) :: stat + logical :: IsAllocAssoc + integer(B8Ki) :: PtrIdx + type(c_ptr) :: Ptr if (RF%ErrStat /= ErrID_None) return call AD_UnpackInitInput(RF, OutData%AD) ! AD call ADI_UnpackIW_InputData(RF, OutData%IW_InitInp) ! IW_InitInp @@ -488,6 +504,24 @@ subroutine ADI_UnPackInitInput(RF, OutData) call RegUnpack(RF, OutData%WrVTK); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%WrVTK_Type); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%WtrDpth); if (RegCheckErr(RF, RoutineName)) return + if (associated(OutData%FlowField)) deallocate(OutData%FlowField) + call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return + if (IsAllocAssoc) then + call RegUnpackPointer(RF, Ptr, PtrIdx); if (RegCheckErr(RF, RoutineName)) return + if (c_associated(Ptr)) then + call c_f_pointer(Ptr, OutData%FlowField) + else + allocate(OutData%FlowField,stat=stat) + if (stat /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating OutData%FlowField.', RF%ErrStat, RF%ErrMsg, RoutineName) + return + end if + RF%Pointers(PtrIdx) = c_loc(OutData%FlowField) + call IfW_FlowField_UnpackFlowFieldType(RF, OutData%FlowField) ! FlowField + end if + else + OutData%FlowField => null() + end if end subroutine subroutine ADI_CopyInitOutput(SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg) @@ -1992,8 +2026,6 @@ subroutine ADI_VarPackContState(V, x, ValAry) VarVals = x%AD%rotors(DL%i1)%BEMT%DBEMT%element(DL%i2, DL%i3)%vind_1(V%iLB:V%iUB) ! Rank 1 Array case (ADI_x_AD_rotors_BEMT_V_w) VarVals = x%AD%rotors(DL%i1)%BEMT%V_w(V%iLB:V%iUB) ! Rank 1 Array - case (ADI_x_AD_rotors_AA_DummyContState) - VarVals(1) = x%AD%rotors(DL%i1)%AA%DummyContState ! Scalar case (ADI_x_AD_FVW_W_Gamma_NW) VarVals = x%AD%FVW%W(DL%i1)%Gamma_NW(V%iLB:V%iUB,V%j) ! Rank 2 Array case (ADI_x_AD_FVW_W_Gamma_FW) @@ -2038,8 +2070,6 @@ subroutine ADI_VarUnpackContState(V, ValAry, x) x%AD%rotors(DL%i1)%BEMT%DBEMT%element(DL%i2, DL%i3)%vind_1(V%iLB:V%iUB) = VarVals ! Rank 1 Array case (ADI_x_AD_rotors_BEMT_V_w) x%AD%rotors(DL%i1)%BEMT%V_w(V%iLB:V%iUB) = VarVals ! Rank 1 Array - case (ADI_x_AD_rotors_AA_DummyContState) - x%AD%rotors(DL%i1)%AA%DummyContState = VarVals(1) ! Scalar case (ADI_x_AD_FVW_W_Gamma_NW) x%AD%FVW%W(DL%i1)%Gamma_NW(V%iLB:V%iUB, V%j) = VarVals ! Rank 2 Array case (ADI_x_AD_FVW_W_Gamma_FW) @@ -2070,8 +2100,6 @@ function ADI_ContinuousStateFieldName(DL) result(Name) Name = "x%AD%rotors("//trim(Num2LStr(DL%i1))//")%BEMT%DBEMT%element("//trim(Num2LStr(DL%i2))//", "//trim(Num2LStr(DL%i3))//")%vind_1" case (ADI_x_AD_rotors_BEMT_V_w) Name = "x%AD%rotors("//trim(Num2LStr(DL%i1))//")%BEMT%V_w" - case (ADI_x_AD_rotors_AA_DummyContState) - Name = "x%AD%rotors("//trim(Num2LStr(DL%i1))//")%AA%DummyContState" case (ADI_x_AD_FVW_W_Gamma_NW) Name = "x%AD%FVW%W("//trim(Num2LStr(DL%i1))//")%Gamma_NW" case (ADI_x_AD_FVW_W_Gamma_FW) @@ -2115,8 +2143,6 @@ subroutine ADI_VarPackContStateDeriv(V, x, ValAry) VarVals = x%AD%rotors(DL%i1)%BEMT%DBEMT%element(DL%i2, DL%i3)%vind_1(V%iLB:V%iUB) ! Rank 1 Array case (ADI_x_AD_rotors_BEMT_V_w) VarVals = x%AD%rotors(DL%i1)%BEMT%V_w(V%iLB:V%iUB) ! Rank 1 Array - case (ADI_x_AD_rotors_AA_DummyContState) - VarVals(1) = x%AD%rotors(DL%i1)%AA%DummyContState ! Scalar case (ADI_x_AD_FVW_W_Gamma_NW) VarVals = x%AD%FVW%W(DL%i1)%Gamma_NW(V%iLB:V%iUB,V%j) ! Rank 2 Array case (ADI_x_AD_FVW_W_Gamma_FW) diff --git a/modules/aerodyn/src/AeroDyn_Registry.txt b/modules/aerodyn/src/AeroDyn_Registry.txt index a709f48016..d82817278f 100644 --- a/modules/aerodyn/src/AeroDyn_Registry.txt +++ b/modules/aerodyn/src/AeroDyn_Registry.txt @@ -18,6 +18,7 @@ usefrom UnsteadyAero_Registry.txt usefrom AeroAcoustics_Registry.txt usefrom InflowWind.txt usefrom SeaSt_WaveField.txt +usefrom GridInterp.txt param AeroDyn/AD - IntKi ModelUnknown - -1 - "" - param ^ - IntKi WakeMod_none - 0 - "Wake model - none" - @@ -259,7 +260,6 @@ typedef ^ AD_InputFile RotInputFile rotors {:} - - "Rotor (blades and tower # ..... States .................................................................................................................... # Define continuous (differentiable) states here: typedef ^ RotContinuousStateType BEMT_ContinuousStateType BEMT - - - "Continuous states from the BEMT module" - -typedef ^ RotContinuousStateType AA_ContinuousStateType AA - - - "Continuous states from the AA module" - typedef ^ ContinuousStateType RotContinuousStateType rotors {:} - - "Continuous states for each rotor" - typedef ^ ContinuousStateType FVW_ContinuousStateType FVW - - - "Continuous states from the FVW module" - @@ -274,7 +274,6 @@ typedef ^ DiscreteStateType FVW_DiscreteStateType FVW - - - "Discrete states fro # Define constraint states here: typedef ^ RotConstraintStateType BEMT_ConstraintStateType BEMT - - - "Constraint states from the BEMT module" - -typedef ^ RotConstraintStateType AA_ConstraintStateType AA - - - "Constraint states from the AA module" - typedef ^ ConstraintStateType RotConstraintStateType rotors {:} - - "Constraint states for each rotor" - typedef ^ ConstraintStateType FVW_ConstraintStateType FVW - - - "Constraint states from the FVW module" - @@ -526,7 +525,7 @@ typedef ^ MiscVarType FVW_MiscVarType FVW - - - "MiscVars from the FVW module" - typedef ^ MiscVarType ReKi WindPos {:}{:} - - "XYZ coordinates to query for wind velocity/acceleration" - typedef ^ MiscVarType ReKi WindVel {:}{:} - - "XYZ components of wind velocity" - typedef ^ MiscVarType ReKi WindAcc {:}{:} - - "XYZ components of wind acceleration" - -typedef ^ MiscVarType SeaSt_WaveField_MiscVarType WaveField_m - - - "misc var information from the SeaState WaveField module" - +typedef ^ MiscVarType GridInterp_MiscVarType WaveField_m - - - "misc var information from the SeaState WaveField module" - typedef ^ MiscVarType AD_InflowType Inflow {:} - - "Inflow storage (size of u for history of inputs)" - typedef ^ MiscVarType AD_InputType u_perturb - - - "input perturbation for linearization" - typedef ^ MiscVarType AD_OutputType y_lin - - - "output perturbation for linearization" - diff --git a/modules/aerodyn/src/AeroDyn_Types.f90 b/modules/aerodyn/src/AeroDyn_Types.f90 index 6db664e30e..d7cc9f3738 100644 --- a/modules/aerodyn/src/AeroDyn_Types.f90 +++ b/modules/aerodyn/src/AeroDyn_Types.f90 @@ -278,7 +278,6 @@ MODULE AeroDyn_Types ! ========= RotContinuousStateType ======= TYPE, PUBLIC :: RotContinuousStateType TYPE(BEMT_ContinuousStateType) :: BEMT !< Continuous states from the BEMT module [-] - TYPE(AA_ContinuousStateType) :: AA !< Continuous states from the AA module [-] END TYPE RotContinuousStateType ! ======================= ! ========= AD_ContinuousStateType ======= @@ -302,7 +301,6 @@ MODULE AeroDyn_Types ! ========= RotConstraintStateType ======= TYPE, PUBLIC :: RotConstraintStateType TYPE(BEMT_ConstraintStateType) :: BEMT !< Constraint states from the BEMT module [-] - TYPE(AA_ConstraintStateType) :: AA !< Constraint states from the AA module [-] END TYPE RotConstraintStateType ! ======================= ! ========= AD_ConstraintStateType ======= @@ -570,7 +568,7 @@ MODULE AeroDyn_Types REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: WindPos !< XYZ coordinates to query for wind velocity/acceleration [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: WindVel !< XYZ components of wind velocity [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: WindAcc !< XYZ components of wind acceleration [-] - TYPE(SeaSt_WaveField_MiscVarType) :: WaveField_m !< misc var information from the SeaState WaveField module [-] + TYPE(GridInterp_MiscVarType) :: WaveField_m !< misc var information from the SeaState WaveField module [-] TYPE(AD_InflowType) , DIMENSION(:), ALLOCATABLE :: Inflow !< Inflow storage (size of u for history of inputs) [-] TYPE(AD_InputType) :: u_perturb !< input perturbation for linearization [-] TYPE(AD_OutputType) :: y_lin !< output perturbation for linearization [-] @@ -580,20 +578,19 @@ MODULE AeroDyn_Types integer(IntKi), public, parameter :: AD_x_BEMT_DBEMT_element_vind = 2 ! AD%BEMT%DBEMT%element(DL%i1, DL%i2)%vind integer(IntKi), public, parameter :: AD_x_BEMT_DBEMT_element_vind_1 = 3 ! AD%BEMT%DBEMT%element(DL%i1, DL%i2)%vind_1 integer(IntKi), public, parameter :: AD_x_BEMT_V_w = 4 ! AD%BEMT%V_w - integer(IntKi), public, parameter :: AD_x_AA_DummyContState = 5 ! AD%AA%DummyContState - integer(IntKi), public, parameter :: AD_u_NacelleMotion = 6 ! AD%NacelleMotion - integer(IntKi), public, parameter :: AD_u_TowerMotion = 7 ! AD%TowerMotion - integer(IntKi), public, parameter :: AD_u_HubMotion = 8 ! AD%HubMotion - integer(IntKi), public, parameter :: AD_u_BladeRootMotion = 9 ! AD%BladeRootMotion(DL%i1) - integer(IntKi), public, parameter :: AD_u_BladeMotion = 10 ! AD%BladeMotion(DL%i1) - integer(IntKi), public, parameter :: AD_u_TFinMotion = 11 ! AD%TFinMotion - integer(IntKi), public, parameter :: AD_u_UserProp = 12 ! AD%UserProp - integer(IntKi), public, parameter :: AD_y_NacelleLoad = 13 ! AD%NacelleLoad - integer(IntKi), public, parameter :: AD_y_HubLoad = 14 ! AD%HubLoad - integer(IntKi), public, parameter :: AD_y_TowerLoad = 15 ! AD%TowerLoad - integer(IntKi), public, parameter :: AD_y_BladeLoad = 16 ! AD%BladeLoad(DL%i1) - integer(IntKi), public, parameter :: AD_y_TFinLoad = 17 ! AD%TFinLoad - integer(IntKi), public, parameter :: AD_y_WriteOutput = 18 ! AD%WriteOutput + integer(IntKi), public, parameter :: AD_u_NacelleMotion = 5 ! AD%NacelleMotion + integer(IntKi), public, parameter :: AD_u_TowerMotion = 6 ! AD%TowerMotion + integer(IntKi), public, parameter :: AD_u_HubMotion = 7 ! AD%HubMotion + integer(IntKi), public, parameter :: AD_u_BladeRootMotion = 8 ! AD%BladeRootMotion(DL%i1) + integer(IntKi), public, parameter :: AD_u_BladeMotion = 9 ! AD%BladeMotion(DL%i1) + integer(IntKi), public, parameter :: AD_u_TFinMotion = 10 ! AD%TFinMotion + integer(IntKi), public, parameter :: AD_u_UserProp = 11 ! AD%UserProp + integer(IntKi), public, parameter :: AD_y_NacelleLoad = 12 ! AD%NacelleLoad + integer(IntKi), public, parameter :: AD_y_HubLoad = 13 ! AD%HubLoad + integer(IntKi), public, parameter :: AD_y_TowerLoad = 14 ! AD%TowerLoad + integer(IntKi), public, parameter :: AD_y_BladeLoad = 15 ! AD%BladeLoad(DL%i1) + integer(IntKi), public, parameter :: AD_y_TFinLoad = 16 ! AD%TFinLoad + integer(IntKi), public, parameter :: AD_y_WriteOutput = 17 ! AD%WriteOutput contains @@ -2480,9 +2477,6 @@ subroutine AD_CopyRotContinuousStateType(SrcRotContinuousStateTypeData, DstRotCo call BEMT_CopyContState(SrcRotContinuousStateTypeData%BEMT, DstRotContinuousStateTypeData%BEMT, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return - call AA_CopyContState(SrcRotContinuousStateTypeData%AA, DstRotContinuousStateTypeData%AA, CtrlCode, ErrStat2, ErrMsg2) - call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - if (ErrStat >= AbortErrLev) return end subroutine subroutine AD_DestroyRotContinuousStateType(RotContinuousStateTypeData, ErrStat, ErrMsg) @@ -2496,8 +2490,6 @@ subroutine AD_DestroyRotContinuousStateType(RotContinuousStateTypeData, ErrStat, ErrMsg = '' call BEMT_DestroyContState(RotContinuousStateTypeData%BEMT, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - call AA_DestroyContState(RotContinuousStateTypeData%AA, ErrStat2, ErrMsg2) - call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) end subroutine subroutine AD_PackRotContinuousStateType(RF, Indata) @@ -2506,7 +2498,6 @@ subroutine AD_PackRotContinuousStateType(RF, Indata) character(*), parameter :: RoutineName = 'AD_PackRotContinuousStateType' if (RF%ErrStat >= AbortErrLev) return call BEMT_PackContState(RF, InData%BEMT) - call AA_PackContState(RF, InData%AA) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -2516,7 +2507,6 @@ subroutine AD_UnPackRotContinuousStateType(RF, OutData) character(*), parameter :: RoutineName = 'AD_UnPackRotContinuousStateType' if (RF%ErrStat /= ErrID_None) return call BEMT_UnpackContState(RF, OutData%BEMT) ! BEMT - call AA_UnpackContState(RF, OutData%AA) ! AA end subroutine subroutine AD_CopyContState(SrcContStateData, DstContStateData, CtrlCode, ErrStat, ErrMsg) @@ -2792,9 +2782,6 @@ subroutine AD_CopyRotConstraintStateType(SrcRotConstraintStateTypeData, DstRotCo call BEMT_CopyConstrState(SrcRotConstraintStateTypeData%BEMT, DstRotConstraintStateTypeData%BEMT, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return - call AA_CopyConstrState(SrcRotConstraintStateTypeData%AA, DstRotConstraintStateTypeData%AA, CtrlCode, ErrStat2, ErrMsg2) - call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - if (ErrStat >= AbortErrLev) return end subroutine subroutine AD_DestroyRotConstraintStateType(RotConstraintStateTypeData, ErrStat, ErrMsg) @@ -2808,8 +2795,6 @@ subroutine AD_DestroyRotConstraintStateType(RotConstraintStateTypeData, ErrStat, ErrMsg = '' call BEMT_DestroyConstrState(RotConstraintStateTypeData%BEMT, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - call AA_DestroyConstrState(RotConstraintStateTypeData%AA, ErrStat2, ErrMsg2) - call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) end subroutine subroutine AD_PackRotConstraintStateType(RF, Indata) @@ -2818,7 +2803,6 @@ subroutine AD_PackRotConstraintStateType(RF, Indata) character(*), parameter :: RoutineName = 'AD_PackRotConstraintStateType' if (RF%ErrStat >= AbortErrLev) return call BEMT_PackConstrState(RF, InData%BEMT) - call AA_PackConstrState(RF, InData%AA) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -2828,7 +2812,6 @@ subroutine AD_UnPackRotConstraintStateType(RF, OutData) character(*), parameter :: RoutineName = 'AD_UnPackRotConstraintStateType' if (RF%ErrStat /= ErrID_None) return call BEMT_UnpackConstrState(RF, OutData%BEMT) ! BEMT - call AA_UnpackConstrState(RF, OutData%AA) ! AA end subroutine subroutine AD_CopyConstrState(SrcConstrStateData, DstConstrStateData, CtrlCode, ErrStat, ErrMsg) @@ -6189,7 +6172,7 @@ subroutine AD_CopyMisc(SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg) end if DstMiscData%WindAcc = SrcMiscData%WindAcc end if - call SeaSt_WaveField_CopyMisc(SrcMiscData%WaveField_m, DstMiscData%WaveField_m, CtrlCode, ErrStat2, ErrMsg2) + call GridInterp_CopyMisc(SrcMiscData%WaveField_m, DstMiscData%WaveField_m, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return if (allocated(SrcMiscData%Inflow)) then @@ -6258,7 +6241,7 @@ subroutine AD_DestroyMisc(MiscData, ErrStat, ErrMsg) if (allocated(MiscData%WindAcc)) then deallocate(MiscData%WindAcc) end if - call SeaSt_WaveField_DestroyMisc(MiscData%WaveField_m, ErrStat2, ErrMsg2) + call GridInterp_DestroyMisc(MiscData%WaveField_m, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (allocated(MiscData%Inflow)) then LB(1:1) = lbound(MiscData%Inflow) @@ -6305,7 +6288,7 @@ subroutine AD_PackMisc(RF, Indata) call RegPackAlloc(RF, InData%WindPos) call RegPackAlloc(RF, InData%WindVel) call RegPackAlloc(RF, InData%WindAcc) - call SeaSt_WaveField_PackMisc(RF, InData%WaveField_m) + call GridInterp_PackMisc(RF, InData%WaveField_m) call RegPack(RF, allocated(InData%Inflow)) if (allocated(InData%Inflow)) then call RegPackBounds(RF, 1, lbound(InData%Inflow), ubound(InData%Inflow)) @@ -6360,7 +6343,7 @@ subroutine AD_UnPackMisc(RF, OutData) call RegUnpackAlloc(RF, OutData%WindPos); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%WindVel); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%WindAcc); if (RegCheckErr(RF, RoutineName)) return - call SeaSt_WaveField_UnpackMisc(RF, OutData%WaveField_m) ! WaveField_m + call GridInterp_UnpackMisc(RF, OutData%WaveField_m) ! WaveField_m if (allocated(OutData%Inflow)) deallocate(OutData%Inflow) call RegUnpack(RF, IsAllocAssoc); if (RegCheckErr(RF, RoutineName)) return if (IsAllocAssoc) then @@ -7140,8 +7123,6 @@ subroutine AD_VarPackContState(V, x, ValAry) VarVals = x%BEMT%DBEMT%element(DL%i1, DL%i2)%vind_1(V%iLB:V%iUB) ! Rank 1 Array case (AD_x_BEMT_V_w) VarVals = x%BEMT%V_w(V%iLB:V%iUB) ! Rank 1 Array - case (AD_x_AA_DummyContState) - VarVals(1) = x%AA%DummyContState ! Scalar case default VarVals = 0.0_R8Ki end select @@ -7172,8 +7153,6 @@ subroutine AD_VarUnpackContState(V, ValAry, x) x%BEMT%DBEMT%element(DL%i1, DL%i2)%vind_1(V%iLB:V%iUB) = VarVals ! Rank 1 Array case (AD_x_BEMT_V_w) x%BEMT%V_w(V%iLB:V%iUB) = VarVals ! Rank 1 Array - case (AD_x_AA_DummyContState) - x%AA%DummyContState = VarVals(1) ! Scalar end select end associate end subroutine @@ -7190,8 +7169,6 @@ function AD_ContinuousStateFieldName(DL) result(Name) Name = "x%BEMT%DBEMT%element("//trim(Num2LStr(DL%i1))//", "//trim(Num2LStr(DL%i2))//")%vind_1" case (AD_x_BEMT_V_w) Name = "x%BEMT%V_w" - case (AD_x_AA_DummyContState) - Name = "x%AA%DummyContState" case default Name = "Unknown Field" end select @@ -7221,8 +7198,6 @@ subroutine AD_VarPackContStateDeriv(V, x, ValAry) VarVals = x%BEMT%DBEMT%element(DL%i1, DL%i2)%vind_1(V%iLB:V%iUB) ! Rank 1 Array case (AD_x_BEMT_V_w) VarVals = x%BEMT%V_w(V%iLB:V%iUB) ! Rank 1 Array - case (AD_x_AA_DummyContState) - VarVals(1) = x%AA%DummyContState ! Scalar case default VarVals = 0.0_R8Ki end select diff --git a/modules/awae/src/AWAE.f90 b/modules/awae/src/AWAE.f90 index e86e14613d..34225aeab6 100644 --- a/modules/awae/src/AWAE.f90 +++ b/modules/awae/src/AWAE.f90 @@ -748,14 +748,6 @@ subroutine LowResGridCalcOutput(n, u, p, xd, y, m, errStat, errMsg) ELSE ! All subsequent calls to AWAE_CalcOutput - ! If wake planes leave the low-resolution domain, output warning - if (u%p_plane(1,np,nt) < p%LowRes%GridPoints(1,1)) call SetErrStat(ErrID_Warn, 'The center of wake plane #'//trim(num2lstr(np))//' for turbine #'//trim(num2lstr(nt))//' has passed the lowest-most X boundary of the low-resolution domain.', errStat, errMsg, RoutineName) - if (u%p_plane(2,np,nt) < p%LowRes%GridPoints(2,1)) call SetErrStat(ErrID_Warn, 'The center of wake plane #'//trim(num2lstr(np))//' for turbine #'//trim(num2lstr(nt))//' has passed the lowest-most Y boundary of the low-resolution domain.', errStat, errMsg, RoutineName) - if (u%p_plane(3,np,nt) < p%LowRes%GridPoints(3,1)) call SetErrStat(ErrID_Warn, 'The center of wake plane #'//trim(num2lstr(np))//' for turbine #'//trim(num2lstr(nt))//' has passed the lowest-most Z boundary of the low-resolution domain.', errStat, errMsg, RoutineName) - if (u%p_plane(1,np,nt) > p%LowRes%GridPoints(1,p%LowRes%nPoints)) call SetErrStat(ErrID_Warn, 'The center of wake plane #'//trim(num2lstr(np))//' for turbine #'//trim(num2lstr(nt))//' has passed the upper-most X boundary of the low-resolution domain.' , errStat, errMsg, RoutineName) - if (u%p_plane(2,np,nt) > p%LowRes%GridPoints(2,p%LowRes%nPoints)) call SetErrStat(ErrID_Warn, 'The center of wake plane #'//trim(num2lstr(np))//' for turbine #'//trim(num2lstr(nt))//' has passed the upper-most Y boundary of the low-resolution domain.' , errStat, errMsg, RoutineName) - if (u%p_plane(3,np,nt) > p%LowRes%GridPoints(3,p%LowRes%nPoints)) call SetErrStat(ErrID_Warn, 'The center of wake plane #'//trim(num2lstr(np))//' for turbine #'//trim(num2lstr(nt))//' has passed the upper-most Z boundary of the low-resolution domain.' , errStat, errMsg, RoutineName) - xplane_sq = u%xhat_plane(1,np,nt)**2.0_ReKi yplane_sq = u%xhat_plane(2,np,nt)**2.0_ReKi xysq_Z = [0.0_ReKi, 0.0_ReKi, xplane_sq+yplane_sq] @@ -823,6 +815,14 @@ subroutine LowResGridCalcOutput(n, u, p, xd, y, m, errStat, errMsg) end if + ! - no messages if inside bounds, so put error handling inside if + call PlaneOutOfDomain(u%D_wake(np,nt),u%p_plane(:,np,nt),y%V_plane(:,np,nt),m%planeDomainExit(np,nt),ErrStat2,ErrMsg2) + if (m%planeDomainExit(np,nt) /= 0_IntKi) then + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + cycle + endif + + ! Calculate y%V_plane y%V_plane(:,np,nt) = 0.0_ReKi wsum_tmp = 0.0_ReKi @@ -873,7 +873,102 @@ logical function Failed() call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) Failed = ErrStat >= AbortErrLev end function Failed - + + !> Check if the center of this wwake plane has left the domain. + !! If a plane exits the domain, or previously exited the domain: + !! - Set warning about first time this plane leaves. + !! - Set component perpendicular to plane exit direction to kick it outside the domain entirely + !! - Target distance outside boundary = D. Use a quadratic asymptotic distance per step to approach target distance. + !! - Add background flow in X or Y to keep the plane moving with others parallel to boundary it crossed (only using X and Y velocity) + !! NOTE: using m%planeDomainExit to track which boundary a plane crossed. + !! 0: Still in domain + !! +/-1: +/-X + !! +/-2: +/-Y + !! +/-3: +/-Z + !! To understand intent, consider 2 cases for mean velocity in +X direction: + !! plane exits +Y boundary: + !! 1. plane with get a kick towards one wake diameter outside +Y boundary + !! 2. overall farm velocity added to keep plane drifting in +X following the target Y location (some jitter due to farm level Y velocity term) + !! plane exits +X boundary (travels beyond domain end in direction of overall flow) + !! 1. plane will get a kick outside the end of the domain towards +X boundary plus wake diameter + !! 2. farm velocity added will keep trying to push this plane further downstream, but step 1. will try to force it back. + !! --> effectively 1. and 2. will constant be working against each other to hold the plane somewhere near the target location beyond +X boundary, + !! but this shouldn't really matter as the plane will get dropped at some point. Even if multiple planes end up there, it shouldn't affect + !! any planes still in bounds -- so we really don't care if it jitters around at all + subroutine PlaneOutOfDomain(D_Wake,p_plane,V_plane,planeDomainExit,ErrStat3,ErrMsg3) + real(ReKi), intent(in ) :: D_wake !< u%D_wake(np,nt) + real(ReKi), intent(in ) :: p_plane(3) !< u%p_plane(:,np,nt) + real(ReKi), intent(inout) :: V_plane(3) !< y%V_plane(:,np,nt) + integer(IntKi), intent(inout) :: planeDomainExit !< m%planeDomainExit(np,nt) + integer(IntKi), intent( out) :: ErrStat3 !< Error status of the operation + character(ErrMsgLen), intent( out) :: ErrMsg3 !< Error message if errStat /= ErrID_None + character(12) :: tmpStr12 !< for constructing error message + real(ReKi) :: D_tgt !< target distance outside bounds + ! Step 1: did a plane that was in the low res domain just cross out? + ! If plane crossed boundary, set message and tracking of it + if (planeDomainExit == 0_IntKi) then + if (p_plane(1) < p%LowRes%oXYZ(1)) then ! lower x boundary + ErrStat3 = ErrID_Warn + tmpStr12 = 'lower-most X' + planeDomainExit = -1 + elseif ( p_plane(1) > p%LowRes%oXYZ(1) + p%LowRes%Size(1)) then ! upper x boundary + ErrStat3 = ErrID_Warn + tmpStr12 = 'upper-most X' + planeDomainExit = 1 + elseif ( p_plane(2) < p%LowRes%oXYZ(2)) then ! lower y boundary + ErrStat3 = ErrID_Warn + tmpStr12 = 'lower-most Y' + planeDomainExit = -2 + elseif ( p_plane(2) > p%LowRes%oXYZ(2) + p%LowRes%Size(2)) then ! upper y boundary + ErrStat3 = ErrID_Warn + tmpStr12 = 'upper-most Y' + planeDomainExit = 2 + elseif ( p_plane(3) < p%LowRes%oXYZ(3)) then ! lower z boundary + ErrStat3 = ErrID_Warn + tmpStr12 = 'lower-most Z' + planeDomainExit = -3 + elseif ( p_plane(3) > p%LowRes%oXYZ(3) + p%LowRes%Size(3)) then ! upper z boundary + ErrStat3 = ErrID_Warn + tmpStr12 = 'upper-most Z' + planeDomainExit = 3 + endif + if (errStat3 == ErrID_Warn) then + ErrMsg3 = 'The center of wake plane #'//trim(num2lstr(np))//' for turbine #'//trim(num2lstr(nt))//' has passed the ' & + //tmpStr12//' boundary of the low-resolution domain. Further warnings are suppressed.' + endif + endif + + ! Step 2: for planes outside boundary (including one that just crossed outside) set velocity component to approach target offset. + ! asymptotically approach a distance D_wake away from the boundary (quadratic approach) + ! example: V at -Y boundary: + ! Vy = (Y_target - Y_pos) / (2 * DT) + select case (planeDomainExit) + case (0_IntKi) + return + case (-1_IntKi) ! Crossed -X + D_tgt = p%LowRes%oXYZ(1) - D_wake + V_plane(1) = (D_tgt - p_plane(1)) / (2.0_ReKi * real(p%dt_low,ReKi)) ! push towards (-X_bound - D_wake) + case ( 1_IntKi) ! Crossed +X + D_tgt = p%LowRes%oXYZ(1) + p%LowRes%Size(1) + D_wake + V_plane(1) = (D_tgt - p_plane(1)) / (2.0_ReKi * real(p%dt_low,ReKi)) ! push towards (+X_bound + D_wake) + case (-2_IntKi) ! Crossed -Y + D_tgt = p%LowRes%oXYZ(2) - D_wake + V_plane(2) = (D_tgt - p_plane(2)) / (2.0_ReKi * real(p%dt_low,ReKi)) ! push towards (-Y_bound - D_wake) + case ( 2_IntKi) ! Crossed +Y + D_tgt = p%LowRes%oXYZ(2) + p%LowRes%Size(2) + D_wake + V_plane(2) = (D_tgt - p_plane(2)) / (2.0_ReKi * real(p%dt_low,ReKi)) ! push towards (-Y_bound - D_wake) + case (-3_IntKi) ! Crossed -Z + D_tgt = p%LowRes%oXYZ(3) - D_wake + V_plane(3) = (D_tgt - p_plane(3)) / (2.0_ReKi * real(p%dt_low,ReKi)) ! push towards (-Z_bound - D_wake) + case ( 3_IntKi) ! Crossed +Z + D_tgt = p%LowRes%oXYZ(3) + p%LowRes%Size(3) + D_wake + V_plane(3) = (D_tgt - p_plane(3)) / (2.0_ReKi * real(p%dt_low,ReKi)) ! push towards (+Z_bound + D_wake) + end select + + ! Step 3: add background XYZ flow to keep plane drifting (will have already returned on any planes still in bounds) + V_plane(1:3) = V_plane(1:3) + xd%Ufarm(1:3) + + end subroutine PlaneOutOfDomain end subroutine LowResGridCalcOutput !---------------------------------------------------------------------------------------------------------------------------------- @@ -1421,6 +1516,11 @@ subroutine AWAE_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitO ! Initialize the KdTree with no active points call kdtree_build(m%KdT, m%AllPlanePoints(:,1:1), n_max=p%MaxPlanes*p%NumTurbines) + + ! track if a plan has left the domain (all planes start in domain). + ! Value indicates edge number (+/-1: +/-X, +/-2: +/-Y, +/-3: +/-Z) the plane crossed + allocate(m%planeDomainExit(0:p%MaxPlanes-1,1:p%NumTurbines), STAT=ErrStat2); if (Failed0('m%planeDomainExit.')) return; + m%planeDomainExit = 0_IntKi ! Read-in the ambient wind data for the initial calculate output call AWAE_UpdateStates(0, u, p, x, xd, z, OtherState, m, errStat2, errMsg2 ); if(Failed()) return; diff --git a/modules/awae/src/AWAE_Registry.txt b/modules/awae/src/AWAE_Registry.txt index 478c00ade2..13af09df01 100644 --- a/modules/awae/src/AWAE_Registry.txt +++ b/modules/awae/src/AWAE_Registry.txt @@ -144,6 +144,7 @@ typedef ^ MiscVarType InflowWind_OutputType y_IfW_High {:} - - "InflowWi #wake added turbulence typedef ^ MiscVarType ReKi V_amb_low_disk {:}{:} - - "Rotor averaged ambiend wind speed for each wind turbine (3 x nWT)" m/s +typedef ^ MiscVarType IntKi planeDomainExit {:}{:} 0 - "Value indicates edge number (0: still in domain, +/-1: +/-X, +/-2: +/-Y, +/-3: +/-Z) the plane crossed" - # Low-resolution grid chunk data typedef ^ LRGChunkType IntKi iChunk {3} - - "XYZ index of chunk" - diff --git a/modules/awae/src/AWAE_Types.f90 b/modules/awae/src/AWAE_Types.f90 index 4be43dd03f..ef2a7cffa0 100644 --- a/modules/awae/src/AWAE_Types.f90 +++ b/modules/awae/src/AWAE_Types.f90 @@ -168,6 +168,7 @@ MODULE AWAE_Types TYPE(InflowWind_OutputType) :: y_IfW_Low !< InflowWind module outputs for the low-resolution grid [-] TYPE(InflowWind_OutputType) , DIMENSION(:), ALLOCATABLE :: y_IfW_High !< InflowWind module outputs for the high-resolution grid [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: V_amb_low_disk !< Rotor averaged ambiend wind speed for each wind turbine (3 x nWT) [m/s] + INTEGER(IntKi) , DIMENSION(:,:), ALLOCATABLE :: planeDomainExit !< Value indicates edge number (0: still in domain, +/-1: +/-X, +/-2: +/-Y, +/-3: +/-Z) the plane crossed [-] END TYPE AWAE_MiscVarType ! ======================= ! ========= LRGChunkType ======= @@ -1429,6 +1430,18 @@ subroutine AWAE_CopyMisc(SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg) end if DstMiscData%V_amb_low_disk = SrcMiscData%V_amb_low_disk end if + if (allocated(SrcMiscData%planeDomainExit)) then + LB(1:2) = lbound(SrcMiscData%planeDomainExit) + UB(1:2) = ubound(SrcMiscData%planeDomainExit) + if (.not. allocated(DstMiscData%planeDomainExit)) then + allocate(DstMiscData%planeDomainExit(LB(1):UB(1),LB(2):UB(2)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%planeDomainExit.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstMiscData%planeDomainExit = SrcMiscData%planeDomainExit + end if end subroutine subroutine AWAE_DestroyMisc(MiscData, ErrStat, ErrMsg) @@ -1538,6 +1551,9 @@ subroutine AWAE_DestroyMisc(MiscData, ErrStat, ErrMsg) if (allocated(MiscData%V_amb_low_disk)) then deallocate(MiscData%V_amb_low_disk) end if + if (allocated(MiscData%planeDomainExit)) then + deallocate(MiscData%planeDomainExit) + end if end subroutine subroutine AWAE_PackMisc(RF, Indata) @@ -1599,6 +1615,7 @@ subroutine AWAE_PackMisc(RF, Indata) end do end if call RegPackAlloc(RF, InData%V_amb_low_disk) + call RegPackAlloc(RF, InData%planeDomainExit) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -1675,6 +1692,7 @@ subroutine AWAE_UnPackMisc(RF, OutData) end do end if call RegUnpackAlloc(RF, OutData%V_amb_low_disk); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%planeDomainExit); if (RegCheckErr(RF, RoutineName)) return end subroutine subroutine AWAE_CopyLRGChunkType(SrcLRGChunkTypeData, DstLRGChunkTypeData, CtrlCode, ErrStat, ErrMsg) diff --git a/modules/elastodyn/src/ElastoDyn.f90 b/modules/elastodyn/src/ElastoDyn.f90 index 7a0298b38e..6ea27ca802 100644 --- a/modules/elastodyn/src/ElastoDyn.f90 +++ b/modules/elastodyn/src/ElastoDyn.f90 @@ -3344,6 +3344,7 @@ SUBROUTINE SetPrimaryParameters( InitInp, p, InputFileData, ErrStat, ErrMsg ) !p%Twr2Shft = InputFileData%Twr2Shft !p%HubIner = InputFileData%HubIner + !p%HubIner_Teeter = InputFileData%HubIner_Teeter !p%NacYIner = InputFileData%NacYIner @@ -4643,7 +4644,8 @@ END SUBROUTINE SetOutParam !> This routine is used to compute rotor (blade and hub) properties: !! KBF(), KBE(), CBF(), CBE(), FreqBF(), FreqBE(), AxRedBld(), !! TwistedSF(), BldMass(), FirstMom(), SecondMom(), BldCG(), -!! RotMass, RotIner, Hubg1Iner, Hubg2Iner, and BElmtMass() +!! RotMass, RotIner, Hubf1Iner, Hubf2Iner, rSAerCenn1(), and +!! rSAerCenn2(), BElmtMass() !! tower properties: !! KTFA(), KTSS(), CTFA(), CTSS(), FreqTFA(), FreqTSS(), !! AxRedTFA(), AxRedTSS(), TwrFASF(), TwrSSSF(), TwrMass, and @@ -4747,28 +4749,32 @@ SUBROUTINE Coeff(p,InputFileData, ErrStat, ErrMsg) END IF ! Calculate hub inertia about its centerline passing through its c.g.. - ! This calculation assumes that the hub for a 2-blader is essentially - ! a uniform cylinder whose centerline is transverse through the cylinder - ! passing through its c.g.. That is, for a 2-blader, Hubg1Iner = - ! Hubg2Iner is the inertia of the hub about both the g1- and g2- axes. For - ! 3-bladers, Hubg1Iner is simply equal to HubIner and Hubg2Iner is zero. + ! For a 2-bladed turbine: + ! - Hub interia about the rotor axis: Hubf1Iner is equal to HubIner + ! - Hub inertia about the teeter axis: Hubf2Iner is obtained by applying + ! parallel-axis theorem to HubIner_Teeter + ! Note that f-axes are used and therefore, inertias are not corrected + ! for the delta-3 angle + ! For a 3-bladed turbine: + ! - Hub interia about the rotor axis: Hubf1Iner is equal to HubIner + ! - Hub inertia about the teeter axis: Hubf2Iner is zero ! Also, Initialize RotMass and RotIner to associated hub properties: IF ( p%NumBl == 2 ) THEN ! 2-blader - p%Hubg1Iner = ( InputFileData%HubIner - p%HubMass*( ( p%UndSling - p%HubCM )**2 ) )/( p%CosDel3**2 ) - p%Hubg2Iner = p%Hubg1Iner - IF ( p%Hubg1Iner < 0.0 ) THEN + p%Hubf1Iner = InputFileData%HubIner + p%Hubf2Iner = InputFileData%HubIner_Teeter - p%HubMass*( ( p%UndSling - p%HubCM )**2 ) + IF ( p%Hubf1Iner < 0.0 ) THEN ErrStat = ErrID_Fatal - ErrMsg = ' HubIner must not be less than HubMass*( UndSling - HubCM )^2 for 2-blader.' + ErrMsg = ' HubIner_Teeter must not be less than HubMass*( UndSling - HubCM )^2 for 2-blader.' RETURN END IF ELSE ! 3-blader - p%Hubg1Iner = InputFileData%HubIner - p%Hubg2Iner = 0.0 + p%Hubf1Iner = InputFileData%HubIner + p%Hubf2Iner = 0.0 ENDIF p%RotMass = p%HubMass - p%RotIner = p%Hubg1Iner + p%RotIner = p%Hubf1Iner !............................................................................................................................... @@ -7692,8 +7698,8 @@ SUBROUTINE CalculateForcesMoments( p, x, CoordSys, u, RtHSdat ) TmpVec2 = CROSS_PRODUCT( RtHSdat%rPC, TmpVec1 ) ! The portion of PMomLPRot associated with the HubMass RtHSdat%PFrcPRot (:,p%DOFs%PCE(I)) = TmpVec1 - RtHSdat%PMomLPRot(:,p%DOFs%PCE(I)) = TmpVec2 - p%Hubg1Iner*CoordSys%g1*DOT_PRODUCT( CoordSys%g1, RtHSdat%PAngVelEH(p%DOFs%PCE(I),0,:) ) & - - p%Hubg2Iner*CoordSys%g2*DOT_PRODUCT( CoordSys%g2, RtHSdat%PAngVelEH(p%DOFs%PCE(I),0,:) ) + RtHSdat%PMomLPRot(:,p%DOFs%PCE(I)) = TmpVec2 - p%Hubf1Iner*CoordSys%f1*DOT_PRODUCT( CoordSys%f1, RtHSdat%PAngVelEH(p%DOFs%PCE(I),0,:) ) & + - p%Hubf2Iner*CoordSys%f2*DOT_PRODUCT( CoordSys%f2, RtHSdat%PAngVelEH(p%DOFs%PCE(I),0,:) ) ENDDO ! I - All active (enabled) DOFs that contribute to the QD2T-related linear accelerations of the hub center of mass (point C) @@ -7726,17 +7732,17 @@ SUBROUTINE CalculateForcesMoments( p, x, CoordSys, u, RtHSdat ) TmpVec1 = -p%HubMass*( p%Gravity*CoordSys%z2 + RtHSdat%LinAccECt ) ! The portion of FrcPRott associated with the HubMass TmpVec2 = CROSS_PRODUCT( RtHSdat%rPC, TmpVec1 ) ! The portion of MomLPRott associated with the HubMass - TmpVec = p%Hubg1Iner*CoordSys%g1*DOT_PRODUCT( CoordSys%g1, RtHSdat%AngVelEH ) & ! = ( Hub inertia dyadic ) dot ( angular velocity of hub in the inertia frame ) - + p%Hubg2Iner*CoordSys%g2*DOT_PRODUCT( CoordSys%g2, RtHSdat%AngVelEH ) + TmpVec = p%Hubf1Iner*CoordSys%f1*DOT_PRODUCT( CoordSys%f1, RtHSdat%AngVelEH ) & ! = ( Hub inertia dyadic ) dot ( angular velocity of hub in the inertia frame ) + + p%Hubf2Iner*CoordSys%f2*DOT_PRODUCT( CoordSys%f2, RtHSdat%AngVelEH ) TmpVec3 = CROSS_PRODUCT( -RtHSdat%AngVelEH, TmpVec ) ! = ( -angular velocity of hub in the inertia frame ) cross ( TmpVec ) RtHSdat%FrcPRott(1) = TmpVec1(1) + u%HubPtLoad%Force(1,1) RtHSdat%FrcPRott(2) = TmpVec1(2) + u%HubPtLoad%Force(3,1) RtHSdat%FrcPRott(3) = TmpVec1(3) - u%HubPtLoad%Force(2,1) - - RtHSdat%MomLPRott = TmpVec2 + TmpVec3 - p%Hubg1Iner*CoordSys%g1*DOT_PRODUCT( CoordSys%g1, RtHSdat%AngAccEHt ) & - - p%Hubg2Iner*CoordSys%g2*DOT_PRODUCT( CoordSys%g2, RtHSdat%AngAccEHt ) - + + RtHSdat%MomLPRott = TmpVec2 + TmpVec3 - p%Hubf1Iner*CoordSys%f1*DOT_PRODUCT( CoordSys%f1, RtHSdat%AngAccEHt ) & + - p%Hubf2Iner*CoordSys%f2*DOT_PRODUCT( CoordSys%f2, RtHSdat%AngAccEHt ) + RtHSdat%MomLPRott(1) = RtHSdat%MomLPRott(1) + u%HubPtLoad%Moment(1,1) RtHSdat%MomLPRott(2) = RtHSdat%MomLPRott(2) + u%HubPtLoad%Moment(3,1) RtHSdat%MomLPRott(3) = RtHSdat%MomLPRott(3) - u%HubPtLoad%Moment(2,1) diff --git a/modules/elastodyn/src/ElastoDyn_IO.f90 b/modules/elastodyn/src/ElastoDyn_IO.f90 index 0b8b9a9adc..a9161b98ec 100644 --- a/modules/elastodyn/src/ElastoDyn_IO.f90 +++ b/modules/elastodyn/src/ElastoDyn_IO.f90 @@ -3156,8 +3156,16 @@ SUBROUTINE ReadPrimaryFile( InputFile, InputFileData, BldFile, FurlFile, TwrFile RETURN END IF - ! HubIner - Hub inertia about teeter axis (2-blader) or rotor axis (3-blader) (kg m^2): - CALL ReadVar( UnIn, InputFile, InputFileData%HubIner, "HubIner", "Hub inertia about teeter axis (2-blader) or rotor axis (3-blader) (kg m^2)", ErrStat2, ErrMsg2, UnEc) + ! HubIner - Hub inertia about rotor axis (2 or 3-blader) (kg m^2): + CALL ReadVar( UnIn, InputFile, InputFileData%HubIner, "HubIner", "Hub inertia about rotor axis (2 or 3-blader) (kg m^2)", ErrStat2, ErrMsg2, UnEc) + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + IF ( ErrStat >= AbortErrLev ) THEN + CALL Cleanup() + RETURN + END IF + + ! HubIner_teeter - Hub inertia about teeter axis (2-blader) (kg m^2): + CALL ReadVar( UnIn, InputFile, InputFileData%HubIner_Teeter, "HubIner_Teeter", "Hub inertia about teeter axis (2-blader) (kg m^2)", ErrStat2, ErrMsg2, UnEc) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF ( ErrStat >= AbortErrLev ) THEN CALL Cleanup() @@ -4330,6 +4338,9 @@ SUBROUTINE ValidatePrimaryData( InputFileData, BD4Blades, Linearize, MHK, ErrSta IF ( InputFileData%NacYIner < 0.0_ReKi) call SetErrStat(ErrID_Fatal,'NacYIner must not be negative.',ErrStat,ErrMsg,RoutineName) IF ( InputFileData%GenIner < 0.0_ReKi) call SetErrStat(ErrID_Fatal,'GenIner must not be negative.',ErrStat,ErrMsg,RoutineName) IF ( InputFileData%HubIner < 0.0_ReKi) call SetErrStat(ErrID_Fatal,'HubIner must not be negative.',ErrStat,ErrMsg,RoutineName) + IF ( InputFileData%NumBl == 2 ) THEN + IF ( InputFileData%HubIner_Teeter < 0.0_ReKi) call SetErrStat(ErrID_Fatal,'HubIner_Teeter must not be negative.',ErrStat,ErrMsg,RoutineName) + ENDIF ! Check that TowerHt is in the range [0,inf): IF ( MHK /= MHK_Floating ) THEN diff --git a/modules/elastodyn/src/ElastoDyn_Registry.txt b/modules/elastodyn/src/ElastoDyn_Registry.txt index 646f7a494a..74510f4f2f 100644 --- a/modules/elastodyn/src/ElastoDyn_Registry.txt +++ b/modules/elastodyn/src/ElastoDyn_Registry.txt @@ -141,7 +141,8 @@ typedef ^ ED_InputFile ReKi TipMass {:} - - "Tip-brake masses" kg typedef ^ ED_InputFile ReKi PBrIner {:} - - "Pitch bearing inertia about the pitch axis" kg m^2 typedef ^ ED_InputFile ReKi BlPIner {:} - - "Blade inertia about the pitch axis" kg m^2 typedef ^ ED_InputFile ReKi HubMass - - - "Hub mass" kg -typedef ^ ED_InputFile ReKi HubIner - - - "Hub inertia about teeter axis (2-blader) or rotor axis (3-blader)" "kg m^2" +typedef ^ ED_InputFile ReKi HubIner - - - "Hub inertia about rotor axis (2 or 3-blader)" "kg m^2" +typedef ^ ED_InputFile ReKi HubIner_Teeter - - - "Hub inertia about teeter axis (2-blader)" "kg m^2" typedef ^ ED_InputFile ReKi GenIner - - - "Generator inertia about HSS" "kg m^2" typedef ^ ED_InputFile ReKi NacMass - - - "Nacelle mass" kg typedef ^ ED_InputFile ReKi NacYIner - - - "Nacelle yaw inertia" "kg m^2" @@ -652,8 +653,8 @@ typedef ^ ParameterType ReKi BldMass {:} - - "Blade masses" typedef ^ ParameterType ReKi BoomMass - - - "Tail boom mass" typedef ^ ParameterType ReKi FirstMom {:} - - "First mass moment of inertia of blades wrt the root" typedef ^ ParameterType ReKi GenIner - - - "Generator inertia about HSS" -typedef ^ ParameterType ReKi Hubg1Iner - - - "Inertia of hub about g1-axis (rotor centerline)" -typedef ^ ParameterType ReKi Hubg2Iner - - - "Inertia of hub about g2-axis (transverse to the cyclinder and passing through its c.g.)" +typedef ^ ParameterType ReKi Hubf1Iner - - - "Inertia of hub about f1-axis (rotor centerline)" +typedef ^ ParameterType ReKi Hubf2Iner - - - "Inertia of hub about f2-axis (teeter axis)" typedef ^ ParameterType ReKi HubMass - - - "Hub mass" typedef ^ ParameterType ReKi Nacd2Iner - - - "Inertia of nacelle about the d2-axis whose origin is the nacelle center of mass" typedef ^ ParameterType ReKi NacMass - - - "Nacelle mass" diff --git a/modules/elastodyn/src/ElastoDyn_Types.f90 b/modules/elastodyn/src/ElastoDyn_Types.f90 index 78a2d01996..fce1bfb976 100644 --- a/modules/elastodyn/src/ElastoDyn_Types.f90 +++ b/modules/elastodyn/src/ElastoDyn_Types.f90 @@ -164,7 +164,8 @@ MODULE ElastoDyn_Types REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: PBrIner !< Pitch bearing inertia about the pitch axis [kg] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: BlPIner !< Blade inertia about the pitch axis [kg] REAL(ReKi) :: HubMass = 0.0_ReKi !< Hub mass [kg] - REAL(ReKi) :: HubIner = 0.0_ReKi !< Hub inertia about teeter axis (2-blader) or rotor axis (3-blader) [kg m^2] + REAL(ReKi) :: HubIner = 0.0_ReKi !< Hub inertia about rotor axis (2 or 3-blader) [kg m^2] + REAL(ReKi) :: HubIner_Teeter = 0.0_ReKi !< Hub inertia about teeter axis (2-blader) [kg m^2] REAL(ReKi) :: GenIner = 0.0_ReKi !< Generator inertia about HSS [kg m^2] REAL(ReKi) :: NacMass = 0.0_ReKi !< Nacelle mass [kg] REAL(ReKi) :: NacYIner = 0.0_ReKi !< Nacelle yaw inertia [kg m^2] @@ -659,8 +660,8 @@ MODULE ElastoDyn_Types REAL(ReKi) :: BoomMass = 0.0_ReKi !< Tail boom mass [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: FirstMom !< First mass moment of inertia of blades wrt the root [-] REAL(ReKi) :: GenIner = 0.0_ReKi !< Generator inertia about HSS [-] - REAL(ReKi) :: Hubg1Iner = 0.0_ReKi !< Inertia of hub about g1-axis (rotor centerline) [-] - REAL(ReKi) :: Hubg2Iner = 0.0_ReKi !< Inertia of hub about g2-axis (transverse to the cyclinder and passing through its c.g.) [-] + REAL(ReKi) :: Hubf1Iner = 0.0_ReKi !< Inertia of hub about f1-axis (rotor centerline) [-] + REAL(ReKi) :: Hubf2Iner = 0.0_ReKi !< Inertia of hub about f2-axis (teeter axis) [-] REAL(ReKi) :: HubMass = 0.0_ReKi !< Hub mass [-] REAL(ReKi) :: Nacd2Iner = 0.0_ReKi !< Inertia of nacelle about the d2-axis whose origin is the nacelle center of mass [-] REAL(ReKi) :: NacMass = 0.0_ReKi !< Nacelle mass [-] @@ -1588,6 +1589,7 @@ subroutine ED_CopyInputFile(SrcInputFileData, DstInputFileData, CtrlCode, ErrSta end if DstInputFileData%HubMass = SrcInputFileData%HubMass DstInputFileData%HubIner = SrcInputFileData%HubIner + DstInputFileData%HubIner_Teeter = SrcInputFileData%HubIner_Teeter DstInputFileData%GenIner = SrcInputFileData%GenIner DstInputFileData%NacMass = SrcInputFileData%NacMass DstInputFileData%NacYIner = SrcInputFileData%NacYIner @@ -2019,6 +2021,7 @@ subroutine ED_PackInputFile(RF, Indata) call RegPackAlloc(RF, InData%BlPIner) call RegPack(RF, InData%HubMass) call RegPack(RF, InData%HubIner) + call RegPack(RF, InData%HubIner_Teeter) call RegPack(RF, InData%GenIner) call RegPack(RF, InData%NacMass) call RegPack(RF, InData%NacYIner) @@ -2225,6 +2228,7 @@ subroutine ED_UnPackInputFile(RF, OutData) call RegUnpackAlloc(RF, OutData%BlPIner); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%HubMass); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%HubIner); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%HubIner_Teeter); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%GenIner); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%NacMass); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%NacYIner); if (RegCheckErr(RF, RoutineName)) return @@ -5181,8 +5185,8 @@ subroutine ED_CopyParam(SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg) DstParamData%FirstMom = SrcParamData%FirstMom end if DstParamData%GenIner = SrcParamData%GenIner - DstParamData%Hubg1Iner = SrcParamData%Hubg1Iner - DstParamData%Hubg2Iner = SrcParamData%Hubg2Iner + DstParamData%Hubf1Iner = SrcParamData%Hubf1Iner + DstParamData%Hubf2Iner = SrcParamData%Hubf2Iner DstParamData%HubMass = SrcParamData%HubMass DstParamData%Nacd2Iner = SrcParamData%Nacd2Iner DstParamData%NacMass = SrcParamData%NacMass @@ -6032,8 +6036,8 @@ subroutine ED_PackParam(RF, Indata) call RegPack(RF, InData%BoomMass) call RegPackAlloc(RF, InData%FirstMom) call RegPack(RF, InData%GenIner) - call RegPack(RF, InData%Hubg1Iner) - call RegPack(RF, InData%Hubg2Iner) + call RegPack(RF, InData%Hubf1Iner) + call RegPack(RF, InData%Hubf2Iner) call RegPack(RF, InData%HubMass) call RegPack(RF, InData%Nacd2Iner) call RegPack(RF, InData%NacMass) @@ -6303,8 +6307,8 @@ subroutine ED_UnPackParam(RF, OutData) call RegUnpack(RF, OutData%BoomMass); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%FirstMom); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%GenIner); if (RegCheckErr(RF, RoutineName)) return - call RegUnpack(RF, OutData%Hubg1Iner); if (RegCheckErr(RF, RoutineName)) return - call RegUnpack(RF, OutData%Hubg2Iner); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Hubf1Iner); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Hubf2Iner); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%HubMass); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%Nacd2Iner); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%NacMass); if (RegCheckErr(RF, RoutineName)) return diff --git a/modules/externalinflow/src/ExternalInflow_Types.f90 b/modules/externalinflow/src/ExternalInflow_Types.f90 index acea016639..1c478fe073 100644 --- a/modules/externalinflow/src/ExternalInflow_Types.f90 +++ b/modules/externalinflow/src/ExternalInflow_Types.f90 @@ -312,11 +312,6 @@ subroutine ExtInfw_PackInitInput(RF, Indata) character(*), parameter :: RoutineName = 'ExtInfw_PackInitInput' logical :: PtrInIndex if (RF%ErrStat >= AbortErrLev) return - if (c_associated(InData%C_obj%object)) then - RF%ErrStat = ErrID_Fatal - RF%ErrMsg = RoutineName//': C_obj%object cannot be packed.' - return - end if call RegPack(RF, InData%NumActForcePtsBlade) call RegPack(RF, InData%NumActForcePtsTower) call RegPackPtr(RF, InData%StructBldRNodes) @@ -523,11 +518,6 @@ subroutine ExtInfw_PackInitOutput(RF, Indata) character(*), parameter :: RoutineName = 'ExtInfw_PackInitOutput' logical :: PtrInIndex if (RF%ErrStat >= AbortErrLev) return - if (c_associated(InData%C_obj%object)) then - RF%ErrStat = ErrID_Fatal - RF%ErrMsg = RoutineName//': C_obj%object cannot be packed.' - return - end if call RegPackAlloc(RF, InData%WriteOutputHdr) call RegPackAlloc(RF, InData%WriteOutputUnt) call NWTC_Library_PackProgDesc(RF, InData%Ver) @@ -769,11 +759,6 @@ subroutine ExtInfw_PackMisc(RF, Indata) integer(B4Ki) :: LB(1), UB(1) logical :: PtrInIndex if (RF%ErrStat >= AbortErrLev) return - if (c_associated(InData%C_obj%object)) then - RF%ErrStat = ErrID_Fatal - RF%ErrMsg = RoutineName//': C_obj%object cannot be packed.' - return - end if call NWTC_Library_PackModJacType(RF, InData%Jac) call RegPack(RF, allocated(InData%ActForceMotionsPoints)) if (allocated(InData%ActForceMotionsPoints)) then @@ -1031,11 +1016,6 @@ subroutine ExtInfw_PackParam(RF, Indata) character(*), parameter :: RoutineName = 'ExtInfw_PackParam' logical :: PtrInIndex if (RF%ErrStat >= AbortErrLev) return - if (c_associated(InData%C_obj%object)) then - RF%ErrStat = ErrID_Fatal - RF%ErrMsg = RoutineName//': C_obj%object cannot be packed.' - return - end if call RegPack(RF, InData%AirDens) call RegPack(RF, InData%NumBl) call RegPack(RF, InData%NMappings) @@ -1579,11 +1559,6 @@ subroutine ExtInfw_PackInput(RF, Indata) character(*), parameter :: RoutineName = 'ExtInfw_PackInput' logical :: PtrInIndex if (RF%ErrStat >= AbortErrLev) return - if (c_associated(InData%C_obj%object)) then - RF%ErrStat = ErrID_Fatal - RF%ErrMsg = RoutineName//': C_obj%object cannot be packed.' - return - end if call RegPackPtr(RF, InData%pxVel) call RegPackPtr(RF, InData%pyVel) call RegPackPtr(RF, InData%pzVel) @@ -2198,11 +2173,6 @@ subroutine ExtInfw_PackOutput(RF, Indata) character(*), parameter :: RoutineName = 'ExtInfw_PackOutput' logical :: PtrInIndex if (RF%ErrStat >= AbortErrLev) return - if (c_associated(InData%C_obj%object)) then - RF%ErrStat = ErrID_Fatal - RF%ErrMsg = RoutineName//': C_obj%object cannot be packed.' - return - end if call RegPackPtr(RF, InData%u) call RegPackPtr(RF, InData%v) call RegPackPtr(RF, InData%w) diff --git a/modules/extloads/src/ExtLoadsDX_Types.f90 b/modules/extloads/src/ExtLoadsDX_Types.f90 index fbe69b1978..87ce9d0f91 100644 --- a/modules/extloads/src/ExtLoadsDX_Types.f90 +++ b/modules/extloads/src/ExtLoadsDX_Types.f90 @@ -282,11 +282,6 @@ subroutine ExtLdDX_PackInput(RF, Indata) character(*), parameter :: RoutineName = 'ExtLdDX_PackInput' logical :: PtrInIndex if (RF%ErrStat >= AbortErrLev) return - if (c_associated(InData%C_obj%object)) then - RF%ErrStat = ErrID_Fatal - RF%ErrMsg = RoutineName//': C_obj%object cannot be packed.' - return - end if call RegPackPtr(RF, InData%twrDef) call RegPackPtr(RF, InData%bldDef) call RegPackPtr(RF, InData%hubDef) @@ -778,11 +773,6 @@ subroutine ExtLdDX_PackParam(RF, Indata) character(*), parameter :: RoutineName = 'ExtLdDX_PackParam' logical :: PtrInIndex if (RF%ErrStat >= AbortErrLev) return - if (c_associated(InData%C_obj%object)) then - RF%ErrStat = ErrID_Fatal - RF%ErrMsg = RoutineName//': C_obj%object cannot be packed.' - return - end if call RegPackPtr(RF, InData%nBlades) call RegPackPtr(RF, InData%nBladeNodes) call RegPackPtr(RF, InData%nTowerNodes) @@ -1226,11 +1216,6 @@ subroutine ExtLdDX_PackOutput(RF, Indata) character(*), parameter :: RoutineName = 'ExtLdDX_PackOutput' logical :: PtrInIndex if (RF%ErrStat >= AbortErrLev) return - if (c_associated(InData%C_obj%object)) then - RF%ErrStat = ErrID_Fatal - RF%ErrMsg = RoutineName//': C_obj%object cannot be packed.' - return - end if call RegPackPtr(RF, InData%twrLd) call RegPackPtr(RF, InData%bldLd) if (RegCheckErr(RF, RoutineName)) return diff --git a/modules/hydrodyn/src/HydroDyn.f90 b/modules/hydrodyn/src/HydroDyn.f90 index ff75e252af..d1ec250277 100644 --- a/modules/hydrodyn/src/HydroDyn.f90 +++ b/modules/hydrodyn/src/HydroDyn.f90 @@ -1371,8 +1371,7 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, LOGICAL, SAVE :: FrstWarn_LrgY = .TRUE. logical :: calcMorisonHstLdsLocal - ! Initialize ErrStat - + ! Initialize ErrStat ErrStat = ErrID_None ErrMsg = "" @@ -1389,7 +1388,7 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, IF ( (p%OutSwtch == 1 .OR. p%OutSwtch == 3) .AND. ( Time > m%LastOutTime ) ) THEN CALL HDOut_WriteOutputs( m%LastOutTime, y, p, m%Decimate, ErrStat2, ErrMsg2 ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (Failed()) return END IF m%LastOutTime = Time ! time associated with the next values of y%WriteOutput @@ -1409,8 +1408,8 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, IF ( (ABS( WrapToPi(PRPRotation(3)-PtfmRefY) ) > LrgAngle) .AND. FrstWarn_LrgY ) THEN ErrStat2 = ErrID_Severe ErrMsg2 = 'Yaw angle at PRP relative to the reference yaw position (PtfmRefY) violated the small angle assumption. The solution might be inaccurate. Consider using PtfmYMod=1 and adjust PtfmYCutoff. Simulation continuing, but future warnings will be suppressed.' - call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) FrstWarn_LrgY = .FALSE. + if (Failed()) return END IF !------------------------------------------------------------------- @@ -1419,16 +1418,15 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, !------------------------------------------------------------------- - if ( p%PotMod == 1 ) then ! Transformation matrices between global and PRP frame - ALLOCATE(RRb2g(6*p%NBody,6*p%NBody),STAT=ErrStat2) - ALLOCATE(RRg2b(6*p%NBody,6*p%NBody),STAT=ErrStat2) + ALLOCATE(RRb2g(6*p%NBody,6*p%NBody),STAT=ErrStat2); if (Failed0("RRb2g")) return; + ALLOCATE(RRg2b(6*p%NBody,6*p%NBody),STAT=ErrStat2); if (Failed0("RRg2b")) return; RRg2b(:,:) = 0.0_ReKi do iBody = 1, p%NBody ! Determine the rotational angles from the direction-cosine matrix ! rotdisp = GetRotAngs ( u%PtfmRefY, u%WAMITMesh%Orientation(:,:,iBody), ErrStat2, ErrMsg2 ) - ! CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + ! if (Failed()) return rotdisp = EulerExtractZYX(u%WAMITMesh%Orientation(:,:,iBody)) indxStart = (iBody-1)*6+1 indxEnd = indxStart+5 @@ -1443,9 +1441,7 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, end do RRb2g = transpose(RRg2b) - !FIXME: Error handling appears to be broken here. if ( p%NBodyMod == 1 ) then - ! Compute the load contirbution from user-supplied added stiffness and damping m%F_PtfmAdd = p%AddF0(:,1) - matmul(p%AddCLin(:,:,1), q) & - matmul( matmul(RRb2g,p%AddBLin(:,:,1) ), matmul(RRg2b,qdot) ) & @@ -1483,14 +1479,13 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, if ( p%NBodyMod == 1 .or. p%NBody == 1 ) then ! Copy the inputs from the HD mesh into the WAMIT mesh call MeshCopy( u%WAMITMesh, m%u_WAMIT(1)%Mesh, MESH_UPDATECOPY, ErrStat2, ErrMsg2 ) - call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - if ( ErrStat >= AbortErrLev ) return + if (Failed()) return ! m%u_WAMIT(1)%PtfmRefY = u%PtfmRefY m%u_WAMIT(1)%PtfmRefY = PtfmRefY call WAMIT_CalcOutput( Time, m%u_WAMIT(1), p%WAMIT(1), x%WAMIT(1), xd%WAMIT(1), & z%WAMIT, OtherState%WAMIT(1), y%WAMIT(1), m%WAMIT(1), ErrStat2, ErrMsg2 ) - call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (Failed()) return do iBody=1,p%NBody y%WAMITMesh%Force (:,iBody) = y%WAMITMesh%Force (:,iBody) + y%WAMIT(1)%Mesh%Force (:,iBody) y%WAMITMesh%Moment(:,iBody) = y%WAMITMesh%Moment(:,iBody) + y%WAMIT(1)%Mesh%Moment(:,iBody) @@ -1512,7 +1507,7 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, m%u_WAMIT(iBody)%PtfmRefY = PtfmRefY call WAMIT_CalcOutput( Time, m%u_WAMIT(iBody), p%WAMIT(iBody), x%WAMIT(iBody), xd%WAMIT(iBody), & z%WAMIT, OtherState%WAMIT(iBody), y%WAMIT(iBody), m%WAMIT(iBody), ErrStat2, ErrMsg2 ) - call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (Failed()) return y%WAMITMesh%Force (:,iBody) = y%WAMITMesh%Force (:,iBody) + y%WAMIT(iBody)%Mesh%Force (:,1) y%WAMITMesh%Moment(:,iBody) = y%WAMITMesh%Moment(:,iBody) + y%WAMIT(iBody)%Mesh%Moment(:,1) @@ -1532,7 +1527,7 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, if ( p%NBodyMod == 1 .or. p%NBody == 1 ) then call WAMIT2_CalcOutput( Time, PtfmRefY, p%WaveField, p%WAMIT2(1), y%WAMIT2(1), m%WAMIT2(1), ErrStat2, ErrMsg2 ) - call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (Failed()) return do iBody=1,p%NBody y%WAMITMesh%Force (:,iBody) = y%WAMITMesh%Force (:,iBody) + y%WAMIT2(1)%Mesh%Force (:,iBody) y%WAMITMesh%Moment(:,iBody) = y%WAMITMesh%Moment(:,iBody) + y%WAMIT2(1)%Mesh%Moment(:,iBody) @@ -1543,7 +1538,7 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, do iBody=1,p%NBody call WAMIT2_CalcOutput( Time, PtfmRefY, p%WaveField, p%WAMIT2(iBody), y%WAMIT2(iBody), m%WAMIT2(iBody), ErrStat2, ErrMsg2 ) - call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (Failed()) return y%WAMITMesh%Force (:,iBody) = y%WAMITMesh%Force (:,iBody) + y%WAMIT2(iBody)%Mesh%Force (:,1) y%WAMITMesh%Moment(:,iBody) = y%WAMITMesh%Moment(:,iBody) + y%WAMIT2(iBody)%Mesh%Moment(:,1) @@ -1563,6 +1558,7 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, Inputs_FIT%si_t(:) = u%WAMITMesh%TranslationDisp(:,1) Inputs_FIT%vel_t(:) = u%WAMITMesh%TranslationVel (:,1) CALL FIT_CalcOutput( Time, Inputs_FIT, p%FIT, FIT_x, xd%FIT, FIT_z, OtherState%FIT, y%FIT, ErrStat2, ErrMsg2 ) + if (Failed()) return ! Add FIT forces to the HydroDyn output mesh y%WAMITMesh%Force (:,1) = y%WAMITMesh%Force (:,1) + y%FIT%F(:) @@ -1583,11 +1579,11 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, ! Integrate all the mesh loads onto the platfrom reference Point (PRP) at (0,0,0) m%F_Hydro = CalcLoadsAtWRP( y, u, m%AllHdroOrigin, m%HD_MeshMap, ErrStat2, ErrMsg2 ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (Failed()) return ! Map calculated results into the first p%NumOuts values of the y%WriteOutput Array CALL HDOut_MapOutputs( p, y, m%WAMIT, m%WAMIT2, m%F_PtfmAdd, m%F_Waves, m%F_Hydro, u%PRPMesh, PtfmRefY, q, qdot, qdotdot, ErrStat2, ErrMsg2 ) - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (Failed()) return ! Aggregate the sub-module outputs IF (p%Morison%NumOuts > 0) THEN @@ -1603,6 +1599,21 @@ SUBROUTINE HydroDyn_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, IF (ALLOCATED(RRb2g)) DEALLOCATE(RRb2g) IF (ALLOCATED(RRg2b)) DEALLOCATE(RRg2b) +contains + logical function Failed() + call SetErrStat(errStat2, errMsg2, errStat, errMsg, RoutineName) + Failed = errStat >= AbortErrLev + end function Failed + ! check for failed where /= 0 is fatal + logical function Failed0(txt) + character(*), intent(in) :: txt + if (ErrStat2 /= 0) then + ErrStat2 = ErrID_Fatal + ErrMsg2 = "Could not allocate "//trim(txt) + call SetErrStat(ErrStat2, ErrMsg2, errStat, errMsg, RoutineName) + endif + Failed0 = ErrStat >= AbortErrLev + end function Failed0 END SUBROUTINE HydroDyn_CalcOutput @@ -1717,6 +1728,9 @@ function CalcLoadsAtWRP( y, u, AllHdroOrigin, MeshMapData, ErrStat, ErrMsg ) integer(IntKi) :: ErrStat2 ! temporary Error status of the operation character(ErrMsgLen) :: ErrMsg2 ! temporary Error message if ErrStat /= ErrID_None + ErrStat = ErrID_None + ErrMsg = "" + CalcLoadsAtWRP = 0.0_ReKi if ( y%WAMITMesh%Committed ) then diff --git a/modules/hydrodyn/src/HydroDyn_C_Binding.f90 b/modules/hydrodyn/src/HydroDyn_C_Binding.f90 index 4c7c5bc4dd..efa64402d1 100644 --- a/modules/hydrodyn/src/HydroDyn_C_Binding.f90 +++ b/modules/hydrodyn/src/HydroDyn_C_Binding.f90 @@ -31,6 +31,7 @@ MODULE HydroDyn_C_BINDING PUBLIC :: HydroDyn_C_Init PUBLIC :: HydroDyn_C_CalcOutput + PUBLIC :: HydroDyn_C_CalcOutput_and_AddedMass PUBLIC :: HydroDyn_C_UpdateStates PUBLIC :: HydroDyn_C_End @@ -304,6 +305,7 @@ SUBROUTINE HydroDyn_C_Init( call AllocAry( tmpNodeVel, 6, NumNodePts, "tmpNodeVel", ErrStat2, ErrMsg2 ); if (Failed()) return call AllocAry( tmpNodeAcc, 6, NumNodePts, "tmpNodeAcc", ErrStat2, ErrMsg2 ); if (Failed()) return call AllocAry( tmpNodeFrc, 6, NumNodePts, "tmpNodeFrc", ErrStat2, ErrMsg2 ); if (Failed()) return + ! structural mesh reference position tmpNodePos(1:6,1:NumNodePts) = reshape( real(InitNodePositions_C(1:6*NumNodePts),ReKi), (/6,NumNodePts/) ) !---------------------------------------------------- @@ -441,6 +443,10 @@ SUBROUTINE HydroDyn_C_Init( HD%InitInp%Gravity = REAL(Gravity_C, ReKi) HD%InitInp%TMax = REAL(TMax_C, DbKi) +!FIXME: initial platform position does not work!!! + ! Initial platform position +! HD%InitInp%PlatformPos = (/ REAL(PtfmRefPtPositionX_C, ReKi), REAL(PtfmRefPtPositionX_C, ReKi), 0 /) + ! Transfer data from SeaState ! Need to set up other module's InitInput data here because we will also need to clean up SeaState data and would rather not defer that cleanup HD%InitInp%InvalidWithSSExctn = SeaSt%InitOutData%InvalidWithSSExctn @@ -500,6 +506,7 @@ SUBROUTINE HydroDyn_C_Init( !-------------------------------------------------------------------------------------------------------------------------------- ! Set the interface meshes and outputs + ! -- uses the InitNodePositions_C location/orientation to set the structural mesh reference location !-------------------------------------------------------------------------------------------------------------------------------- call SetMotionLoadsInterfaceMeshes(ErrStat2,ErrMsg2); if (Failed()) return @@ -684,6 +691,11 @@ end subroutine SetMotionLoadsInterfaceMeshes !! If more than one input node was passed in, but only a single HD node !! exits (single Morison or single WAMIT), then give error that too many !! nodes passed. + !! More than one node is passed in (NumNodePts>1) indicates that the structure + !! is modeled as flexible. This requires more than one destination node on + !! either the Morison or WAMIT meshes. Note that some nodes may be + !! co-located, so checking that the total number of nodes is the same does + !! not work. subroutine CheckNodes(ErrStat3,ErrMsg3) integer(IntKi), intent( out) :: ErrStat3 !< temporary error status character(ErrMsgLen), intent( out) :: ErrMsg3 !< temporary error message @@ -691,19 +703,19 @@ subroutine CheckNodes(ErrStat3,ErrMsg3) ErrMsg3 = "" if ( NumNodePts > 1 ) then if ( HD%u(1)%Morison%Mesh%Committed .and. HD%u(1)%WAMITMesh%Committed ) then - if ( (HD%u(1)%Morison%Mesh%Nnodes + HD%u(1)%WAMITMesh%Nnodes) < NumNodePts ) then + if ( (HD%u(1)%Morison%Mesh%Nnodes + HD%u(1)%WAMITMesh%Nnodes) < 2_IntKi) then ErrStat3 = ErrID_Fatal - ErrMsg3 = "More nodes passed into library than exist in HydroDyn model" + ErrMsg3 = "More than one node passed into library, but only one HydroDyn node exists." endif elseif ( HD%u(1)%Morison%Mesh%Committed ) then ! No WAMIT - if ( HD%u(1)%Morison%Mesh%Nnodes < NumNodePts ) then + if ( HD%u(1)%Morison%Mesh%Nnodes < 2_IntKi ) then ErrStat3 = ErrID_Fatal - ErrMsg3 = "More nodes passed into library than exist in HydroDyn model Morison mesh" + ErrMsg3 = "More than one node passed into library, but only one HydroDyn node exists on Morison mesh." endif elseif ( HD%u(1)%WAMITMesh%Committed ) then ! No Morison - if ( HD%u(1)%WAMITMesh%Nnodes < NumNodePts ) then + if ( HD%u(1)%WAMITMesh%Nnodes < 2_IntKi ) then ErrStat3 = ErrID_Fatal - ErrMsg3 = "More nodes passed into library than exist in HydroDyn model WAMIT mesh" + ErrMsg3 = "More than one node passed into library, but only one HydroDyn node exists on the WAMIT mesh." endif endif endif @@ -842,8 +854,11 @@ logical function Failed() end function Failed END SUBROUTINE HydroDyn_C_CalcOutput + !=============================================================================================================== !-------------------------------------- HydroDyn CalcOutput_and_AddedMass -------------------------------------- +!> This routine is similar to the HydroDyn_C_CalcOutput, but splits the forces returned from HydroDyn_CalcOutput +!! into the hydrodynamic forces without added mass, and a separate added mass matrix. !=============================================================================================================== SUBROUTINE HydroDyn_C_CalcOutput_and_AddedMass(Time_C, NumNodePts_C, NodePos_C, NodeVel_C, & diff --git a/modules/hydrodyn/src/Morison.f90 b/modules/hydrodyn/src/Morison.f90 index 569f5fca1f..3ef23955b9 100644 --- a/modules/hydrodyn/src/Morison.f90 +++ b/modules/hydrodyn/src/Morison.f90 @@ -1400,21 +1400,21 @@ SUBROUTINE SetDepthBasedCoefs_Cyl( z, tMG, NCoefDpth, CoefDpths, Cd, Ca, Cp, AxC s = ( CoefDpths(indx1)%Dpth - z ) / dd END IF if ( tMG > 0.0_ReKi ) then - Cd = CoefDpths(indx1)%DpthCdMG*(1-s) + CoefDpths(indx2)%DpthCdMG*s - Ca = CoefDpths(indx1)%DpthCaMG*(1-s) + CoefDpths(indx2)%DpthCaMG*s - Cp = CoefDpths(indx1)%DpthCpMG*(1-s) + CoefDpths(indx2)%DpthCpMG*s - AxCd = CoefDpths(indx1)%DpthAxCdMG*(1-s) + CoefDpths(indx2)%DpthAxCdMG*s - AxCa = CoefDpths(indx1)%DpthAxCaMG*(1-s) + CoefDpths(indx2)%DpthAxCaMG*s - AxCp = CoefDpths(indx1)%DpthAxCpMG*(1-s) + CoefDpths(indx2)%DpthAxCpMG*s - Cb = CoefDpths(indx1)%DpthCbMG*(1-s) + CoefDpths(indx2)%DpthCbMG*s + Cd = CoefDpths(indx1)%DpthCdMG *(1.0-s) + CoefDpths(indx2)%DpthCdMG *s + Ca = CoefDpths(indx1)%DpthCaMG *(1.0-s) + CoefDpths(indx2)%DpthCaMG *s + Cp = CoefDpths(indx1)%DpthCpMG *(1.0-s) + CoefDpths(indx2)%DpthCpMG *s + AxCd = CoefDpths(indx1)%DpthAxCdMG *(1.0-s) + CoefDpths(indx2)%DpthAxCdMG *s + AxCa = CoefDpths(indx1)%DpthAxCaMG *(1.0-s) + CoefDpths(indx2)%DpthAxCaMG *s + AxCp = CoefDpths(indx1)%DpthAxCpMG *(1.0-s) + CoefDpths(indx2)%DpthAxCpMG *s + Cb = CoefDpths(indx1)%DpthCbMG *(1.0-s) + CoefDpths(indx2)%DpthCbMG *s else - Cd = CoefDpths(indx1)%DpthCd*(1-s) + CoefDpths(indx2)%DpthCd*s - Ca = CoefDpths(indx1)%DpthCa*(1-s) + CoefDpths(indx2)%DpthCa*s - Cp = CoefDpths(indx1)%DpthCp*(1-s) + CoefDpths(indx2)%DpthCp*s - AxCd = CoefDpths(indx1)%DpthAxCd*(1-s) + CoefDpths(indx2)%DpthAxCd*s - AxCa = CoefDpths(indx1)%DpthAxCa*(1-s) + CoefDpths(indx2)%DpthAxCa*s - AxCp = CoefDpths(indx1)%DpthAxCp*(1-s) + CoefDpths(indx2)%DpthAxCp*s - Cb = CoefDpths(indx1)%DpthCb*(1-s) + CoefDpths(indx2)%DpthCb*s + Cd = CoefDpths(indx1)%DpthCd *(1.0-s) + CoefDpths(indx2)%DpthCd *s + Ca = CoefDpths(indx1)%DpthCa *(1.0-s) + CoefDpths(indx2)%DpthCa *s + Cp = CoefDpths(indx1)%DpthCp *(1.0-s) + CoefDpths(indx2)%DpthCp *s + AxCd = CoefDpths(indx1)%DpthAxCd *(1.0-s) + CoefDpths(indx2)%DpthAxCd *s + AxCa = CoefDpths(indx1)%DpthAxCa *(1.0-s) + CoefDpths(indx2)%DpthAxCa *s + AxCp = CoefDpths(indx1)%DpthAxCp *(1.0-s) + CoefDpths(indx2)%DpthAxCp *s + Cb = CoefDpths(indx1)%DpthCb *(1.0-s) + CoefDpths(indx2)%DpthCb *s end if @@ -1474,25 +1474,25 @@ SUBROUTINE SetDepthBasedCoefs_Rec( z, tMG, NCoefDpth, CoefDpths, CdA, CdB, CaA, s = ( CoefDpths(indx1)%Dpth - z ) / dd END IF if ( tMG > 0.0_ReKi ) then - CdA = CoefDpths(indx1)%DpthCdAMG*(1-s) + CoefDpths(indx2)%DpthCdAMG*s - CdB = CoefDpths(indx1)%DpthCdBMG*(1-s) + CoefDpths(indx2)%DpthCdBMG*s - CaA = CoefDpths(indx1)%DpthCaAMG*(1-s) + CoefDpths(indx2)%DpthCaAMG*s - CaB = CoefDpths(indx1)%DpthCaBMG*(1-s) + CoefDpths(indx2)%DpthCaBMG*s - Cp = CoefDpths(indx1)%DpthCpMG*(1-s) + CoefDpths(indx2)%DpthCpMG*s - AxCd = CoefDpths(indx1)%DpthAxCdMG*(1-s) + CoefDpths(indx2)%DpthAxCdMG*s - AxCa = CoefDpths(indx1)%DpthAxCaMG*(1-s) + CoefDpths(indx2)%DpthAxCaMG*s - AxCp = CoefDpths(indx1)%DpthAxCpMG*(1-s) + CoefDpths(indx2)%DpthAxCpMG*s - Cb = CoefDpths(indx1)%DpthCbMG*(1-s) + CoefDpths(indx2)%DpthCbMG*s + CdA = CoefDpths(indx1)%DpthCdAMG *(1.0-s) + CoefDpths(indx2)%DpthCdAMG *s + CdB = CoefDpths(indx1)%DpthCdBMG *(1.0-s) + CoefDpths(indx2)%DpthCdBMG *s + CaA = CoefDpths(indx1)%DpthCaAMG *(1.0-s) + CoefDpths(indx2)%DpthCaAMG *s + CaB = CoefDpths(indx1)%DpthCaBMG *(1.0-s) + CoefDpths(indx2)%DpthCaBMG *s + Cp = CoefDpths(indx1)%DpthCpMG *(1.0-s) + CoefDpths(indx2)%DpthCpMG *s + AxCd = CoefDpths(indx1)%DpthAxCdMG *(1.0-s) + CoefDpths(indx2)%DpthAxCdMG *s + AxCa = CoefDpths(indx1)%DpthAxCaMG *(1.0-s) + CoefDpths(indx2)%DpthAxCaMG *s + AxCp = CoefDpths(indx1)%DpthAxCpMG *(1.0-s) + CoefDpths(indx2)%DpthAxCpMG *s + Cb = CoefDpths(indx1)%DpthCbMG *(1.0-s) + CoefDpths(indx2)%DpthCbMG *s else - CdA = CoefDpths(indx1)%DpthCdA*(1-s) + CoefDpths(indx2)%DpthCdA*s - CdB = CoefDpths(indx1)%DpthCdB*(1-s) + CoefDpths(indx2)%DpthCdB*s - CaA = CoefDpths(indx1)%DpthCaA*(1-s) + CoefDpths(indx2)%DpthCaA*s - CaB = CoefDpths(indx1)%DpthCaB*(1-s) + CoefDpths(indx2)%DpthCaB*s - Cp = CoefDpths(indx1)%DpthCp*(1-s) + CoefDpths(indx2)%DpthCp*s - AxCd = CoefDpths(indx1)%DpthAxCd*(1-s) + CoefDpths(indx2)%DpthAxCd*s - AxCa = CoefDpths(indx1)%DpthAxCa*(1-s) + CoefDpths(indx2)%DpthAxCa*s - AxCp = CoefDpths(indx1)%DpthAxCp*(1-s) + CoefDpths(indx2)%DpthAxCp*s - Cb = CoefDpths(indx1)%DpthCb*(1-s) + CoefDpths(indx2)%DpthCb*s + CdA = CoefDpths(indx1)%DpthCdA *(1.0-s) + CoefDpths(indx2)%DpthCdA *s + CdB = CoefDpths(indx1)%DpthCdB *(1.0-s) + CoefDpths(indx2)%DpthCdB *s + CaA = CoefDpths(indx1)%DpthCaA *(1.0-s) + CoefDpths(indx2)%DpthCaA *s + CaB = CoefDpths(indx1)%DpthCaB *(1.0-s) + CoefDpths(indx2)%DpthCaB *s + Cp = CoefDpths(indx1)%DpthCp *(1.0-s) + CoefDpths(indx2)%DpthCp *s + AxCd = CoefDpths(indx1)%DpthAxCd *(1.0-s) + CoefDpths(indx2)%DpthAxCd *s + AxCa = CoefDpths(indx1)%DpthAxCa *(1.0-s) + CoefDpths(indx2)%DpthAxCa *s + AxCp = CoefDpths(indx1)%DpthAxCp *(1.0-s) + CoefDpths(indx2)%DpthAxCp *s + Cb = CoefDpths(indx1)%DpthCb *(1.0-s) + CoefDpths(indx2)%DpthCb *s end if @@ -1567,22 +1567,23 @@ SUBROUTINE SetExternalHydroCoefs_Cyl( MSL2SWL, MCoefMod, MmbrCoefIDIndx, SimplC do i = 1, member%NElements + 1 ! Pull member end-node data from the tables and then linearly interpolate it onto the interior member nodes s = (real(i,ReKi)-1.0) / real(member%NElements,ReKi) + if (member%flipped) s = 1.0-s if ( member%tMG(i) > 0.0_ReKi ) then - member%Cd (i) = CoefMembers(MmbrCoefIDIndx)%MemberCdMG1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberCdMG2 *s - member%Ca (i) = CoefMembers(MmbrCoefIDIndx)%MemberCaMG1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberCaMG2 *s - member%Cp (i) = CoefMembers(MmbrCoefIDIndx)%MemberCpMG1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberCpMG2 *s - member%Cb (i) = CoefMembers(MmbrCoefIDIndx)%MemberCbMG1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberCbMG2 *s - member%AxCd (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCaMG1*(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCdMG2*s - member%AxCa (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCaMG1*(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCaMG2*s - member%AxCp (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCpMG1*(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCpMG2*s + member%Cd (i) = CoefMembers(MmbrCoefIDIndx)%MemberCdMG1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCdMG2 *s + member%Ca (i) = CoefMembers(MmbrCoefIDIndx)%MemberCaMG1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCaMG2 *s + member%Cp (i) = CoefMembers(MmbrCoefIDIndx)%MemberCpMG1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCpMG2 *s + member%Cb (i) = CoefMembers(MmbrCoefIDIndx)%MemberCbMG1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCbMG2 *s + member%AxCd (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCdMG1*(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCdMG2*s + member%AxCa (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCaMG1*(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCaMG2*s + member%AxCp (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCpMG1*(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCpMG2*s else - member%Cd (i) = CoefMembers(MmbrCoefIDIndx)%MemberCd1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberCd2 *s - member%Ca (i) = CoefMembers(MmbrCoefIDIndx)%MemberCa1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberCa2 *s - member%Cp (i) = CoefMembers(MmbrCoefIDIndx)%MemberCp1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberCp2 *s - member%Cb (i) = CoefMembers(MmbrCoefIDIndx)%MemberCb1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberCb2 *s - member%AxCd (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCd1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCd2 *s - member%AxCa (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCa1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCa2 *s - member%AxCp (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCp1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCp2 *s + member%Cd (i) = CoefMembers(MmbrCoefIDIndx)%MemberCd1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCd2 *s + member%Ca (i) = CoefMembers(MmbrCoefIDIndx)%MemberCa1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCa2 *s + member%Cp (i) = CoefMembers(MmbrCoefIDIndx)%MemberCp1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCp2 *s + member%Cb (i) = CoefMembers(MmbrCoefIDIndx)%MemberCb1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCb2 *s + member%AxCd (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCd1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCd2 *s + member%AxCa (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCa1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCa2 *s + member%AxCp (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCp1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCp2 *s end if end do member%propMCF = CoefMembers(MmbrCoefIDIndx)%MemberMCF @@ -1665,26 +1666,27 @@ SUBROUTINE SetExternalHydroCoefs_Rec( MSL2SWL, MCoefMod, MmbrCoefIDIndx, SimplC do i = 1, member%NElements + 1 ! Pull member end-node data from the tables and then linearly interpolate it onto the interior member nodes s = (real(i,ReKi)-1.0) / real(member%NElements,ReKi) + if (member%flipped) s = 1.0-s if ( member%tMG(i) > 0.0_ReKi ) then - member%CdA (i) = CoefMembers(MmbrCoefIDIndx)%MemberCdAMG1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberCdAMG2 *s - member%CdB (i) = CoefMembers(MmbrCoefIDIndx)%MemberCdBMG1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberCdBMG2 *s - member%CaA (i) = CoefMembers(MmbrCoefIDIndx)%MemberCaAMG1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberCaAMG2 *s - member%CaB (i) = CoefMembers(MmbrCoefIDIndx)%MemberCaBMG1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberCaBMG2 *s - member%Cp (i) = CoefMembers(MmbrCoefIDIndx)%MemberCpMG1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberCpMG2 *s - member%Cb (i) = CoefMembers(MmbrCoefIDIndx)%MemberCbMG1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberCbMG2 *s - member%AxCd (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCaMG1*(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCdMG2*s - member%AxCa (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCaMG1*(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCaMG2*s - member%AxCp (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCpMG1*(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCpMG2*s + member%CdA (i) = CoefMembers(MmbrCoefIDIndx)%MemberCdAMG1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCdAMG2 *s + member%CdB (i) = CoefMembers(MmbrCoefIDIndx)%MemberCdBMG1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCdBMG2 *s + member%CaA (i) = CoefMembers(MmbrCoefIDIndx)%MemberCaAMG1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCaAMG2 *s + member%CaB (i) = CoefMembers(MmbrCoefIDIndx)%MemberCaBMG1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCaBMG2 *s + member%Cp (i) = CoefMembers(MmbrCoefIDIndx)%MemberCpMG1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCpMG2 *s + member%Cb (i) = CoefMembers(MmbrCoefIDIndx)%MemberCbMG1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCbMG2 *s + member%AxCd (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCdMG1*(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCdMG2*s + member%AxCa (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCaMG1*(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCaMG2*s + member%AxCp (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCpMG1*(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCpMG2*s else - member%CdA (i) = CoefMembers(MmbrCoefIDIndx)%MemberCdA1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberCdA2 *s - member%CdB (i) = CoefMembers(MmbrCoefIDIndx)%MemberCdB1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberCdB2 *s - member%CaA (i) = CoefMembers(MmbrCoefIDIndx)%MemberCaA1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberCaA2 *s - member%CaB (i) = CoefMembers(MmbrCoefIDIndx)%MemberCaB1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberCaB2 *s - member%Cp (i) = CoefMembers(MmbrCoefIDIndx)%MemberCp1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberCp2 *s - member%Cb (i) = CoefMembers(MmbrCoefIDIndx)%MemberCb1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberCb2 *s - member%AxCd (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCd1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCd2 *s - member%AxCa (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCa1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCa2 *s - member%AxCp (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCp1 *(1-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCp2 *s + member%CdA (i) = CoefMembers(MmbrCoefIDIndx)%MemberCdA1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCdA2 *s + member%CdB (i) = CoefMembers(MmbrCoefIDIndx)%MemberCdB1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCdB2 *s + member%CaA (i) = CoefMembers(MmbrCoefIDIndx)%MemberCaA1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCaA2 *s + member%CaB (i) = CoefMembers(MmbrCoefIDIndx)%MemberCaB1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCaB2 *s + member%Cp (i) = CoefMembers(MmbrCoefIDIndx)%MemberCp1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCp2 *s + member%Cb (i) = CoefMembers(MmbrCoefIDIndx)%MemberCb1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberCb2 *s + member%AxCd (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCd1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCd2 *s + member%AxCa (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCa1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCa2 *s + member%AxCp (i) = CoefMembers(MmbrCoefIDIndx)%MemberAxCp1 *(1.0-s) + CoefMembers(MmbrCoefIDIndx)%MemberAxCp2 *s end if end do member%propMCF = CoefMembers(MmbrCoefIDIndx)%MemberMCF @@ -3482,7 +3484,7 @@ SUBROUTINE Morison_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, errStat, REAL(ReKi) :: sinBeta, sinBeta1, sinBeta2 REAL(ReKi) :: cosBeta, cosBeta1, cosBeta2 REAL(ReKi) :: CMatrix(3,3), CMatrix1(3,3), CMatrix2(3,3), CTrans(3,3) ! Direction cosine matrix for element, and its transpose - REAL(ReKi) :: l, z1, z2, zMid, r1, r2, r1b, r2b, r1In, r2In, rMidIn, rn, rn1, rn2, z_hi, zFillGroup + REAL(ReKi) :: l, z1, z2, zMid, r1, r2, r1b, r2b, r1In, r2In, rMidIn, z_hi, zFillGroup REAL(ReKi) :: Sa1, Sa2, Sa1b, Sa2b, SaMidb, Sa1In, Sa2In, SaMidIn REAL(ReKi) :: Sb1, Sb2, Sb1b, Sb2b, SbMidb, Sb1In, Sb2In, SbMidIn REAL(ReKi) :: dRdl_mg, dSadl_mg, dSbdl_mg ! shorthand for taper including marine growth of element i @@ -3798,17 +3800,10 @@ SUBROUTINE Morison_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, errStat, ! Get free surface elevation and normal at the element midpoint (both assumed constant over the element) posMid = 0.5 * (pos1+pos2) - ! rn is only used to estimate free surface normal numerically - IF (mem%MSecGeom == MSecGeom_Cyl) THEN - rn = 0.5 * (r1b +r2b ) - ELSE IF (mem%MSecGeom == MSecGeom_Rec) THEN - rn = MAX( 0.5*(Sa1b+Sa2b), 0.5*(Sb1b+Sb2b) ) - END IF - IF (p%WaveField%WaveStMod > 0) THEN CALL GetTotalWaveElev(p, m, Time, posMid, ZetaMid, ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - CALL GetFreeSurfaceNormal( p, m, Time, posMid, rn, n_hat, ErrStat2, ErrMsg2 ) + CALL GetFreeSurfaceNormal( p, m, Time, posMid, n_hat, ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) FSPt = (/posMid(1),posMid(2),ZetaMid/) ! Reference point on the free surface ELSE @@ -4587,22 +4582,18 @@ SUBROUTINE Morison_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, errStat, if (mem%MSecGeom==MSecGeom_Cyl) then r1 = mem%RMGB( 1) r2 = mem%RMGB(N+1) - rn1 = r1 - rn2 = r2 else if (mem%MSecGeom==MSecGeom_Rec) then Sa1 = mem%SaMGB( 1) Sa2 = mem%SaMGB(N+1) Sb1 = mem%SbMGB( 1) Sb2 = mem%SbMGB(N+1) - rn1 = MAX(Sa1,Sb1) - rn2 = MAX(Sa2,Sb2) end if if (mem%i_floor == 0) then ! both ends above or at seabed ! Compute loads on the end plate of node 1 IF (p%WaveField%WaveStMod > 0) THEN CALL GetTotalWaveElev(p, m, Time, pos1, Zeta1, ErrStat2, ErrMsg2) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - CALL GetFreeSurfaceNormal(p, m, Time, pos1, rn1, n_hat, ErrStat2, ErrMsg2) + CALL GetFreeSurfaceNormal(p, m, Time, pos1, n_hat, ErrStat2, ErrMsg2) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) FSPt = (/pos1(1),pos1(2),Zeta1/) ! Reference point on the free surface ELSE @@ -4630,7 +4621,7 @@ SUBROUTINE Morison_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, errStat, IF (p%WaveField%WaveStMod > 0) THEN CALL GetTotalWaveElev(p, m, Time, pos2, Zeta2, ErrStat2, ErrMsg2) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - CALL GetFreeSurfaceNormal(p, m, Time, pos2, rn2, n_hat, ErrStat2, ErrMsg2) + CALL GetFreeSurfaceNormal(p, m, Time, pos2, n_hat, ErrStat2, ErrMsg2) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) FSPt = (/pos2(1),pos2(2),Zeta2/) ! Reference point on the free surface ELSE @@ -4659,7 +4650,7 @@ SUBROUTINE Morison_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, errStat, IF (p%WaveField%WaveStMod > 0) THEN CALL GetTotalWaveElev(p, m, Time, pos2, Zeta2, ErrStat2, ErrMsg2) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - CALL GetFreeSurfaceNormal(p, m, Time, pos2, rn2, n_hat, ErrStat2, ErrMsg2) + CALL GetFreeSurfaceNormal(p, m, Time, pos2, n_hat, ErrStat2, ErrMsg2) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) FSPt = (/pos2(1),pos2(2),Zeta2/) ! Reference point on the free surface ELSE @@ -4805,12 +4796,11 @@ SUBROUTINE GetTotalWaveElev(p, m, Time, pos, Zeta, ErrStat, ErrMsg ) END SUBROUTINE GetTotalWaveElev - SUBROUTINE GetFreeSurfaceNormal(p, m, Time, pos, r, n, ErrStat, ErrMsg) + SUBROUTINE GetFreeSurfaceNormal(p, m, Time, pos, n, ErrStat, ErrMsg) TYPE(Morison_ParameterType), INTENT( IN ) :: p TYPE(Morison_MiscVarType), INTENT( INOUT ) :: m REAL(DbKi), INTENT( In ) :: Time REAL(ReKi), INTENT( In ) :: pos(:) ! Position at which free-surface normal is to be calculated. Third entry ignored if present. - REAL(ReKi), INTENT( In ) :: r ! Distance for central differencing REAL(ReKi), INTENT( OUT ) :: n(3) ! Free-surface normal vector INTEGER(IntKi), INTENT( OUT ) :: ErrStat ! Error status of the operation CHARACTER(*), INTENT( OUT ) :: ErrMsg ! Error message if errStat /= ErrID_None @@ -4820,7 +4810,7 @@ SUBROUTINE GetFreeSurfaceNormal(p, m, Time, pos, r, n, ErrStat, ErrMsg) ErrStat = ErrID_None ErrMsg = "" - CALL WaveField_GetNodeWaveNormal( p%WaveField, m%WaveField_m, Time, pos, r, n, ErrStat2, ErrMsg2 ) + CALL WaveField_GetNodeWaveNormal( p%WaveField, m%WaveField_m, Time, pos, n, ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) END SUBROUTINE GetFreeSurfaceNormal @@ -5945,7 +5935,7 @@ SUBROUTINE getElementHstLds_Mod1(p, m, mem, Time, pos1, pos2, Zeta1, Zeta2, k_ha rh = r1 + h0*dRdl ! Estimate the free-surface normal at the free-surface intersection, n_hat IF ( p%WaveField%WaveStMod > 0_IntKi ) THEN ! If wave stretching is enabled, compute free surface normal - CALL GetFreeSurfaceNormal(p, m, Time, FSInt, rh, n_hat, ErrStat2, ErrMsg2 ) + CALL GetFreeSurfaceNormal(p, m, Time, FSInt, n_hat, ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) ELSE ! Without wave stretching, use the normal of the SWL n_hat = (/0.0_ReKi,0.0_ReKi,1.0_ReKi/) @@ -6206,6 +6196,7 @@ SUBROUTINE GetDistDrag_Rec(p, m, u, xd, Time, mem, i, dSadl_p, dSbdl_p, f_hydro, Real(ReKi) :: posFC(3) ! Position of face center Real(ReKi) :: SVFC(3) ! Structure velocity at face center Real(SiKi) :: FVFC(3) ! Flow velocity at face center + Real(SiKi) :: FAFC(3) ! Flow acceleration at face center Real(ReKi) :: vrelFC ! Relative (flow-structure) velocity at face centers Real(ReKi) :: vrelFCf ! High-pass filtered relative (flow-structure) velocity at face centers Real(ReKi) :: STV(3) ! Structure translational velocity at the current node/free-surface intersection @@ -6424,7 +6415,7 @@ SUBROUTINE Morison_UpdateDiscState( Time, u, p, x, xd, z, OtherState, m, errStat INTEGER(IntKi) :: nodeInWater, tmpInt REAL(ReKi) :: pos(3), vrel(3), FV(3), vmag, vmagf, An_End(3) REAL(ReKi) :: posFC(3), SVFC(3), vrelFC, vrelFCf - REAL(SiKi) :: FVTmp(3) + REAL(SiKi) :: FVTmp(3),FATmp(3) TYPE(Morison_MemberType) :: mem !< Current member INTEGER(IntKi) :: errStat2 diff --git a/modules/hydrodyn/src/Morison.txt b/modules/hydrodyn/src/Morison.txt index 28d332824d..fe912f470c 100644 --- a/modules/hydrodyn/src/Morison.txt +++ b/modules/hydrodyn/src/Morison.txt @@ -462,7 +462,7 @@ typedef ^ ^ ReKi typedef ^ ^ ReKi V_rel_n_HiPass {:} - - "High-pass filtered normal relative flow velocity at joints" m/s typedef ^ ^ ReKi zFillGroup {:} - - "Instantaneous highest point of each filled group" m typedef ^ ^ MeshMapType VisMeshMap - - - "Mesh mapping for visualization mesh" - -typedef ^ ^ SeaSt_WaveField_MiscVarType WaveField_m - - - "misc var information from the SeaState WaveField module" - +typedef ^ ^ GridInterp_MiscVarType WaveField_m - - - "misc var information from the Grid Interpolation module" - # ..... Parameters ................................................................................................................ # Define parameters here: diff --git a/modules/hydrodyn/src/Morison_Output.f90 b/modules/hydrodyn/src/Morison_Output.f90 index 4aa0d64dd6..642309b98c 100644 --- a/modules/hydrodyn/src/Morison_Output.f90 +++ b/modules/hydrodyn/src/Morison_Output.f90 @@ -8037,7 +8037,9 @@ SUBROUTINE MrsnOut_Init( InitInp, y, p, InitOut, ErrStat, ErrMsg ) ! Need to search mesh for the two markers which surround the requested output location and then store those marker indices and compute the ! scale factor based on how far they are from the requested output location. ! Since this is being done on markers and not nodes, the subroutine must be called after the Morison_Init() subroutine is called - + IF (p%Members(memberIndx)%Flipped) THEN + p%MOutLst(I)%NodeLocs(J) = 1.0 - p%MOutLst(I)%NodeLocs(J) + END IF CALL GetNeighboringNodes(p%Members(memberIndx), p%MOutLst(I)%NodeLocs(J), m1, m2, i1, i2, s, ErrStat, ErrMsg) p%MOutLst(I)%MeshIndx1(J) = m1 diff --git a/modules/hydrodyn/src/Morison_Types.f90 b/modules/hydrodyn/src/Morison_Types.f90 index 42ea365396..0daf2686a9 100644 --- a/modules/hydrodyn/src/Morison_Types.f90 +++ b/modules/hydrodyn/src/Morison_Types.f90 @@ -530,7 +530,7 @@ MODULE Morison_Types REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: V_rel_n_HiPass !< High-pass filtered normal relative flow velocity at joints [m/s] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: zFillGroup !< Instantaneous highest point of each filled group [m] TYPE(MeshMapType) :: VisMeshMap !< Mesh mapping for visualization mesh [-] - TYPE(SeaSt_WaveField_MiscVarType) :: WaveField_m !< misc var information from the SeaState WaveField module [-] + TYPE(GridInterp_MiscVarType) :: WaveField_m !< misc var information from the Grid Interpolation module [-] END TYPE Morison_MiscVarType ! ======================= ! ========= Morison_ParameterType ======= @@ -4700,7 +4700,7 @@ subroutine Morison_CopyMisc(SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg) call NWTC_Library_CopyMeshMapType(SrcMiscData%VisMeshMap, DstMiscData%VisMeshMap, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return - call SeaSt_WaveField_CopyMisc(SrcMiscData%WaveField_m, DstMiscData%WaveField_m, CtrlCode, ErrStat2, ErrMsg2) + call GridInterp_CopyMisc(SrcMiscData%WaveField_m, DstMiscData%WaveField_m, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return end subroutine @@ -4787,7 +4787,7 @@ subroutine Morison_DestroyMisc(MiscData, ErrStat, ErrMsg) end if call NWTC_Library_DestroyMeshMapType(MiscData%VisMeshMap, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - call SeaSt_WaveField_DestroyMisc(MiscData%WaveField_m, ErrStat2, ErrMsg2) + call GridInterp_DestroyMisc(MiscData%WaveField_m, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) end subroutine @@ -4828,7 +4828,7 @@ subroutine Morison_PackMisc(RF, Indata) call RegPackAlloc(RF, InData%V_rel_n_HiPass) call RegPackAlloc(RF, InData%zFillGroup) call NWTC_Library_PackMeshMapType(RF, InData%VisMeshMap) - call SeaSt_WaveField_PackMisc(RF, InData%WaveField_m) + call GridInterp_PackMisc(RF, InData%WaveField_m) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -4875,7 +4875,7 @@ subroutine Morison_UnPackMisc(RF, OutData) call RegUnpackAlloc(RF, OutData%V_rel_n_HiPass); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%zFillGroup); if (RegCheckErr(RF, RoutineName)) return call NWTC_Library_UnpackMeshMapType(RF, OutData%VisMeshMap) ! VisMeshMap - call SeaSt_WaveField_UnpackMisc(RF, OutData%WaveField_m) ! WaveField_m + call GridInterp_UnpackMisc(RF, OutData%WaveField_m) ! WaveField_m end subroutine subroutine Morison_CopyParam(SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg) diff --git a/modules/hydrodyn/src/SS_Excitation.txt b/modules/hydrodyn/src/SS_Excitation.txt index 1372e9a823..01a573b589 100644 --- a/modules/hydrodyn/src/SS_Excitation.txt +++ b/modules/hydrodyn/src/SS_Excitation.txt @@ -43,7 +43,7 @@ typedef ^ ^ SS_Exc_ContinuousStateType # Define any data that are used only for efficiency purposes (these variables are not associated with time): # e.g. indices for searching in an array, large arrays that are local variables in any routine called multiple times, etc. typedef ^ MiscVarType INTEGER LastIndWave - 1 - "last used index in the WaveTime array" - -typedef ^ ^ SeaSt_WaveField_MiscVarType WaveField_m - - - "misc var information from the SeaState Interpolation module" - +typedef ^ ^ GridInterp_MiscVarType WaveField_m - - - "misc var information from the Grid Interpolation module" - # ..... Parameters ......................... diff --git a/modules/hydrodyn/src/SS_Excitation_Types.f90 b/modules/hydrodyn/src/SS_Excitation_Types.f90 index 671a06a553..d721957885 100644 --- a/modules/hydrodyn/src/SS_Excitation_Types.f90 +++ b/modules/hydrodyn/src/SS_Excitation_Types.f90 @@ -73,7 +73,7 @@ MODULE SS_Excitation_Types ! ========= SS_Exc_MiscVarType ======= TYPE, PUBLIC :: SS_Exc_MiscVarType INTEGER(IntKi) :: LastIndWave = 1 !< last used index in the WaveTime array [-] - TYPE(SeaSt_WaveField_MiscVarType) :: WaveField_m !< misc var information from the SeaState Interpolation module [-] + TYPE(GridInterp_MiscVarType) :: WaveField_m !< misc var information from the Grid Interpolation module [-] END TYPE SS_Exc_MiscVarType ! ======================= ! ========= SS_Exc_ParameterType ======= @@ -499,7 +499,7 @@ subroutine SS_Exc_CopyMisc(SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg) ErrStat = ErrID_None ErrMsg = '' DstMiscData%LastIndWave = SrcMiscData%LastIndWave - call SeaSt_WaveField_CopyMisc(SrcMiscData%WaveField_m, DstMiscData%WaveField_m, CtrlCode, ErrStat2, ErrMsg2) + call GridInterp_CopyMisc(SrcMiscData%WaveField_m, DstMiscData%WaveField_m, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return end subroutine @@ -513,7 +513,7 @@ subroutine SS_Exc_DestroyMisc(MiscData, ErrStat, ErrMsg) character(*), parameter :: RoutineName = 'SS_Exc_DestroyMisc' ErrStat = ErrID_None ErrMsg = '' - call SeaSt_WaveField_DestroyMisc(MiscData%WaveField_m, ErrStat2, ErrMsg2) + call GridInterp_DestroyMisc(MiscData%WaveField_m, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) end subroutine @@ -523,7 +523,7 @@ subroutine SS_Exc_PackMisc(RF, Indata) character(*), parameter :: RoutineName = 'SS_Exc_PackMisc' if (RF%ErrStat >= AbortErrLev) return call RegPack(RF, InData%LastIndWave) - call SeaSt_WaveField_PackMisc(RF, InData%WaveField_m) + call GridInterp_PackMisc(RF, InData%WaveField_m) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -533,7 +533,7 @@ subroutine SS_Exc_UnPackMisc(RF, OutData) character(*), parameter :: RoutineName = 'SS_Exc_UnPackMisc' if (RF%ErrStat /= ErrID_None) return call RegUnpack(RF, OutData%LastIndWave); if (RegCheckErr(RF, RoutineName)) return - call SeaSt_WaveField_UnpackMisc(RF, OutData%WaveField_m) ! WaveField_m + call GridInterp_UnpackMisc(RF, OutData%WaveField_m) ! WaveField_m end subroutine subroutine SS_Exc_CopyParam(SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg) diff --git a/modules/hydrodyn/src/WAMIT.f90 b/modules/hydrodyn/src/WAMIT.f90 index bf1260f4ca..5194aae156 100644 --- a/modules/hydrodyn/src/WAMIT.f90 +++ b/modules/hydrodyn/src/WAMIT.f90 @@ -241,7 +241,7 @@ SUBROUTINE WAMIT_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, ErrS ! Set up wave excitation grid - Can no longer use the WaveField parameters due to different headings ! Copy WaveField grid parameters - call SeaSt_WaveField_CopyParam(p%WaveField%GridParams, p%ExctnGridParams, 0, ErrStat2, ErrMsg2); CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call GridInterp_CopyParam(p%WaveField%VolGridParams, p%ExctnGridParams, 0, ErrStat2, ErrMsg2); CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if ( p%ExctnDisp == 0 ) then p%ExctnGridParams%n(2:3) = 1_IntKi p%ExctnGridParams%delta(2:3) = 0.0_SiKi @@ -257,7 +257,6 @@ SUBROUTINE WAMIT_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, ErrS p%ExctnGridParams%pZero(4) = -Pi end if p%ExctnGridParams%n(4) = p%NExctnHdg+1 - p%ExctnGridParams%Z_depth = -1.0 ! Set to Z_depth to a negative value to indicate uniform "z" grid for platform heading ! This module's implementation requires that if NBodyMod = 2 or 3, then there is one instance of a WAMIT module for each body, therefore, HydroDyn may have NBody > 1, but this WAMIT module will have NBody = 1 if ( (p%NBodyMod > 1) .and. (p%NBody > 1) ) then @@ -1101,16 +1100,18 @@ SUBROUTINE WAMIT_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, ErrS ! Apply rotation only for NBodyMod = 1,3 do J = 1, NInpWvDir do I = 1, NInpFreq - - Ctmp1 = ( HdroExctn(I,J,1)*cos(InitInp%PtfmRefztRot(1)) ) - ( HdroExctn(I,J,2)*sin(InitInp%PtfmRefztRot(1)) ) - Ctmp2 = ( HdroExctn(I,J,1)*sin(InitInp%PtfmRefztRot(1)) ) + ( HdroExctn(I,J,2)*cos(InitInp%PtfmRefztRot(1)) ) - Ctmp4 = ( HdroExctn(I,J,4)*cos(InitInp%PtfmRefztRot(1)) ) - ( HdroExctn(I,J,5)*sin(InitInp%PtfmRefztRot(1)) ) - Ctmp5 = ( HdroExctn(I,J,4)*sin(InitInp%PtfmRefztRot(1)) ) + ( HdroExctn(I,J,5)*cos(InitInp%PtfmRefztRot(1)) ) - - HdroExctn(I,J,1) = Ctmp1 - HdroExctn(I,J,2) = Ctmp2 - HdroExctn(I,J,4) = Ctmp4 - HdroExctn(I,J,5) = Ctmp5 + do iBody = 1, p%NBody + K = 6*(iBody-1) + Ctmp1 = ( HdroExctn(I,J,K+1)*cos(InitInp%PtfmRefztRot(iBody)) ) - ( HdroExctn(I,J,K+2)*sin(InitInp%PtfmRefztRot(iBody)) ) + Ctmp2 = ( HdroExctn(I,J,K+1)*sin(InitInp%PtfmRefztRot(iBody)) ) + ( HdroExctn(I,J,K+2)*cos(InitInp%PtfmRefztRot(iBody)) ) + Ctmp4 = ( HdroExctn(I,J,K+4)*cos(InitInp%PtfmRefztRot(iBody)) ) - ( HdroExctn(I,J,K+5)*sin(InitInp%PtfmRefztRot(iBody)) ) + Ctmp5 = ( HdroExctn(I,J,K+4)*sin(InitInp%PtfmRefztRot(iBody)) ) + ( HdroExctn(I,J,K+5)*cos(InitInp%PtfmRefztRot(iBody)) ) + + HdroExctn(I,J,K+1) = Ctmp1 + HdroExctn(I,J,K+2) = Ctmp2 + HdroExctn(I,J,K+4) = Ctmp4 + HdroExctn(I,J,K+5) = Ctmp5 + end do end do end do diff --git a/modules/hydrodyn/src/WAMIT.txt b/modules/hydrodyn/src/WAMIT.txt index 0d07cfe542..f826ab565a 100644 --- a/modules/hydrodyn/src/WAMIT.txt +++ b/modules/hydrodyn/src/WAMIT.txt @@ -17,6 +17,7 @@ usefrom Conv_Radiation.txt usefrom SS_Radiation.txt usefrom SS_Excitation.txt usefrom SeaSt_WaveField.txt +usefrom GridInterp.txt typedef WAMIT/WAMIT InitInputType INTEGER NBody - - - "[>=1; only used when PotMod=1. If NBodyMod=1, the WAMIT data contains a vector of size 6*NBody x 1 and matrices of size 6*NBody x 6*NBody; if NBodyMod>1, there are NBody sets of WAMIT data each with a vector of size 6 x 1 and matrices of size 6 x 6]" - typedef ^ ^ INTEGER NBodyMod - - - "Body coupling model {1: include coupling terms between each body and NBody in HydroDyn equals NBODY in WAMIT, 2: neglect coupling terms between each body and NBODY=1 with XBODY=0 in WAMIT, 3: Neglect coupling terms between each body and NBODY=1 with XBODY=/0 in WAMIT} (switch) [only used when PotMod=1]" - @@ -95,7 +96,7 @@ typedef ^ ^ SS_Exc_Outp typedef ^ ^ Conv_Rdtn_MiscVarType Conv_Rdtn - - - "" - typedef ^ ^ Conv_Rdtn_InputType Conv_Rdtn_u - - - "" - typedef ^ ^ Conv_Rdtn_OutputType Conv_Rdtn_y - - - "" - -typedef ^ ^ SeaSt_WaveField_MiscVarType WaveField_m - - - "misc var information from the SeaState Interpolation module" - +typedef ^ ^ GridInterp_MiscVarType WaveField_m - - - "misc var information from the Grid Interpolation module" - # ..... Parameters ................................................................................................................ # Define parameters here: @@ -120,7 +121,7 @@ typedef ^ ^ SS_Exc_Para typedef ^ ^ DbKi DT - - - "" - typedef ^ ^ SeaSt_WaveFieldType *WaveField - - - "Pointer to wave field" typedef ^ ^ INTEGER PtfmYMod - - - "Large yaw model" - -typedef ^ ^ SeaSt_WaveField_ParameterType ExctnGridParams - - - "Parameters of WaveExctnGrid" - +typedef ^ ^ GridInterp_ParameterType ExctnGridParams - - - "Parameters of WaveExctnGrid" - # # # ..... Inputs .................................................................................................................... diff --git a/modules/hydrodyn/src/WAMIT2.f90 b/modules/hydrodyn/src/WAMIT2.f90 index 3c38462510..ef92fab0fa 100644 --- a/modules/hydrodyn/src/WAMIT2.f90 +++ b/modules/hydrodyn/src/WAMIT2.f90 @@ -2646,7 +2646,7 @@ SUBROUTINE CheckInitInput( InitInp, p, MnDriftData, NewmanAppData, DiffQTFData, ! Set up 2nd-order wave excitation grid ! Copy WaveField grid parameters - call SeaSt_WaveField_CopyParam(InitInp%WaveField%GridParams, p%Exctn2GridParams, 0, ErrStatTmp, ErrMsgTmp); CALL SetErrStat( ErrStatTmp, ErrMsgTmp, ErrStat, ErrMsg, RoutineName) + call GridInterp_CopyParam(InitInp%WaveField%VolGridParams, p%Exctn2GridParams, 0, ErrStatTmp, ErrMsgTmp); CALL SetErrStat( ErrStatTmp, ErrMsgTmp, ErrStat, ErrMsg, RoutineName) ! x and y grids are currently not used for second-order wave excitation p%Exctn2GridParams%n(2:3) = 1_IntKi p%Exctn2GridParams%delta(2:3) = 0.0_SiKi @@ -2662,7 +2662,6 @@ SUBROUTINE CheckInitInput( InitInp, p, MnDriftData, NewmanAppData, DiffQTFData, p%Exctn2GridParams%pZero(4) = -Pi end if p%Exctn2GridParams%n(4) = p%NExctnHdg+1 - p%Exctn2GridParams%Z_depth = -1.0 ! Set to Z_depth to a negative value to indicate uniform "z" grid for platform heading !> 1. Check that we only specified one of MnDrift, NewmanApp, or DiffQTF diff --git a/modules/hydrodyn/src/WAMIT2.txt b/modules/hydrodyn/src/WAMIT2.txt index ad3ec0d6f3..9fa2e20306 100644 --- a/modules/hydrodyn/src/WAMIT2.txt +++ b/modules/hydrodyn/src/WAMIT2.txt @@ -14,6 +14,7 @@ # make sure that the file name does not have any trailing white spaces! include Registry_NWTC_Library.txt usefrom SeaSt_WaveField.txt +usefrom GridInterp.txt param WAMIT2/WAMIT2 unused INTEGER MaxWAMIT2Outputs - 6 - "" - @@ -50,7 +51,7 @@ typedef ^ ^ LOGICAL SumQTFF # e.g. indices for searching in an array, large arrays that are local variables in any routine called multiple times, etc. typedef ^ MiscVarType INTEGER LastIndWave : - - "Index for last interpolation step of 2nd order forces" - typedef ^ ^ ReKi F_Waves2 {:} - - "2nd order force from this timestep" - -typedef ^ ^ SeaSt_WaveField_MiscVarType WaveField_m - - - "misc var information from the SeaState Interpolation module" - +typedef ^ ^ GridInterp_MiscVarType WaveField_m - - - "misc var information from the Grid Interpolation module" - # ..... Parameters ................................................................................................................ # Define parameters here: @@ -61,7 +62,7 @@ typedef ^ ^ INTEGER NBodyMod #The 2nd order force time series grid typedef ^ ^ SiKi WaveExctn2Grid {:}{:}{:}{:}{:} - - "Grid of time series of the resulting 2nd order force (Index 1: Time, Index 2: x, Index 3: y, Index 4: platform heading, and Index 5: load component)" (N) -typedef ^ ^ SeaSt_WaveField_ParameterType Exctn2GridParams - - - "Parameters of WaveExctn2Grid" - +typedef ^ ^ GridInterp_ParameterType Exctn2GridParams - - - "Parameters of WaveExctn2Grid" - #Flags set for dimensions to use with each method (MnDrift, NewmanApp, etc). These are stored by method because .8 files that can be used in MnDrift or NewmanApp don't have some of the dimensions. typedef ^ ^ LOGICAL MnDriftDims {6} - - "Flags for which dimensions to calculate in MnDrift calculations" - diff --git a/modules/hydrodyn/src/WAMIT2_Types.f90 b/modules/hydrodyn/src/WAMIT2_Types.f90 index e4b8800d70..8cc965695e 100644 --- a/modules/hydrodyn/src/WAMIT2_Types.f90 +++ b/modules/hydrodyn/src/WAMIT2_Types.f90 @@ -65,7 +65,7 @@ MODULE WAMIT2_Types TYPE, PUBLIC :: WAMIT2_MiscVarType INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: LastIndWave !< Index for last interpolation step of 2nd order forces [-] REAL(ReKi) , DIMENSION(:), ALLOCATABLE :: F_Waves2 !< 2nd order force from this timestep [-] - TYPE(SeaSt_WaveField_MiscVarType) :: WaveField_m !< misc var information from the SeaState Interpolation module [-] + TYPE(GridInterp_MiscVarType) :: WaveField_m !< misc var information from the Grid Interpolation module [-] END TYPE WAMIT2_MiscVarType ! ======================= ! ========= WAMIT2_ParameterType ======= @@ -73,7 +73,7 @@ MODULE WAMIT2_Types INTEGER(IntKi) :: NBody = 0_IntKi !< [>=1; only used when PotMod=1. If NBodyMod=1, the WAMIT data contains a vector of size 6*NBody x 1 and matrices of size 6*NBody x 6*NBody; if NBodyMod>1, there are NBody sets of WAMIT data each with a vector of size 6 x 1 and matrices of size 6 x 6] [-] INTEGER(IntKi) :: NBodyMod = 0_IntKi !< Body coupling model {1: include coupling terms between each body and NBody in HydroDyn equals NBODY in WAMIT, 2: neglect coupling terms between each body and NBODY=1 with XBODY=0 in WAMIT, 3: Neglect coupling terms between each body and NBODY=1 with XBODY=/0 in WAMIT} (switch) [only used when PotMod=1] [-] REAL(SiKi) , DIMENSION(:,:,:,:,:), ALLOCATABLE :: WaveExctn2Grid !< Grid of time series of the resulting 2nd order force (Index 1: Time, Index 2: x, Index 3: y, Index 4: platform heading, and Index 5: load component) [(N)] - TYPE(SeaSt_WaveField_ParameterType) :: Exctn2GridParams !< Parameters of WaveExctn2Grid [-] + TYPE(GridInterp_ParameterType) :: Exctn2GridParams !< Parameters of WaveExctn2Grid [-] LOGICAL , DIMENSION(1:6) :: MnDriftDims = .false. !< Flags for which dimensions to calculate in MnDrift calculations [-] LOGICAL , DIMENSION(1:6) :: NewmanAppDims = .false. !< Flags for which dimensions to calculate in NewmanApp calculations [-] LOGICAL , DIMENSION(1:6) :: DiffQTFDims = .false. !< Flags for which dimensions to calculate in DiffQTF calculations [-] @@ -323,7 +323,7 @@ subroutine WAMIT2_CopyMisc(SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg) end if DstMiscData%F_Waves2 = SrcMiscData%F_Waves2 end if - call SeaSt_WaveField_CopyMisc(SrcMiscData%WaveField_m, DstMiscData%WaveField_m, CtrlCode, ErrStat2, ErrMsg2) + call GridInterp_CopyMisc(SrcMiscData%WaveField_m, DstMiscData%WaveField_m, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return end subroutine @@ -343,7 +343,7 @@ subroutine WAMIT2_DestroyMisc(MiscData, ErrStat, ErrMsg) if (allocated(MiscData%F_Waves2)) then deallocate(MiscData%F_Waves2) end if - call SeaSt_WaveField_DestroyMisc(MiscData%WaveField_m, ErrStat2, ErrMsg2) + call GridInterp_DestroyMisc(MiscData%WaveField_m, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) end subroutine @@ -354,7 +354,7 @@ subroutine WAMIT2_PackMisc(RF, Indata) if (RF%ErrStat >= AbortErrLev) return call RegPackAlloc(RF, InData%LastIndWave) call RegPackAlloc(RF, InData%F_Waves2) - call SeaSt_WaveField_PackMisc(RF, InData%WaveField_m) + call GridInterp_PackMisc(RF, InData%WaveField_m) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -368,7 +368,7 @@ subroutine WAMIT2_UnPackMisc(RF, OutData) if (RF%ErrStat /= ErrID_None) return call RegUnpackAlloc(RF, OutData%LastIndWave); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%F_Waves2); if (RegCheckErr(RF, RoutineName)) return - call SeaSt_WaveField_UnpackMisc(RF, OutData%WaveField_m) ! WaveField_m + call GridInterp_UnpackMisc(RF, OutData%WaveField_m) ! WaveField_m end subroutine subroutine WAMIT2_CopyParam(SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg) @@ -397,7 +397,7 @@ subroutine WAMIT2_CopyParam(SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMs end if DstParamData%WaveExctn2Grid = SrcParamData%WaveExctn2Grid end if - call SeaSt_WaveField_CopyParam(SrcParamData%Exctn2GridParams, DstParamData%Exctn2GridParams, CtrlCode, ErrStat2, ErrMsg2) + call GridInterp_CopyParam(SrcParamData%Exctn2GridParams, DstParamData%Exctn2GridParams, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return DstParamData%MnDriftDims = SrcParamData%MnDriftDims @@ -424,7 +424,7 @@ subroutine WAMIT2_DestroyParam(ParamData, ErrStat, ErrMsg) if (allocated(ParamData%WaveExctn2Grid)) then deallocate(ParamData%WaveExctn2Grid) end if - call SeaSt_WaveField_DestroyParam(ParamData%Exctn2GridParams, ErrStat2, ErrMsg2) + call GridInterp_DestroyParam(ParamData%Exctn2GridParams, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) end subroutine @@ -436,7 +436,7 @@ subroutine WAMIT2_PackParam(RF, Indata) call RegPack(RF, InData%NBody) call RegPack(RF, InData%NBodyMod) call RegPackAlloc(RF, InData%WaveExctn2Grid) - call SeaSt_WaveField_PackParam(RF, InData%Exctn2GridParams) + call GridInterp_PackParam(RF, InData%Exctn2GridParams) call RegPack(RF, InData%MnDriftDims) call RegPack(RF, InData%NewmanAppDims) call RegPack(RF, InData%DiffQTFDims) @@ -461,7 +461,7 @@ subroutine WAMIT2_UnPackParam(RF, OutData) call RegUnpack(RF, OutData%NBody); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%NBodyMod); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%WaveExctn2Grid); if (RegCheckErr(RF, RoutineName)) return - call SeaSt_WaveField_UnpackParam(RF, OutData%Exctn2GridParams) ! Exctn2GridParams + call GridInterp_UnpackParam(RF, OutData%Exctn2GridParams) ! Exctn2GridParams call RegUnpack(RF, OutData%MnDriftDims); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%NewmanAppDims); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%DiffQTFDims); if (RegCheckErr(RF, RoutineName)) return diff --git a/modules/hydrodyn/src/WAMIT_Interp.f90 b/modules/hydrodyn/src/WAMIT_Interp.f90 index 0777c2fe1c..a41191bcf4 100644 --- a/modules/hydrodyn/src/WAMIT_Interp.f90 +++ b/modules/hydrodyn/src/WAMIT_Interp.f90 @@ -29,8 +29,9 @@ MODULE WAMIT_Interp USE NWTC_Library - use SeaSt_WaveField_Types, only: SeaSt_WaveField_ParameterType, SeaSt_WaveField_MiscVarType - use SeaSt_WaveField, only: WaveField_Interp_Setup3D, WaveField_Interp_Setup4D + use GridInterp_Types, only: GridInterp_ParameterType, GridInterp_MiscVarType + use GridInterp, only: GridInterpSetup3D, GridInterpSetup4D, GridInterp3DVec6, GridInterp4DVec6 + IMPLICIT NONE PRIVATE @@ -654,30 +655,16 @@ function WAMIT_ForceWaves_Interp_3D_vec6(Time, pos, pKinXX, WF_p, WF_m, ErrStat3 real(DbKi), intent(in ) :: Time real(ReKi), intent(in ) :: pos(2) !< position real(SiKi), intent(in ) :: pKinXX(0:,:,:,:) !< 3D Wave excitation data (SiKi for storage space reasons) - type(SeaSt_WaveField_ParameterType), intent(in ) :: WF_p !< wavefield parameters - type(SeaSt_WaveField_MiscVarType), intent(inout) :: WF_m !< wavefield misc/optimization variables + type(GridInterp_ParameterType), intent(in ) :: WF_p !< Wave excitation grid parameters + type(GridInterp_MiscVarType), intent(inout) :: WF_m !< GridInterp misc/optimization variables integer(IntKi), intent( out) :: ErrStat3 character(*), intent( out) :: ErrMsg3 real(SiKi) :: WAMIT_ForceWaves_Interp_3D_vec6(6) - real(SiKi) :: u(8) - integer(IntKi) :: i - - ! get the bounding indices from the WaveField info (same indexing used in WAMIT) - call WaveField_Interp_Setup3D( Time, pos, WF_p, WF_m, ErrStat3, ErrMsg3 ) - - ! interpolate - do i = 1,6 - u(1) = pKinXX( WF_m%Indx_Lo(1), WF_m%Indx_Lo(2), WF_m%Indx_Lo(3), i ) - u(2) = pKinXX( WF_m%Indx_Hi(1), WF_m%Indx_Lo(2), WF_m%Indx_Lo(3), i ) - u(3) = pKinXX( WF_m%Indx_Lo(1), WF_m%Indx_Hi(2), WF_m%Indx_Lo(3), i ) - u(4) = pKinXX( WF_m%Indx_Hi(1), WF_m%Indx_Hi(2), WF_m%Indx_Lo(3), i ) - u(5) = pKinXX( WF_m%Indx_Lo(1), WF_m%Indx_Lo(2), WF_m%Indx_Hi(3), i ) - u(6) = pKinXX( WF_m%Indx_Hi(1), WF_m%Indx_Lo(2), WF_m%Indx_Hi(3), i ) - u(7) = pKinXX( WF_m%Indx_Lo(1), WF_m%Indx_Hi(2), WF_m%Indx_Hi(3), i ) - u(8) = pKinXX( WF_m%Indx_Hi(1), WF_m%Indx_Hi(2), WF_m%Indx_Hi(3), i ) - WAMIT_ForceWaves_Interp_3D_vec6(i) = dot_product(WF_m%N3D, u) - end do + + call GridInterpSetup3D( (/Real(Time,ReKi),pos(1),pos(2)/), WF_p, WF_m, ErrStat3, ErrMsg3 ) + WAMIT_ForceWaves_Interp_3D_vec6 = GridInterp3DVec6( pKinXX, WF_m ) + end function @@ -687,38 +674,16 @@ function WAMIT_ForceWaves_Interp_4D_vec6(Time, pos, pKinXX, WF_p, WF_m, ErrStat3 real(DbKi), intent(in ) :: Time real(ReKi), intent(in ) :: pos(3) !< position real(SiKi), intent(in ) :: pKinXX(0:,:,:,:,:) !< 4D Wave excitation data (SiKi for storage space reasons) - type(SeaSt_WaveField_ParameterType), intent(in ) :: WF_p !< wavefield parameters - type(SeaSt_WaveField_MiscVarType), intent(inout) :: WF_m !< wavefield misc/optimization variables + type(GridInterp_ParameterType), intent(in ) :: WF_p !< Wave excitation grid parameters + type(GridInterp_MiscVarType), intent(inout) :: WF_m !< GridInterp misc/optimization variables integer(IntKi), intent( out) :: ErrStat3 character(*), intent( out) :: ErrMsg3 real(SiKi) :: WAMIT_ForceWaves_Interp_4D_vec6(6) - real(SiKi) :: u(16) - integer(IntKi) :: i - - ! get the bounding indices from the WaveField info (same indexing used in WAMIT) - call WaveField_Interp_Setup4D( Time, pos, WF_p, WF_m, ErrStat3, ErrMsg3 ) - - ! interpolate - do i = 1,6 - u( 1) = pKinXX( WF_m%Indx_Lo(1), WF_m%Indx_Lo(2), WF_m%Indx_Lo(3), WF_m%Indx_Lo(4), i ) - u( 2) = pKinXX( WF_m%Indx_Hi(1), WF_m%Indx_Lo(2), WF_m%Indx_Lo(3), WF_m%Indx_Lo(4), i ) - u( 3) = pKinXX( WF_m%Indx_Lo(1), WF_m%Indx_Hi(2), WF_m%Indx_Lo(3), WF_m%Indx_Lo(4), i ) - u( 4) = pKinXX( WF_m%Indx_Hi(1), WF_m%Indx_Hi(2), WF_m%Indx_Lo(3), WF_m%Indx_Lo(4), i ) - u( 5) = pKinXX( WF_m%Indx_Lo(1), WF_m%Indx_Lo(2), WF_m%Indx_Hi(3), WF_m%Indx_Lo(4), i ) - u( 6) = pKinXX( WF_m%Indx_Hi(1), WF_m%Indx_Lo(2), WF_m%Indx_Hi(3), WF_m%Indx_Lo(4), i ) - u( 7) = pKinXX( WF_m%Indx_Lo(1), WF_m%Indx_Hi(2), WF_m%Indx_Hi(3), WF_m%Indx_Lo(4), i ) - u( 8) = pKinXX( WF_m%Indx_Hi(1), WF_m%Indx_Hi(2), WF_m%Indx_Hi(3), WF_m%Indx_Lo(4), i ) - u( 9) = pKinXX( WF_m%Indx_Lo(1), WF_m%Indx_Lo(2), WF_m%Indx_Lo(3), WF_m%Indx_Hi(4), i ) - u(10) = pKinXX( WF_m%Indx_Hi(1), WF_m%Indx_Lo(2), WF_m%Indx_Lo(3), WF_m%Indx_Hi(4), i ) - u(11) = pKinXX( WF_m%Indx_Lo(1), WF_m%Indx_Hi(2), WF_m%Indx_Lo(3), WF_m%Indx_Hi(4), i ) - u(12) = pKinXX( WF_m%Indx_Hi(1), WF_m%Indx_Hi(2), WF_m%Indx_Lo(3), WF_m%Indx_Hi(4), i ) - u(13) = pKinXX( WF_m%Indx_Lo(1), WF_m%Indx_Lo(2), WF_m%Indx_Hi(3), WF_m%Indx_Hi(4), i ) - u(14) = pKinXX( WF_m%Indx_Hi(1), WF_m%Indx_Lo(2), WF_m%Indx_Hi(3), WF_m%Indx_Hi(4), i ) - u(15) = pKinXX( WF_m%Indx_Lo(1), WF_m%Indx_Hi(2), WF_m%Indx_Hi(3), WF_m%Indx_Hi(4), i ) - u(16) = pKinXX( WF_m%Indx_Hi(1), WF_m%Indx_Hi(2), WF_m%Indx_Hi(3), WF_m%Indx_Hi(4), i ) - WAMIT_ForceWaves_Interp_4D_vec6(i) = dot_product(WF_m%N4D, u) - end do + + call GridInterpSetup4D( (/Real(Time,ReKi),pos(1),pos(2),pos(3)/), WF_p, WF_m, ErrStat3, ErrMsg3 ) + WAMIT_ForceWaves_Interp_4D_vec6 = GridInterp4DVec6( pKinXX, WF_m ) + end function diff --git a/modules/hydrodyn/src/WAMIT_Types.f90 b/modules/hydrodyn/src/WAMIT_Types.f90 index 930eab2644..251a720b0b 100644 --- a/modules/hydrodyn/src/WAMIT_Types.f90 +++ b/modules/hydrodyn/src/WAMIT_Types.f90 @@ -109,7 +109,7 @@ MODULE WAMIT_Types TYPE(Conv_Rdtn_MiscVarType) :: Conv_Rdtn !< [-] TYPE(Conv_Rdtn_InputType) :: Conv_Rdtn_u !< [-] TYPE(Conv_Rdtn_OutputType) :: Conv_Rdtn_y !< [-] - TYPE(SeaSt_WaveField_MiscVarType) :: WaveField_m !< misc var information from the SeaState Interpolation module [-] + TYPE(GridInterp_MiscVarType) :: WaveField_m !< misc var information from the Grid Interpolation module [-] END TYPE WAMIT_MiscVarType ! ======================= ! ========= WAMIT_ParameterType ======= @@ -133,7 +133,7 @@ MODULE WAMIT_Types REAL(DbKi) :: DT = 0.0_R8Ki !< [-] TYPE(SeaSt_WaveFieldType) , POINTER :: WaveField => NULL() !< Pointer to wave field [-] INTEGER(IntKi) :: PtfmYMod = 0_IntKi !< Large yaw model [-] - TYPE(SeaSt_WaveField_ParameterType) :: ExctnGridParams !< Parameters of WaveExctnGrid [-] + TYPE(GridInterp_ParameterType) :: ExctnGridParams !< Parameters of WaveExctnGrid [-] END TYPE WAMIT_ParameterType ! ======================= ! ========= WAMIT_InputType ======= @@ -749,7 +749,7 @@ subroutine WAMIT_CopyMisc(SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg) call Conv_Rdtn_CopyOutput(SrcMiscData%Conv_Rdtn_y, DstMiscData%Conv_Rdtn_y, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return - call SeaSt_WaveField_CopyMisc(SrcMiscData%WaveField_m, DstMiscData%WaveField_m, CtrlCode, ErrStat2, ErrMsg2) + call GridInterp_CopyMisc(SrcMiscData%WaveField_m, DstMiscData%WaveField_m, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return end subroutine @@ -793,7 +793,7 @@ subroutine WAMIT_DestroyMisc(MiscData, ErrStat, ErrMsg) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) call Conv_Rdtn_DestroyOutput(MiscData%Conv_Rdtn_y, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - call SeaSt_WaveField_DestroyMisc(MiscData%WaveField_m, ErrStat2, ErrMsg2) + call GridInterp_DestroyMisc(MiscData%WaveField_m, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) end subroutine @@ -816,7 +816,7 @@ subroutine WAMIT_PackMisc(RF, Indata) call Conv_Rdtn_PackMisc(RF, InData%Conv_Rdtn) call Conv_Rdtn_PackInput(RF, InData%Conv_Rdtn_u) call Conv_Rdtn_PackOutput(RF, InData%Conv_Rdtn_y) - call SeaSt_WaveField_PackMisc(RF, InData%WaveField_m) + call GridInterp_PackMisc(RF, InData%WaveField_m) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -842,7 +842,7 @@ subroutine WAMIT_UnPackMisc(RF, OutData) call Conv_Rdtn_UnpackMisc(RF, OutData%Conv_Rdtn) ! Conv_Rdtn call Conv_Rdtn_UnpackInput(RF, OutData%Conv_Rdtn_u) ! Conv_Rdtn_u call Conv_Rdtn_UnpackOutput(RF, OutData%Conv_Rdtn_y) ! Conv_Rdtn_y - call SeaSt_WaveField_UnpackMisc(RF, OutData%WaveField_m) ! WaveField_m + call GridInterp_UnpackMisc(RF, OutData%WaveField_m) ! WaveField_m end subroutine subroutine WAMIT_CopyParam(SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg) @@ -937,7 +937,7 @@ subroutine WAMIT_CopyParam(SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg DstParamData%DT = SrcParamData%DT DstParamData%WaveField => SrcParamData%WaveField DstParamData%PtfmYMod = SrcParamData%PtfmYMod - call SeaSt_WaveField_CopyParam(SrcParamData%ExctnGridParams, DstParamData%ExctnGridParams, CtrlCode, ErrStat2, ErrMsg2) + call GridInterp_CopyParam(SrcParamData%ExctnGridParams, DstParamData%ExctnGridParams, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return end subroutine @@ -973,7 +973,7 @@ subroutine WAMIT_DestroyParam(ParamData, ErrStat, ErrMsg) call SS_Exc_DestroyParam(ParamData%SS_Exctn, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) nullify(ParamData%WaveField) - call SeaSt_WaveField_DestroyParam(ParamData%ExctnGridParams, ErrStat2, ErrMsg2) + call GridInterp_DestroyParam(ParamData%ExctnGridParams, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) end subroutine @@ -1008,7 +1008,7 @@ subroutine WAMIT_PackParam(RF, Indata) end if end if call RegPack(RF, InData%PtfmYMod) - call SeaSt_WaveField_PackParam(RF, InData%ExctnGridParams) + call GridInterp_PackParam(RF, InData%ExctnGridParams) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -1058,7 +1058,7 @@ subroutine WAMIT_UnPackParam(RF, OutData) OutData%WaveField => null() end if call RegUnpack(RF, OutData%PtfmYMod); if (RegCheckErr(RF, RoutineName)) return - call SeaSt_WaveField_UnpackParam(RF, OutData%ExctnGridParams) ! ExctnGridParams + call GridInterp_UnpackParam(RF, OutData%ExctnGridParams) ! ExctnGridParams end subroutine subroutine WAMIT_CopyInput(SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg) diff --git a/modules/inflowwind/src/IfW_C_Binding.f90 b/modules/inflowwind/src/IfW_C_Binding.f90 index b3bc732f67..79df59b106 100644 --- a/modules/inflowwind/src/IfW_C_Binding.f90 +++ b/modules/inflowwind/src/IfW_C_Binding.f90 @@ -19,18 +19,23 @@ !********************************************************************************************************************************** MODULE InflowWind_C_BINDING - USE ISO_C_BINDING - USE InflowWind - USE InflowWind_Subs, only: MaxOutPts - USE InflowWind_Types - USE NWTC_Library - USE VersionInfo + use ISO_C_BINDING + use IfW_FlowField, only: IfW_FlowField_GetVelAcc + use InflowWind + use InflowWind_Subs, only: MaxOutPts + use InflowWind_Types + use NWTC_Library + use VersionInfo + use NWTC_C_Binding, only: ErrMsgLen_C, IntfStrLen, SetErrStat_F2C IMPLICIT NONE PUBLIC :: IfW_C_Init PUBLIC :: IfW_C_CalcOutput PUBLIC :: IfW_C_End + PUBLIC :: IfW_C_GetFlowFieldPointer + PUBLIC :: IfW_C_SetFlowFieldPointer + PUBLIC :: IfW_C_GetWindVel !------------------------------------------------------------------------------------ ! Version info for display @@ -58,43 +63,14 @@ MODULE InflowWind_C_BINDING type(InflowWind_OutputType) :: y !< Initial output (outputs are not calculated; only the output mesh is initialized) type(InflowWind_MiscVarType) :: m !< Misc variables for optimization (not copied in glue code) - !------------------------------------------------------------------------------------ - ! Error handling - ! This must exactly match the value in the python-lib. If ErrMsgLen changes at - ! some point in the nwtc-library, this should be updated, but the logic exists - ! to correctly handle different lengths of the strings - integer(IntKi), parameter :: ErrMsgLen_C = 1025 - integer(IntKi), parameter :: IntfStrLen = 1025 ! length of other strings through the C interface - - - CONTAINS -!> This routine sets the error status in C_CHAR for export to calling code. -!! Make absolutely certain that we do not overrun the end of ErrMsg_C. That is hard coded to 1025, -!! but ErrMsgLen is set in the nwtc_library, and could change without updates here. We don't want an -!! inadvertant buffer overrun -- that can lead to bad things. -subroutine SetErr(ErrStat, ErrMsg, ErrStat_C, ErrMsg_C) - integer, intent(in ) :: ErrStat !< aggregated error message (fortran type) - character(ErrMsgLen), intent(in ) :: ErrMsg !< aggregated error message (fortran type) - integer(c_int), intent( out) :: ErrStat_C - character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) - ErrStat_C = ErrStat ! We will send back the same error status that is used in OpenFAST - if (ErrMsgLen > ErrMsgLen_C-1) then ! If ErrMsgLen is > the space in ErrMsg_C, do not copy everything over - ErrMsg_C = TRANSFER( trim(ErrMsg(1:ErrMsgLen_C-1))//C_NULL_CHAR, ErrMsg_C ) - else - ErrMsg_C = TRANSFER( trim(ErrMsg)//C_NULL_CHAR, ErrMsg_C ) - endif -end subroutine SetErr - - !=============================================================================================================== !--------------------------------------------- IFW INIT -------------------------------------------------------- !=============================================================================================================== -SUBROUTINE IfW_C_Init(IfWinputFilePassed, IfWinputFileString_C, IfWinputFileStringLength_C, OutRootName_C, & - NumWindPts_C, DT_C, DebugLevel_in, NumChannels_C, OutputChannelNames_C, OutputChannelUnits_C, & +SUBROUTINE IfW_C_Init(IfWinputFilePassed, IfWinputFileString_C, IfWinputFileStringLength_C, OutRootName_C, & + NumWindPts_C, DT_C, DebugLevel_in, NumChannels_C, OutputChannelNames_C, OutputChannelUnits_C, & ErrStat_C, ErrMsg_C) BIND (C, NAME='IfW_C_Init') - IMPLICIT NONE #ifndef IMPLICIT_DLLEXPORT !DEC$ ATTRIBUTES DLLEXPORT :: IfW_C_Init !GCC$ ATTRIBUTES DLLEXPORT :: IfW_C_Init @@ -129,6 +105,9 @@ SUBROUTINE IfW_C_Init(IfWinputFilePassed, IfWinputFileString_C, IfWinputFileStri ErrStat = ErrID_None ErrMsg = "" + ! clear out any leftover memory that might be allocated from a previous call + call MemClear(ErrStat2, ErrMsg2); if (Failed()) return + CALL NWTC_Init( ProgNameIn=version%Name ) CALL DispCopyrightLicense( version%Name ) CALL DispCompileRuntimeInfo( version%Name ) @@ -160,9 +139,7 @@ SUBROUTINE IfW_C_Init(IfWinputFilePassed, IfWinputFileString_C, IfWinputFileStri endif ! For debugging the interface: - if (DebugLevel > 0) then - call ShowPassedData() - endif + if (DebugLevel > 0) call ShowPassedData() ! Get fortran pointer to C_NULL_CHAR deliniated input file as a string CALL C_F_pointer(IfWinputFileString_C, IfWinputFileString) @@ -217,7 +194,8 @@ SUBROUTINE IfW_C_Init(IfWinputFilePassed, IfWinputFileString_C, IfWinputFileStri call Cleanup() - call SetErr(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + call SetErrStat_F2C(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + CONTAINS logical function Failed() @@ -225,7 +203,7 @@ logical function Failed() Failed = ErrStat >= AbortErrLev if (Failed) then call Cleanup() - call SetErr(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + call SetErrStat_F2C(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) endif end function Failed subroutine Cleanup() ! NOTE: we are ignoring any error reporting from here @@ -238,8 +216,7 @@ subroutine ShowPassedData() integer :: i,j call WrSCr("") call WrScr("-----------------------------------------------------------") - call WrScr("Interface debugging: Variables passed in through interface") - call WrScr(" IfW_C_Init") + call WrScr("Interface debugging: IfW_C_Init") call WrScr(" --------------------------------------------------------") call WrScr(" FileInfo") TmpFlag="F"; if (IfWinputFilePassed==1_c_int) TmpFlag="T" @@ -260,15 +237,14 @@ END SUBROUTINE IfW_C_Init !--------------------------------------------- IFW CALCOUTPUT -------------------------------------------------- !=============================================================================================================== -SUBROUTINE IfW_C_CalcOutput(Time_C,Positions_C,Velocities_C,OutputChannelValues_C,ErrStat_C,ErrMsg_C) BIND (C, NAME='IfW_C_CalcOutput') - IMPLICIT NONE +SUBROUTINE IfW_C_CalcOutput(Time_C,Pos_C,Vel_C,OutputChannelValues_C,ErrStat_C,ErrMsg_C) BIND (C, NAME='IfW_C_CalcOutput') #ifndef IMPLICIT_DLLEXPORT !DEC$ ATTRIBUTES DLLEXPORT :: IfW_C_CalcOutput !GCC$ ATTRIBUTES DLLEXPORT :: IfW_C_CalcOutput #endif REAL(C_DOUBLE) , INTENT(IN ) :: Time_C - REAL(C_FLOAT) , INTENT(IN ) :: Positions_C(3*InitInp%NumWindPoints) - REAL(C_FLOAT) , INTENT( OUT) :: Velocities_C(3*InitInp%NumWindPoints) + REAL(C_FLOAT) , INTENT(IN ) :: Pos_C(3*InitInp%NumWindPoints) + REAL(C_FLOAT) , INTENT( OUT) :: Vel_C(3*InitInp%NumWindPoints) REAL(C_FLOAT) , INTENT( OUT) :: OutputChannelValues_C(p%NumOuts) INTEGER(C_INT) , INTENT( OUT) :: ErrStat_C CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: ErrMsg_C(ErrMsgLen_C) @@ -285,52 +261,238 @@ SUBROUTINE IfW_C_CalcOutput(Time_C,Positions_C,Velocities_C,OutputChannelValues_ ErrStat = ErrID_None ErrMsg = "" + ! Interface debugging + if (DebugLevel > 0) call ShowPassedData() + ! Convert the inputs from C to Fortran Time = REAL(Time_C,DbKi) - InputData%PositionXYZ = reshape( real(Positions_C,ReKi), (/3, InitInp%NumWindPoints/) ) + InputData%PositionXYZ = reshape( real(Pos_C,ReKi), (/3, InitInp%NumWindPoints/) ) ! Call the main subroutine InflowWind_CalcOutput to get the velocities CALL InflowWind_CalcOutput( Time, InputData, p, ContStates, DiscStates, ConstrStates, OtherStates, y, m, ErrStat2, ErrMsg2 ) if (Failed()) return ! Get velocities out of y and flatten them (still in same spot in memory) - Velocities_C = reshape( REAL(y%VelocityUVW, C_FLOAT), (/3*InitInp%NumWindPoints/) ) ! VelocityUVW is 2D array of ReKi (might need reshape or make into pointer); size [3,N] + Vel_C = reshape( REAL(y%VelocityUVW, C_FLOAT), (/3*InitInp%NumWindPoints/) ) ! VelocityUVW is 2D array of ReKi (might need reshape or make into pointer); size [3,N] + + ! Interface debugging + if (DebugLevel > 0) call ShowReturnData() ! Get the output channel info out of y OutputChannelValues_C = REAL(y%WriteOutput, C_FLOAT) - call SetErr(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + call SetErrStat_F2C(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) CONTAINS logical function Failed() CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) Failed = ErrStat >= AbortErrLev - if (Failed) call SetErr(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + if (Failed) call SetErrStat_F2C(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) end function Failed + !> This subroutine prints out all the variables that are passed in. Use this only + !! for debugging the interface on the Fortran side. + subroutine ShowPassedData() + integer(IntKi) :: i + character(4) :: TmpCh + call WrSCr("") + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: IfW_C_CalcOutput") + call WrScr(" --------------------------------------------------------") + call WrScr(" Time_C -> "//trim(Num2LStr(Time_C))) + do i=1,InitInp%NumWindPoints + write(TmpCh, '(i4)') i + call WrScr(" Pos_C("//TmpCh//") -> ("//trim(Num2LStr(Pos_C((i-1)*3+1)))//","//trim(Num2LStr(Pos_C((i-1)*3+2)))//","//trim(Num2LStr(Pos_C((i-1)*3+3)))//")") + enddo + end subroutine ShowPassedData + subroutine ShowReturnData() + integer(IntKi) :: i + character(4) :: TmpCh + do i=1,InitInp%NumWindPoints + call WrScr(" Vel_C("//TmpCh//") <- ("//trim(Num2LStr(Vel_C((i-1)*3+1)))//","//trim(Num2LStr(Vel_C((i-1)*3+2)))//","//trim(Num2LStr(Vel_C((i-1)*3+3)))//")") + enddo + call WrScr("-----------------------------------------------------------") + end subroutine ShowReturnData END SUBROUTINE IfW_C_CalcOutput !=============================================================================================================== !--------------------------------------------------- IFW END --------------------------------------------------- !=============================================================================================================== - -SUBROUTINE IfW_C_End(ErrStat_C,ErrMsg_C) BIND (C, NAME='IfW_C_End') - IMPLICIT NONE +subroutine IfW_C_End(ErrStat_C,ErrMsg_C) BIND (C, NAME='IfW_C_End') #ifndef IMPLICIT_DLLEXPORT !DEC$ ATTRIBUTES DLLEXPORT :: IfW_C_End !GCC$ ATTRIBUTES DLLEXPORT :: IfW_C_End #endif - INTEGER(C_INT) , INTENT( OUT) :: ErrStat_C - CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: ErrMsg_C(ErrMsgLen_C) + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer :: ErrStat,ErrStat2 + character(ErrMsgLen) :: ErrMsg,ErrMsg2 + character(*), parameter :: RoutineName = 'IfW_C_End' - ! Local variables - INTEGER :: ErrStat - CHARACTER(ErrMsgLen) :: ErrMsg + ErrStat = ErrID_None + ErrMsg = "" ! Call the main subroutine InflowWind_End - CALL InflowWind_End( InputData, p, ContStates, DiscStates, ConstrStates, OtherStates, y, m, ErrStat, ErrMsg ) + call InflowWind_End( InputData, p, ContStates, DiscStates, ConstrStates, OtherStates, y, m, ErrStat2, ErrMsg2 ) + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + + ! Clear extra memory within library + call MemClear(ErrStat2, ErrMsg2) + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + + call SetErrStat_F2C(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) +end subroutine IfW_C_End + + +!> basic routine to get the wind velocity at a single point in time and space +subroutine IfW_C_GetWindVel(Time_C,Pos_C,Vel_C,ErrStat_C,ErrMsg_C) BIND (C, NAME='IfW_C_GetWindVel') + real(c_double), intent(in ) :: Time_C + real(c_float), intent(in ) :: Pos_C(3) + real(c_float), intent( out) :: Vel_C(3) + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + real(dbki) :: Time + integer :: ErrStat, ErrStat2 + character(ErrMsgLen) :: ErrMsg, ErrMsg2 + character(*), parameter :: RoutineName = 'IfW_C_GetWindVel' + integer(intKi) :: StartNode + real(ReKi) :: Pos(3,1), Vel(3,1), PosOffset(3) + real(ReKi), allocatable :: NoAcc(:,:) + + ErrStat = ErrID_None + ErrMsg = "" + + ! Interface debugging + if (DebugLevel > 0) call ShowPassedData() + + if (.not. associated(p%FlowField)) then + ErrStat = ErrID_Fatal + ErrMsg = "Invalid pointer to FlowField data. Is the data initialized?" + Vel_C = 0.0_c_float + endif + + ! Initialize node. Since this is standalone, set to 1. + StartNode = 1 + ! no offset + PosOffset = 0.0_ReKi + + ! Convert the inputs from C to Fortran + Time = REAL(Time_C,DbKi) + Pos(1:3,1) = real(Pos_C,ReKi) + + ! call wind routine to get single point velocity + call IfW_FlowField_GetVelAcc(p%FlowField, StartNode, Time, Pos, Vel, NoAcc, ErrStat2, ErrMsg2) + if (Failed()) return + Vel_C = real(Vel(1:3,1), c_float) + + ! Interface debugging + if (DebugLevel > 0) call ShowReturnData() + + call SetErrStat_F2C(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) +contains + logical function Failed() + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + Failed = ErrStat >= AbortErrLev + if (Failed) call SetErrStat_F2C(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + end function Failed + !> This subroutine prints out all the variables that are passed in. Use this only + !! for debugging the interface on the Fortran side. + subroutine ShowPassedData() + call WrSCr("") + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: IfW_C_GetWindVel") + call WrScr(" --------------------------------------------------------") + call WrScr(" Time_C -> "//trim(Num2LStr(Time_C))) + call WrScr(" Pos_C -> ("//trim(Num2LStr(Pos_C(1)))//","//trim(Num2LStr(Pos_C(2)))//","//trim(Num2LStr(Pos_C(3)))//")") + end subroutine ShowPassedData + subroutine ShowReturnData() + call WrScr(" Vel_C <- ("//trim(Num2LStr(Vel_C(1)))//","//trim(Num2LStr(Vel_C(2)))//","//trim(Num2LStr(Vel_C(3)))//")") + call WrScr("-----------------------------------------------------------") + end subroutine ShowReturnData +end subroutine IfW_C_GetWindVel + + +!> clear local memory that isn't stored in `_End` routine +subroutine MemClear(ErrStat,ErrMsg) + integer, intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + call InflowWind_DestroyInitInput( InitInp, ErrStat, ErrMsg) + call InflowWind_DestroyInitOutput(InitOutData, ErrStat, ErrMsg) +end subroutine MemClear + + + +!> return the pointer to the WaveField data +subroutine IfW_C_GetFlowFieldPointer(FlowFieldPointer_C,ErrStat_C,ErrMsg_C) BIND (C, NAME='IfW_C_GetFlowFieldPointer') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: IfW_C_GetFlowFieldPointer +!GCC$ ATTRIBUTES DLLEXPORT :: IfW_C_GetFlowFieldPointer +#endif + type(c_ptr), intent( out) :: FlowFieldPointer_C + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer :: ErrStat + character(ErrMsgLen) :: ErrMsg + character(*), parameter :: RoutineName = 'IfW_C_GetFlowFieldPointer' + ErrStat = ErrID_None + ErrMSg = "" + if (associated(p%FlowField)) then + FlowFieldPointer_C = C_LOC(p%FlowField) + else + FlowFieldPointer_C = C_NULL_PTR + call SetErrStat(ErrID_Fatal,"Pointer to FlowField data not valid: data not initialized",ErrStat,ErrMsg,RoutineName) + endif + call SetErrStat_F2C( ErrStat, ErrMsg, ErrStat_C, ErrMsg_C ) + if (DebugLevel > 1) call ShowPassedData() + return +contains + subroutine ShowPassedData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: IfW_C_GetFlowFieldPointer") + call WrScr(" --------------------------------------------------------") + call WrScr(" FlowFieldPointer_C -> "//trim(Num2LStr(loc(p%FlowField)))) + call WrScr("-----------------------------------------------------------") + end subroutine ShowPassedData +end subroutine + + +!FIXME: this will require changes to IfW_C_Init to instantiate an empty IfW instance +! so before exposing this publicly, the initialization should be updated. +!> set the pointer to the FlowField data +subroutine IfW_C_SetFlowFieldPointer(FlowFieldPointer_C,ErrStat_C,ErrMsg_C) BIND (C, NAME='IfW_C_SetFlowFieldPointer') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: IfW_C_SetFlowFieldPointer +!GCC$ ATTRIBUTES DLLEXPORT :: IfW_C_SetFlowFieldPointer +#endif + type(c_ptr), intent(in ) :: FlowFieldPointer_C + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer :: ErrStat + character(ErrMsgLen) :: ErrMsg + character(*), parameter :: RoutineName = 'IfW_C_SetFlowFieldPointer' + ErrStat = ErrID_None + ErrMSg = "" + call C_F_POINTER(FlowFieldPointer_C, p%FlowField) + if (associated(p%FlowField)) then + ! basic sanity check + if (p%FlowField%FieldType <= 0_IntKi) then + call SetErrStat(ErrID_Fatal,"Invalid pointer passed in, or FlowField not initialized",ErrStat,ErrMsg,RoutineName) + endif + else + call SetErrStat(ErrID_Fatal,"Invalid pointer passed in, or FlowField not initialized",ErrStat,ErrMsg,RoutineName) + endif + call SetErrStat_F2C( ErrStat, ErrMsg, ErrStat_C, ErrMsg_C ) + if (DebugLevel > 1) call ShowPassedData() + return +contains + subroutine ShowPassedData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: IfW_C_SetFlowFieldPointer") + call WrScr(" --------------------------------------------------------") + call WrScr(" FlowFieldPointer_C <- "//trim(Num2LStr(loc(p%FlowField)))) + call WrScr("-----------------------------------------------------------") + end subroutine ShowPassedData +end subroutine - call SetErr(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) -END SUBROUTINE IfW_C_End END MODULE diff --git a/modules/inflowwind/src/IfW_FlowField.f90 b/modules/inflowwind/src/IfW_FlowField.f90 index 586452aa40..4f3334bbe0 100644 --- a/modules/inflowwind/src/IfW_FlowField.f90 +++ b/modules/inflowwind/src/IfW_FlowField.f90 @@ -1590,7 +1590,7 @@ subroutine Grid4DField_GetVel(G4D, Time, Position, Velocity, ErrStat, ErrMsg) real(ReKi) :: P(3, 16) ! Point values real(ReKi) :: tmp integer(IntKi) :: i - character(60) :: PtLoc + character(60) :: PtLoc, BoxLL, BoxUR ErrStat = ErrID_None ErrMsg = "" @@ -1627,12 +1627,16 @@ subroutine Grid4DField_GetVel(G4D, Time, Position, Velocity, ErrStat, ErrMsg) do i = 1, 4 if (Indx_Lo(i) <= 0) then Indx_Lo(i) = 1 - write(PtLoc,'(A1,3(f8.2,A1))') '(',Position(1),',',Position(2),',',Position(3),')' - call SetErrStat(ErrID_Fatal, 'Outside the grid bounds: '//trim(PtLoc), ErrStat, ErrMsg, RoutineName) + write(PtLoc, '(A1,3(f8.2,A1))') '(',Position(1),',',Position(2),',',Position(3),')' + write(BoxLL, '(A1,3(f8.2,A1))') '(',G4D%pZero(1),',',G4D%pZero(2),',',G4D%pZero(3),')' + write(BoxUR, '(A1,3(f8.2,A1))') '(',G4D%pZero(1)+(G4D%n(1)-1)*G4D%delta(1),',',G4D%pZero(2)+(G4D%n(2)-1)*G4D%delta(2),',',G4D%pZero(3)+(G4D%n(3)-1)*G4D%delta(3),')' + call SetErrStat(ErrID_Fatal, 'Outside the grid bounds: '//trim(PtLoc)//'; box bounds: '//trim(BoxLL)//' to '//trim(BoxUR), ErrStat, ErrMsg, RoutineName) return elseif (Indx_Lo(i) >= G4D%n(i)) then - write(PtLoc,'(A1,3(f8.2,A1))') '(',Position(1),',',Position(2),',',Position(3),')' - call SetErrStat(ErrID_Fatal, 'Outside the grid bounds: '//trim(PtLoc), ErrStat, ErrMsg, RoutineName) + write(PtLoc, '(A1,3(f8.2,A1))') '(',Position(1),',',Position(2),',',Position(3),')' + write(BoxLL, '(A1,3(f8.2,A1))') '(',G4D%pZero(1),',',G4D%pZero(2),',',G4D%pZero(3),')' + write(BoxUR, '(A1,3(f8.2,A1))') '(',G4D%pZero(1)+(G4D%n(1)-1)*G4D%delta(1),',',G4D%pZero(2)+(G4D%n(2)-1)*G4D%delta(2),',',G4D%pZero(3)+(G4D%n(3)-1)*G4D%delta(3),')' + call SetErrStat(ErrID_Fatal, 'Outside the grid bounds: '//trim(PtLoc)//'; box bounds: '//trim(BoxLL)//' to '//trim(BoxUR), ErrStat, ErrMsg, RoutineName) return end if Indx_Hi(i) = min(Indx_Lo(i) + 1, G4D%n(i)) ! make sure it's a valid index diff --git a/modules/map/src/MAP_Types.f90 b/modules/map/src/MAP_Types.f90 index 88b811d4dc..01791f82a9 100644 --- a/modules/map/src/MAP_Types.f90 +++ b/modules/map/src/MAP_Types.f90 @@ -314,11 +314,6 @@ subroutine MAP_PackInitInput(RF, Indata) type(MAP_InitInputType), intent(in) :: InData character(*), parameter :: RoutineName = 'MAP_PackInitInput' if (RF%ErrStat >= AbortErrLev) return - if (c_associated(InData%C_obj%object)) then - RF%ErrStat = ErrID_Fatal - RF%ErrMsg = RoutineName//': C_obj%object cannot be packed.' - return - end if call RegPack(RF, InData%gravity) call RegPack(RF, InData%sea_density) call RegPack(RF, InData%depth) @@ -489,11 +484,6 @@ subroutine MAP_PackInitOutput(RF, Indata) type(MAP_InitOutputType), intent(in) :: InData character(*), parameter :: RoutineName = 'MAP_PackInitOutput' if (RF%ErrStat >= AbortErrLev) return - if (c_associated(InData%C_obj%object)) then - RF%ErrStat = ErrID_Fatal - RF%ErrMsg = RoutineName//': C_obj%object cannot be packed.' - return - end if call RegPack(RF, InData%progName) call RegPack(RF, InData%version) call RegPack(RF, InData%compilingData) @@ -591,11 +581,6 @@ subroutine MAP_PackContState(RF, Indata) type(MAP_ContinuousStateType), intent(in) :: InData character(*), parameter :: RoutineName = 'MAP_PackContState' if (RF%ErrStat >= AbortErrLev) return - if (c_associated(InData%C_obj%object)) then - RF%ErrStat = ErrID_Fatal - RF%ErrMsg = RoutineName//': C_obj%object cannot be packed.' - return - end if call RegPack(RF, InData%dummy) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -672,11 +657,6 @@ subroutine MAP_PackDiscState(RF, Indata) type(MAP_DiscreteStateType), intent(in) :: InData character(*), parameter :: RoutineName = 'MAP_PackDiscState' if (RF%ErrStat >= AbortErrLev) return - if (c_associated(InData%C_obj%object)) then - RF%ErrStat = ErrID_Fatal - RF%ErrMsg = RoutineName//': C_obj%object cannot be packed.' - return - end if call RegPack(RF, InData%dummy) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -1090,11 +1070,6 @@ subroutine MAP_PackOtherState(RF, Indata) character(*), parameter :: RoutineName = 'MAP_PackOtherState' logical :: PtrInIndex if (RF%ErrStat >= AbortErrLev) return - if (c_associated(InData%C_obj%object)) then - RF%ErrStat = ErrID_Fatal - RF%ErrMsg = RoutineName//': C_obj%object cannot be packed.' - return - end if call RegPackPtr(RF, InData%H) call RegPackPtr(RF, InData%V) call RegPackPtr(RF, InData%Ha) @@ -1709,11 +1684,6 @@ subroutine MAP_PackConstrState(RF, Indata) character(*), parameter :: RoutineName = 'MAP_PackConstrState' logical :: PtrInIndex if (RF%ErrStat >= AbortErrLev) return - if (c_associated(InData%C_obj%object)) then - RF%ErrStat = ErrID_Fatal - RF%ErrMsg = RoutineName//': C_obj%object cannot be packed.' - return - end if call RegPackPtr(RF, InData%H) call RegPackPtr(RF, InData%V) call RegPackPtr(RF, InData%x) @@ -1935,11 +1905,6 @@ subroutine MAP_PackParam(RF, Indata) type(MAP_ParameterType), intent(in) :: InData character(*), parameter :: RoutineName = 'MAP_PackParam' if (RF%ErrStat >= AbortErrLev) return - if (c_associated(InData%C_obj%object)) then - RF%ErrStat = ErrID_Fatal - RF%ErrMsg = RoutineName//': C_obj%object cannot be packed.' - return - end if call RegPack(RF, InData%g) call RegPack(RF, InData%depth) call RegPack(RF, InData%rho_sea) @@ -2112,11 +2077,6 @@ subroutine MAP_PackInput(RF, Indata) character(*), parameter :: RoutineName = 'MAP_PackInput' logical :: PtrInIndex if (RF%ErrStat >= AbortErrLev) return - if (c_associated(InData%C_obj%object)) then - RF%ErrStat = ErrID_Fatal - RF%ErrMsg = RoutineName//': C_obj%object cannot be packed.' - return - end if call RegPackPtr(RF, InData%x) call RegPackPtr(RF, InData%y) call RegPackPtr(RF, InData%z) @@ -2384,11 +2344,6 @@ subroutine MAP_PackOutput(RF, Indata) character(*), parameter :: RoutineName = 'MAP_PackOutput' logical :: PtrInIndex if (RF%ErrStat >= AbortErrLev) return - if (c_associated(InData%C_obj%object)) then - RF%ErrStat = ErrID_Fatal - RF%ErrMsg = RoutineName//': C_obj%object cannot be packed.' - return - end if call RegPackPtr(RF, InData%Fx) call RegPackPtr(RF, InData%Fy) call RegPackPtr(RF, InData%Fz) @@ -2594,11 +2549,6 @@ subroutine MAP_PackMisc(RF, Indata) type(MAP_MiscVarType), intent(in) :: InData character(*), parameter :: RoutineName = 'MAP_PackMisc' if (RF%ErrStat >= AbortErrLev) return - if (c_associated(InData%C_obj%object)) then - RF%ErrStat = ErrID_Fatal - RF%ErrMsg = RoutineName//': C_obj%object cannot be packed.' - return - end if call NWTC_Library_PackModJacType(RF, InData%Jac) call MAP_PackInput(RF, InData%u_perturb) call MAP_PackConstrState(RF, InData%z_lin) diff --git a/modules/moordyn/CMakeLists.txt b/modules/moordyn/CMakeLists.txt index 5ec97ef21c..ef15882385 100644 --- a/modules/moordyn/CMakeLists.txt +++ b/modules/moordyn/CMakeLists.txt @@ -37,15 +37,27 @@ add_executable(moordyn_driver target_link_libraries(moordyn_driver moordynlib versioninfolib) # C-bindings interface library -add_library(moordyn_c_binding SHARED - src/MoorDyn_C_Binding.f90 -) -target_link_libraries(moordyn_c_binding moordynlib seastlib versioninfolib) +# create object instead of directly linking into shared and static -- causes issues in parallel builds +# This is only required because we are static linking the library for wavetank +# NOTE: target linking at the object, static, and shared libraries. Different CMake versions handle this +# slightly differently with unpredictable results if I don't. +add_library(moordyn_c_binding_object OBJECT src/MoorDyn_C_Binding.f90) +target_link_libraries(moordyn_c_binding_object moordynlib seastlib nwtclibs versioninfolib) +set_property(TARGET moordyn_c_binding_object PROPERTY POSITION_INDEPENDENT_CODE 1) # required for shared libs if(APPLE OR UNIX) - target_compile_definitions(moordyn_c_binding PRIVATE IMPLICIT_DLLEXPORT) + target_compile_definitions(moordyn_c_binding_object PRIVATE IMPLICIT_DLLEXPORT) endif() -install(TARGETS moordynlib moordyn_driver moordyn_c_binding +# Shared +add_library(moordyn_c_binding SHARED $) +target_link_libraries(moordyn_c_binding moordynlib seastlib nwtclibs versioninfolib) + +# C-bindings non-shared interface +# This is a workaround for building wavetank into a single DLL (also allows setting CU globaly for sending screen to file for labview integration) +add_library(moordyn_c_bind_static SHARED $) +target_link_libraries(moordyn_c_bind_static moordynlib seastlib nwtclibs versioninfolib) + +install(TARGETS moordynlib moordyn_driver moordyn_c_binding moordyn_c_bind_static EXPORT "${CMAKE_PROJECT_NAME}Libraries" RUNTIME DESTINATION bin ARCHIVE DESTINATION lib diff --git a/modules/moordyn/src/MoorDyn.f90 b/modules/moordyn/src/MoorDyn.f90 index 2d40bd2b0b..67191d4609 100644 --- a/modules/moordyn/src/MoorDyn.f90 +++ b/modules/moordyn/src/MoorDyn.f90 @@ -1116,12 +1116,13 @@ SUBROUTINE MD_Init(InitInp, u, p, x, xd, z, other, y, m, DTcoupling, InitOut, Er if ((let1 == "ANCHOR") .or. (let1 == "FIXED") .or. (let1 == "FIX")) then m%RodList(l)%typeNum = 2 - CALL Body_AddRod(m%GroundBody, l, tempArray) ! add rod l to Ground body - + CALL Body_AddRod(m%GroundBody, l, tempArray, ErrStat2, ErrMsg2) ! add rod l to Ground body + if (Failed()) return else if ((let1 == "PINNED") .or. (let1 == "PIN")) then m%RodList(l)%typeNum = 1 - CALL Body_AddRod(m%GroundBody, l, tempArray) ! add rod l to Ground body + CALL Body_AddRod(m%GroundBody, l, tempArray, ErrStat2, ErrMsg2) ! add rod l to Ground body + if (Failed()) return p%nFreeRods=p%nFreeRods+1 ! add this pinned rod to the free list because it is half free @@ -1139,7 +1140,8 @@ SUBROUTINE MD_Init(InitInp, u, p, x, xd, z, other, y, m, DTcoupling, InitOut, Er if ((J <= p%nBodies) .and. (J > 0)) then - CALL Body_AddRod(m%BodyList(J), l, tempArray) ! add rod l to the body + CALL Body_AddRod(m%BodyList(J), l, tempArray, ErrStat2, ErrMsg2) ! add rod l to the body + if (Failed()) return if ( (let2 == "PINNED") .or. (let2 == "PIN") ) then m%RodList(l)%typeNum = 1 @@ -1363,7 +1365,8 @@ SUBROUTINE MD_Init(InitInp, u, p, x, xd, z, other, y, m, DTcoupling, InitOut, Er !m%PointList(l)%r = tempArray(1:3) ! set initial node position - CALL Body_AddPoint(m%GroundBody, l, tempArray(1:3)) ! add point l to Ground body + CALL Body_AddPoint(m%GroundBody, l, tempArray(1:3), ErrStat2, ErrMsg2) ! add point l to Ground body + if (Failed()) return else if (let1 == "BODY") then ! attached to a body if (len_trim(num1) > 0) then @@ -1372,7 +1375,8 @@ SUBROUTINE MD_Init(InitInp, u, p, x, xd, z, other, y, m, DTcoupling, InitOut, Er if ((J <= p%nBodies) .and. (J > 0)) then m%PointList(l)%typeNum = 1 - CALL Body_AddPoint(m%BodyList(J), l, tempArray(1:3)) ! add point l to Ground body + CALL Body_AddPoint(m%BodyList(J), l, tempArray(1:3), ErrStat2, ErrMsg2) ! add point l to Ground body + if (Failed()) return else CALL SetErrStat( ErrID_Fatal, "Body ID out of bounds for Point "//trim(Num2LStr(l))//".", ErrStat, ErrMsg, RoutineName ) @@ -1549,9 +1553,11 @@ SUBROUTINE MD_Init(InitInp, u, p, x, xd, z, other, y, m, DTcoupling, InitOut, Er if ((J <= p%nRods) .and. (J > 0)) then if (let2 == "A") then - CALL Rod_AddLine(m%RodList(J), l, 0, 0) ! add line l (end A, denoted by 0) to rod J (end A, denoted by 0) + CALL Rod_AddLine(m%RodList(J), l, 0, 0, ErrStat2, ErrMsg2) ! add line l (end A, denoted by 0) to rod J (end A, denoted by 0) + if (Failed()) return else if (let2 == "B") then - CALL Rod_AddLine(m%RodList(J), l, 0, 1) ! add line l (end A, denoted by 0) to rod J (end B, denoted by 1) + CALL Rod_AddLine(m%RodList(J), l, 0, 1, ErrStat2, ErrMsg2) ! add line l (end A, denoted by 0) to rod J (end B, denoted by 1) + if (Failed()) return else CALL SetErrStat( ErrID_Fatal, "Error: rod end (A or B) must be specified for line "//trim(Num2LStr(l))//" end A attachment. Instead seeing "//let2, ErrStat, ErrMsg, RoutineName ) CALL CleanUp() @@ -1567,7 +1573,8 @@ SUBROUTINE MD_Init(InitInp, u, p, x, xd, z, other, y, m, DTcoupling, InitOut, Er else if ((len_trim(let1)==0) .or. (let1 == "P") .or. (let1 == "POINT")) then if ((J <= p%nPoints) .and. (J > 0)) then - CALL Point_AddLine(m%PointList(J), l, 0) ! add line l (end A, denoted by 0) to point J + CALL Point_AddLine(m%PointList(J), l, 0, ErrStat2, ErrMsg2) ! add line l (end A, denoted by 0) to point J + if (Failed()) return else CALL SetErrStat( ErrID_Fatal, "Error: point out of bounds for line "//trim(Num2LStr(l))//" end A attachment.", ErrStat, ErrMsg, RoutineName ) CALL CleanUp() @@ -1594,9 +1601,11 @@ SUBROUTINE MD_Init(InitInp, u, p, x, xd, z, other, y, m, DTcoupling, InitOut, Er if ((J <= p%nRods) .and. (J > 0)) then if (let2 == "A") then - CALL Rod_AddLine(m%RodList(J), l, 1, 0) ! add line l (end B, denoted by 1) to rod J (end A, denoted by 0) + CALL Rod_AddLine(m%RodList(J), l, 1, 0, ErrStat2, ErrMsg2) ! add line l (end B, denoted by 1) to rod J (end A, denoted by 0) + if (Failed()) return else if (let2 == "B") then - CALL Rod_AddLine(m%RodList(J), l, 1, 1) ! add line l (end B, denoted by 1) to rod J (end B, denoted by 1) + CALL Rod_AddLine(m%RodList(J), l, 1, 1, ErrStat2, ErrMsg2) ! add line l (end B, denoted by 1) to rod J (end B, denoted by 1) + if (Failed()) return else CALL SetErrStat( ErrID_Fatal, "Error: rod end (A or B) must be specified for line "//trim(Num2LStr(l))//" end B attachment. Instead seeing "//let2, ErrStat, ErrMsg, RoutineName ) CALL CleanUp() @@ -1612,7 +1621,8 @@ SUBROUTINE MD_Init(InitInp, u, p, x, xd, z, other, y, m, DTcoupling, InitOut, Er else if ((len_trim(let1)==0) .or. (let1 == "P") .or. (let1 == "POINT")) then if ((J <= p%nPoints) .and. (J > 0)) then - CALL Point_AddLine(m%PointList(J), l, 1) ! add line l (end B, denoted by 1) to point J + CALL Point_AddLine(m%PointList(J), l, 1, ErrStat2, ErrMsg2) ! add line l (end B, denoted by 1) to point J + if (Failed()) return else CALL SetErrStat( ErrID_Fatal, "Error: point out of bounds for line "//trim(Num2LStr(l))//" end B attachment.", ErrStat, ErrMsg, RoutineName ) CALL CleanUp() @@ -2018,6 +2028,13 @@ SUBROUTINE MD_Init(InitInp, u, p, x, xd, z, other, y, m, DTcoupling, InitOut, Er ! get lines m%FailList(l)%nLinesToDetach = N + + ! Check that N is less than MD_MaxFailLines -- this would result in an out bounds array access + if (m%FailList(l)%nLinesToDetach > MD_MaxFailLines) then + call SetErrStat( ErrID_Fatal, ' More than hard coded limit of '//trim(Num2LStr(MD_MaxFailLines))//' lines to detach specified for line failure '//trim(Num2LStr(l))//'.', ErrStat, ErrMsg, RoutineName ) + call CleanUp() + return + endif DO il = 1, m%FailList(l)%nLinesToDetach if (TempIDnums(il) <= p%nLines) then ! ensure line ID is in range @@ -3574,6 +3591,8 @@ SUBROUTINE DetachLines (attachID, isRod, lineIDs, lineTops, nLinesToDetach, time REAL(DbKi), INTENT(IN ) :: time INTEGER(IntKi) :: k ! index REAL(DbKi) :: dummyPointState(6) = 0.0_DbKi ! dummy state array to hold kinematics of old attachment point (format in terms of part of point state vector: r[J] = X[3 + J]; rd[J] = X[J]; ) + integer(IntKi) :: ErrStat3 + character(ErrMsgLen) :: ErrMsg3 ! add point to list of free ones and add states for it p%nPoints = p%nPoints + 1 ! add 1 to the number of points (this is now the number of the new point) @@ -3626,7 +3645,9 @@ SUBROUTINE DetachLines (attachID, isRod, lineIDs, lineTops, nLinesToDetach, time ! attach lines to new point DO k=1,nLinesToDetach ! for each relevant line - CALL Point_AddLine(m%PointList(p%nPoints), lineIDs(k), lineTops(k)) + CALL Point_AddLine(m%PointList(p%nPoints), lineIDs(k), lineTops(k), ErrStat3, ErrMsg3) + call CheckError(ErrStat3, ErrMsg3) + if (ErrStat >= AbortErrLev) return ENDDO ! update point kinematics to match old line attachment point kinematics and set positions of attached line ends @@ -4222,15 +4243,10 @@ SUBROUTINE MD_End(u, p, x, xd, z, other, y, m, ErrStat , ErrMsg) CALL MD_DestroyMisc(m, ErrStat2, ErrMsg2) CALL CheckError( ErrStat2, ErrMsg2 ) - IF (p%UnLog > 0_IntKi) CLOSE( p%UnLog ) ! close log file if it's open - !TODO: any need to specifically deallocate things like m%xTemp%states in the above? <<<< - - ! IF ( ErrStat==ErrID_None) THEN - ! CALL WrScr('MoorDyn closed without errors') - ! ELSE - ! CALL WrScr('MoorDyn closed with errors') - ! END IF - + IF (p%UnLog > 0_IntKi) then + CLOSE( p%UnLog ) ! close log file if it's open + p%UnLog = -1 ! in case we call end a second time for whatever reason + endif CONTAINS diff --git a/modules/moordyn/src/MoorDyn_Body.f90 b/modules/moordyn/src/MoorDyn_Body.f90 index 3fbf2d6bac..19245533c6 100644 --- a/modules/moordyn/src/MoorDyn_Body.f90 +++ b/modules/moordyn/src/MoorDyn_Body.f90 @@ -613,21 +613,26 @@ END SUBROUTINE Body_GetCoupledForce ! this function handles assigning a point to a body !-------------------------------------------------------------- - SUBROUTINE Body_AddPoint(Body, pointID, coords) + SUBROUTINE Body_AddPoint(Body, pointID, coords, ErrStat, ErrMsg) - Type(MD_Body), INTENT(INOUT) :: Body ! the Point object - Integer(IntKi), INTENT(IN ) :: pointID - REAL(DbKi), INTENT(IN ) :: coords(3) + Type(MD_Body), INTENT(INOUT) :: Body ! the Point object + Integer(IntKi), INTENT(IN ) :: pointID + REAL(DbKi), INTENT(IN ) :: coords(3) + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen),intent( out) :: ErrMsg IF (wordy > 0) Print*, "P", pointID, "->B", Body%IdNum - IF(Body%nAttachedP < 30) THEN ! this is currently just a maximum imposed by a fixed array size. could be improved. + IF(Body%nAttachedP < MD_MaxBdAtch) THEN ! this is currently just a maximum imposed by a fixed array size. could be improved. Body%nAttachedP = Body%nAttachedP + 1 ! increment the number pointed Body%AttachedC(Body%nAttachedP) = pointID Body%rPointRel(:,Body%nAttachedP) = coords ! store relative position of point on body + ErrStat = ErrID_None + ErrMsg = '' ELSE - call WrScr("too many Points attached to Body "//trim(num2lstr(Body%IdNum))//" in MoorDyn!") + ErrStat = ErrID_Fatal + ErrMsg = "too many Points attached to Body "//trim(num2lstr(Body%IdNum))//" in MoorDyn!" END IF END SUBROUTINE Body_AddPoint @@ -635,18 +640,20 @@ END SUBROUTINE Body_AddPoint ! this function handles assigning a rod to a body !-------------------------------------------------------------- - SUBROUTINE Body_AddRod(Body, rodID, coords) + SUBROUTINE Body_AddRod(Body, rodID, coords, ErrStat, ErrMsg) - Type(MD_Body), INTENT(INOUT) :: Body ! the Point object - Integer(IntKi), INTENT(IN ) :: rodID - REAL(DbKi), INTENT(IN ) :: coords(6) ! positions of rod ends A and B relative to body + Type(MD_Body), INTENT(INOUT) :: Body ! the Point object + Integer(IntKi), INTENT(IN ) :: rodID + REAL(DbKi), INTENT(IN ) :: coords(6) ! positions of rod ends A and B relative to body + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen),intent( out) :: ErrMsg REAL(DbKi) :: tempUnitVec(3) REAL(DbKi) :: dummyLength IF (wordy > 0) Print*, "R", rodID, "->B", Body%IdNum - IF(Body%nAttachedR < 30) THEN ! this is currently just a maximum imposed by a fixed array size. could be improved. + IF(Body%nAttachedR < MD_MaxBdAtch) THEN ! this is currently just a maximum imposed by a fixed array size. could be improved. Body%nAttachedR = Body%nAttachedR + 1 ! increment the number connected ! store rod ID @@ -657,8 +664,11 @@ SUBROUTINE Body_AddRod(Body, rodID, coords) Body%r6RodRel(1:3, Body%nAttachedR) = coords(1:3) Body%r6RodRel(4:6, Body%nAttachedR) = tempUnitVec + ErrStat = ErrID_None + ErrMsg = '' ELSE - call WrScr("too many rods attached to Body "//trim(num2lstr(Body%IdNum))//" in MoorDyn") + ErrStat = ErrID_Fatal + ErrMsg = "too many rods attached to Body "//trim(num2lstr(Body%IdNum))//" in MoorDyn" END IF END SUBROUTINE Body_AddRod diff --git a/modules/moordyn/src/MoorDyn_C_Binding.f90 b/modules/moordyn/src/MoorDyn_C_Binding.f90 index b2d136c46f..ef70233506 100644 --- a/modules/moordyn/src/MoorDyn_C_Binding.f90 +++ b/modules/moordyn/src/MoorDyn_C_Binding.f90 @@ -33,7 +33,9 @@ MODULE MoorDyn_C PUBLIC :: MD_C_UpdateStates PUBLIC :: MD_C_CalcOutput PUBLIC :: MD_C_End +PUBLIC :: MD_C_SetWaveFieldData +PRIVATE !------------------------------------------------------------------------------------ ! Version info for display @@ -129,6 +131,7 @@ MODULE MoorDyn_C !=============================================================================================================== !---------------------------------------------- MD INIT -------------------------------------------------------- !=============================================================================================================== +!FIXME: add ShowPassed and DebugLevel SUBROUTINE MD_C_Init( & InputFilePassed, InputFileString_C, InputFileStringLength_C, & DT_C, G_C, RHO_C, DEPTH_C, PtfmInit_C, & @@ -147,11 +150,12 @@ SUBROUTINE MD_C_Init( & REAL(C_FLOAT) , INTENT(IN ) :: G_C REAL(C_FLOAT) , INTENT(IN ) :: RHO_C REAL(C_FLOAT) , INTENT(IN ) :: DEPTH_C +!FIXME: PtfmInit_C should be resized for N nodes (6xN position), but can stay as euler angle for angles REAL(C_FLOAT) , INTENT(IN ) :: PtfmInit_C(6) ! TODO: make this more flexible, can we not have 6 DOF only coupling? INTEGER(C_INT) , INTENT(IN ) :: InterpOrder_C INTEGER(C_INT) , INTENT( OUT) :: NumChannels_C - CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: OutputChannelNames_C(100000) - CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: OutputChannelUnits_C(100000) + CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: OutputChannelNames_C(ChanLen*1000) ! The size of these arrays was chosen as a "big number", it isn't set by MoorDyn. Watch out, it might be bigger than this! + CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: OutputChannelUnits_C(ChanLen*1000) INTEGER(C_INT) , INTENT( OUT) :: ErrStat_C CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: ErrMsg_C(ErrMsgLen_C) @@ -171,7 +175,28 @@ SUBROUTINE MD_C_Init( & CALL DispCopyrightLicense( version%Name ) CALL DispCompileRuntimeInfo( version%Name ) - + ! Destroy global memory + if (allocated(u)) then + do i = 1, size(u) + call MD_DestroyInput(u(i), ErrStat_F, ErrMsg_F) + end do + deallocate(u) + end if + call MD_DestroyParam(p, ErrStat_F, ErrMsg_F) + do i = 0, 2 + call MD_DestroyContState(x(i), ErrStat_F, ErrMsg_F) + end do + do i = 0, 2 + call MD_DestroyDiscState(xd(i), ErrStat_F, ErrMsg_F) + end do + do i = 0, 2 + call MD_DestroyConstrState(z(i), ErrStat_F, ErrMsg_F) + end do + do i = 0, 2 + call MD_DestroyOtherState(other(i), ErrStat_F, ErrMsg_F) + end do + call MD_DestroyOutput(y, ErrStat_F, ErrMsg_F) + call MD_DestroyMisc(m, ErrStat_F, ErrMsg_F) ! Convert the MD input file to FileInfoType !---------------------------------------------------------------------------------------------------------------------------------------------- @@ -530,8 +555,13 @@ SUBROUTINE MD_C_End(ErrStat_C,ErrMsg_C) BIND (C, NAME='MD_C_End') ErrMsg_F = '' ! Call the main subroutine MD_End - CALL MD_End(u(1), p, x(1), xd(1), z(1), other(1), y, m, ErrStat_F2, ErrMsg_F2) - call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) + ! If u is not allocated, then we didn't get far at all in initialization, + ! or AD_C_End got called before Init. We don't want a segfault, so check + ! for allocation. + if (allocated(u)) then + CALL MD_End(u(1), p, x(1), xd(1), z(1), other(1), y, m, ErrStat_F2, ErrMsg_F2) + call SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) + endif ! NOTE: MoorDyn_End only takes 1 instance of u, not the array. So extra ! logic is required here (this isn't necessary in the fortran driver @@ -585,7 +615,6 @@ END SUBROUTINE MD_C_End !----------------------------------------------- MD SetWaveFieldData ------------------------------------------- !=============================================================================================================== !> Set the wave field data pointer from an external source such as SeaState -!! Assumes that MD_C_Init has been run since it uses those values to check that there's a valid pointer SUBROUTINE MD_C_SetWaveFieldData(WaveFieldData_C) BIND (C, NAME='MD_C_SetWaveFieldData') #ifndef IMPLICIT_DLLEXPORT !DEC$ ATTRIBUTES DLLEXPORT :: MD_C_SetWaveFieldData diff --git a/modules/moordyn/src/MoorDyn_IO.f90 b/modules/moordyn/src/MoorDyn_IO.f90 index 51a2afd714..3061b350f9 100644 --- a/modules/moordyn/src/MoorDyn_IO.f90 +++ b/modules/moordyn/src/MoorDyn_IO.f90 @@ -31,9 +31,6 @@ MODULE MoorDyn_IO INTEGER(IntKi), PARAMETER :: wordy = 0 ! verbosity level. >1 = more console output - INTEGER, PARAMETER :: nCoef = 30 ! maximum number of entries to allow in nonlinear coefficient lookup tables - ! it would be nice if the above worked for everything, but I think it needs to also be matched in the Registry - ! --------------------------- Output definitions ----------------------------------------- ! The following are some definitions for use with the output options in MoorDyn. @@ -249,8 +246,8 @@ SUBROUTINE getCoefficientOrCurve(inputString, LineProp_c, LineProp_npoints, Line CHARACTER(40), INTENT(IN ) :: inputString REAL(DbKi), INTENT(INOUT) :: LineProp_c INTEGER(IntKi), INTENT( OUT) :: LineProp_nPoints - REAL(DbKi), INTENT( OUT) :: LineProp_Xs (nCoef) - REAL(DbKi), INTENT( OUT) :: LineProp_Ys (nCoef) + REAL(DbKi), INTENT( OUT) :: LineProp_Xs (MD_MaxNCoef) ! MD_MaxNCoef set in registry + REAL(DbKi), INTENT( OUT) :: LineProp_Ys (MD_MaxNCoef) ! MD_MaxNCoef set in registry INTEGER(IntKi), INTENT( OUT) :: ErrStat3 ! Error status of the operation CHARACTER(*), INTENT( OUT) :: ErrMsg3 ! Error message if ErrStat /= ErrID_None @@ -289,7 +286,7 @@ SUBROUTINE getCoefficientOrCurve(inputString, LineProp_c, LineProp_npoints, Line READ(UnCoef,'(A)',IOSTAT=ErrStat4) Line2 READ(UnCoef,'(A)',IOSTAT=ErrStat4) Line2 - DO I = 1, nCoef + DO I = 1, MD_MaxNCoef READ(UnCoef,'(A)',IOSTAT=ErrStat4) Line2 !read into a line @@ -1320,22 +1317,20 @@ SUBROUTINE MDIO_CloseOutput ( p, m, ErrStat, ErrMsg ) INTEGER, INTENT( OUT ) :: ErrStat ! a non-zero value indicates an error occurred CHARACTER(*), INTENT( OUT ) :: ErrMsg ! Error message if ErrStat /= ErrID_None - INTEGER(IntKi) :: I ! generic counter - + INTEGER(IntKi) :: I ! generic counter + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*), parameter :: RoutineName = 'MDIO_CloseOutput' ErrStat = 0 ErrMsg = "" - -!FIXME: make sure thes are actually open before trying to close them. Segfault will occur otherwise!!!! -! This bug can be triggered by an early failure of the parsing routines, before these files were ever opened -! which returns MD to OpenFAST as ErrID_Fatal, then OpenFAST calls MD_End, which calls this. - ! close main MoorDyn output file if (p%MDUnOut > 0) then - CLOSE( p%MDUnOut, IOSTAT = ErrStat ) - IF ( ErrStat /= 0 ) THEN - ErrMsg = 'Error closing output file' + CLOSE( p%MDUnOut, IOSTAT = ErrStat2 ) + p%MDUnOut = -1 + IF ( ErrStat2 /= 0 ) THEN + call SetErrStat(ErrID_Severe,'Error closing output file',ErrStat,ErrMsg,RoutineName) END IF end if @@ -1343,9 +1338,10 @@ SUBROUTINE MDIO_CloseOutput ( p, m, ErrStat, ErrMsg ) DO I=1,p%NRods if (allocated(m%RodList)) then if (m%RodList(I)%RodUnOut > 0) then - CLOSE( m%RodList(I)%RodUnOut, IOSTAT = ErrStat ) - IF ( ErrStat /= 0 ) THEN - ErrMsg = 'Error closing rod output file' + CLOSE( m%RodList(I)%RodUnOut, IOSTAT = ErrStat2 ) + m%RodList(I)%RodUnOut = -1 + IF ( ErrStat2 /= 0 ) THEN + call SetErrStat(ErrID_Severe,'Error closing rod output file',ErrStat,ErrMsg,RoutineName) END IF end if end if @@ -1355,9 +1351,10 @@ SUBROUTINE MDIO_CloseOutput ( p, m, ErrStat, ErrMsg ) DO I=1,p%NLines if (allocated(m%LineList)) then if (m%LineList(I)%LineUnOut > 0) then - CLOSE( m%LineList(I)%LineUnOut, IOSTAT = ErrStat ) - IF ( ErrStat /= 0 ) THEN - ErrMsg = 'Error closing line output file' + CLOSE( m%LineList(I)%LineUnOut, IOSTAT = ErrStat2 ) + m%LineList(I)%LineUnOut = -1 + IF ( ErrStat2 /= 0 ) THEN + call SetErrStat(ErrID_Severe,'Error closing line output file',ErrStat,ErrMsg,RoutineName) END IF end if end if diff --git a/modules/moordyn/src/MoorDyn_Misc.f90 b/modules/moordyn/src/MoorDyn_Misc.f90 index 9ae7a4fb6b..3c661f1766 100644 --- a/modules/moordyn/src/MoorDyn_Misc.f90 +++ b/modules/moordyn/src/MoorDyn_Misc.f90 @@ -1160,7 +1160,7 @@ SUBROUTINE setupWaterKin(WaterKinString, p, Tmax, ErrStat, ErrMsg) END IF ! Check for if SeaState grid does not match water depth - IF (p%WaveField%GridParams%Z_Depth /= p%WtrDpth) THEN + IF (p%WaveField%GridDepth /= p%WtrDpth) THEN IF (p%writeLog > 0) THEN WRITE(p%UnLog, '(A)' ) " INFO SeaState grid depth does not match MoorDyn water depth." ENDIF diff --git a/modules/moordyn/src/MoorDyn_Point.f90 b/modules/moordyn/src/MoorDyn_Point.f90 index af320e528e..b64a5efb70 100644 --- a/modules/moordyn/src/MoorDyn_Point.f90 +++ b/modules/moordyn/src/MoorDyn_Point.f90 @@ -359,11 +359,13 @@ END SUBROUTINE Point_GetNetForceAndMass ! this function handles assigning a line to a connection node !-------------------------------------------------------------- - SUBROUTINE Point_AddLine(Point, lineID, TopOfLine) + SUBROUTINE Point_AddLine(Point, lineID, TopOfLine, ErrStat, ErrMsg) Type(MD_Point), INTENT (INOUT) :: Point ! the Point object Integer(IntKi), INTENT( IN ) :: lineID Integer(IntKi), INTENT( IN ) :: TopOfLine + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen),intent( out) :: ErrMsg IF (wordy > 0) Print*, "L", lineID, "->C", Point%IdNum @@ -371,7 +373,10 @@ SUBROUTINE Point_AddLine(Point, lineID, TopOfLine) Point%nAttached = Point%nAttached + 1 ! add the line to the number connected Point%Attached(Point%nAttached) = lineID Point%Top(Point%nAttached) = TopOfLine ! attached to line ... 1 = top/fairlead(end B), 0 = bottom/anchor(end A) + ErrStat = ErrID_None + ErrMsg = '' ELSE + ErrStat = ErrID_Fatal call WrScr("Too many lines connected to Point "//trim(num2lstr(Point%IdNum))//" in MoorDyn!") END IF diff --git a/modules/moordyn/src/MoorDyn_Registry.txt b/modules/moordyn/src/MoorDyn_Registry.txt index 41b44ca6e0..210b98cf0b 100644 --- a/modules/moordyn/src/MoorDyn_Registry.txt +++ b/modules/moordyn/src/MoorDyn_Registry.txt @@ -16,6 +16,13 @@ include Registry_NWTC_Library.txt usefrom SeaSt_WaveField.txt + +## ====== parameters ======= +param MoorDyn/MD - IntKi MD_MaxNCoef - 30 - "maximum number of entries to allow in nonlinear coefficient lookup tables" - +param MoorDyn/MD - IntKi MD_MaxBdAtch - 100 - "maximum number of attachments to a body" - +param MoorDyn/MD - IntKi MD_MaxPtAtch - 100 - "maximum number of attachments to a point" - +param MoorDyn/MD - IntKi MD_MaxFailLines - 30 - "maximum number of line failures that can be simulated" - + ## ====== some data read from the input file, but not needed after init ====== typedef MoorDyn/MD MD_InputFileType DbKi DTIC - 0.5 - "convergence check time step for IC generation" "[s]" typedef ^ ^ DbKi TMaxIC - 120 - "maximum time to allow for getting converged ICs" "[s]" @@ -68,14 +75,14 @@ typedef ^ ^ DbKi dF - typedef ^ ^ DbKi cF - - - "Center VIV synchronization in non-dimensional frequency" typedef ^ ^ IntKi ElasticMod - - - "Which elasticity model to use: {1 basic, 2 viscoelastic, 3 viscoelastic+meanload} " - typedef ^ ^ IntKi nEApoints - - - "number of values in stress-strain lookup table (0 means using constant E)" -typedef ^ ^ DbKi stiffXs {30} - - "x array for stress-strain lookup table (up to nCoef)" -typedef ^ ^ DbKi stiffYs {30} - - "y array for stress-strain lookup table" +typedef ^ ^ DbKi stiffXs {MD_MaxNCoef} - - "x array for stress-strain lookup table (up to MD_MaxNCoef)" +typedef ^ ^ DbKi stiffYs {MD_MaxNCoef} - - "y array for stress-strain lookup table" typedef ^ ^ IntKi nBApoints - - - "number of values in stress-strainrate lookup table (0 means using constant c)" -typedef ^ ^ DbKi dampXs {30} - - "x array for stress-strainrate lookup table (up to nCoef)" -typedef ^ ^ DbKi dampYs {30} - - "y array for stress-strainrate lookup table" +typedef ^ ^ DbKi dampXs {MD_MaxNCoef} - - "x array for stress-strainrate lookup table (up to MD_MaxNCoef)" +typedef ^ ^ DbKi dampYs {MD_MaxNCoef} - - "y array for stress-strainrate lookup table" typedef ^ ^ IntKi nEIpoints - - - "number of values in bending stress-strain lookup table (0 means using constant E)" -typedef ^ ^ DbKi bstiffXs {30} - - "x array for stress-strain lookup table (up to nCoef)" -typedef ^ ^ DbKi bstiffYs {30} - - "y array for stress-strain lookup table" +typedef ^ ^ DbKi bstiffXs {MD_MaxNCoef} - - "x array for stress-strain lookup table (up to MD_MaxNCoef)" +typedef ^ ^ DbKi bstiffYs {MD_MaxNCoef} - - "y array for stress-strain lookup table" # rod properties from rod dictionary input typedef ^ MD_RodProp IntKi IdNum - - - "integer identifier of this set of rod properties" @@ -92,12 +99,12 @@ typedef ^ ^ DbKi CaEnd - # this is the Body type, which holds data for each body object typedef ^ MD_Body IntKi IdNum - - - "integer identifier of this Point" typedef ^ ^ IntKi typeNum - - - "integer identifying the type. 0=free, 1=fixed, -1=coupled, 2=coupledpinned" -typedef ^ ^ IntKi AttachedC {30} - - "list of IdNums of points attached to this body" -typedef ^ ^ IntKi AttachedR {30} - - "list of IdNums of rods attached to this body" +typedef ^ ^ IntKi AttachedC {MD_MaxBdAtch} - - "list of IdNums of points attached to this body" +typedef ^ ^ IntKi AttachedR {MD_MaxBdAtch} - - "list of IdNums of rods attached to this body" typedef ^ ^ IntKi nAttachedP - - - "number of attached points" typedef ^ ^ IntKi nAttachedR - - - "number of attached rods" -typedef ^ ^ DbKi rPointRel {3}{30} - - "relative position of point on body" -typedef ^ ^ DbKi r6RodRel {6}{30} - - "relative position and orientation of rod on body" +typedef ^ ^ DbKi rPointRel {3}{MD_MaxBdAtch} - - "relative position of point on body" +typedef ^ ^ DbKi r6RodRel {6}{MD_MaxBdAtch} - - "relative position and orientation of rod on body" typedef ^ ^ DbKi bodyM - - - "body mass (separate from attached objects)" "[kg]" typedef ^ ^ DbKi bodyV - - - "body volume (for buoyancy calculation)" "[m^3]" typedef ^ ^ DbKi bodyI {3} - - "body 3x3 inertia matrix diagonals" "[kg-m^2]" @@ -127,8 +134,8 @@ typedef ^ ^ DbKi BquadL {3} typedef ^ MD_Point IntKi IdNum - - - "integer identifier of this point" typedef ^ ^ CHARACTER(10) type - - - "type of point: fix, vessel, point" typedef ^ ^ IntKi typeNum - - - "integer identifying the type. 1=fixed, -1=coupled, 0=free" -typedef ^ ^ IntKi Attached {10} - - "list of IdNums of lines attached to this point node" -typedef ^ ^ IntKi Top {10} - - "list of ints specifying whether each line is attached at 1 = top/fairlead(end B), 0 = bottom/anchor(end A)" +typedef ^ ^ IntKi Attached {MD_MaxPtAtch} - - "list of IdNums of lines attached to this point node" +typedef ^ ^ IntKi Top {MD_MaxPtAtch} - - "list of ints specifying whether each line is attached at 1 = top/fairlead(end B), 0 = bottom/anchor(end A)" typedef ^ ^ IntKi nAttached - - - "number of attached lines" typedef ^ ^ DbKi pointM - - - "point mass" "[kg]" typedef ^ ^ DbKi pointV - - - "point volume" "[m^3]" @@ -156,10 +163,10 @@ typedef ^ MD_Rod IntKi IdNum - typedef ^ ^ CHARACTER(10) type - - - "type of Rod. should match one of RodProp names" typedef ^ ^ IntKi PropsIdNum - - - "the IdNum of the associated rod properties" - typedef ^ ^ IntKi typeNum - - - "integer identifying the type. 0=free, 1=pinned, 2=fixed, -1=coupledpinned, -2=coupled" -typedef ^ ^ IntKi AttachedA {10} - - "list of IdNums of lines attached to end A" -typedef ^ ^ IntKi AttachedB {10} - - "list of IdNums of lines attached to end B" -typedef ^ ^ IntKi TopA {10} - - "list of ints specifying whether each line is attached at 1 = top/fairlead(end B), 0 = bottom/anchor(end A)" -typedef ^ ^ IntKi TopB {10} - - "list of ints specifying whether each line is attached at 1 = top/fairlead(end B), 0 = bottom/anchor(end A)" +typedef ^ ^ IntKi AttachedA {MD_MaxPtAtch} - - "list of IdNums of lines attached to end A" +typedef ^ ^ IntKi AttachedB {MD_MaxPtAtch} - - "list of IdNums of lines attached to end B" +typedef ^ ^ IntKi TopA {MD_MaxPtAtch} - - "list of ints specifying whether each line is attached at 1 = top/fairlead(end B), 0 = bottom/anchor(end A)" +typedef ^ ^ IntKi TopB {MD_MaxPtAtch} - - "list of ints specifying whether each line is attached at 1 = top/fairlead(end B), 0 = bottom/anchor(end A)" typedef ^ ^ IntKi nAttachedA - - - "number of attached lines to Rod end A" typedef ^ ^ IntKi nAttachedB - - - "number of attached lines to Rod end B" typedef ^ ^ IntKi OutFlagList {55} - - "array specifying what line quantities should be output (1 vs 0)" - @@ -249,14 +256,14 @@ typedef ^ ^ DbKi Cl - typedef ^ ^ DbKi dF - - - "+- range of VIV synchronization in non-dimensional frequency" typedef ^ ^ DbKi cF - - - "Center VIV synchronization in non-dimensional frequency" typedef ^ ^ IntKi nEApoints - - - "number of values in stress-strain lookup table (0 means using constant E)" -typedef ^ ^ DbKi stiffXs {30} - - "x array for stress-strain lookup table (up to nCoef)" -typedef ^ ^ DbKi stiffYs {30} - - "y array for stress-strain lookup table" +typedef ^ ^ DbKi stiffXs {MD_MaxNCoef} - - "x array for stress-strain lookup table (up to MD_MaxNCoef)" +typedef ^ ^ DbKi stiffYs {MD_MaxNCoef} - - "y array for stress-strain lookup table" typedef ^ ^ IntKi nBApoints - - - "number of values in stress-strainrate lookup table (0 means using constant c)" -typedef ^ ^ DbKi dampXs {30} - - "x array for stress-strainrate lookup table (up to nCoef)" -typedef ^ ^ DbKi dampYs {30} - - "y array for stress-strainrate lookup table" +typedef ^ ^ DbKi dampXs {MD_MaxNCoef} - - "x array for stress-strainrate lookup table (up to MD_MaxNCoef)" +typedef ^ ^ DbKi dampYs {MD_MaxNCoef} - - "y array for stress-strainrate lookup table" typedef ^ ^ IntKi nEIpoints - - - "number of values in bending stress-strain lookup table (0 means using constant E)" -typedef ^ ^ DbKi bstiffXs {30} - - "x array for stress-strain lookup table (up to nCoef)" -typedef ^ ^ DbKi bstiffYs {30} - - "y array for stress-strain lookup table" +typedef ^ ^ DbKi bstiffXs {MD_MaxNCoef} - - "x array for stress-strain lookup table (up to MD_MaxNCoef)" +typedef ^ ^ DbKi bstiffYs {MD_MaxNCoef} - - "y array for stress-strain lookup table" typedef ^ ^ DbKi time - - - "current time" "[s]" typedef ^ ^ DbKi r {:}{:} - - "node positions" - typedef ^ ^ DbKi rd {:}{:} - - "node velocities" - @@ -312,8 +319,8 @@ typedef ^ ^ LOGICAL isGlobal - typedef ^ MD_Fail IntKi IdNum - - - "integer identifier of this failure" "-" typedef ^ ^ IntKi attachID - - - "ID of connection or Rod the lines are attached to" "-" typedef ^ ^ IntKi isRod - - - "1 Rod end A, 2 Rod end B, 0 if point" "-" -typedef ^ ^ IntKi lineIDs {30} - - "array of one or more lines to detach (starting from 1...)" "-" -typedef ^ ^ IntKi lineTops {30} - - "an array that will be FILLED IN to return which end of each line was disconnected ... 1 = top/fairlead(end B), 0 = bottom/anchor(end A)" "-" +typedef ^ ^ IntKi lineIDs {MD_MaxFailLines} - - "array of one or more lines to detach (starting from 1...)" "-" +typedef ^ ^ IntKi lineTops {MD_MaxFailLines} - - "an array that will be FILLED IN to return which end of each line was disconnected ... 1 = top/fairlead(end B), 0 = bottom/anchor(end A)" "-" typedef ^ ^ IntKi nLinesToDetach - - - "how many lines to dettach" "-" typedef ^ ^ DbKi failTime - - - "time of failure" "s" typedef ^ ^ DbKi failTen - - - "tension threshold of failure" "N" @@ -493,5 +500,5 @@ typedef ^ ^ DbKi BathymetryGrid {:}{:} typedef ^ ^ DbKi BathGrid_Xs {:} - - "array of x-coordinates in the bathymetry grid" typedef ^ ^ DbKi BathGrid_Ys {:} - - "array of y-coordinates in the bathymetry grid" typedef ^ ^ IntKi BathGrid_npoints {:} - - "number of grid points to describe the bathymetry grid" -typedef ^ ^ SeaSt_WaveField_MiscVarType WaveField_m - - - "misc var information from the SeaState Interpolation module" - +typedef ^ ^ GridInterp_MiscVarType WaveField_m - - - "misc var information from the SeaState Interpolation module" - typedef ^ ^ LOGICAL IC_gen - .FALSE. - "boolean to indicate dynamic relaxation occuring" "-" diff --git a/modules/moordyn/src/MoorDyn_Rod.f90 b/modules/moordyn/src/MoorDyn_Rod.f90 index 3261b44be8..34b2eb9339 100644 --- a/modules/moordyn/src/MoorDyn_Rod.f90 +++ b/modules/moordyn/src/MoorDyn_Rod.f90 @@ -1148,13 +1148,15 @@ END SUBROUTINE Rod_GetNetForceAndMass ! this function handles assigning a line to a point node - SUBROUTINE Rod_AddLine(Rod, lineID, TopOfLine, endB) + SUBROUTINE Rod_AddLine(Rod, lineID, TopOfLine, endB, ErrStat, ErrMsg) Type(MD_Rod), INTENT (INOUT) :: Rod ! the Point object Integer(IntKi), INTENT( IN ) :: lineID Integer(IntKi), INTENT( IN ) :: TopOfLine Integer(IntKi), INTENT( IN ) :: endB ! add line to end B if 1, end A if 0 + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen),intent( out) :: ErrMsg if (endB==1) then ! attaching to end B @@ -1164,8 +1166,11 @@ SUBROUTINE Rod_AddLine(Rod, lineID, TopOfLine, endB) Rod%nAttachedB = Rod%nAttachedB + 1 ! add the line to the number connected Rod%AttachedB(Rod%nAttachedB) = lineID Rod%TopB(Rod%nAttachedB) = TopOfLine ! attached to line ... 1 = top/fairlead(end B), 0 = bottom/anchor(end A) + ErrStat = ErrID_None + ErrMsg = "" ELSE - call WrScr("too many lines connected to Rod "//trim(num2lstr(Rod%IdNum))//" in MoorDyn!") + ErrStat = ErrID_Fatal + ErrMsg = "too many lines connected to Rod "//trim(num2lstr(Rod%IdNum))//" in MoorDyn!" END IF else ! attaching to end A @@ -1176,8 +1181,11 @@ SUBROUTINE Rod_AddLine(Rod, lineID, TopOfLine, endB) Rod%nAttachedA = Rod%nAttachedA + 1 ! add the line to the number connected Rod%AttachedA(Rod%nAttachedA) = lineID Rod%TopA(Rod%nAttachedA) = TopOfLine ! attached to line ... 1 = top/fairlead(end B), 0 = bottom/anchor(end A) + ErrStat = ErrID_None + ErrMsg = "" ELSE - call WrScr("too many lines connected to Rod "//trim(num2lstr(Rod%IdNum))//" in MoorDyn!") + ErrStat = ErrID_Fatal + ErrMsg = "too many lines connected to Rod "//trim(num2lstr(Rod%IdNum))//" in MoorDyn!" END IF end if diff --git a/modules/moordyn/src/MoorDyn_Types.f90 b/modules/moordyn/src/MoorDyn_Types.f90 index 6b2f1f763c..86703d7aa7 100644 --- a/modules/moordyn/src/MoorDyn_Types.f90 +++ b/modules/moordyn/src/MoorDyn_Types.f90 @@ -34,6 +34,10 @@ MODULE MoorDyn_Types USE SeaSt_WaveField_Types USE NWTC_Library IMPLICIT NONE + INTEGER(IntKi), PUBLIC, PARAMETER :: MD_MaxNCoef = 30 ! maximum number of entries to allow in nonlinear coefficient lookup tables [-] + INTEGER(IntKi), PUBLIC, PARAMETER :: MD_MaxBdAtch = 100 ! maximum number of attachments to a body [-] + INTEGER(IntKi), PUBLIC, PARAMETER :: MD_MaxPtAtch = 100 ! maximum number of attachments to a point [-] + INTEGER(IntKi), PUBLIC, PARAMETER :: MD_MaxFailLines = 30 ! maximum number of line failures that can be simulated [-] ! ========= MD_InputFileType ======= TYPE, PUBLIC :: MD_InputFileType REAL(DbKi) :: DTIC = 0.5 !< convergence check time step for IC generation [[s]] @@ -84,14 +88,14 @@ MODULE MoorDyn_Types REAL(DbKi) :: cF = 0.0_R8Ki !< Center VIV synchronization in non-dimensional frequency [-] INTEGER(IntKi) :: ElasticMod = 0_IntKi !< Which elasticity model to use: {1 basic, 2 viscoelastic, 3 viscoelastic+meanload} [-] INTEGER(IntKi) :: nEApoints = 0_IntKi !< number of values in stress-strain lookup table (0 means using constant E) [-] - REAL(DbKi) , DIMENSION(1:30) :: stiffXs = 0.0_R8Ki !< x array for stress-strain lookup table (up to nCoef) [-] - REAL(DbKi) , DIMENSION(1:30) :: stiffYs = 0.0_R8Ki !< y array for stress-strain lookup table [-] + REAL(DbKi) , DIMENSION(1:MD_MaxNCoef) :: stiffXs = 0.0_R8Ki !< x array for stress-strain lookup table (up to MD_MaxNCoef) [-] + REAL(DbKi) , DIMENSION(1:MD_MaxNCoef) :: stiffYs = 0.0_R8Ki !< y array for stress-strain lookup table [-] INTEGER(IntKi) :: nBApoints = 0_IntKi !< number of values in stress-strainrate lookup table (0 means using constant c) [-] - REAL(DbKi) , DIMENSION(1:30) :: dampXs = 0.0_R8Ki !< x array for stress-strainrate lookup table (up to nCoef) [-] - REAL(DbKi) , DIMENSION(1:30) :: dampYs = 0.0_R8Ki !< y array for stress-strainrate lookup table [-] + REAL(DbKi) , DIMENSION(1:MD_MaxNCoef) :: dampXs = 0.0_R8Ki !< x array for stress-strainrate lookup table (up to MD_MaxNCoef) [-] + REAL(DbKi) , DIMENSION(1:MD_MaxNCoef) :: dampYs = 0.0_R8Ki !< y array for stress-strainrate lookup table [-] INTEGER(IntKi) :: nEIpoints = 0_IntKi !< number of values in bending stress-strain lookup table (0 means using constant E) [-] - REAL(DbKi) , DIMENSION(1:30) :: bstiffXs = 0.0_R8Ki !< x array for stress-strain lookup table (up to nCoef) [-] - REAL(DbKi) , DIMENSION(1:30) :: bstiffYs = 0.0_R8Ki !< y array for stress-strain lookup table [-] + REAL(DbKi) , DIMENSION(1:MD_MaxNCoef) :: bstiffXs = 0.0_R8Ki !< x array for stress-strain lookup table (up to MD_MaxNCoef) [-] + REAL(DbKi) , DIMENSION(1:MD_MaxNCoef) :: bstiffYs = 0.0_R8Ki !< y array for stress-strain lookup table [-] END TYPE MD_LineProp ! ======================= ! ========= MD_RodProp ======= @@ -112,12 +116,12 @@ MODULE MoorDyn_Types TYPE, PUBLIC :: MD_Body INTEGER(IntKi) :: IdNum = 0_IntKi !< integer identifier of this Point [-] INTEGER(IntKi) :: typeNum = 0_IntKi !< integer identifying the type. 0=free, 1=fixed, -1=coupled, 2=coupledpinned [-] - INTEGER(IntKi) , DIMENSION(1:30) :: AttachedC = 0_IntKi !< list of IdNums of points attached to this body [-] - INTEGER(IntKi) , DIMENSION(1:30) :: AttachedR = 0_IntKi !< list of IdNums of rods attached to this body [-] + INTEGER(IntKi) , DIMENSION(1:MD_MaxBdAtch) :: AttachedC = 0_IntKi !< list of IdNums of points attached to this body [-] + INTEGER(IntKi) , DIMENSION(1:MD_MaxBdAtch) :: AttachedR = 0_IntKi !< list of IdNums of rods attached to this body [-] INTEGER(IntKi) :: nAttachedP = 0_IntKi !< number of attached points [-] INTEGER(IntKi) :: nAttachedR = 0_IntKi !< number of attached rods [-] - REAL(DbKi) , DIMENSION(1:3,1:30) :: rPointRel = 0.0_R8Ki !< relative position of point on body [-] - REAL(DbKi) , DIMENSION(1:6,1:30) :: r6RodRel = 0.0_R8Ki !< relative position and orientation of rod on body [-] + REAL(DbKi) , DIMENSION(1:3,1:MD_MaxBdAtch) :: rPointRel = 0.0_R8Ki !< relative position of point on body [-] + REAL(DbKi) , DIMENSION(1:6,1:MD_MaxBdAtch) :: r6RodRel = 0.0_R8Ki !< relative position and orientation of rod on body [-] REAL(DbKi) :: bodyM = 0.0_R8Ki !< body mass (separate from attached objects) [[kg]] REAL(DbKi) :: bodyV = 0.0_R8Ki !< body volume (for buoyancy calculation) [[m^3]] REAL(DbKi) , DIMENSION(1:3) :: bodyI = 0.0_R8Ki !< body 3x3 inertia matrix diagonals [[kg-m^2]] @@ -149,8 +153,8 @@ MODULE MoorDyn_Types INTEGER(IntKi) :: IdNum = 0_IntKi !< integer identifier of this point [-] CHARACTER(10) :: type !< type of point: fix, vessel, point [-] INTEGER(IntKi) :: typeNum = 0_IntKi !< integer identifying the type. 1=fixed, -1=coupled, 0=free [-] - INTEGER(IntKi) , DIMENSION(1:10) :: Attached = 0_IntKi !< list of IdNums of lines attached to this point node [-] - INTEGER(IntKi) , DIMENSION(1:10) :: Top = 0_IntKi !< list of ints specifying whether each line is attached at 1 = top/fairlead(end B), 0 = bottom/anchor(end A) [-] + INTEGER(IntKi) , DIMENSION(1:MD_MaxPtAtch) :: Attached = 0_IntKi !< list of IdNums of lines attached to this point node [-] + INTEGER(IntKi) , DIMENSION(1:MD_MaxPtAtch) :: Top = 0_IntKi !< list of ints specifying whether each line is attached at 1 = top/fairlead(end B), 0 = bottom/anchor(end A) [-] INTEGER(IntKi) :: nAttached = 0_IntKi !< number of attached lines [-] REAL(DbKi) :: pointM = 0.0_R8Ki !< point mass [[kg]] REAL(DbKi) :: pointV = 0.0_R8Ki !< point volume [[m^3]] @@ -180,10 +184,10 @@ MODULE MoorDyn_Types CHARACTER(10) :: type !< type of Rod. should match one of RodProp names [-] INTEGER(IntKi) :: PropsIdNum = 0_IntKi !< the IdNum of the associated rod properties [-] INTEGER(IntKi) :: typeNum = 0_IntKi !< integer identifying the type. 0=free, 1=pinned, 2=fixed, -1=coupledpinned, -2=coupled [-] - INTEGER(IntKi) , DIMENSION(1:10) :: AttachedA = 0_IntKi !< list of IdNums of lines attached to end A [-] - INTEGER(IntKi) , DIMENSION(1:10) :: AttachedB = 0_IntKi !< list of IdNums of lines attached to end B [-] - INTEGER(IntKi) , DIMENSION(1:10) :: TopA = 0_IntKi !< list of ints specifying whether each line is attached at 1 = top/fairlead(end B), 0 = bottom/anchor(end A) [-] - INTEGER(IntKi) , DIMENSION(1:10) :: TopB = 0_IntKi !< list of ints specifying whether each line is attached at 1 = top/fairlead(end B), 0 = bottom/anchor(end A) [-] + INTEGER(IntKi) , DIMENSION(1:MD_MaxPtAtch) :: AttachedA = 0_IntKi !< list of IdNums of lines attached to end A [-] + INTEGER(IntKi) , DIMENSION(1:MD_MaxPtAtch) :: AttachedB = 0_IntKi !< list of IdNums of lines attached to end B [-] + INTEGER(IntKi) , DIMENSION(1:MD_MaxPtAtch) :: TopA = 0_IntKi !< list of ints specifying whether each line is attached at 1 = top/fairlead(end B), 0 = bottom/anchor(end A) [-] + INTEGER(IntKi) , DIMENSION(1:MD_MaxPtAtch) :: TopB = 0_IntKi !< list of ints specifying whether each line is attached at 1 = top/fairlead(end B), 0 = bottom/anchor(end A) [-] INTEGER(IntKi) :: nAttachedA = 0_IntKi !< number of attached lines to Rod end A [-] INTEGER(IntKi) :: nAttachedB = 0_IntKi !< number of attached lines to Rod end B [-] INTEGER(IntKi) , DIMENSION(1:55) :: OutFlagList = 0_IntKi !< array specifying what line quantities should be output (1 vs 0) [-] @@ -273,14 +277,14 @@ MODULE MoorDyn_Types REAL(DbKi) :: dF = 0.0_R8Ki !< +- range of VIV synchronization in non-dimensional frequency [-] REAL(DbKi) :: cF = 0.0_R8Ki !< Center VIV synchronization in non-dimensional frequency [-] INTEGER(IntKi) :: nEApoints = 0_IntKi !< number of values in stress-strain lookup table (0 means using constant E) [-] - REAL(DbKi) , DIMENSION(1:30) :: stiffXs = 0.0_R8Ki !< x array for stress-strain lookup table (up to nCoef) [-] - REAL(DbKi) , DIMENSION(1:30) :: stiffYs = 0.0_R8Ki !< y array for stress-strain lookup table [-] + REAL(DbKi) , DIMENSION(1:MD_MaxNCoef) :: stiffXs = 0.0_R8Ki !< x array for stress-strain lookup table (up to MD_MaxNCoef) [-] + REAL(DbKi) , DIMENSION(1:MD_MaxNCoef) :: stiffYs = 0.0_R8Ki !< y array for stress-strain lookup table [-] INTEGER(IntKi) :: nBApoints = 0_IntKi !< number of values in stress-strainrate lookup table (0 means using constant c) [-] - REAL(DbKi) , DIMENSION(1:30) :: dampXs = 0.0_R8Ki !< x array for stress-strainrate lookup table (up to nCoef) [-] - REAL(DbKi) , DIMENSION(1:30) :: dampYs = 0.0_R8Ki !< y array for stress-strainrate lookup table [-] + REAL(DbKi) , DIMENSION(1:MD_MaxNCoef) :: dampXs = 0.0_R8Ki !< x array for stress-strainrate lookup table (up to MD_MaxNCoef) [-] + REAL(DbKi) , DIMENSION(1:MD_MaxNCoef) :: dampYs = 0.0_R8Ki !< y array for stress-strainrate lookup table [-] INTEGER(IntKi) :: nEIpoints = 0_IntKi !< number of values in bending stress-strain lookup table (0 means using constant E) [-] - REAL(DbKi) , DIMENSION(1:30) :: bstiffXs = 0.0_R8Ki !< x array for stress-strain lookup table (up to nCoef) [-] - REAL(DbKi) , DIMENSION(1:30) :: bstiffYs = 0.0_R8Ki !< y array for stress-strain lookup table [-] + REAL(DbKi) , DIMENSION(1:MD_MaxNCoef) :: bstiffXs = 0.0_R8Ki !< x array for stress-strain lookup table (up to MD_MaxNCoef) [-] + REAL(DbKi) , DIMENSION(1:MD_MaxNCoef) :: bstiffYs = 0.0_R8Ki !< y array for stress-strain lookup table [-] REAL(DbKi) :: time = 0.0_R8Ki !< current time [[s]] REAL(DbKi) , DIMENSION(:,:), ALLOCATABLE :: r !< node positions [-] REAL(DbKi) , DIMENSION(:,:), ALLOCATABLE :: rd !< node velocities [-] @@ -338,8 +342,8 @@ MODULE MoorDyn_Types INTEGER(IntKi) :: IdNum = 0_IntKi !< integer identifier of this failure [-] INTEGER(IntKi) :: attachID = 0_IntKi !< ID of connection or Rod the lines are attached to [-] INTEGER(IntKi) :: isRod = 0_IntKi !< 1 Rod end A, 2 Rod end B, 0 if point [-] - INTEGER(IntKi) , DIMENSION(1:30) :: lineIDs = 0_IntKi !< array of one or more lines to detach (starting from 1...) [-] - INTEGER(IntKi) , DIMENSION(1:30) :: lineTops = 0_IntKi !< an array that will be FILLED IN to return which end of each line was disconnected ... 1 = top/fairlead(end B), 0 = bottom/anchor(end A) [-] + INTEGER(IntKi) , DIMENSION(1:MD_MaxFailLines) :: lineIDs = 0_IntKi !< array of one or more lines to detach (starting from 1...) [-] + INTEGER(IntKi) , DIMENSION(1:MD_MaxFailLines) :: lineTops = 0_IntKi !< an array that will be FILLED IN to return which end of each line was disconnected ... 1 = top/fairlead(end B), 0 = bottom/anchor(end A) [-] INTEGER(IntKi) :: nLinesToDetach = 0_IntKi !< how many lines to dettach [-] REAL(DbKi) :: failTime = 0.0_R8Ki !< time of failure [s] REAL(DbKi) :: failTen = 0.0_R8Ki !< tension threshold of failure [N] @@ -527,7 +531,7 @@ MODULE MoorDyn_Types REAL(DbKi) , DIMENSION(:), ALLOCATABLE :: BathGrid_Xs !< array of x-coordinates in the bathymetry grid [-] REAL(DbKi) , DIMENSION(:), ALLOCATABLE :: BathGrid_Ys !< array of y-coordinates in the bathymetry grid [-] INTEGER(IntKi) , DIMENSION(:), ALLOCATABLE :: BathGrid_npoints !< number of grid points to describe the bathymetry grid [-] - TYPE(SeaSt_WaveField_MiscVarType) :: WaveField_m !< misc var information from the SeaState Interpolation module [-] + TYPE(GridInterp_MiscVarType) :: WaveField_m !< misc var information from the SeaState Interpolation module [-] LOGICAL :: IC_gen = .FALSE. !< boolean to indicate dynamic relaxation occuring [-] END TYPE MD_MiscVarType ! ======================= @@ -4458,7 +4462,7 @@ subroutine MD_CopyMisc(SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg) end if DstMiscData%BathGrid_npoints = SrcMiscData%BathGrid_npoints end if - call SeaSt_WaveField_CopyMisc(SrcMiscData%WaveField_m, DstMiscData%WaveField_m, CtrlCode, ErrStat2, ErrMsg2) + call GridInterp_CopyMisc(SrcMiscData%WaveField_m, DstMiscData%WaveField_m, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return DstMiscData%IC_gen = SrcMiscData%IC_gen @@ -4622,7 +4626,7 @@ subroutine MD_DestroyMisc(MiscData, ErrStat, ErrMsg) if (allocated(MiscData%BathGrid_npoints)) then deallocate(MiscData%BathGrid_npoints) end if - call SeaSt_WaveField_DestroyMisc(MiscData%WaveField_m, ErrStat2, ErrMsg2) + call GridInterp_DestroyMisc(MiscData%WaveField_m, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) end subroutine @@ -4739,7 +4743,7 @@ subroutine MD_PackMisc(RF, Indata) call RegPackAlloc(RF, InData%BathGrid_Xs) call RegPackAlloc(RF, InData%BathGrid_Ys) call RegPackAlloc(RF, InData%BathGrid_npoints) - call SeaSt_WaveField_PackMisc(RF, InData%WaveField_m) + call GridInterp_PackMisc(RF, InData%WaveField_m) call RegPack(RF, InData%IC_gen) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -4891,7 +4895,7 @@ subroutine MD_UnPackMisc(RF, OutData) call RegUnpackAlloc(RF, OutData%BathGrid_Xs); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%BathGrid_Ys); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%BathGrid_npoints); if (RegCheckErr(RF, RoutineName)) return - call SeaSt_WaveField_UnpackMisc(RF, OutData%WaveField_m) ! WaveField_m + call GridInterp_UnpackMisc(RF, OutData%WaveField_m) ! WaveField_m call RegUnpack(RF, OutData%IC_gen); if (RegCheckErr(RF, RoutineName)) return end subroutine diff --git a/modules/nwtc-library/CMakeLists.txt b/modules/nwtc-library/CMakeLists.txt index f9a64c6f5f..1e5395447c 100644 --- a/modules/nwtc-library/CMakeLists.txt +++ b/modules/nwtc-library/CMakeLists.txt @@ -17,6 +17,7 @@ if (GENERATE_TYPES) generate_f90_types(src/Registry_NWTC_Library_base.txt ${CMAKE_CURRENT_SOURCE_DIR}/src/NWTC_Library_Types.f90 -noextrap) generate_f90_types(src/Registry_NWTC_Library_mesh.txt ${CMAKE_CURRENT_SOURCE_DIR}/src/NWTC_Library_IncSubs.f90 -incsubs -noextrap) + generate_f90_types(src/GridInterp.txt ${CMAKE_CURRENT_SOURCE_DIR}/src/GridInterp_Types.f90 -noextrap) # Generate Registry_NWTC_Library.txt by concatenating _base.txt and _mesh.txt set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS src/Registry_NWTC_Library_mesh.txt @@ -87,6 +88,9 @@ set(NWTCLIBS_SOURCES src/JSON.f90 src/KdTree.f90 + src/GridInterp.f90 + src/GridInterp_Types.f90 + # RanLux sources src/ranlux/RANLUX.f90 diff --git a/modules/nwtc-library/src/GridInterp.f90 b/modules/nwtc-library/src/GridInterp.f90 new file mode 100644 index 0000000000..3398ca5269 --- /dev/null +++ b/modules/nwtc-library/src/GridInterp.f90 @@ -0,0 +1,845 @@ +MODULE GridInterp + +USE GridInterp_Types + +IMPLICIT NONE + +PRIVATE SetIndex +PRIVATE GetN1D +PRIVATE GetN1Ddx + +PUBLIC GridInterp_SetParams +PUBLIC GridInterpSetup3D +PUBLIC GridInterpSetup4D +PUBLIC GridInterpSetupN + +INTERFACE GridInterp3D + MODULE PROCEDURE GridInterp3DR4 + MODULE PROCEDURE GridInterp3DR8 +END INTERFACE + +INTERFACE GridInterp3DVec + MODULE PROCEDURE GridInterp3DVecR4 + MODULE PROCEDURE GridInterp3DVecR8 +END INTERFACE + +INTERFACE GridInterp3DVec6 + MODULE PROCEDURE GridInterp3DVec6R4 + MODULE PROCEDURE GridInterp3DVec6R8 +END INTERFACE + +INTERFACE GridInterp4D + MODULE PROCEDURE GridInterp4DR4 + MODULE PROCEDURE GridInterp4DR8 +END INTERFACE + +INTERFACE GridInterp4DVec + MODULE PROCEDURE GridInterp4DVecR4 + MODULE PROCEDURE GridInterp4DVecR8 +END INTERFACE + +INTERFACE GridInterp4DVec6 + MODULE PROCEDURE GridInterp4DVec6R4 + MODULE PROCEDURE GridInterp4DVec6R8 +END INTERFACE + +INTERFACE GridInterpN + MODULE PROCEDURE GridInterpNR4 + MODULE PROCEDURE GridInterpNR8 +END INTERFACE + +INTERFACE GridInterpS + MODULE PROCEDURE GridInterpSR4 + MODULE PROCEDURE GridInterpSR8 +END INTERFACE + +CONTAINS + +Subroutine GridInterp_SetParams(dim, n, delta, pZero, IsPeriodic, p, ErrStat, ErrMsg) + + Integer(IntKi), intent(in ) :: dim + Integer(IntKi), intent(in ) :: n(:) + Real(ReKi), intent(in ) :: delta(:) + Real(ReKi), intent(in ) :: pZero(:) + Logical, intent(in ) :: IsPeriodic(:) + Type(GridInterp_ParameterType), intent(inout) :: p + + Integer(IntKi), INTENT(OUT) :: ErrStat + Character(*), INTENT(OUT) :: ErrMsg + + Integer(IntKi) :: i + + ErrStat = ErrID_None + ErrMsg = "" + + if (dim/=3 .and. dim/=4) then + ErrStat = ErrID_Fatal + ErrMsg = 'GridInterp_Init: dim must be 3 or 4' + return + end if + + do i = 1,dim + p%n(i) = n(i) + p%delta(i) = delta(i) + p%pZero(i) = pZero(i) + p%IsPeriodic(i) = IsPeriodic(i) + end do + +End Subroutine GridInterp_SetParams + +Subroutine SetIndex(pIn,pZero,delta,nMax,IsPeriodic,Indx,isopc,Support,FirstWarn,ErrStat,ErrMsg) + + Real(ReKi), intent(in ) :: pIn + Real(ReKi), intent(in ) :: pZero + Real(ReKi), intent(in ) :: delta + Integer(IntKi), intent(in ) :: nMax + Logical, intent(in ) :: IsPeriodic + Integer(IntKi), intent(inout) :: Indx(:) + Real(ReKi), intent(inout) :: isopc + Integer(IntKi), intent(inout) :: Support ! = 0 for linear interpolation, = 1 for quadratic with one point to the left, = 2 for quadratic with one point to the right, = 3 for cubic + Logical, intent(inout) :: FirstWarn + Integer(IntKi), intent( out) :: ErrStat + Character(*), intent( out) :: ErrMsg + + Real(ReKi) :: p, pMax + Integer(IntKi) :: i + + ErrStat = ErrID_None + ErrMsg = "" + + isopc = 0.0_ReKi + Indx = 0_IntKi + + if ( nMax .EQ. 1_IntKi ) then ! Only one grid point, effectively ignore this dimension + ! Construct a dummy linear interpolation for now + Indx(1) = 0_IntKi + Indx(2) = 0_IntKi + Indx(3) = 0_IntKi + Indx(4) = 0_IntKi + isopc = 0.5_ReKi + Support = 0 + return + end if + + ! Compute normalized coordinate + p = (pIn-pZero) / delta + + if (isPeriodic) then + + ! Calculate normalized coordinate between 0 and 1 + isopc = p - floor(p,ReKi) + + ! Get the normalized coordinates of the two nearest nodes to the left and to the right + Indx = floor( p, IntKi ) + [-1,0,1,2] + + ! Make sure the coordinates are not out of bound using periodicity + do i = 1,4 + if ( Indx(i) < 0 ) then + Indx(i) = Indx(i) - nMax*floor( Real(Indx(i),ReKi) / Real(nMax,ReKi) ) + end if + Indx(i) = mod(Indx(i),nMax) + end do + + ! Always use cubic interpolation for periodic dimensions + support = 3 ! Cubic interpolation + + else + + pMax = Real(nMax-1_IntKi,ReKi) + + if (p<0) then + + p = 0.0_ReKi + + if (FirstWarn) then + call SetErrStat(ErrID_Warn,'Position has been clamped to the grid boundary. Warning will not be repeated though condition may persist.',ErrStat,ErrMsg,'SetIndex') + FirstWarn = .false. + end if + + else if (p>pMax) then + + p = pMax + + if (FirstWarn) then ! don't warn if we are exactly at the boundary + call SetErrStat(ErrID_Warn,'Position has been clamped to the grid boundary. Warning will not be repeated though condition may persist.',ErrStat,ErrMsg,'SetIndex') + FirstWarn = .false. + end if + + end if + + if ( EqualRealNos( p, pMax ) ) then + ! Calculate normalized coordinate between 0 and 1 + isopc = 1.0_ReKi + ! Get the normalized coordinates of the two nearest nodes to the left and to the right + Indx = nMax + [-3,-2,-1,0] + else + ! Calculate normalized coordinate between 0 and 1 + isopc = p - floor(p,ReKi) + ! Get the normalized coordinates of the two nearest nodes to the left and to the right + Indx = floor( p, IntKi ) + [-1,0,1,2] + end if + + ! Supported interpolation method + if ( Indx(1) < 0_IntKi ) then + Indx(1) = 0_IntKi + if (Indx(4) > (nMax-1_IntKi) ) then + support = 0 ! Linear interpolation + Indx(4) = nMax-1_IntKi + else + support = 1 ! Quadratic interpolation with only one node to the left + end if + else if ( Indx(4) > (nMax-1_IntKi) ) then + support = 2 ! Quadratic interpolation with only one node to the right + Indx(4) = nMax-1_IntKi + else + support = 3 ! Cubic interpolation + end if + + end if + +End Subroutine SetIndex + +Subroutine GetN1D(isopc, support, N1D) + + real(ReKi), intent(in ) :: isopc ! isoparametric coordinates + integer(IntKi), intent(in ) :: support + real(ReKi), intent(inout) :: N1D(4) + real(ReKi) :: isopc2,isopc3 + + select case ( Support ) + case ( 3 ) ! Cubic interpolation + + isopc2 = isopc*isopc + isopc3 = isopc*isopc2 + + N1D(1) = -0.5_ReKi*isopc3 + isopc2 - 0.5_ReKi*isopc + N1D(2) = 1.5_ReKi*isopc3 - 2.5_ReKi*isopc2 + 1.0_ReKi + N1D(3) = -1.5_ReKi*isopc3 + 2.0_ReKi*isopc2 + 0.5_ReKi*isopc + N1D(4) = 0.5_ReKi*isopc3 - 0.5_ReKi*isopc2 + + case ( 1 ) ! Quadratic interpolation with only one node to the left + + isopc2 = isopc*isopc + + N1D(1) = 0.0_ReKi + N1D(2) = 0.5_ReKi*isopc2 - 1.5_ReKi*isopc + 1.0_ReKi + N1D(3) = -1.0_ReKi*isopc2 + 2.0_ReKi*isopc + N1D(4) = 0.5_ReKi*isopc2 - 0.5_ReKi*isopc + + case ( 2 ) ! Quadratic interpolation with only one node to the right + + isopc2 = isopc*isopc + + N1D(1) = 0.5_ReKi*isopc2 - 0.5_ReKi*isopc + N1D(2) = -1.0_ReKi*isopc2 + 1.0_ReKi + N1D(3) = 0.5_ReKi*isopc2 + 0.5_ReKi*isopc + N1D(4) = 0.0_ReKi + + case default ! Support == 0 Linear interpolation + + N1D(1) = 0.0_ReKi + N1D(2) = -isopc + 1.0_ReKi + N1D(3) = isopc + N1D(4) = 0.0_ReKi + + end select + +End Subroutine GetN1D + +Subroutine GetN1Ddx(isopc, support, N1D) + + real(ReKi), intent(in ) :: isopc ! isoparametric coordinates + integer(IntKi), intent(in ) :: support + real(ReKi), intent(inout) :: N1D(4) + real(ReKi) :: isopc2 + + select case ( Support ) + case ( 3 ) ! Cubic interpolation + + isopc2 = isopc*isopc + + N1D(1) = -1.5_ReKi*isopc2 + 2.0_ReKi*isopc - 0.5_ReKi + N1D(2) = 4.5_ReKi*isopc2 - 5.0_ReKi*isopc + N1D(3) = -4.5_ReKi*isopc2 + 4.0_ReKi*isopc + 0.5_ReKi + N1D(4) = 1.5_ReKi*isopc2 - isopc + + case ( 1 ) ! Quadratic interpolation with only one node to the left + + N1D(1) = 0.0_ReKi + N1D(2) = isopc - 1.5_ReKi + N1D(3) = -2.0_ReKi*isopc + 2.0_ReKi + N1D(4) = isopc - 0.5_ReKi + + case ( 2 ) ! Quadratic interpolation with only one node to the right + + N1D(1) = isopc - 0.5_ReKi + N1D(2) = -2.0_ReKi*isopc + N1D(3) = isopc + 0.5_ReKi + N1D(4) = 0.0_ReKi + + case default ! Support == 0 Linear interpolation + + N1D(1) = 0.0_ReKi + N1D(2) = -1.0_ReKi + N1D(3) = 1.0_ReKi + N1D(4) = 0.0_ReKi + + end select + +End Subroutine GetN1Ddx + +Subroutine GridInterpSetup3D( position, p, m, ErrStat, ErrMsg ) + + real(ReKi), intent(in ) :: Position(3) !< Array of 3 coordinates + type(GridInterp_ParameterType), intent(in ) :: p !< Parameters + type(GridInterp_MiscVarType), intent(inout) :: m !< MiscVars + integer(IntKi), intent( out) :: ErrStat !< Error status + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + character(*), parameter :: RoutineName = 'GridInterpSetup3D' + integer(IntKi) :: dim,i,j,k + integer(IntKi) :: support + real(ReKi) :: N1D(4,3) + real(ReKi) :: isopc ! isoparametric coordinates + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + + ErrStat = ErrID_None + ErrMsg = "" + + do dim = 1,3 + call SetIndex(Position(dim), p%pZero(dim), p%delta(dim), p%n(dim), p%IsPeriodic(dim), m%Indx(:,dim), isopc, Support, m%FirstWarn_Clamp, ErrStat2, ErrMsg2) + if (Failed()) return; + call GetN1D(isopc, Support, N1D(:,dim)) + end do + + do k = 1,4 + do j = 1,4 + do i = 1,4 + m%N3D(i,j,k) = N1D(i,1)*N1D(j,2)*N1D(k,3) + end do + end do + end do + +contains + logical function Failed() + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + Failed = ErrStat >= AbortErrLev + end function + +End Subroutine GridInterpSetup3D + +Subroutine GridInterpSetup4D( position, p, m, ErrStat, ErrMsg ) + + real(ReKi), intent(in ) :: Position(4) !< Array of 4 coordinates + type(GridInterp_ParameterType), intent(in ) :: p !< Parameters + type(GridInterp_MiscVarType), intent(inout) :: m !< MiscVars + integer(IntKi), intent( out) :: ErrStat !< Error status + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + character(*), parameter :: RoutineName = 'GridInterpSetup4D' + integer(IntKi) :: dim,i,j,k,l + integer(IntKi) :: support + real(ReKi) :: N1D(4,4) + real(ReKi) :: isopc ! isoparametric coordinates + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + + ErrStat = ErrID_None + ErrMsg = "" + + do dim = 1,4 + call SetIndex(Position(dim), p%pZero(dim), p%delta(dim), p%n(dim), p%IsPeriodic(dim), m%Indx(:,dim), isopc, Support, m%FirstWarn_Clamp, ErrStat2, ErrMsg2) + if (Failed()) return; + call GetN1D(isopc, Support, N1D(:,dim)) + end do + + do l = 1,4 + do k = 1,4 + do j = 1,4 + do i = 1,4 + m%N4D(i,j,k,l) = N1D(i,1)*N1D(j,2)*N1D(k,3)*N1D(l,4) + end do + end do + end do + end do + +contains + logical function Failed() + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + Failed = ErrStat >= AbortErrLev + end function + +End Subroutine GridInterpSetup4D + +Subroutine GridInterpSetupN( position, p, m, ErrStat, ErrMsg ) + + real(ReKi), intent(in ) :: Position(3) !< Array of 3 coordinates + type(GridInterp_ParameterType), intent(in ) :: p !< Parameters + type(GridInterp_MiscVarType), intent(inout) :: m !< MiscVars + integer(IntKi), intent( out) :: ErrStat !< Error status + character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None + + character(*), parameter :: RoutineName = 'GridInterpSetupN' + integer(IntKi) :: dim,i,j,k + integer(IntKi) :: support + real(ReKi) :: N1D(4,3) + real(ReKi) :: N1Ddx(4,2:3) + real(ReKi) :: isopc ! isoparametric coordinates + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + + ErrStat = ErrID_None + ErrMsg = "" + + do dim = 1,3 + call SetIndex(Position(dim), p%pZero(dim), p%delta(dim), p%n(dim), p%IsPeriodic(dim), m%Indx(:,dim), isopc, Support, m%FirstWarn_Clamp, ErrStat2, ErrMsg2) + if (Failed()) return; + call GetN1D(isopc, Support, N1D(:,dim)) + if (dim>1) then + call GetN1Ddx(isopc, Support, N1Ddx(:,dim)) + end if + end do + + ! Need two sets of weights for d(.)/dx and d(.)/dy. Borrow m%N4D for this. + do k = 1,4 + do j = 1,4 + do i = 1,4 + m%N4D(i,j,k,1) = N1D(i,1)*N1Ddx(j,2)*N1D (k,3) + m%N4D(i,j,k,2) = N1D(i,1)*N1D (j,2)*N1Ddx(k,3) + end do + end do + end do + +contains + logical function Failed() + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + Failed = ErrStat >= AbortErrLev + end function + +End Subroutine GridInterpSetupN + + +!============================================================================================================= +! INTERFACE GridInterp3D +! - GridInterp3DR4 +! - GridInterp3DR8 +!============================================================================================================= +function GridInterp3DR4( data, m ) + real(SiKi), intent(in ) :: data(0:,0:,0:) !< 3D grid of scalar data + type(GridInterp_MiscVarType), intent(in ) :: m !< MiscVars + + character(*), parameter :: RoutineName = 'GridInterp3DR4' + real(SiKi) :: GridInterp3DR4 + integer(IntKi) :: i,j,k + + ! interpolate + GridInterp3DR4 = 0.0_SiKi + do k = 1,4 + do j = 1,4 + do i = 1,4 + GridInterp3DR4 = GridInterp3DR4 + m%N3D(i,j,k) * data( m%Indx(i,1), m%Indx(j,2), m%Indx(k,3) ) + end do + end do + end do + +end function GridInterp3DR4 + +function GridInterp3DR8( data, m ) + real(DbKi), intent(in ) :: data(0:,0:,0:) !< 3D grid of scalar data + type(GridInterp_MiscVarType), intent(in ) :: m !< MiscVars + + character(*), parameter :: RoutineName = 'GridInterp3DR8' + real(DbKi) :: GridInterp3DR8 + integer(IntKi) :: i,j,k + + ! interpolate + GridInterp3DR8 = 0.0_DbKi + do k = 1,4 + do j = 1,4 + do i = 1,4 + GridInterp3DR8 = GridInterp3DR8 + m%N3D(i,j,k) * data( m%Indx(i,1), m%Indx(j,2), m%Indx(k,3) ) + end do + end do + end do + +end function GridInterp3DR8 + +!============================================================================================================= +! INTERFACE GridInterp3DVec +! - GridInterp3DVecR4 +! - GridInterp3DVecR8 +!============================================================================================================= +function GridInterp3DVecR4( data, m ) + real(SiKi), intent(in ) :: data(0:,0:,0:,:) !< 3D grid of vector data + type(GridInterp_MiscVarType), intent(in ) :: m !< MiscVars + + character(*), parameter :: RoutineName = 'GridInterp3DVecR4' + integer(IntKi), parameter :: vDim = 3 + integer(IntKi) :: i,j,k,vi + real(SiKi) :: GridInterp3DVecR4(vDim) + + ! interpolate + GridInterp3DVecR4 = 0.0_SiKi + do k = 1,4 + do j = 1,4 + do i = 1,4 + do vi = 1,vDim + GridInterp3DVecR4(vi) = GridInterp3DVecR4(vi) + m%N3D(i,j,k) * data( m%Indx(i,1), m%Indx(j,2), m%Indx(k,3), vi ) + end do + end do + end do + end do + +end function GridInterp3DVecR4 + +function GridInterp3DVecR8( data, m ) + real(DbKi), intent(in ) :: data(0:,0:,0:,:) !< 3D grid of vector data + type(GridInterp_MiscVarType), intent(in ) :: m !< MiscVars + + character(*), parameter :: RoutineName = 'GridInterp3DVecR8' + integer(IntKi), parameter :: vDim = 3 + integer(IntKi) :: i,j,k,vi + real(DbKi) :: GridInterp3DVecR8(vDim) + + ! interpolate + GridInterp3DVecR8 = 0.0_DbKi + do k = 1,4 + do j = 1,4 + do i = 1,4 + do vi = 1,vDim + GridInterp3DVecR8(vi) = GridInterp3DVecR8(vi) + m%N3D(i,j,k) * data( m%Indx(i,1), m%Indx(j,2), m%Indx(k,3), vi ) + end do + end do + end do + end do + +end function GridInterp3DVecR8 + +!============================================================================================================= +! INTERFACE GridInterp3DVec6 +! - GridInterp3DVec6R4 +! - GridInterp3DVec6R8 +!============================================================================================================= +function GridInterp3DVec6R4( data, m ) + real(SiKi), intent(in ) :: data(0:,0:,0:,:) !< 3D grid of vector data + type(GridInterp_MiscVarType), intent(in ) :: m !< MiscVars + + character(*), parameter :: RoutineName = 'GridInterp3DVec6R4' + integer(IntKi), parameter :: vDim = 6 + integer(IntKi) :: i,j,k,vi + real(SiKi) :: GridInterp3DVec6R4(vDim) + + ! interpolate + GridInterp3DVec6R4 = 0.0_SiKi + do k = 1,4 + do j = 1,4 + do i = 1,4 + do vi = 1,vDim + GridInterp3DVec6R4(vi) = GridInterp3DVec6R4(vi) + m%N3D(i,j,k) * data( m%Indx(i,1), m%Indx(j,2), m%Indx(k,3), vi ) + end do + end do + end do + end do + +end function GridInterp3DVec6R4 + +function GridInterp3DVec6R8( data, m ) + real(DbKi), intent(in ) :: data(0:,0:,0:,:) !< 3D grid of vector data + type(GridInterp_MiscVarType), intent(in ) :: m !< MiscVars + + character(*), parameter :: RoutineName = 'GridInterp3DVec6R8' + integer(IntKi), parameter :: vDim = 6 + integer(IntKi) :: i,j,k,vi + real(DbKi) :: GridInterp3DVec6R8(vDim) + + ! interpolate + GridInterp3DVec6R8 = 0.0_DbKi + do k = 1,4 + do j = 1,4 + do i = 1,4 + do vi = 1,vDim + GridInterp3DVec6R8(vi) = GridInterp3DVec6R8(vi) + m%N3D(i,j,k) * data( m%Indx(i,1), m%Indx(j,2), m%Indx(k,3), vi ) + end do + end do + end do + end do + +end function GridInterp3DVec6R8 + +!============================================================================================================= +! INTERFACE GridInterp4D +! - GridInterp4DR4 +! - GridInterp4DR8 +!============================================================================================================= +function GridInterp4DR4( data, m ) + real(SiKi), intent(in ) :: data(0:,0:,0:,0:) !< 4D grid of scalar data + type(GridInterp_MiscVarType), intent(in ) :: m !< MiscVars + + character(*), parameter :: RoutineName = 'GridInterp4DR4' + real(SiKi) :: GridInterp4DR4 + integer(IntKi) :: i,j,k,l + + ! interpolate + GridInterp4DR4 = 0.0_SiKi + do l = 1,4 + do k = 1,4 + do j = 1,4 + do i = 1,4 + GridInterp4DR4 = GridInterp4DR4 + m%N4D(i,j,k,l) * data( m%Indx(i,1), m%Indx(j,2), m%Indx(k,3), m%Indx(l,4) ) + end do + end do + end do + end do + +end function GridInterp4DR4 + +function GridInterp4DR8( data, m ) + real(DbKi), intent(in ) :: data(0:,0:,0:,0:) !< 4D grid of scalar data + type(GridInterp_MiscVarType), intent(in ) :: m !< MiscVars + + character(*), parameter :: RoutineName = 'GridInterp4DR8' + real(DbKi) :: GridInterp4DR8 + integer(IntKi) :: i,j,k,l + + ! interpolate + GridInterp4DR8 = 0.0_DbKi + do l = 1,4 + do k = 1,4 + do j = 1,4 + do i = 1,4 + GridInterp4DR8 = GridInterp4DR8 + m%N4D(i,j,k,l) * data( m%Indx(i,1), m%Indx(j,2), m%Indx(k,3), m%Indx(l,4) ) + end do + end do + end do + end do + +end function GridInterp4DR8 + +!============================================================================================================= +! INTERFACE GridInterp4DVec +! - GridInterp4DVecR4 +! - GridInterp4DVecR8 +!============================================================================================================= +function GridInterp4DVecR4( data, m ) + real(SiKi), intent(in ) :: data(0:,0:,0:,0:,:) !< 4D grid of vector data + type(GridInterp_MiscVarType), intent(in ) :: m !< MiscVars + + character(*), parameter :: RoutineName = 'GridInterp4DVecR4' + integer(IntKi), parameter :: vDim = 3 + integer(IntKi) :: i,j,k,l,vi + real(SiKi) :: GridInterp4DVecR4(vDim) + + ! interpolate + GridInterp4DVecR4 = 0.0_SiKi + do l = 1,4 + do k = 1,4 + do j = 1,4 + do i = 1,4 + do vi = 1,vDim + GridInterp4DVecR4(vi) = GridInterp4DVecR4(vi) + m%N4D(i,j,k,l) * data( m%Indx(i,1), m%Indx(j,2), m%Indx(k,3), m%Indx(l,4), vi ) + end do + end do + end do + end do + end do + +end function GridInterp4DVecR4 + +function GridInterp4DVecR8( data, m ) + real(DbKi), intent(in ) :: data(0:,0:,0:,0:,:) !< 4D grid of vector data + type(GridInterp_MiscVarType), intent(in ) :: m !< MiscVars + + character(*), parameter :: RoutineName = 'GridInterp4DVecR8' + integer(IntKi), parameter :: vDim = 3 + integer(IntKi) :: i,j,k,l,vi + real(DbKi) :: GridInterp4DVecR8(vDim) + + ! interpolate + GridInterp4DVecR8 = 0.0_DbKi + do l = 1,4 + do k = 1,4 + do j = 1,4 + do i = 1,4 + do vi = 1,vDim + GridInterp4DVecR8(vi) = GridInterp4DVecR8(vi) + m%N4D(i,j,k,l) * data( m%Indx(i,1), m%Indx(j,2), m%Indx(k,3), m%Indx(l,4), vi ) + end do + end do + end do + end do + end do + +end function GridInterp4DVecR8 + +!============================================================================================================= +! INTERFACE GridInterp4DVec6 +! - GridInterp4DVec6R4 +! - GridInterp4DVec6R8 +!============================================================================================================= +function GridInterp4DVec6R4( data, m ) + real(SiKi), intent(in ) :: data(0:,0:,0:,0:,:) !< 4D grid of vector data + type(GridInterp_MiscVarType), intent(in ) :: m !< MiscVars + + character(*), parameter :: RoutineName = 'GridInterp4DVec6R4' + integer(IntKi), parameter :: vDim = 6 + integer(IntKi) :: i,j,k,l,vi + real(SiKi) :: GridInterp4DVec6R4(vDim) + + ! interpolate + GridInterp4DVec6R4 = 0.0_SiKi + do l = 1,4 + do k = 1,4 + do j = 1,4 + do i = 1,4 + do vi = 1,vDim + GridInterp4DVec6R4(vi) = GridInterp4DVec6R4(vi) + m%N4D(i,j,k,l) * data( m%Indx(i,1), m%Indx(j,2), m%Indx(k,3), m%Indx(l,4), vi ) + end do + end do + end do + end do + end do + +end function GridInterp4DVec6R4 + +function GridInterp4DVec6R8( data, m ) + real(DbKi), intent(in ) :: data(0:,0:,0:,0:,:) !< 4D grid of vector data + type(GridInterp_MiscVarType), intent(in ) :: m !< MiscVars + + character(*), parameter :: RoutineName = 'GridInterp4DVec6R8' + integer(IntKi), parameter :: vDim = 6 + integer(IntKi) :: i,j,k,l,vi + real(DbKi) :: GridInterp4DVec6R8(vDim) + + ! interpolate + GridInterp4DVec6R8 = 0.0_DbKi + do l = 1,4 + do k = 1,4 + do j = 1,4 + do i = 1,4 + do vi = 1,vDim + GridInterp4DVec6R8(vi) = GridInterp4DVec6R8(vi) + m%N4D(i,j,k,l) * data( m%Indx(i,1), m%Indx(j,2), m%Indx(k,3), m%Indx(l,4), vi ) + end do + end do + end do + end do + end do + +end function GridInterp4DVec6R8 + +!============================================================================================================= +! INTERFACE GridInterpN +! - GridInterpNR4 +! - GridInterpNR8 +!============================================================================================================= +function GridInterpNR4( data, p, m ) + real(SiKi), intent(in ) :: data(0:,0:,0:) !< 3D grid of scalar data + type(GridInterp_ParameterType), intent(in ) :: p !< Parameters + type(GridInterp_MiscVarType), intent(in ) :: m !< MiscVars + + character(*), parameter :: RoutineName = 'GridInterpNR4' + real(SiKi) :: GridInterpNR4(3) + real(SiKi) :: dZetadx, dZetady + integer(IntKi) :: i,j,k + + ! interpolate slope + dZetadx = 0.0_SiKi + dZetady = 0.0_SiKi + do k = 1,4 + do j = 1,4 + do i = 1,4 + dZetadx = dZetadx + m%N4D(i,j,k,1) * data( m%Indx(i,1), m%Indx(j,2), m%Indx(k,3) ) + dZetady = dZetady + m%N4D(i,j,k,2) * data( m%Indx(i,1), m%Indx(j,2), m%Indx(k,3) ) + end do + end do + end do + dZetadx = dZetadx / p%delta(2) + dZetady = dZetady / p%delta(3) + + GridInterpNR4 = [-dZetadx,-dZetady,1.0_SiKi] + GridInterpNR4 = GridInterpNR4 / TwoNorm(GridInterpNR4) + +end function GridInterpNR4 + +function GridInterpNR8( data, p, m ) + real(DbKi), intent(in ) :: data(0:,0:,0:) !< 3D grid of scalar data + type(GridInterp_ParameterType), intent(in ) :: p !< Parameters + type(GridInterp_MiscVarType), intent(in ) :: m !< MiscVars + + character(*), parameter :: RoutineName = 'GridInterpNR8' + real(DbKi) :: GridInterpNR8(3) + real(DbKi) :: dZetadx, dZetady + integer(IntKi) :: i,j,k + + ! interpolate slope + dZetadx = 0.0_DbKi + dZetady = 0.0_DbKi + do k = 1,4 + do j = 1,4 + do i = 1,4 + dZetadx = dZetadx + m%N4D(i,j,k,1) * data( m%Indx(i,1), m%Indx(j,2), m%Indx(k,3) ) + dZetady = dZetady + m%N4D(i,j,k,2) * data( m%Indx(i,1), m%Indx(j,2), m%Indx(k,3) ) + end do + end do + end do + dZetadx = dZetadx / p%delta(2) + dZetady = dZetady / p%delta(3) + + GridInterpNR8 = (/-dZetadx,-dZetady,1.0_DbKi/) + GridInterpNR8 = GridInterpNR8 / TwoNorm(GridInterpNR8) + +end function GridInterpNR8 + +!============================================================================================================= +! INTERFACE GridInterpS +! - GridInterpSR4 +! - GridInterpSR8 +!============================================================================================================= +function GridInterpSR4( data, p, m ) + real(SiKi), intent(in ) :: data(0:,0:,0:) !< 3D grid of scalar data + type(GridInterp_ParameterType), intent(in ) :: p !< Parameters + type(GridInterp_MiscVarType), intent(in ) :: m !< MiscVars + + character(*), parameter :: RoutineName = 'GridInterpSR4' + real(SiKi) :: GridInterpSR4(2) + integer(IntKi) :: i,j,k,dir + + ! interpolate slope + GridInterpSR4 = 0.0_SiKi + do k = 1,4 + do j = 1,4 + do i = 1,4 + do dir = 1,2 + GridInterpSR4(dir) = GridInterpSR4(dir) + m%N4D(i,j,k,dir) * data( m%Indx(i,1), m%Indx(j,2), m%Indx(k,3) ) + end do + end do + end do + end do + GridInterpSR4 = GridInterpSR4 / p%delta(2:3) + +end function GridInterpSR4 + +function GridInterpSR8( data, p, m ) + real(DbKi), intent(in ) :: data(0:,0:,0:) !< 3D grid of scalar data + type(GridInterp_ParameterType), intent(in ) :: p !< Parameters + type(GridInterp_MiscVarType), intent(in ) :: m !< MiscVars + + character(*), parameter :: RoutineName = 'GridInterpSR8' + real(DbKi) :: GridInterpSR8(2) + integer(IntKi) :: i,j,k,dir + + ! interpolate slope + GridInterpSR8 = 0.0_DbKi + do k = 1,4 + do j = 1,4 + do i = 1,4 + do dir = 1,2 + GridInterpSR8(dir) = GridInterpSR8(dir) + m%N4D(i,j,k,dir) * data( m%Indx(i,1), m%Indx(j,2), m%Indx(k,3) ) + end do + end do + end do + end do + GridInterpSR8 = GridInterpSR8 / p%delta(2:3) + +end function GridInterpSR8 + +END MODULE GridInterp \ No newline at end of file diff --git a/modules/nwtc-library/src/GridInterp.txt b/modules/nwtc-library/src/GridInterp.txt new file mode 100644 index 0000000000..cf562f7cb7 --- /dev/null +++ b/modules/nwtc-library/src/GridInterp.txt @@ -0,0 +1,11 @@ +#--------------------------------------------------------------------------------------------------------------------------------------------------------- +# +#--------------------------------------------------------------------------------------------------------------------------------------------------------- +typedef GridInterp ParameterType IntKi n {4} - - "number of evenly-spaced grid points in each dimension" - +typedef ^ ParameterType ReKi delta {4} - - "size between 2 consecutive grid points in each grid direction" - +typedef ^ ParameterType ReKi pZero {4} - - "fixed position of the grid starting corner (i.e., XYZW coordinates of m%V(0,0,0,0,:))" - +typedef ^ ParameterType logical IsPeriodic {4} .false. - "flag to indicate whether this dimension should be treated as periodic" - +typedef ^ MiscVarType ReKi N3D {4}{4}{4} - - "this is the weights for 3D grid values" - +typedef ^ MiscVarType ReKi N4D {4}{4}{4}{4} - - "this is the weights for 4D grid values" - +typedef ^ MiscVarType integer Indx {4}{4} - - "this is the neighboring node index into the grid" - +typedef ^ MiscVarType logical FirstWarn_Clamp - .true. - "used to avoid too many 'Position has been clamped to the grid boundary' warning messages " - diff --git a/modules/nwtc-library/src/GridInterp_Types.f90 b/modules/nwtc-library/src/GridInterp_Types.f90 new file mode 100644 index 0000000000..798452b308 --- /dev/null +++ b/modules/nwtc-library/src/GridInterp_Types.f90 @@ -0,0 +1,151 @@ +!STARTOFREGISTRYGENERATEDFILE 'GridInterp_Types.f90' +! +! WARNING This file is generated automatically by the FAST registry. +! Do not edit. Your changes to this file will be lost. +! +! FAST Registry +!********************************************************************************************************************************* +! GridInterp_Types +!................................................................................................................................. +! This file is part of GridInterp. +! +! Copyright (C) 2012-2016 National Renewable Energy Laboratory +! +! Licensed under the Apache License, Version 2.0 (the "License"); +! you may not use this file except in compliance with the License. +! You may obtain a copy of the License at +! +! http://www.apache.org/licenses/LICENSE-2.0 +! +! Unless required by applicable law or agreed to in writing, software +! distributed under the License is distributed on an "AS IS" BASIS, +! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +! See the License for the specific language governing permissions and +! limitations under the License. +! +! +! W A R N I N G : This file was automatically generated from the FAST registry. Changes made to this file may be lost. +! +!********************************************************************************************************************************* +!> This module contains the user-defined types needed in GridInterp. It also contains copy, destroy, pack, and +!! unpack routines associated with each defined data type. This code is automatically generated by the FAST Registry. +MODULE GridInterp_Types +!--------------------------------------------------------------------------------------------------------------------------------- +USE NWTC_Library +IMPLICIT NONE +! ========= GridInterp_ParameterType ======= + TYPE, PUBLIC :: GridInterp_ParameterType + INTEGER(IntKi) , DIMENSION(1:4) :: n = 0_IntKi !< number of evenly-spaced grid points in each dimension [-] + REAL(ReKi) , DIMENSION(1:4) :: delta = 0.0_ReKi !< size between 2 consecutive grid points in each grid direction [-] + REAL(ReKi) , DIMENSION(1:4) :: pZero = 0.0_ReKi !< fixed position of the grid starting corner (i.e., XYZW coordinates of m%V(0,0,0,0,:)) [-] + LOGICAL , DIMENSION(1:4) :: IsPeriodic = .false. !< flag to indicate whether this dimension should be treated as periodic [-] + END TYPE GridInterp_ParameterType +! ======================= +! ========= GridInterp_MiscVarType ======= + TYPE, PUBLIC :: GridInterp_MiscVarType + REAL(ReKi) , DIMENSION(1:4,1:4,1:4) :: N3D = 0.0_ReKi !< this is the weights for 3D grid values [-] + REAL(ReKi) , DIMENSION(1:4,1:4,1:4,1:4) :: N4D = 0.0_ReKi !< this is the weights for 4D grid values [-] + INTEGER(IntKi) , DIMENSION(1:4,1:4) :: Indx = 0_IntKi !< this is the neighboring node index into the grid [-] + LOGICAL :: FirstWarn_Clamp = .true. !< used to avoid too many 'Position has been clamped to the grid boundary' warning messages [-] + END TYPE GridInterp_MiscVarType +! ======================= + +contains + +subroutine GridInterp_CopyParam(SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg) + type(GridInterp_ParameterType), intent(in) :: SrcParamData + type(GridInterp_ParameterType), intent(inout) :: DstParamData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'GridInterp_CopyParam' + ErrStat = ErrID_None + ErrMsg = '' + DstParamData%n = SrcParamData%n + DstParamData%delta = SrcParamData%delta + DstParamData%pZero = SrcParamData%pZero + DstParamData%IsPeriodic = SrcParamData%IsPeriodic +end subroutine + +subroutine GridInterp_DestroyParam(ParamData, ErrStat, ErrMsg) + type(GridInterp_ParameterType), intent(inout) :: ParamData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'GridInterp_DestroyParam' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine GridInterp_PackParam(RF, Indata) + type(RegFile), intent(inout) :: RF + type(GridInterp_ParameterType), intent(in) :: InData + character(*), parameter :: RoutineName = 'GridInterp_PackParam' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%n) + call RegPack(RF, InData%delta) + call RegPack(RF, InData%pZero) + call RegPack(RF, InData%IsPeriodic) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine GridInterp_UnPackParam(RF, OutData) + type(RegFile), intent(inout) :: RF + type(GridInterp_ParameterType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'GridInterp_UnPackParam' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%n); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%delta); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%pZero); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%IsPeriodic); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine GridInterp_CopyMisc(SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg) + type(GridInterp_MiscVarType), intent(in) :: SrcMiscData + type(GridInterp_MiscVarType), intent(inout) :: DstMiscData + integer(IntKi), intent(in ) :: CtrlCode + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'GridInterp_CopyMisc' + ErrStat = ErrID_None + ErrMsg = '' + DstMiscData%N3D = SrcMiscData%N3D + DstMiscData%N4D = SrcMiscData%N4D + DstMiscData%Indx = SrcMiscData%Indx + DstMiscData%FirstWarn_Clamp = SrcMiscData%FirstWarn_Clamp +end subroutine + +subroutine GridInterp_DestroyMisc(MiscData, ErrStat, ErrMsg) + type(GridInterp_MiscVarType), intent(inout) :: MiscData + integer(IntKi), intent( out) :: ErrStat + character(*), intent( out) :: ErrMsg + character(*), parameter :: RoutineName = 'GridInterp_DestroyMisc' + ErrStat = ErrID_None + ErrMsg = '' +end subroutine + +subroutine GridInterp_PackMisc(RF, Indata) + type(RegFile), intent(inout) :: RF + type(GridInterp_MiscVarType), intent(in) :: InData + character(*), parameter :: RoutineName = 'GridInterp_PackMisc' + if (RF%ErrStat >= AbortErrLev) return + call RegPack(RF, InData%N3D) + call RegPack(RF, InData%N4D) + call RegPack(RF, InData%Indx) + call RegPack(RF, InData%FirstWarn_Clamp) + if (RegCheckErr(RF, RoutineName)) return +end subroutine + +subroutine GridInterp_UnPackMisc(RF, OutData) + type(RegFile), intent(inout) :: RF + type(GridInterp_MiscVarType), intent(inout) :: OutData + character(*), parameter :: RoutineName = 'GridInterp_UnPackMisc' + if (RF%ErrStat /= ErrID_None) return + call RegUnpack(RF, OutData%N3D); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%N4D); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%Indx); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%FirstWarn_Clamp); if (RegCheckErr(RF, RoutineName)) return +end subroutine + +END MODULE GridInterp_Types + +!ENDOFREGISTRYGENERATEDFILE diff --git a/modules/nwtc-library/src/NWTC_C_Binding.f90 b/modules/nwtc-library/src/NWTC_C_Binding.f90 index ca3a574710..fa95c356c5 100644 --- a/modules/nwtc-library/src/NWTC_C_Binding.f90 +++ b/modules/nwtc-library/src/NWTC_C_Binding.f90 @@ -32,17 +32,18 @@ MODULE NWTC_C_Binding ! to correctly handle different lengths of the strings INTEGER(IntKi), PARAMETER :: ErrMsgLen_C = ErrMsgLen + 1 ! Currently, this is 8197 INTEGER(IntKi), PARAMETER :: IntfStrLen = 1025 ! length of other strings through the C interface such as file paths +integer(c_int), PARAMETER :: AbortErrLev_C = 4_c_int CONTAINS !> This routine sets the error status in C_CHAR for export to calling code. SUBROUTINE SetErrStat_F2C(ErrStat_F, ErrMsg_F, ErrStat_C, ErrMsg_C) - INTEGER, INTENT(IN ) :: ErrStat_F !< aggregated error status (fortran type) + INTEGER(IntKi), INTENT(IN ) :: ErrStat_F !< aggregated error status (fortran type) CHARACTER(ErrMsgLen), INTENT(IN ) :: ErrMsg_F !< aggregated error message (fortran type) INTEGER(C_INT), INTENT( OUT) :: ErrStat_C CHARACTER(KIND=C_CHAR), INTENT( OUT) :: ErrMsg_C(ErrMsgLen_C) - ErrStat_C = ErrStat_F ! We will send back the same error status that is used in OpenFAST + ErrStat_C = int(ErrStat_F,c_int) ! We will send back the same error status that is used in OpenFAST if (ErrMsgLen > ErrMsgLen_C-1) then ! If ErrMsgLen is > the space in ErrMsg_C, do not copy everything over ErrMsg_C = TRANSFER( TRIM(ErrMsg_F(1:ErrMsgLen_C-1))//C_NULL_CHAR, ErrMsg_C ) else diff --git a/modules/nwtc-library/src/NWTC_IO.f90 b/modules/nwtc-library/src/NWTC_IO.f90 index 67bbe42136..1fbe24a6cc 100644 --- a/modules/nwtc-library/src/NWTC_IO.f90 +++ b/modules/nwtc-library/src/NWTC_IO.f90 @@ -2354,6 +2354,8 @@ SUBROUTINE NWTC_DisplaySyntax( DefaultInputFile, ThisProgName ) //TRIM( DefaultInputFile )//'".' ) END IF CALL WrScr ( NewLine//' Note: values enclosed in square brackets [] are optional. Do not enter the brackets.') + CALL WrScr ( NewLine//' For more information and documentation, visit:' ) + CALL WrScr ( ' https://openfast.readthedocs.io/' ) CALL WrScr ( ' ') END SUBROUTINE NWTC_DisplaySyntax @@ -2398,8 +2400,9 @@ SUBROUTINE OpenBInpFile ( Un, InFile, ErrStat, ErrMsg ) OPEN( Un, FILE=TRIM( InFile ), STATUS='OLD', FORM='UNFORMATTED', ACCESS='STREAM', IOSTAT=ErrStat, ACTION='READ' ) IF ( ErrStat /= 0 ) THEN + ErrMsg = 'OpenBInpFile:Cannot open file "'//TRIM( InFile )//'" for reading. Another program may have locked it.' & + //' (IOSTAT is '//TRIM(Num2LStr(ErrStat))//')' ErrStat = ErrID_Fatal - ErrMsg = 'OpenBInpFile:Cannot open file "'//TRIM( InFile )//'" for reading. Another program may have locked it.' ELSE ErrStat = ErrID_None ErrMsg = '' @@ -2427,9 +2430,9 @@ SUBROUTINE OpenBOutFile ( Un, OutFile, ErrStat, ErrMsg ) OPEN( Un, FILE=TRIM( OutFile ), STATUS='UNKNOWN', FORM='UNFORMATTED' , ACCESS='STREAM', IOSTAT=ErrStat, ACTION='WRITE' ) IF ( ErrStat /= 0 ) THEN - ErrStat = ErrID_Fatal ErrMsg = 'OpenBOutFile:Cannot open file "'//TRIM( OutFile )//'". Another program may have locked it for writing.' & //' (IOSTAT is '//TRIM(Num2LStr(ErrStat))//')' + ErrStat = ErrID_Fatal ELSE ErrStat = ErrID_None ErrMsg = '' diff --git a/modules/nwtc-library/src/SysFlangLinux.f90 b/modules/nwtc-library/src/SysFlangLinux.f90 index 42f04c2d60..621e405528 100644 --- a/modules/nwtc-library/src/SysFlangLinux.f90 +++ b/modules/nwtc-library/src/SysFlangLinux.f90 @@ -55,7 +55,7 @@ MODULE SysSubs END INTERFACE INTEGER, PARAMETER :: ConRecL = 120 ! The record length for console output. - INTEGER, PARAMETER :: CU = 6 ! The I/O unit for the console. Unit 6 causes ADAMS to crash. + INTEGER, PUBLIC :: CU = 6 ! The I/O unit for the console. Unit 6 causes ADAMS to crash. INTEGER, PARAMETER :: MaxWrScrLen = 98 ! The maximum number of characters allowed to be written to a line in WrScr LOGICAL, PARAMETER :: KBInputOK = .TRUE. ! A flag to tell the program that keyboard input is allowed in the environment. CHARACTER(*), PARAMETER :: NewLine = ACHAR(10) ! The delimiter for New Lines [ Windows is CHAR(13)//CHAR(10); MAC is CHAR(13); Unix is CHAR(10) {CHAR(13)=\r is a line feed, CHAR(10)=\n is a new line}] @@ -107,7 +107,15 @@ FUNCTION dlClose(handle) BIND(C,NAME="dlclose") CONTAINS - + !======================================================================= + SUBROUTINE SetConsoleUnit( Unit ) + ! This subroutine sets the console unit for output. + + INTEGER, INTENT(IN) :: Unit !< The new I/O unit number for the console. + CU = Unit + + END SUBROUTINE SetConsoleUnit + !======================================================================= FUNCTION Is_NaN( DblNum ) ! This routine determines if a REAL(DbKi) variable holds a proper number. @@ -458,4 +466,4 @@ SUBROUTINE FreeDynamicLib ( DLL, ErrStat, ErrMsg ) END SUBROUTINE FreeDynamicLib !======================================================================= END MODULE SysSubs - \ No newline at end of file + diff --git a/modules/nwtc-library/src/SysGnuLinux.f90 b/modules/nwtc-library/src/SysGnuLinux.f90 index d45d88b3c4..0fb51921b3 100644 --- a/modules/nwtc-library/src/SysGnuLinux.f90 +++ b/modules/nwtc-library/src/SysGnuLinux.f90 @@ -56,7 +56,7 @@ MODULE SysSubs END INTERFACE INTEGER, PARAMETER :: ConRecL = 120 ! The record length for console output. - INTEGER, PARAMETER :: CU = 6 ! The I/O unit for the console. + INTEGER, PUBLIC :: CU = 6 ! The I/O unit for the console (Can be changed with SetConsoleUnit subroutine) INTEGER, PARAMETER :: MaxWrScrLen = 98 ! The maximum number of characters allowed to be written to a line in WrScr LOGICAL, PARAMETER :: KBInputOK = .TRUE. ! A flag to tell the program that keyboard input is allowed in the environment. CHARACTER(*), PARAMETER :: NewLine = ACHAR(10) ! The delimiter for New Lines [ Windows is CHAR(13)//CHAR(10); MAC is CHAR(13); Unix is CHAR(10) {CHAR(13)=\r is a line feed, CHAR(10)=\n is a new line}] @@ -89,6 +89,14 @@ FUNCTION FileSize( Unit ) RETURN END FUNCTION FileSize ! ( Unit ) !======================================================================= +SUBROUTINE SetConsoleUnit( Unit ) + ! This subroutine sets the console unit for output. + +INTEGER, INTENT(IN) :: Unit !< The new I/O unit number for the console. +CU = Unit + +END SUBROUTINE SetConsoleUnit +!======================================================================= FUNCTION Is_NaN( DblNum ) ! This routine determines if a REAL(DbKi) variable holds a proper number. diff --git a/modules/nwtc-library/src/SysGnuWin.f90 b/modules/nwtc-library/src/SysGnuWin.f90 index 0905792bbc..95794cf195 100644 --- a/modules/nwtc-library/src/SysGnuWin.f90 +++ b/modules/nwtc-library/src/SysGnuWin.f90 @@ -56,7 +56,7 @@ MODULE SysSubs END INTERFACE INTEGER, PARAMETER :: ConRecL = 120 ! The record length for console output. - INTEGER, PARAMETER :: CU = 6 ! The I/O unit for the console. + INTEGER, PUBLIC :: CU = 6 ! The I/O unit for the console (Can be changed with SetConsoleUnit subroutine) INTEGER, PARAMETER :: MaxWrScrLen = 98 ! The maximum number of characters allowed to be written to a line in WrScr LOGICAL, PARAMETER :: KBInputOK = .TRUE. ! A flag to tell the program that keyboard input is allowed in the environment. CHARACTER(*), PARAMETER :: NewLine = ACHAR(10) ! The delimiter for New Lines [ Windows is CHAR(13)//CHAR(10); MAC is CHAR(13); Unix is CHAR(10) {CHAR(13)=\r is a line feed, CHAR(10)=\n is a new line}] @@ -89,6 +89,14 @@ FUNCTION FileSize( Unit ) RETURN END FUNCTION FileSize ! ( Unit ) !======================================================================= +SUBROUTINE SetConsoleUnit( Unit ) + ! This subroutine sets the console unit for output. + +INTEGER, INTENT(IN) :: Unit !< The new I/O unit number for the console. +CU = Unit + +END SUBROUTINE SetConsoleUnit +!======================================================================= FUNCTION Is_NaN( DblNum ) ! This routine determines if a REAL(DbKi) variable holds a proper number. diff --git a/modules/nwtc-library/src/SysIFL.f90 b/modules/nwtc-library/src/SysIFL.f90 index c6c439e890..f2ccbe1cde 100644 --- a/modules/nwtc-library/src/SysIFL.f90 +++ b/modules/nwtc-library/src/SysIFL.f90 @@ -56,7 +56,7 @@ MODULE SysSubs END INTERFACE INTEGER, PARAMETER :: ConRecL = 120 ! The record length for console output. - INTEGER, PARAMETER :: CU = 6 ! The I/O unit for the console. + INTEGER, PUBLIC :: CU = 6 ! The I/O unit for the console (Can be changed with SetConsoleUnit subroutine) INTEGER, PARAMETER :: MaxWrScrLen = 98 ! The maximum number of characters allowed to be written to a line in WrScr LOGICAL, PARAMETER :: KBInputOK = .TRUE. ! A flag to tell the program that keyboard input is allowed in the environment. CHARACTER(*), PARAMETER :: NewLine = ACHAR(10) ! The delimiter for New Lines [ Windows is CHAR(13)//CHAR(10); MAC is CHAR(13); Unix is CHAR(10) {CHAR(13)=\r is a line feed, CHAR(10)=\n is a new line}] @@ -91,6 +91,14 @@ FUNCTION FileSize( Unit ) RETURN END FUNCTION FileSize ! ( Unit ) !======================================================================= +SUBROUTINE SetConsoleUnit( Unit ) + ! This subroutine sets the console unit for output. + +INTEGER, INTENT(IN) :: Unit !< The new I/O unit number for the console. +CU = Unit + +END SUBROUTINE SetConsoleUnit +!======================================================================= FUNCTION Is_NaN( DblNum ) ! This routine determines if a REAL(DbKi) variable holds a proper number. diff --git a/modules/nwtc-library/src/SysIVF.f90 b/modules/nwtc-library/src/SysIVF.f90 index 2a6304dbdb..d0e7227657 100644 --- a/modules/nwtc-library/src/SysIVF.f90 +++ b/modules/nwtc-library/src/SysIVF.f90 @@ -56,7 +56,7 @@ MODULE SysSubs END INTERFACE INTEGER, PARAMETER :: ConRecL = 120 ! The record length for console output. - INTEGER, PARAMETER :: CU = 7 ! The I/O unit for the console. + INTEGER, PUBLIC :: CU = 7 ! The I/O unit for the console (Can be changed with SetConsoleUnit subroutine) INTEGER, PARAMETER :: MaxWrScrLen = 98 ! The maximum number of characters allowed to be written to a line in WrScr LOGICAL, PARAMETER :: KBInputOK = .TRUE. ! A flag to tell the program that keyboard input is allowed in the environment. CHARACTER(*), PARAMETER :: NewLine = ACHAR(10) ! The delimiter for New Lines [ Windows is CHAR(13)//CHAR(10); MAC is CHAR(13); Unix is CHAR(10) {CHAR(13)=\r is a line feed, CHAR(10)=\n is a new line}]; Note: NewLine change to ACHAR(10) here on Windows to fix issues with C/Fortran interoperability using WrScr @@ -90,6 +90,14 @@ FUNCTION FileSize( Unit ) RETURN END FUNCTION FileSize ! ( Unit ) !======================================================================= +SUBROUTINE SetConsoleUnit( Unit ) + ! This subroutine sets the console unit for output. + +INTEGER, INTENT(IN) :: Unit !< The new I/O unit number for the console. +CU = Unit + +END SUBROUTINE SetConsoleUnit +!======================================================================= FUNCTION Is_NaN( DblNum ) ! This routine determines if a REAL(DbKi) variable holds a NaN (not-a-number) value. diff --git a/modules/nwtc-library/src/SysIVF_Labview.f90 b/modules/nwtc-library/src/SysIVF_Labview.f90 index 529ed52f32..ac42539000 100644 --- a/modules/nwtc-library/src/SysIVF_Labview.f90 +++ b/modules/nwtc-library/src/SysIVF_Labview.f90 @@ -73,7 +73,7 @@ MODULE SysSubs INTEGER, PARAMETER :: ConRecL = 120 ! The record length for console output. - INTEGER, PARAMETER :: CU = 7 ! The I/O unit for the console. + INTEGER, PUBLIC :: CU = 7 ! The I/O unit for the console (Can be changed with SetConsoleUnit subroutine) INTEGER, PARAMETER :: MaxWrScrLen = 98 ! The maximum number of characters allowed to be written to a line in WrScr LOGICAL, PARAMETER :: KBInputOK = .FALSE. ! A flag to tell the program that keyboard input is allowed in the environment. @@ -125,6 +125,14 @@ FUNCTION FileSize( Unit ) RETURN END FUNCTION FileSize ! ( Unit ) +!======================================================================= + SUBROUTINE SetConsoleUnit( Unit ) + ! This subroutine sets the console unit for output. + + INTEGER, INTENT(IN) :: Unit !< The new I/O unit number for the console. + CU = Unit + + END SUBROUTINE SetConsoleUnit !======================================================================= SUBROUTINE FlushOut ( Unit ) diff --git a/modules/nwtc-library/src/SysMatlabLinuxGnu.f90 b/modules/nwtc-library/src/SysMatlabLinuxGnu.f90 index d69f227433..6b2a847d24 100644 --- a/modules/nwtc-library/src/SysMatlabLinuxGnu.f90 +++ b/modules/nwtc-library/src/SysMatlabLinuxGnu.f90 @@ -59,7 +59,7 @@ MODULE SysSubs END INTERFACE INTEGER, PARAMETER :: ConRecL = 120 ! The record length for console output. - INTEGER, PARAMETER :: CU = 6 ! The I/O unit for the console. + INTEGER, PUBLIC :: CU = 6 ! The I/O unit for the console (Can be changed with SetConsoleUnit subroutine) INTEGER, PARAMETER :: MaxWrScrLen = 98 ! The maximum number of characters allowed to be written to a line in WrScr LOGICAL, PARAMETER :: KBInputOK = .FALSE. ! A flag to tell the program that keyboard input is allowed in the environment. CHARACTER(*), PARAMETER :: NewLine = ACHAR(10) ! The delimiter for New Lines [ Windows is CHAR(13)//CHAR(10); MAC is CHAR(13); Unix is CHAR(10) {CHAR(13)=\r is a line feed, CHAR(10)=\n is a new line}] @@ -92,6 +92,14 @@ FUNCTION FileSize( Unit ) RETURN END FUNCTION FileSize ! ( Unit ) !======================================================================= +SUBROUTINE SetConsoleUnit( Unit ) + ! This subroutine sets the console unit for output. + +INTEGER, INTENT(IN) :: Unit !< The new I/O unit number for the console. +CU = Unit + +END SUBROUTINE SetConsoleUnit +!======================================================================= FUNCTION Is_NaN( DblNum ) ! This routine determines if a REAL(DbKi) variable holds a proper number. diff --git a/modules/nwtc-library/src/SysMatlabLinuxIntel.f90 b/modules/nwtc-library/src/SysMatlabLinuxIntel.f90 index c521e449f0..a31dc15cad 100644 --- a/modules/nwtc-library/src/SysMatlabLinuxIntel.f90 +++ b/modules/nwtc-library/src/SysMatlabLinuxIntel.f90 @@ -59,7 +59,7 @@ MODULE SysSubs END INTERFACE INTEGER, PARAMETER :: ConRecL = 120 ! The record length for console output. - INTEGER, PARAMETER :: CU = 6 ! The I/O unit for the console. + INTEGER, PUBLIC :: CU = 6 ! The I/O unit for the console (Can be changed with SetConsoleUnit subroutine) INTEGER, PARAMETER :: MaxWrScrLen = 98 ! The maximum number of characters allowed to be written to a line in WrScr LOGICAL, PARAMETER :: KBInputOK = .FALSE. ! A flag to tell the program that keyboard input is allowed in the environment. CHARACTER(*), PARAMETER :: NewLine = ACHAR(10) ! The delimiter for New Lines [ Windows is CHAR(13)//CHAR(10); MAC is CHAR(13); Unix is CHAR(10) {CHAR(13)=\r is a line feed, CHAR(10)=\n is a new line}] @@ -94,6 +94,14 @@ FUNCTION FileSize( Unit ) RETURN END FUNCTION FileSize ! ( Unit ) !======================================================================= +SUBROUTINE SetConsoleUnit( Unit ) + ! This subroutine sets the console unit for output. + +INTEGER, INTENT(IN) :: Unit !< The new I/O unit number for the console. +CU = Unit + +END SUBROUTINE SetConsoleUnit +!======================================================================= FUNCTION Is_NaN( DblNum ) ! This routine determines if a REAL(DbKi) variable holds a proper number. diff --git a/modules/nwtc-library/src/SysMatlabWindows.f90 b/modules/nwtc-library/src/SysMatlabWindows.f90 index 09c931aff2..c462d92df9 100644 --- a/modules/nwtc-library/src/SysMatlabWindows.f90 +++ b/modules/nwtc-library/src/SysMatlabWindows.f90 @@ -47,7 +47,7 @@ MODULE SysSubs !======================================================================= - INTEGER, PARAMETER :: CU = 6 ! The I/O unit for the console. + INTEGER, PUBLIC :: CU = 6 ! The I/O unit for the console (Can be changed with SetConsoleUnit subroutine) INTEGER, PARAMETER :: MaxWrScrLen = 98 ! The maximum number of characters allowed to be written to a line in WrScr LOGICAL, PARAMETER :: KBInputOK = .FALSE. ! A flag to tell the program that keyboard input is allowed in the environment. @@ -82,6 +82,14 @@ FUNCTION FileSize( Unit ) RETURN END FUNCTION FileSize ! ( Unit ) +!======================================================================= +SUBROUTINE SetConsoleUnit( Unit ) + ! This subroutine sets the console unit for output. + +INTEGER, INTENT(IN) :: Unit !< The new I/O unit number for the console. +CU = Unit + +END SUBROUTINE SetConsoleUnit !======================================================================= SUBROUTINE FlushOut ( Unit ) diff --git a/modules/nwtc-library/tests/NWTC_Library_test_tools.F90 b/modules/nwtc-library/tests/NWTC_Library_test_tools.F90 index 01df14383a..685cefcff1 100644 --- a/modules/nwtc-library/tests/NWTC_Library_test_tools.F90 +++ b/modules/nwtc-library/tests/NWTC_Library_test_tools.F90 @@ -12,16 +12,14 @@ module nwtc_library_test_tools character(11), parameter :: terminal="/dev/stdout" #endif -integer, parameter :: stdout=CU - contains subroutine hide_terminal_output() - open(unit=stdout, file=trim(nullfile)) + open(unit=CU, file=trim(nullfile)) end subroutine subroutine show_terminal_output() - open(unit=stdout, file=terminal, status="old") + open(unit=CU, file=terminal, status="old") end subroutine end module diff --git a/modules/openfast-library/src/FAST_AeroMap.f90 b/modules/openfast-library/src/FAST_AeroMap.f90 index ce7268c7ef..67b5393549 100644 --- a/modules/openfast-library/src/FAST_AeroMap.f90 +++ b/modules/openfast-library/src/FAST_AeroMap.f90 @@ -120,6 +120,12 @@ subroutine FAST_AeroMapDriver(AM, m, p_FAST, m_FAST, y_FAST, T, ErrStat, ErrMsg) CompAeroMaps, ErrStat2, ErrMsg2) if (Failed()) return + ! If BeamDyn blades are being used, return error + if (T%p_FAST%CompElast == Module_BD) then + call SetErrStat(ErrID_Fatal, "AeroMap does not currently work with BeamDyn blades, support will be added in a future version of OpenFAST", ErrStat, ErrMsg, RoutineName) + return + end if + ! Initialize module data transfer mappings call FAST_InitMappings(m%Mappings, m%ModData, T, ErrStat2, ErrMsg2) if (Failed()) return diff --git a/modules/openfast-library/src/FAST_Subs.f90 b/modules/openfast-library/src/FAST_Subs.f90 index d3f6974156..f1cdb30760 100644 --- a/modules/openfast-library/src/FAST_Subs.f90 +++ b/modules/openfast-library/src/FAST_Subs.f90 @@ -584,6 +584,7 @@ SUBROUTINE FAST_InitializeAll( t_initial, m_Glue, p_FAST, y_FAST, m_FAST, ED, SE Init%InData_SeaSt%hasIce = p_FAST%CompIce /= Module_None Init%InData_SeaSt%InputFile = p_FAST%SeaStFile Init%InData_SeaSt%OutRootName = TRIM(p_FAST%OutFileRoot)//'.'//TRIM(y_FAST%Module_Abrev(Module_SeaSt)) + Init%InData_SeaSt%WaveTimeShift = 0.0_DbKi ! for phase shifting wave field in time (positive value only) ! these values support wave field handling Init%InData_SeaSt%WaveFieldMod = p_FAST%WaveFieldMod @@ -1112,13 +1113,6 @@ SUBROUTINE FAST_InitializeAll( t_initial, m_Glue, p_FAST, y_FAST, m_FAST, ED, SE !bjj: until we modify this, MAP requires HydroDyn to be used. (perhaps we could send air density from AeroDyn or something...) - ! If mode shape visualization requested when MAP is active, set error and return - if (p_FAST%WrVTK == VTK_ModeShapes) then - call SetErrStat(ErrID_Fatal, "Mode shape visualization is not supported when using MAP.", ErrStat, ErrMsg, RoutineName) - call Cleanup() - return - end if - CALL WrScr(NewLine) !bjj: I'm printing two blank lines here because MAP seems to be writing over the last line on the screen. ! Init%InData_MAP%rootname = p_FAST%OutFileRoot ! Output file name @@ -1954,7 +1948,7 @@ SUBROUTINE ValidateInputData(p, m_FAST, ErrStat, ErrMsg) IF ( p%TMax < 0.0_DbKi ) THEN CALL SetErrStat( ErrID_Fatal, 'TMax must not be a negative number.', ErrStat, ErrMsg, RoutineName ) ELSE IF ( p%TMax < p%TStart ) THEN - CALL SetErrStat( ErrID_Fatal, 'TStart ('//trim(num2lstr(p%TStart))//') should be greater than TMax ('//trim(num2lstr(p%TMax))//') in OpenFAST input file.', ErrStat, ErrMsg, RoutineName ) + CALL SetErrStat( ErrID_Fatal, 'TMax ('//trim(num2lstr(p%TMax))//') should be greater than TStart ('//trim(num2lstr(p%TStart))//') in the OpenFAST input file.', ErrStat, ErrMsg, RoutineName ) END IF IF ( p%n_ChkptTime < p%n_TMax_m1 ) THEN @@ -2066,7 +2060,7 @@ SUBROUTINE ValidateInputData(p, m_FAST, ErrStat, ErrMsg) IF (p%MHK /= MHK_None .and. p%MHK /= MHK_FixedBottom .and. p%MHK /= MHK_Floating) CALL SetErrStat( ErrID_Fatal, 'MHK switch is invalid. Set MHK to 0, 1, or 2 in the FAST input file.', ErrStat, ErrMsg, RoutineName ) - IF (p%MHK /= MHK_None .and. p%CompSeaSt == Module_SeaSt .and. p%CompInflow /= Module_IfW) CALL SetErrStat( ErrID_Fatal, 'InflowWind must be activated for MHK turbines when SeaState is used.', ErrStat, ErrMsg, RoutineName ) + ! IF (p%MHK /= MHK_None .and. p%CompSeaSt == Module_SeaSt .and. p%CompInflow /= Module_IfW) CALL SetErrStat( ErrID_Fatal, 'InflowWind must be activated for MHK turbines when SeaState is used.', ErrStat, ErrMsg, RoutineName ) IF (p%Gravity < 0.0_ReKi) CALL SetErrStat( ErrID_Fatal, 'Gravity must not be negative.', ErrStat, ErrMsg, RoutineName ) @@ -7128,13 +7122,7 @@ SUBROUTINE FAST_CreateCheckpoint_T(t_initial, n_t_global, NumTurbines, Turbine, ! init error status ErrStat = ErrID_None ErrMsg = "" - - ! Writing checkpoint files is not supported when using MAP - if (Turbine%p_FAST%CompMooring == Module_MAP) then - call SetErrStat(ErrID_Fatal, "Writing checkpoint files is not supported when using MAP.", ErrStat, ErrMsg, RoutineName) - return - end if - + FileName = TRIM(CheckpointRoot)//'.chkp' DLLFileName = TRIM(CheckpointRoot)//'.dll.chkp' diff --git a/modules/openfast-registry/src/registry.hpp b/modules/openfast-registry/src/registry.hpp index eb7112f029..48a92447ca 100644 --- a/modules/openfast-registry/src/registry.hpp +++ b/modules/openfast-registry/src/registry.hpp @@ -527,9 +527,11 @@ struct Registry std::map, ci_less> interface_map; std::map, ci_less> modules; std::map, ci_less> data_types; + std::map, ci_less> data_types_isocbinding; bool gen_c_code = false; bool no_extrap_interp = false; bool gen_inc_subs = false; + bool use_isocbinding = false; Registry() { @@ -542,6 +544,10 @@ struct Registry auto R8Ki = std::make_shared("R8Ki", "REAL(R8Ki)", DataType::Tag::Real, 64); auto DbKi = std::make_shared("DbKi", "REAL(DbKi)", DataType::Tag::Real, 64); auto logical = std::make_shared("Logical", "LOGICAL", DataType::Tag::Logical); + auto c_int = std::make_shared("c_int", "INTEGER(c_int)", DataType::Tag::Integer, 32); + auto c_float = std::make_shared("c_float", "REAL(c_float)", DataType::Tag::Real, 32); + auto c_double = std::make_shared("c_double","REAL(c_double)", DataType::Tag::Real, 64); + auto c_char = std::make_shared("c_char", "CHARACTER(c_char)", DataType::Tag::Character); // Derived types auto mesh = std::make_shared(nullptr, "MeshType", "MeshType", "MeshType"); @@ -562,6 +568,10 @@ struct Registry {"logical", logical}, {"meshtype", mesh}, {"dll_type", dll}, + {"c_int",c_int}, + {"c_float",c_float}, + {"c_double",c_double}, + {"c_char",c_char}, }; this->interface_map = std::map, ci_less>{ @@ -589,6 +599,15 @@ struct Registry {"PartialConstrStatePInputType", std::make_shared("PartialConstrStatePInputType", "dZdu", true)}, }; + + // Map of ISO_C_BINDING types (for checks only) + this->data_types_isocbinding = std::map, ci_less>{ + {"c_int",c_int}, + {"c_float",c_float}, + {"c_double",c_double}, + {"c_char",c_char}, + }; + } // Parsing @@ -601,12 +620,20 @@ struct Registry // Pointer to type std::shared_ptr data_type; + // if using ISO_C_BINDING, search these types first + auto it = data_types_isocbinding.find(type_name); + if (it != data_types_isocbinding.end()) + { + this->use_isocbinding = true; + return it->second; + } + // Get map of data types to search // If module was provided, search it; otherwise, search registry auto &data_types = mod == nullptr ? this->data_types : mod->data_types; // Search for type in registry, return if found - auto it = data_types.find(type_name); + it = data_types.find(type_name); if (it != data_types.end()) { return it->second; diff --git a/modules/openfast-registry/src/registry_gen_fortran.cpp b/modules/openfast-registry/src/registry_gen_fortran.cpp index 000e2db654..0328df39a0 100644 --- a/modules/openfast-registry/src/registry_gen_fortran.cpp +++ b/modules/openfast-registry/src/registry_gen_fortran.cpp @@ -100,6 +100,10 @@ void Registry::gen_fortran_module(const Module &mod, const std::string &out_dir) // Write preamble w << std::regex_replace(FAST_preamble, std::regex("ModuleName"), mod.name); + // Output USE statements for literal passthroughs (i.e. ISO_C_BINDING) + if (this->use_isocbinding) + w << "USE ISO_C_BINDING\n"; + // Output USE statements for non-root modules for (auto const &mod : this->use_modules) if (tolower(mod).compare("nwtc_library") != 0) @@ -712,15 +716,6 @@ void gen_pack(std::ostream &w, const Module &mod, const DataType::Derived &ddt, w << indent << "if (RF%ErrStat >= AbortErrLev) return"; - if (gen_c_code) - { - w << indent << "if (c_associated(InData%C_obj%object)) then"; - w << indent << " RF%ErrStat = ErrID_Fatal"; - w << indent << " RF%ErrMsg = RoutineName//': C_obj%object cannot be packed.'"; - w << indent << " return"; - w << indent << "end if"; - } - // Pack data for (auto &field : ddt.fields) { diff --git a/modules/seastate/CMakeLists.txt b/modules/seastate/CMakeLists.txt index 151175b7a1..d51f91f441 100644 --- a/modules/seastate/CMakeLists.txt +++ b/modules/seastate/CMakeLists.txt @@ -39,22 +39,39 @@ add_library(seastlib STATIC ) target_link_libraries(seastlib ifwlib nwtclibs versioninfolib) + # C-bindings interface library -add_library(seastate_c_binding SHARED - src/SeaState_C_Binding.f90 -) -target_link_libraries(seastate_c_binding seastlib versioninfolib) +# create object instead of directly linking into shared and static -- causes issues in parallel builds +# This is only required because we are static linking the library for wavetank +# NOTE: target linking at the object, static, and shared libraries. Different CMake versions handle this +# slightly differently with unpredictable results if I don't. +add_library(seastate_c_binding_object OBJECT src/SeaState_C_Binding.f90) +target_link_libraries(seastate_c_binding_object seastlib nwtclibs versioninfolib) +set_property(TARGET seastate_c_binding_object PROPERTY POSITION_INDEPENDENT_CODE 1) # required for shared libs + +# shared +add_library(seastate_c_binding SHARED $) +target_link_libraries(seastate_c_binding seastlib nwtclibs versioninfolib) if(APPLE OR UNIX) target_compile_definitions(seastate_c_binding PRIVATE IMPLICIT_DLLEXPORT) endif() +# C-bindings static interface +# This is a workaround for building wavetank into a single DLL (also allows setting CU globaly for sending screen to file for labview integration) +add_library(seastate_c_bind_static STATIC $) +target_link_libraries(seastate_c_bind_static seastlib nwtclibs versioninfolib) +if(APPLE OR UNIX) + target_compile_definitions(seastate_c_bind_static PRIVATE IMPLICIT_DLLEXPORT) +endif() + + # Driver add_executable(seastate_driver src/SeaState_DriverCode.f90 ) target_link_libraries(seastate_driver seastlib) -install(TARGETS seastate_driver seastlib seastate_c_binding +install(TARGETS seastate_driver seastlib seastate_c_binding seastate_c_bind_static EXPORT "${CMAKE_PROJECT_NAME}Libraries" RUNTIME DESTINATION bin LIBRARY DESTINATION lib diff --git a/modules/seastate/src/SeaSt_WaveField.f90 b/modules/seastate/src/SeaSt_WaveField.f90 index ad42da2264..30d16bc8d4 100644 --- a/modules/seastate/src/SeaSt_WaveField.f90 +++ b/modules/seastate/src/SeaSt_WaveField.f90 @@ -1,7 +1,9 @@ MODULE SeaSt_WaveField +USE GridInterp USE SeaSt_WaveField_Types USE IfW_FlowField, only: IfW_FlowField_GetVelAcc +USE GridInterp_Types IMPLICIT NONE @@ -9,6 +11,7 @@ MODULE SeaSt_WaveField ! Public functions and subroutines PUBLIC WaveField_GetNodeTotalWaveElev +PUBLIC WaveField_GetMinMaxWaveElevEstimate PUBLIC WaveField_GetNodeWaveNormal PUBLIC WaveField_GetNodeWaveKin PUBLIC WaveField_GetNodeWaveVel @@ -17,15 +20,13 @@ MODULE SeaSt_WaveField PUBLIC WaveField_GetWaveVelAcc_AD PUBLIC WaveField_GetMeanDynSurfCurr -public WaveField_Interp_Setup3D, WaveField_Interp_Setup4D - CONTAINS !-------------------- Subroutine for wave elevation ------------------! FUNCTION WaveField_GetNodeTotalWaveElev( WaveField, WaveField_m, Time, pos, ErrStat, ErrMsg, Elev1, Elev2 ) type(SeaSt_WaveFieldType), intent(in ) :: WaveField - type(SeaSt_WaveField_MiscVarType), intent(inout) :: WaveField_m + type(GridInterp_MiscVarType), intent(inout) :: WaveField_m real(DbKi), intent(in ) :: Time real(ReKi), intent(in ) :: pos(*) ! Position at which free-surface elevation is to be calculated. Third entry ignored if present. integer(IntKi), intent( out) :: ErrStat ! Error status of the operation @@ -41,19 +42,19 @@ FUNCTION WaveField_GetNodeTotalWaveElev( WaveField, WaveField_m, Time, pos, ErrS ErrStat = ErrID_None IF (ALLOCATED(WaveField%WaveElev1) .or. ALLOCATED(WaveField%WaveElev2)) then - CALL WaveField_Interp_Setup3D(Time, pos, WaveField%GridParams, WaveField_m, ErrStat2, ErrMsg2) + CALL WaveField_Interp_Setup3D(Time, pos, WaveField%SrfGridParams, WaveField_m, ErrStat2, ErrMsg2) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return end if IF (ALLOCATED(WaveField%WaveElev1)) THEN - Zeta1 = WaveField_Interp_3D(WaveField%WaveElev1, WaveField_m) + Zeta1 = GridInterp3D(WaveField%WaveElev1, WaveField_m) ELSE Zeta1 = 0.0_SiKi END IF IF (ALLOCATED(WaveField%WaveElev2)) THEN - Zeta2 = WaveField_Interp_3D(WaveField%WaveElev2, WaveField_m) + Zeta2 = GridInterp3D(WaveField%WaveElev2, WaveField_m) ELSE Zeta2 = 0.0_SiKi END IF @@ -66,36 +67,61 @@ FUNCTION WaveField_GetNodeTotalWaveElev( WaveField, WaveField_m, Time, pos, ErrS END FUNCTION WaveField_GetNodeTotalWaveElev -SUBROUTINE WaveField_GetNodeWaveNormal( WaveField, WaveField_m, Time, pos, r, n, ErrStat, ErrMsg ) +!> Gives an estimate of the min and max wave elevation. It will overshoot for second order +subroutine WaveField_GetMinMaxWaveElevEstimate( WaveField, MinElev, MaxElev, ErrStat, ErrMsg ) + type(SeaSt_WaveFieldType), pointer, intent(in ) :: WaveField + real(SiKi), intent( out) :: MinElev + real(SiKi), intent( out) :: MaxElev + integer(IntKi), intent( out) :: ErrStat ! Error status of the operation + character(*), intent( out) :: ErrMsg ! Error message if errStat /= ErrID_None + character(*), parameter :: RoutineName = 'WaveField_GetMinMaxWaveElevEstimate' + + ErrStat = ErrID_None + ErrMsg = "" + MinElev = 0.0_SiKi + MaxElev = 0.0_SiKi + + ! Check that data exists + if (.not. associated(WaveField)) then + ErrStat = ErrID_Fatal + ErrMsg = trim(RoutineName)//": WaveField data does not exist." + return + endif + + if (allocated(WaveField%WaveElev1)) then + MinElev = minval(WaveField%WaveElev1) + MaxElev = maxval(WaveField%WaveElev1) + endif + if (allocated(WaveField%WaveElev2)) then + MinElev = MinElev + minval(WaveField%WaveElev2) + MaxElev = MaxElev + maxval(WaveField%WaveElev2) + endif +end subroutine WaveField_GetMinMaxWaveElevEstimate + +SUBROUTINE WaveField_GetNodeWaveNormal( WaveField, WaveField_m, Time, pos, n, ErrStat, ErrMsg ) type(SeaSt_WaveFieldType), intent(in ) :: WaveField - type(SeaSt_WaveField_MiscVarType), intent(inout) :: WaveField_m + type(GridInterp_MiscVarType), intent(inout) :: WaveField_m real(DbKi), intent(in ) :: Time real(ReKi), intent(in ) :: pos(:) ! Position at which free-surface normal is to be calculated. Third entry ignored if present. - real(ReKi), intent(in ) :: r ! Distance for central differencing real(ReKi), intent( out) :: n(3) ! Free-surface normal vector integer(IntKi), intent( out) :: ErrStat ! Error status of the operation character(*), intent( out) :: ErrMsg ! Error message if errStat /= ErrID_None - real(SiKi) :: ZetaP,ZetaM - real(ReKi) :: r1,dZetadx,dZetady + real(SiKi) :: slope(2) character(*), parameter :: RoutineName = 'WaveField_GetNodeWaveNormal' integer(IntKi) :: errStat2 character(ErrMsgLen) :: errMsg2 ErrStat = ErrID_None - r1 = MAX(r,real(1.0e-6,ReKi)) ! In case r is zero - - ZetaP = WaveField_GetNodeTotalWaveElev( WaveField, WaveField_m, Time, (/pos(1)+r1,pos(2)/), ErrStat2, ErrMsg2 ); if (Failed()) return; - ZetaM = WaveField_GetNodeTotalWaveElev( WaveField, WaveField_m, Time, (/pos(1)-r1,pos(2)/), ErrStat2, ErrMsg2 ); if (Failed()) return; - dZetadx = REAL(ZetaP-ZetaM,ReKi)/(2.0_ReKi*r1) - - ZetaP = WaveField_GetNodeTotalWaveElev( WaveField, WaveField_m, Time, (/pos(1),pos(2)+r1/), ErrStat2, ErrMsg2 ); if (Failed()) return; - ZetaM = WaveField_GetNodeTotalWaveElev( WaveField, WaveField_m, Time, (/pos(1),pos(2)-r1/), ErrStat2, ErrMsg2 ); if (Failed()) return; - dZetady = REAL(ZetaP-ZetaM,ReKi)/(2.0_ReKi*r1) + call GridInterpSetupN( (/Real(Time+WaveField%WaveTimeShift,ReKi),pos(1),pos(2)/), WaveField%SrfGridParams, WaveField_m, ErrStat2, ErrMsg2 ) + slope = GridInterpS( WaveField%WaveElev1, WaveField%SrfGridParams, WaveField_m ) + if (ALLOCATED(WaveField%WaveElev2)) then + slope = slope + GridInterpS( WaveField%WaveElev2, WaveField%SrfGridParams, WaveField_m ) + end if - n = (/-dZetadx,-dZetady,1.0_ReKi/) - n = n / SQRT(Dot_Product(n,n)) + n = Real( (/-slope(1),-slope(2),1.0_SiKi/), ReKi) + n = n / TwoNorm(n) contains logical function Failed() @@ -108,7 +134,7 @@ END SUBROUTINE WaveField_GetNodeWaveNormal !-------------------- Subroutine for full wave field kinematics --------------------! SUBROUTINE WaveField_GetNodeWaveKin( WaveField, WaveField_m, Time, pos, forceNodeInWater, fetchDynCurrent, nodeInWater, WaveElev1, WaveElev2, WaveElev, FDynP, FV, FA, FAMCF, ErrStat, ErrMsg ) type(SeaSt_WaveFieldType), intent(in ) :: WaveField - type(SeaSt_WaveField_MiscVarType), intent(inout) :: WaveField_m + type(GridInterp_MiscVarType), intent(inout) :: WaveField_m real(DbKi), intent(in ) :: Time real(ReKi), intent(in ) :: pos(3) logical, intent(in ) :: forceNodeInWater @@ -146,12 +172,12 @@ SUBROUTINE WaveField_GetNodeWaveKin( WaveField, WaveField_m, Time, pos, forceNod IF ( pos(3) <= 0.0_ReKi) THEN ! Node is at or below the SWL nodeInWater = 1_IntKi ! Use location to obtain interpolated values of kinematics - CALL WaveField_Interp_Setup4D( Time, pos, WaveField%GridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; - FV(:) = WaveField_Interp_4D_Vec( WaveField%WaveVel, WaveField_m ) - FA(:) = WaveField_Interp_4D_Vec( WaveField%WaveAcc, WaveField_m ) - FDynP = WaveField_Interp_4D ( WaveField%WaveDynP, WaveField_m ) + CALL WaveField_Interp_Setup4D( Time+WaveField%WaveTimeShift, pos, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + FV(:) = GridInterp4DVec( WaveField%WaveVel, WaveField_m ) + FA(:) = GridInterp4DVec( WaveField%WaveAcc, WaveField_m ) + FDynP = GridInterp4D ( WaveField%WaveDynP, WaveField_m ) IF ( ALLOCATED(WaveField%WaveAccMCF) ) THEN - FAMCF(:) = WaveField_Interp_4D_Vec( WaveField%WaveAccMCF, WaveField_m ) + FAMCF(:) = GridInterp4DVec( WaveField%WaveAccMCF, WaveField_m ) END IF ELSE ! Node is above the SWL nodeInWater = 0_IntKi @@ -172,32 +198,33 @@ SUBROUTINE WaveField_GetNodeWaveKin( WaveField, WaveField_m, Time, pos, forceNod IF ( pos(3) <= 0.0_SiKi) THEN ! Node is below the SWL - evaluate wave dynamics as usual ! Use location to obtain interpolated values of kinematics - CALL WaveField_Interp_Setup4D( Time, pos, WaveField%GridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; - FV(:) = WaveField_Interp_4D_Vec( WaveField%WaveVel, WaveField_m ) - FA(:) = WaveField_Interp_4D_Vec( WaveField%WaveAcc, WaveField_m ) - FDynP = WaveField_Interp_4D ( WaveField%WaveDynP, WaveField_m ) + CALL WaveField_Interp_Setup4D( Time+WaveField%WaveTimeShift, pos, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + FV(:) = GridInterp4DVec( WaveField%WaveVel, WaveField_m ) + FA(:) = GridInterp4DVec( WaveField%WaveAcc, WaveField_m ) + FDynP = GridInterp4D ( WaveField%WaveDynP, WaveField_m ) IF ( ALLOCATED(WaveField%WaveAccMCF) ) THEN - FAMCF(:) = WaveField_Interp_4D_Vec( WaveField%WaveAccMCF, WaveField_m ) + FAMCF(:) = GridInterp4DVec( WaveField%WaveAccMCF, WaveField_m ) END IF ELSE ! Node is above SWL - need wave stretching ! Vertical wave stretching - CALL WaveField_Interp_Setup4D( Time, posXY0, WaveField%GridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; - FV(:) = WaveField_Interp_4D_vec( WaveField%WaveVel, WaveField_m ) - FA(:) = WaveField_Interp_4D_vec( WaveField%WaveAcc, WaveField_m ) - FDynP = WaveField_Interp_4D ( WaveField%WaveDynP, WaveField_m ) + CALL WaveField_Interp_Setup4D( Time+WaveField%WaveTimeShift, posXY0, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + FV(:) = GridInterp4DVec( WaveField%WaveVel, WaveField_m ) + FA(:) = GridInterp4DVec( WaveField%WaveAcc, WaveField_m ) + FDynP = GridInterp4D ( WaveField%WaveDynP, WaveField_m ) IF ( ALLOCATED(WaveField%WaveAccMCF) ) THEN - FAMCF(:) = WaveField_Interp_4D_vec( WaveField%WaveAccMCF, WaveField_m ) + FAMCF(:) = GridInterp4DVec( WaveField%WaveAccMCF, WaveField_m ) END IF ! Extrapolated wave stretching IF (WaveField%WaveStMod == 2) THEN - FV(:) = FV(:) + WaveField_Interp_3D_vec( WaveField%PWaveVel0, WaveField_m ) * pos(3) - FA(:) = FA(:) + WaveField_Interp_3D_vec( WaveField%PWaveAcc0, WaveField_m ) * pos(3) - FDynP = FDynP + WaveField_Interp_3D ( WaveField%PWaveDynP0, WaveField_m ) * pos(3) + CALL WaveField_Interp_Setup3D( Time+WaveField%WaveTimeShift, posXY, WaveField%SrfGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + FV(:) = FV(:) + GridInterp3DVec( WaveField%PWaveVel0, WaveField_m ) * pos(3) + FA(:) = FA(:) + GridInterp3DVec( WaveField%PWaveAcc0, WaveField_m ) * pos(3) + FDynP = FDynP + GridInterp3D ( WaveField%PWaveDynP0, WaveField_m ) * pos(3) IF ( ALLOCATED(WaveField%WaveAccMCF) ) THEN - FAMCF(:) = FAMCF(:) + WaveField_Interp_3D_vec( WaveField%PWaveAccMCF0, WaveField_m ) * pos(3) + FAMCF(:) = FAMCF(:) + GridInterp3DVec( WaveField%PWaveAccMCF0, WaveField_m ) * pos(3) END IF END IF @@ -211,12 +238,12 @@ SUBROUTINE WaveField_GetNodeWaveKin( WaveField, WaveField_m, Time, pos, forceNod posPrime(3) = MIN( posPrime(3), 0.0_ReKi) ! Clamp z-position to zero. Needed when forceNodeInWater=.TRUE. ! Obtain the wave-field variables by interpolation with the mapped position. - CALL WaveField_Interp_Setup4D( Time, posPrime, WaveField%GridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; - FV(:) = WaveField_Interp_4D_Vec( WaveField%WaveVel, WaveField_m ) - FA(:) = WaveField_Interp_4D_Vec( WaveField%WaveAcc, WaveField_m ) - FDynP = WaveField_Interp_4D ( WaveField%WaveDynP, WaveField_m ) + CALL WaveField_Interp_Setup4D( Time+WaveField%WaveTimeShift, posPrime, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + FV(:) = GridInterp4DVec( WaveField%WaveVel, WaveField_m ) + FA(:) = GridInterp4DVec( WaveField%WaveAcc, WaveField_m ) + FDynP = GridInterp4D ( WaveField%WaveDynP, WaveField_m ) IF ( ALLOCATED(WaveField%WaveAccMCF) ) THEN - FAMCF(:) = WaveField_Interp_4D_Vec( WaveField%WaveAccMCF, WaveField_m ) + FAMCF(:) = GridInterp4DVec( WaveField%WaveAccMCF, WaveField_m ) END IF END IF @@ -260,7 +287,7 @@ END SUBROUTINE WaveField_GetNodeWaveKin !-------------------- Subroutine for wave field velocity only --------------------! SUBROUTINE WaveField_GetNodeWaveVel( WaveField, WaveField_m, Time, pos, forceNodeInWater, fetchDynCurrent, nodeInWater, FV, ErrStat, ErrMsg ) type(SeaSt_WaveFieldType), intent(in ) :: WaveField - type(SeaSt_WaveField_MiscVarType), intent(inout) :: WaveField_m + type(GridInterp_MiscVarType), intent(inout) :: WaveField_m real(DbKi), intent(in ) :: Time real(ReKi), intent(in ) :: pos(3) logical, intent(in ) :: forceNodeInWater @@ -291,8 +318,8 @@ SUBROUTINE WaveField_GetNodeWaveVel( WaveField, WaveField_m, Time, pos, forceNod IF ( pos(3) <= 0.0_ReKi) THEN ! Node is at or below the SWL nodeInWater = 1_IntKi ! Use location to obtain interpolated values of kinematics - CALL WaveField_Interp_Setup4D( Time, pos, WaveField%GridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; - FV(:) = WaveField_Interp_4D_Vec( WaveField%WaveVel, WaveField_m ) + CALL WaveField_Interp_Setup4D( Time+WaveField%WaveTimeShift, pos, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + FV(:) = GridInterp4DVec( WaveField%WaveVel, WaveField_m ) ELSE ! Node is above the SWL nodeInWater = 0_IntKi FV(:) = 0.0 @@ -309,18 +336,19 @@ SUBROUTINE WaveField_GetNodeWaveVel( WaveField, WaveField_m, Time, pos, forceNod IF ( pos(3) <= 0.0_SiKi) THEN ! Node is below the SWL - evaluate wave dynamics as usual ! Use location to obtain interpolated values of kinematics - CALL WaveField_Interp_Setup4D( Time, pos, WaveField%GridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; - FV(:) = WaveField_Interp_4D_Vec( WaveField%WaveVel, WaveField_m ) + CALL WaveField_Interp_Setup4D( Time+WaveField%WaveTimeShift, pos, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + FV(:) = GridInterp4DVec( WaveField%WaveVel, WaveField_m ) ELSE ! Node is above SWL - need wave stretching ! Vertical wave stretching - CALL WaveField_Interp_Setup4D( Time, posXY0, WaveField%GridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; - FV(:) = WaveField_Interp_4D_vec( WaveField%WaveVel, WaveField_m ) + CALL WaveField_Interp_Setup4D( Time, posXY0, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + FV(:) = GridInterp4DVec( WaveField%WaveVel, WaveField_m ) ! Extrapolated wave stretching IF (WaveField%WaveStMod == 2) THEN - FV(:) = FV(:) + WaveField_Interp_3D_vec( WaveField%PWaveVel0, WaveField_m ) * pos(3) + CALL WaveField_Interp_Setup3D( Time+WaveField%WaveTimeShift, posXY, WaveField%SrfGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + FV(:) = FV(:) + GridInterp3DVec( WaveField%PWaveVel0, WaveField_m ) * pos(3) END IF END IF ! Node is submerged @@ -333,8 +361,8 @@ SUBROUTINE WaveField_GetNodeWaveVel( WaveField, WaveField_m, Time, pos, forceNod posPrime(3) = MIN( posPrime(3), 0.0_ReKi) ! Clamp z-position to zero. Needed when forceNodeInWater=.TRUE. ! Obtain the wave-field variables by interpolation with the mapped position. - CALL WaveField_Interp_Setup4D( Time, posPrime, WaveField%GridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; - FV(:) = WaveField_Interp_4D_Vec( WaveField%WaveVel, WaveField_m ) + CALL WaveField_Interp_Setup4D( Time+WaveField%WaveTimeShift, posPrime, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + FV(:) = GridInterp4DVec( WaveField%WaveVel, WaveField_m ) END IF @@ -372,7 +400,7 @@ END SUBROUTINE WaveField_GetNodeWaveVel SUBROUTINE WaveField_GetNodeWaveVelAcc( WaveField, WaveField_m, Time, pos, forceNodeInWater, fetchDynCurrent, nodeInWater, FV, FA, ErrStat, ErrMsg ) type(SeaSt_WaveFieldType), intent(in ) :: WaveField - type(SeaSt_WaveField_MiscVarType), intent(inout) :: WaveField_m + type(GridInterp_MiscVarType), intent(inout) :: WaveField_m real(DbKi), intent(in ) :: Time real(ReKi), intent(in ) :: pos(3) logical, intent(in ) :: forceNodeInWater @@ -400,72 +428,70 @@ SUBROUTINE WaveField_GetNodeWaveVelAcc( WaveField, WaveField_m, Time, pos, force WaveElev = WaveField_GetNodeTotalWaveElev( WaveField, WaveField_m, Time, pos, ErrStat2, ErrMsg2 ); if (Failed()) return; IF (WaveField%WaveStMod == 0) THEN ! No wave stretching - + IF ( pos(3) <= 0.0_ReKi) THEN ! Node is at or below the SWL nodeInWater = 1_IntKi - ! Use location to obtain interpolated values of kinematics - CALL WaveField_Interp_Setup4D( Time, pos, WaveField%GridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; - FV(:) = WaveField_Interp_4D_Vec( WaveField%WaveVel, WaveField_m ) - FA(:) = WaveField_Interp_4D_Vec( WaveField%WaveAcc, WaveField_m ) + ! Use location to obtain interpolated values of kinematics + CALL WaveField_Interp_Setup4D( Time+WaveField%WaveTimeShift, pos, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + FV(:) = GridInterp4DVec( WaveField%WaveVel, WaveField_m ) + FA(:) = GridInterp4DVec( WaveField%WaveAcc, WaveField_m ) ELSE ! Node is above the SWL nodeInWater = 0_IntKi FV(:) = 0.0 FA(:) = 0.0 END IF - + ELSE ! Wave stretching enabled - + IF ( (pos(3) <= WaveElev) .OR. forceNodeInWater ) THEN ! Node is submerged - + nodeInWater = 1_IntKi - + IF ( WaveField%WaveStMod < 3 ) THEN ! Vertical or extrapolated wave stretching - + IF ( pos(3) <= 0.0_SiKi) THEN ! Node is below the SWL - evaluate wave dynamics as usual ! Use location to obtain interpolated values of kinematics - CALL WaveField_Interp_Setup4D( Time, pos, WaveField%GridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; - FV(:) = WaveField_Interp_4D_Vec( WaveField%WaveVel, WaveField_m ) - FA(:) = WaveField_Interp_4D_Vec( WaveField%WaveAcc, WaveField_m ) + CALL WaveField_Interp_Setup4D( Time+WaveField%WaveTimeShift, pos, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + FV(:) = GridInterp4DVec( WaveField%WaveVel, WaveField_m ) + FA(:) = GridInterp4DVec( WaveField%WaveAcc, WaveField_m ) ELSE ! Node is above SWL - need wave stretching ! Vertical wave stretching - CALL WaveField_Interp_Setup4D( Time, posXY0, WaveField%GridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; - FV(:) = WaveField_Interp_4D_vec( WaveField%WaveVel, WaveField_m ) - FA(:) = WaveField_Interp_4D_vec( WaveField%WaveAcc, WaveField_m ) + CALL WaveField_Interp_Setup4D( Time, posXY0, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + FV(:) = GridInterp4DVec( WaveField%WaveVel, WaveField_m ) + FA(:) = GridInterp4DVec( WaveField%WaveAcc, WaveField_m ) ! Extrapolated wave stretching IF (WaveField%WaveStMod == 2) THEN - CALL WaveField_Interp_Setup3D( Time, posXY, WaveField%GridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; - FV(:) = FV(:) + WaveField_Interp_3D_vec( WaveField%PWaveVel0, WaveField_m ) * pos(3) - FA(:) = FA(:) + WaveField_Interp_3D_vec( WaveField%PWaveAcc0, WaveField_m ) * pos(3) + CALL WaveField_Interp_Setup3D( Time+WaveField%WaveTimeShift, posXY, WaveField%SrfGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + FV(:) = FV(:) + GridInterp3DVec( WaveField%PWaveVel0, WaveField_m ) * pos(3) + FA(:) = FA(:) + GridInterp3DVec( WaveField%PWaveAcc0, WaveField_m ) * pos(3) END IF - END IF ! Node is above or below SWL - + END IF ! Node is submerged + ELSE ! Wheeler stretching - no need to check whether the node is above or below SWL - - ! Map the node z-position linearly from [-EffWtrDpth,m%WaveElev(j)] to [-EffWtrDpth,0] + + ! Map the node z-position linearly from [-EffWtrDpth,m%WaveElev(j)] to [-EffWtrDpth,0] posPrime = pos posPrime(3) = WaveField%EffWtrDpth*(WaveField%EffWtrDpth+pos(3))/(WaveField%EffWtrDpth+WaveElev)-WaveField%EffWtrDpth - posPrime(3) = MIN( posPrime(3), 0.0_ReKi) ! Clamp z-position to zero. Needed when forceNodeInWater=.TRUE. - + posPrime(3) = MIN( posPrime(3), 0.0_ReKi) ! Clamp z-position to zero. Needed when forceNodeInWater=.TRUE. + ! Obtain the wave-field variables by interpolation with the mapped position. - CALL WaveField_Interp_Setup4D( Time, posPrime, WaveField%GridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; - FV(:) = WaveField_Interp_4D_Vec( WaveField%WaveVel, WaveField_m ) - FA(:) = WaveField_Interp_4D_Vec( WaveField%WaveAcc, WaveField_m ) + CALL WaveField_Interp_Setup4D( Time+WaveField%WaveTimeShift, posPrime, WaveField%GridDepth, WaveField%VolGridParams, WaveField_m, ErrStat2, ErrMsg2 ); if (Failed()) return; + FV(:) = GridInterp4DVec( WaveField%WaveVel, WaveField_m ) + FA(:) = GridInterp4DVec( WaveField%WaveAcc, WaveField_m ) + END IF - END IF ! Wave stretching method - ELSE ! Node is out of water - zero-out all wave dynamics - - nodeInWater = 0_IntKi + + nodeInWater = 0_IntKi FV(:) = 0.0 FA(:) = 0.0 - END IF ! If node is in or out of water - + END IF ! If wave stretching is on or off ! Get dynamic current velocity and acceleration @@ -495,7 +521,7 @@ END SUBROUTINE WaveField_GetNodeWaveVelAcc SUBROUTINE WaveField_GetWaveKin( WaveField, WaveField_m, Time, pos, forceNodeInWater, fetchDynCurrent, nodeInWater, WaveElev1, WaveElev2, WaveElev, FDynP, FV, FA, FAMCF, ErrStat, ErrMsg ) type(SeaSt_WaveFieldType), intent(in ) :: WaveField - type(SeaSt_WaveField_MiscVarType), intent(inout) :: WaveField_m + type(GridInterp_MiscVarType), intent(inout) :: WaveField_m real(DbKi), intent(in ) :: Time real(ReKi), intent(in ) :: pos(:,:) logical, intent(in ) :: forceNodeInWater @@ -566,7 +592,7 @@ end subroutine WaveField_GetWaveKin ! This subroutine is intended for AeroDyn when modeling MHK turbines SUBROUTINE WaveField_GetWaveVelAcc_AD( WaveField, WaveField_m, StartNode, Time, pos, FV, FA, ErrStat, ErrMsg ) type(SeaSt_WaveFieldType), intent(in ) :: WaveField - type(SeaSt_WaveField_MiscVarType), intent(inout) :: WaveField_m + type(GridInterp_MiscVarType), intent(inout) :: WaveField_m integer(IntKi), intent(in ) :: StartNode real(DbKi), intent(in ) :: Time real(ReKi), intent(in ) :: pos(:,:) ! z=0 at MSL @@ -596,13 +622,13 @@ SUBROUTINE WaveField_GetWaveVelAcc_AD( WaveField, WaveField_m, StartNode, Time, ! Note: SeaState wavefield grid has z=0 on the SWL IF (getAcc) THEN DO i = 1, NumPoints - CALL WaveField_GetNodeWaveVelAcc( WaveField, WaveField_m, Time, pos(:,i)-(/0.0,0.0,MSL2SWL/), .FALSE., .FALSE., nodeInWater(i), FV_node, FA_node, ErrStat2, ErrMsg2 ); if (Failed()) return; + CALL WaveField_GetNodeWaveVelAcc( WaveField, WaveField_m, Time, pos(:,i)-[0.0_ReKi,0.0_ReKi,real(MSL2SWL, ReKi)], .FALSE., .FALSE., nodeInWater(i), FV_node, FA_node, ErrStat2, ErrMsg2 ); if (Failed()) return; FV(:, i) = REAL(FV_node, ReKi) FA(:, i) = REAL(FA_node, ReKi) END DO ELSE DO i = 1, NumPoints - CALL WaveField_GetNodeWaveVel( WaveField, WaveField_m, Time, pos(:,i)-(/0.0,0.0,MSL2SWL/), .FALSE., .FALSE., nodeInWater(i), FV_node, ErrStat2, ErrMsg2 ); if (Failed()) return; + CALL WaveField_GetNodeWaveVel( WaveField, WaveField_m, Time, pos(:,i)-[0.0_ReKi,0.0_ReKi,real(MSL2SWL, ReKi)], .FALSE., .FALSE., nodeInWater(i), FV_node, ErrStat2, ErrMsg2 ); if (Failed()) return; FV(:, i) = REAL(FV_node, ReKi) END DO END IF @@ -703,460 +729,61 @@ END SUBROUTINE WaveField_GetMeanDynSurfCurr ! Interpolation related functions !---------------------------------------------------------------------------------------------------- -subroutine SetCartesianXYIndex(p, pZero, delta, nMax, Indx_Lo, Indx_Hi, isopc, FirstWarn, ErrStat, ErrMsg) - REAL(ReKi), intent(in ) :: p - REAL(ReKi), intent(in ) :: pZero - REAL(ReKi), intent(in ) :: delta - INTEGER(IntKi), intent(in ) :: nMax - INTEGER(IntKi), intent(inout) :: Indx_Lo - INTEGER(IntKi), intent(inout) :: Indx_Hi - real(SiKi), intent(inout) :: isopc - logical, intent(inout) :: FirstWarn - INTEGER(IntKi), intent( out) :: ErrStat - CHARACTER(*), intent( out) :: ErrMsg - - real(ReKi) :: Tmp - - ErrStat = ErrID_None - - isopc = -1.0 - Indx_Lo = 0 - Indx_Hi = 0 - - if ( nMax .EQ. 1_IntKi ) then ! Only one grid point - Indx_Lo = 1_IntKi - Indx_Hi = 1_IntKi - isopc = 0_SiKi - return - end if - - Tmp = (p-pZero) / delta - Indx_Lo = INT( Tmp ) + 1 ! convert REAL to INTEGER, then add one since our grid indices start at 1, not 0 - - ! Calculate isoparametric coordinate and clamp between -1 and 1 - isopc = 2.0_ReKi * (Tmp - REAL(Indx_Lo - 1, ReKi)) - 1.0_ReKi - if (isopc < -1.0_SiKi) then - isopc = -1.0_SiKi - else if (isopc > 1.0_SiKi) then - isopc = 1.0_SiKi - end if - - ! Check that lower index is valid - if ( Indx_Lo < 1 ) then - Indx_Lo = 1 - isopc = -1.0 - if (FirstWarn) then - call SetErrStat(ErrID_Warn,'Position has been clamped to the grid boundary. Warning will not be repeated though condition may persist.',ErrStat,ErrMsg,'SetCartesianXYIndex') !error out if time is outside the lower bounds - FirstWarn = .false. - end if - end if - - ! Calculate hi grid index - Indx_Hi = min( Indx_Lo + 1, nMax ) ! make sure it's a valid index, zero-based - - ! Check that upper index is valid - if ( Indx_Lo >= Indx_Hi ) then - ! Need to clamp to grid boundary - if (FirstWarn .and. Indx_Lo /= Indx_Hi) then ! don't warn if we are exactly at the boundary - call SetErrStat(ErrID_Warn,'Position has been clamped to the grid boundary. Warning will not be repeated though condition may persist.',ErrStat,ErrMsg,'SetCartesianXYIndex') !error out if time is outside the lower bounds - FirstWarn = .false. - end if - Indx_Lo = max(Indx_Hi - 1, 1) - isopc = 1.0 - end if - -end subroutine SetCartesianXYIndex - - -subroutine SetCartesianZIndex(p, z_depth, delta, nMax, Indx_Lo, Indx_Hi, isopc, FirstWarn, ErrStat, ErrMsg) - real(ReKi), intent(in ) :: p - real(ReKi), intent(in ) :: z_depth - real(ReKi), intent(in ) :: delta - integer(IntKi), intent(in ) :: nMax - integer(IntKi), intent(inout) :: Indx_Lo - integer(IntKi), intent(inout) :: Indx_Hi - real(SiKi), intent(inout) :: isopc - logical, intent(inout) :: FirstWarn - integer(IntKi), intent( out) :: ErrStat - character(*), intent( out) :: ErrMsg - - real(ReKi) :: Tmp - - ErrStat = ErrID_None - - isopc = -1.0 - Indx_Lo = 0 - Indx_Hi = 0 - - - !Tmp = acos(-p / z_depth) / delta - Tmp = acos( max(-1.0_ReKi, min(1.0_ReKi, 1+(p / z_depth)) ) ) / delta - Tmp = nmax - 1 - Tmp - Indx_Lo = INT( Tmp ) + 1 ! convert REAL to INTEGER, then add one since our grid indices start at 1, not 0 - - ! Calculate isoparametric coordinate and clamp between -1 and 1 - isopc = 2.0_ReKi * (Tmp - REAL(Indx_Lo - 1, ReKi)) - 1.0_ReKi - if (isopc < -1.0_SiKi) then - isopc = -1.0_SiKi - else if (isopc > 1.0_SiKi) then - isopc = 1.0_SiKi - end if - - if ( Indx_Lo < 1 ) then - Indx_Lo = 1 - isopc = -1.0 - if (FirstWarn) then - call SetErrStat(ErrID_Warn,'Position has been clamped to the grid boundary. Warning will not be repeated though condition may persist.',ErrStat,ErrMsg,'SetCartesianZIndex') !error out if z is outside the lower bounds - FirstWarn = .false. - end if - end if - - Indx_Hi = min( Indx_Lo + 1, nMax ) ! make sure it's a valid index, one-based - - if ( Indx_Lo >= Indx_Hi ) then - ! Need to clamp to grid boundary - if (FirstWarn .and. Indx_Lo /= Indx_Hi) then ! don't warn if we are exactly at the boundary - call SetErrStat(ErrID_Warn,'Position has been clamped to the grid boundary. Warning will not be repeated though condition may persist.',ErrStat,ErrMsg,'SetCartesianZIndex') !error out if z is outside the upper bounds - FirstWarn = .false. - end if - Indx_Lo = max(Indx_Hi - 1, 1) - isopc = 1.0 - end if - -end subroutine SetCartesianZIndex - - -subroutine SetTimeIndex(Time, deltaT, nMax, Indx_Lo, Indx_Hi, isopc, ErrStat, ErrMsg) - real(DbKi), intent(in ) :: Time !< time from the start of the simulation - real(ReKi), intent(in ) :: deltaT - integer(IntKi), intent(in ) :: nMax - integer(IntKi), intent(inout) :: Indx_Lo - integer(IntKi), intent(inout) :: Indx_Hi - real(SiKi), intent(inout) :: isopc - integer(IntKi), intent( out) :: ErrStat - character(*), intent( out) :: ErrMsg - - real(ReKi) :: Tmp - - ErrStat = ErrID_None - - isopc = -1.0 - Indx_Lo = 0 - Indx_Hi = 0 - !if ( Time < 0.0_DbKi ) then - ! CALL SetErrStat(ErrID_Fatal,'Time value must be greater than or equal to zero!',ErrStat,ErrMsg,'SetTimeIndex') !error out if time is outside the lower bounds - ! RETURN - !end if - - ! if there are no timesteps, don't proceed - if (EqualRealNos(deltaT,0.0_ReKi) .or. deltaT < 0.0_ReKi) return; - -! NOTE: nMax is the total number of time values in the grid, since this is zero-based indexing, the max index is nMax-1 -! for example: in a time grid with 11 grid points, the indices run from 0,1,2,3,4,5,6,7,8,9,10 -! for the repeating waves feature, index 10 is the same as index 0, so if Indx_Lo = 10 then we want to -! wrap it back to index 0, if Indx_Lo = 11 we want to wrap back to index 1. - - Tmp = real( (Time/ real(deltaT,DbKi)) ,ReKi) - Tmp = MODULO(Tmp,real((nMax), ReKi)) - Indx_Lo = INT( Tmp ) ! convert REAL to INTEGER - - ! Calculate isoparametric coordinate and clamp between -1 and 1 - isopc = 2.0_ReKi * (Tmp - REAL(Indx_Lo, ReKi)) - 1.0_ReKi - if (isopc < -1.0_SiKi) then - isopc = -1.0_SiKi - else if (isopc > 1.0_SiKi) then - isopc = 1.0_SiKi - end if - - Indx_Hi = min( Indx_Lo + 1, nMax ) ! make sure it's a valid index, zero-based - -end subroutine SetTimeIndex - - !==================================================================================================== !> This routine sets up interpolation of a 3-d or 4-d dataset. !! This method is described here: http://rjwagner49.com/Mathematics/Interpolation.pdf -subroutine WaveField_Interp_Setup4D( Time, Position, p, m, ErrStat, ErrMsg ) - real(DbKi), intent(in ) :: Time !< time from the start of the simulation - real(ReKi), intent(in ) :: Position(3) !< Array of XYZ coordinates, 3 - type(SeaSt_WaveField_ParameterType), intent(in ) :: p !< Parameters - type(SeaSt_WaveField_MiscVarType), intent(inout) :: m !< MiscVars +subroutine WaveField_Interp_Setup3D( Time, Position, p, m, ErrStat, ErrMsg ) + real(DbKi), intent(in ) :: Time !< Time from the start of the simulation + real(ReKi), intent(in ) :: Position(2) !< Array of XY coordinates, 2 + type(GridInterp_ParameterType), intent(in ) :: p !< Parameters + type(GridInterp_MiscVarType), intent(inout) :: m !< MiscVars integer(IntKi), intent( out) :: ErrStat !< Error status character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None - - character(*), parameter :: RoutineName = 'WaveField_Interp_Setup4D' - integer(IntKi) :: i - real(SiKi) :: isopc(4) ! isoparametric coordinates - real(SiKi) :: one_m_isopc(4) ! 1 - isoparametric coordinates - real(SiKi) :: one_p_isopc(4) ! 1 + isoparametric coordinates + character(*), parameter :: RoutineName = 'WaveField_Interp_Setup3D' integer(IntKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 ErrStat = ErrID_None - ! Find the bounding indices for time - call SetTimeIndex(Time, p%delta(1), p%n(1), m%Indx_Lo(1), m%Indx_Hi(1), isopc(1), ErrStat2, ErrMsg2) - if (Failed()) return; - - ! Find the bounding indices for XY position - do i=2,3 ! x and y components - call SetCartesianXYIndex(Position(i-1), p%pZero(i), p%delta(i), p%n(i), m%Indx_Lo(i), m%Indx_Hi(i), isopc(i), m%FirstWarn_Clamp, ErrStat2, ErrMsg2) - if (Failed()) return; - enddo - - ! Find the bounding indices for Z position - i=4 ! z component - if (p%Z_Depth>0) then - call SetCartesianZIndex(Position(i-1), p%Z_Depth, p%delta(i), p%n(i), m%Indx_Lo(i), m%Indx_Hi(i), isopc(i), m%FirstWarn_Clamp, ErrStat2, ErrMsg2) - if (Failed()) return; - else ! Regular z-grid - call SetCartesianXYIndex(Position(i-1), p%pZero(i), p%delta(i), p%n(i), m%Indx_Lo(i), m%Indx_Hi(i), isopc(i), m%FirstWarn_Clamp, ErrStat2, ErrMsg2) - if (Failed()) return; - end if - - ! Calculate 1+ and 1- isoparametric coordinates to avoid recalculations - one_m_isopc = 1.0_SiKi - isopc - one_p_isopc = 1.0_SiKi + isopc - - ! compute weighting factors - m%N4D( 1) = one_m_isopc(1) * one_m_isopc(2) * one_m_isopc(3) * one_m_isopc(4) / 16.0_SiKi - m%N4D( 2) = one_p_isopc(1) * one_m_isopc(2) * one_m_isopc(3) * one_m_isopc(4) / 16.0_SiKi - m%N4D( 3) = one_m_isopc(1) * one_p_isopc(2) * one_m_isopc(3) * one_m_isopc(4) / 16.0_SiKi - m%N4D( 4) = one_p_isopc(1) * one_p_isopc(2) * one_m_isopc(3) * one_m_isopc(4) / 16.0_SiKi - m%N4D( 5) = one_m_isopc(1) * one_m_isopc(2) * one_p_isopc(3) * one_m_isopc(4) / 16.0_SiKi - m%N4D( 6) = one_p_isopc(1) * one_m_isopc(2) * one_p_isopc(3) * one_m_isopc(4) / 16.0_SiKi - m%N4D( 7) = one_m_isopc(1) * one_p_isopc(2) * one_p_isopc(3) * one_m_isopc(4) / 16.0_SiKi - m%N4D( 8) = one_p_isopc(1) * one_p_isopc(2) * one_p_isopc(3) * one_m_isopc(4) / 16.0_SiKi - m%N4D( 9) = one_m_isopc(1) * one_m_isopc(2) * one_m_isopc(3) * one_p_isopc(4) / 16.0_SiKi - m%N4D(10) = one_p_isopc(1) * one_m_isopc(2) * one_m_isopc(3) * one_p_isopc(4) / 16.0_SiKi - m%N4D(11) = one_m_isopc(1) * one_p_isopc(2) * one_m_isopc(3) * one_p_isopc(4) / 16.0_SiKi - m%N4D(12) = one_p_isopc(1) * one_p_isopc(2) * one_m_isopc(3) * one_p_isopc(4) / 16.0_SiKi - m%N4D(13) = one_m_isopc(1) * one_m_isopc(2) * one_p_isopc(3) * one_p_isopc(4) / 16.0_SiKi - m%N4D(14) = one_p_isopc(1) * one_m_isopc(2) * one_p_isopc(3) * one_p_isopc(4) / 16.0_SiKi - m%N4D(15) = one_m_isopc(1) * one_p_isopc(2) * one_p_isopc(3) * one_p_isopc(4) / 16.0_SiKi - m%N4D(16) = one_p_isopc(1) * one_p_isopc(2) * one_p_isopc(3) * one_p_isopc(4) / 16.0_SiKi + CALL GridInterpSetup3D((/Real(Time,ReKi),Position(1),Position(2)/), p, m, ErrStat2, ErrMsg2 ) + if (Failed()) return; contains logical function Failed() call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) Failed = ErrStat >= AbortErrLev end function -END Subroutine WaveField_Interp_Setup4D - +END Subroutine WaveField_Interp_Setup3D -subroutine WaveField_Interp_Setup3D( Time, Position, p, m, ErrStat, ErrMsg ) - real(DbKi), intent(in ) :: Time !< time from the start of the simulation - real(ReKi), intent(in ) :: Position(2) !< Array of XYZ coordinates, 3 - type(SeaSt_WaveField_ParameterType), intent(in ) :: p !< Parameters - type(SeaSt_WaveField_MiscVarType), intent(inout) :: m !< MiscVars +subroutine WaveField_Interp_Setup4D( Time, Position, GridDepth, p, m, ErrStat, ErrMsg ) + real(DbKi), intent(in ) :: Time !< Time from the start of the simulation + real(ReKi), intent(in ) :: Position(3) !< Array of XYZ coordinates, 3 + real(SiKi), intent(in ) :: GridDepth !< Depth (>0) of the wave grid below SWL + type(GridInterp_ParameterType), intent(in ) :: p !< Parameters + type(GridInterp_MiscVarType), intent(inout) :: m !< MiscVars integer(IntKi), intent( out) :: ErrStat !< Error status character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None - character(*), parameter :: RoutineName = 'WaveField_Interp_Setup3D' - integer(IntKi) :: i - real(SiKi) :: isopc(3) ! isoparametric coordinates - real(SiKi) :: one_m_isopc(3) ! 1 - isoparametric coordinates - real(SiKi) :: one_p_isopc(3) ! 1 + isoparametric coordinates + real(ReKi) :: kz + + character(*), parameter :: RoutineName = 'WaveField_Interp_Setup4D' integer(IntKi) :: ErrStat2 character(ErrMsgLen) :: ErrMsg2 ErrStat = ErrID_None + ErrMsg = "" - ! Find the bounding indices for time - call SetTimeIndex(Time, p%delta(1), p%n(1), m%Indx_Lo(1), m%Indx_Hi(1), isopc(1), ErrStat2, ErrMsg2) - if (Failed()) return; - - ! Find the bounding indices for XY position - do i=2,3 ! x and y components - call SetCartesianXYIndex(Position(i-1), p%pZero(i), p%delta(i), p%n(i), m%Indx_Lo(i), m%Indx_Hi(i), isopc(i), m%FirstWarn_Clamp, ErrStat2, ErrMsg2) - if (Failed()) return; - enddo - - ! Calculate 1+ and 1- isoparametric coordinates to avoid recalculations - one_m_isopc = 1.0_SiKi - isopc - one_p_isopc = 1.0_SiKi + isopc - - ! compute weighting factors - m%N3D(1) = one_m_isopc(1) * one_m_isopc(2) * one_m_isopc(3) / 8.0_SiKi - m%N3D(2) = one_p_isopc(1) * one_m_isopc(2) * one_m_isopc(3) / 8.0_SiKi - m%N3D(3) = one_m_isopc(1) * one_p_isopc(2) * one_m_isopc(3) / 8.0_SiKi - m%N3D(4) = one_p_isopc(1) * one_p_isopc(2) * one_m_isopc(3) / 8.0_SiKi - m%N3D(5) = one_m_isopc(1) * one_m_isopc(2) * one_p_isopc(3) / 8.0_SiKi - m%N3D(6) = one_p_isopc(1) * one_m_isopc(2) * one_p_isopc(3) / 8.0_SiKi - m%N3D(7) = one_m_isopc(1) * one_p_isopc(2) * one_p_isopc(3) / 8.0_SiKi - m%N3D(8) = one_p_isopc(1) * one_p_isopc(2) * one_p_isopc(3) / 8.0_SiKi + ! Map physical z-coordinate to grid index space + kz = 0.5_ReKi*Pi - acos( max( -1.0_ReKi, min( 1.0_ReKi, 1.0_ReKi + (Position(3) / GridDepth) ) ) ) + call GridInterpSetup4D( (/Real(Time,ReKi),Position(1),Position(2),kz/), p, m, ErrStat, ErrMsg ) contains logical function Failed() call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) Failed = ErrStat >= AbortErrLev end function -END Subroutine WaveField_Interp_Setup3D - - -!==================================================================================================== -!> This routine interpolates a 4-d dataset. -!! This method is described here: http://rjwagner49.com/Mathematics/WaveFieldolation.pdf -function WaveField_Interp_4D( pKinXX, m ) - real(SiKi), intent(in ) :: pKinXX(0:,:,:,:) - type(SeaSt_WaveField_MiscVarType), intent(in ) :: m - - real(SiKi) :: WaveField_Interp_4D - - ! interpolate - WaveField_Interp_4D = & - m%N4D( 1) * pKinXX( m%Indx_Lo(1), m%Indx_Lo(2), m%Indx_Lo(3), m%Indx_Lo(4) ) + & - m%N4D( 2) * pKinXX( m%Indx_Hi(1), m%Indx_Lo(2), m%Indx_Lo(3), m%Indx_Lo(4) ) + & - m%N4D( 3) * pKinXX( m%Indx_Lo(1), m%Indx_Hi(2), m%Indx_Lo(3), m%Indx_Lo(4) ) + & - m%N4D( 4) * pKinXX( m%Indx_Hi(1), m%Indx_Hi(2), m%Indx_Lo(3), m%Indx_Lo(4) ) + & - m%N4D( 5) * pKinXX( m%Indx_Lo(1), m%Indx_Lo(2), m%Indx_Hi(3), m%Indx_Lo(4) ) + & - m%N4D( 6) * pKinXX( m%Indx_Hi(1), m%Indx_Lo(2), m%Indx_Hi(3), m%Indx_Lo(4) ) + & - m%N4D( 7) * pKinXX( m%Indx_Lo(1), m%Indx_Hi(2), m%Indx_Hi(3), m%Indx_Lo(4) ) + & - m%N4D( 8) * pKinXX( m%Indx_Hi(1), m%Indx_Hi(2), m%Indx_Hi(3), m%Indx_Lo(4) ) + & - m%N4D( 9) * pKinXX( m%Indx_Lo(1), m%Indx_Lo(2), m%Indx_Lo(3), m%Indx_Hi(4) ) + & - m%N4D(10) * pKinXX( m%Indx_Hi(1), m%Indx_Lo(2), m%Indx_Lo(3), m%Indx_Hi(4) ) + & - m%N4D(11) * pKinXX( m%Indx_Lo(1), m%Indx_Hi(2), m%Indx_Lo(3), m%Indx_Hi(4) ) + & - m%N4D(12) * pKinXX( m%Indx_Hi(1), m%Indx_Hi(2), m%Indx_Lo(3), m%Indx_Hi(4) ) + & - m%N4D(13) * pKinXX( m%Indx_Lo(1), m%Indx_Lo(2), m%Indx_Hi(3), m%Indx_Hi(4) ) + & - m%N4D(14) * pKinXX( m%Indx_Hi(1), m%Indx_Lo(2), m%Indx_Hi(3), m%Indx_Hi(4) ) + & - m%N4D(15) * pKinXX( m%Indx_Lo(1), m%Indx_Hi(2), m%Indx_Hi(3), m%Indx_Hi(4) ) + & - m%N4D(16) * pKinXX( m%Indx_Hi(1), m%Indx_Hi(2), m%Indx_Hi(3), m%Indx_Hi(4) ) -end function WaveField_Interp_4D - - -!==================================================================================================== -!> This routine interpolates a 4-d dataset. -!! This method is described here: http://rjwagner49.com/Mathematics/Interpolation.pdf -function WaveField_Interp_4D_Vec( pKinXX, m) - real(SiKi), intent(in ) :: pKinXX(0:,:,:,:,:) - type(SeaSt_WaveField_MiscVarType), intent(in ) :: m !< misc vars for interpolation - - real(SiKi) :: WaveField_Interp_4D_Vec(3) - integer(IntKi) :: iDir - - ! interpolate - do iDir = 1,3 - WaveField_Interp_4D_Vec(iDir) = & - m%N4D( 1) * pKinXX( m%Indx_Lo(1), m%Indx_Lo(2), m%Indx_Lo(3), m%Indx_Lo(4), iDir ) + & - m%N4D( 2) * pKinXX( m%Indx_Hi(1), m%Indx_Lo(2), m%Indx_Lo(3), m%Indx_Lo(4), iDir ) + & - m%N4D( 3) * pKinXX( m%Indx_Lo(1), m%Indx_Hi(2), m%Indx_Lo(3), m%Indx_Lo(4), iDir ) + & - m%N4D( 4) * pKinXX( m%Indx_Hi(1), m%Indx_Hi(2), m%Indx_Lo(3), m%Indx_Lo(4), iDir ) + & - m%N4D( 5) * pKinXX( m%Indx_Lo(1), m%Indx_Lo(2), m%Indx_Hi(3), m%Indx_Lo(4), iDir ) + & - m%N4D( 6) * pKinXX( m%Indx_Hi(1), m%Indx_Lo(2), m%Indx_Hi(3), m%Indx_Lo(4), iDir ) + & - m%N4D( 7) * pKinXX( m%Indx_Lo(1), m%Indx_Hi(2), m%Indx_Hi(3), m%Indx_Lo(4), iDir ) + & - m%N4D( 8) * pKinXX( m%Indx_Hi(1), m%Indx_Hi(2), m%Indx_Hi(3), m%Indx_Lo(4), iDir ) + & - m%N4D( 9) * pKinXX( m%Indx_Lo(1), m%Indx_Lo(2), m%Indx_Lo(3), m%Indx_Hi(4), iDir ) + & - m%N4D(10) * pKinXX( m%Indx_Hi(1), m%Indx_Lo(2), m%Indx_Lo(3), m%Indx_Hi(4), iDir ) + & - m%N4D(11) * pKinXX( m%Indx_Lo(1), m%Indx_Hi(2), m%Indx_Lo(3), m%Indx_Hi(4), iDir ) + & - m%N4D(12) * pKinXX( m%Indx_Hi(1), m%Indx_Hi(2), m%Indx_Lo(3), m%Indx_Hi(4), iDir ) + & - m%N4D(13) * pKinXX( m%Indx_Lo(1), m%Indx_Lo(2), m%Indx_Hi(3), m%Indx_Hi(4), iDir ) + & - m%N4D(14) * pKinXX( m%Indx_Hi(1), m%Indx_Lo(2), m%Indx_Hi(3), m%Indx_Hi(4), iDir ) + & - m%N4D(15) * pKinXX( m%Indx_Lo(1), m%Indx_Hi(2), m%Indx_Hi(3), m%Indx_Hi(4), iDir ) + & - m%N4D(16) * pKinXX( m%Indx_Hi(1), m%Indx_Hi(2), m%Indx_Hi(3), m%Indx_Hi(4), iDir ) - end do -END FUNCTION WaveField_Interp_4D_Vec - - -!==================================================================================================== -!> This routine interpolates a 4-d dataset. -!! This method is described here: http://rjwagner49.com/Mathematics/Interpolation.pdf -function WaveField_Interp_4D_Vec6( pKinXX, m) - real(SiKi), intent(in ) :: pKinXX(0:,:,:,:,:) - type(SeaSt_WaveField_MiscVarType), intent(in ) :: m !< misc vars for interpolation - - real(SiKi) :: WaveField_Interp_4D_Vec6(6) - integer(IntKi) :: iDir - - ! interpolate - do iDir = 1,6 - WaveField_Interp_4D_Vec6(iDir) = & - m%N4D( 1) * pKinXX( m%Indx_Lo(1), m%Indx_Lo(2), m%Indx_Lo(3), m%Indx_Lo(4), iDir ) + & - m%N4D( 2) * pKinXX( m%Indx_Hi(1), m%Indx_Lo(2), m%Indx_Lo(3), m%Indx_Lo(4), iDir ) + & - m%N4D( 3) * pKinXX( m%Indx_Lo(1), m%Indx_Hi(2), m%Indx_Lo(3), m%Indx_Lo(4), iDir ) + & - m%N4D( 4) * pKinXX( m%Indx_Hi(1), m%Indx_Hi(2), m%Indx_Lo(3), m%Indx_Lo(4), iDir ) + & - m%N4D( 5) * pKinXX( m%Indx_Lo(1), m%Indx_Lo(2), m%Indx_Hi(3), m%Indx_Lo(4), iDir ) + & - m%N4D( 6) * pKinXX( m%Indx_Hi(1), m%Indx_Lo(2), m%Indx_Hi(3), m%Indx_Lo(4), iDir ) + & - m%N4D( 7) * pKinXX( m%Indx_Lo(1), m%Indx_Hi(2), m%Indx_Hi(3), m%Indx_Lo(4), iDir ) + & - m%N4D( 8) * pKinXX( m%Indx_Hi(1), m%Indx_Hi(2), m%Indx_Hi(3), m%Indx_Lo(4), iDir ) + & - m%N4D( 9) * pKinXX( m%Indx_Lo(1), m%Indx_Lo(2), m%Indx_Lo(3), m%Indx_Hi(4), iDir ) + & - m%N4D(10) * pKinXX( m%Indx_Hi(1), m%Indx_Lo(2), m%Indx_Lo(3), m%Indx_Hi(4), iDir ) + & - m%N4D(11) * pKinXX( m%Indx_Lo(1), m%Indx_Hi(2), m%Indx_Lo(3), m%Indx_Hi(4), iDir ) + & - m%N4D(12) * pKinXX( m%Indx_Hi(1), m%Indx_Hi(2), m%Indx_Lo(3), m%Indx_Hi(4), iDir ) + & - m%N4D(13) * pKinXX( m%Indx_Lo(1), m%Indx_Lo(2), m%Indx_Hi(3), m%Indx_Hi(4), iDir ) + & - m%N4D(14) * pKinXX( m%Indx_Hi(1), m%Indx_Lo(2), m%Indx_Hi(3), m%Indx_Hi(4), iDir ) + & - m%N4D(15) * pKinXX( m%Indx_Lo(1), m%Indx_Hi(2), m%Indx_Hi(3), m%Indx_Hi(4), iDir ) + & - m%N4D(16) * pKinXX( m%Indx_Hi(1), m%Indx_Hi(2), m%Indx_Hi(3), m%Indx_Hi(4), iDir ) - end do -END FUNCTION WaveField_Interp_4D_Vec6 - +END Subroutine WaveField_Interp_Setup4D -!==================================================================================================== -!> This routine interpolates a 3-d dataset with index 1 = time (zero-based indexing), 2 = x-coordinate (1-based indexing), 3 = y-coordinate (1-based indexing) -!! This method is described here: http://rjwagner49.com/Mathematics/Interpolation.pdf -!FIXME: do like the above and call the WaveField_Interp_Setup3D routine ahead -function WaveField_Interp_3D( pKinXX, m ) - real(SiKi), intent(in ) :: pKinXX(0:,:,:) !< 3D Wave elevation data (SiKi for storage space reasons) - type(SeaSt_WaveField_MiscVarType), intent(inout) :: m !< MiscVars - - character(*), parameter :: RoutineName = 'WaveField_Interp_3D' - real(SiKi) :: WaveField_Interp_3D - integer(IntKi) :: i - - ! interpolate - WaveField_Interp_3D = & - m%N3D(1) * pKinXX( m%Indx_Lo(1), m%Indx_Lo(2), m%Indx_Lo(3) ) + & - m%N3D(2) * pKinXX( m%Indx_Hi(1), m%Indx_Lo(2), m%Indx_Lo(3) ) + & - m%N3D(3) * pKinXX( m%Indx_Lo(1), m%Indx_Hi(2), m%Indx_Lo(3) ) + & - m%N3D(4) * pKinXX( m%Indx_Hi(1), m%Indx_Hi(2), m%Indx_Lo(3) ) + & - m%N3D(5) * pKinXX( m%Indx_Lo(1), m%Indx_Lo(2), m%Indx_Hi(3) ) + & - m%N3D(6) * pKinXX( m%Indx_Hi(1), m%Indx_Lo(2), m%Indx_Hi(3) ) + & - m%N3D(7) * pKinXX( m%Indx_Lo(1), m%Indx_Hi(2), m%Indx_Hi(3) ) + & - m%N3D(8) * pKinXX( m%Indx_Hi(1), m%Indx_Hi(2), m%Indx_Hi(3) ) -end function WaveField_Interp_3D - - -FUNCTION WaveField_Interp_3D_VEC( pKinXX, m ) - real(SiKi), intent(in ) :: pKinXX(0:,:,:,:) !< 3D Wave excitation data (SiKi for storage space reasons) - type(SeaSt_WaveField_MiscVarType), intent(inout) :: m !< MiscVars - - real(SiKi) :: WaveField_Interp_3D_VEC(3) - integer(IntKi) :: i - - ! interpolate - do i = 1,3 - WaveField_Interp_3D_VEC(i) = & - m%N3D(1) * pKinXX( m%Indx_Lo(1), m%Indx_Lo(2), m%Indx_Lo(3), i ) + & - m%N3D(2) * pKinXX( m%Indx_Hi(1), m%Indx_Lo(2), m%Indx_Lo(3), i ) + & - m%N3D(3) * pKinXX( m%Indx_Lo(1), m%Indx_Hi(2), m%Indx_Lo(3), i ) + & - m%N3D(4) * pKinXX( m%Indx_Hi(1), m%Indx_Hi(2), m%Indx_Lo(3), i ) + & - m%N3D(5) * pKinXX( m%Indx_Lo(1), m%Indx_Lo(2), m%Indx_Hi(3), i ) + & - m%N3D(6) * pKinXX( m%Indx_Hi(1), m%Indx_Lo(2), m%Indx_Hi(3), i ) + & - m%N3D(7) * pKinXX( m%Indx_Lo(1), m%Indx_Hi(2), m%Indx_Hi(3), i ) + & - m%N3D(8) * pKinXX( m%Indx_Hi(1), m%Indx_Hi(2), m%Indx_Hi(3), i ) - end do -end function WaveField_Interp_3D_VEC - - -function Wavefield_Interp_3D_VEC6( pKinXX, m ) - real(SiKi), intent(in ) :: pKinXX(0:,:,:,:) !< 3D Wave excitation data (SiKi for storage space reasons) - type(SeaSt_WaveField_MiscVarType), intent(inout) :: m !< Miscvars - - real(SiKi) :: Wavefield_Interp_3D_VEC6(6) - integer(IntKi) :: i - - ! interpolate - do i = 1,6 - Wavefield_Interp_3D_VEC6(i) = & - m%N3D(1) * pKinXX( m%Indx_Lo(1), m%Indx_Lo(2), m%Indx_Lo(3), i ) + & - m%N3D(2) * pKinXX( m%Indx_Hi(1), m%Indx_Lo(2), m%Indx_Lo(3), i ) + & - m%N3D(3) * pKinXX( m%Indx_Lo(1), m%Indx_Hi(2), m%Indx_Lo(3), i ) + & - m%N3D(4) * pKinXX( m%Indx_Hi(1), m%Indx_Hi(2), m%Indx_Lo(3), i ) + & - m%N3D(5) * pKinXX( m%Indx_Lo(1), m%Indx_Lo(2), m%Indx_Hi(3), i ) + & - m%N3D(6) * pKinXX( m%Indx_Hi(1), m%Indx_Lo(2), m%Indx_Hi(3), i ) + & - m%N3D(7) * pKinXX( m%Indx_Lo(1), m%Indx_Hi(2), m%Indx_Hi(3), i ) + & - m%N3D(8) * pKinXX( m%Indx_Hi(1), m%Indx_Hi(2), m%Indx_Hi(3), i ) - end do -end function Wavefield_Interp_3D_VEC6 END MODULE SeaSt_WaveField diff --git a/modules/seastate/src/SeaSt_WaveField.txt b/modules/seastate/src/SeaSt_WaveField.txt index eb4ec5a6d5..c1327b8f6e 100644 --- a/modules/seastate/src/SeaSt_WaveField.txt +++ b/modules/seastate/src/SeaSt_WaveField.txt @@ -1,5 +1,6 @@ # ...... Include files ..... usefrom Current.txt +usefrom GridInterp.txt #--------------------------------------------------------------------------------------------------------------------------------------------------------- # Data structures for representing wave fields. # @@ -30,11 +31,6 @@ param SeaSt_WaveField - INTEGER WvCrntMod_Fu #--------------------------------------------------------------------------------------------------------------------------------------------------------- # #--------------------------------------------------------------------------------------------------------------------------------------------------------- -typedef ^ ParameterType IntKi n 4 - - "number of evenly-spaced grid points in the t, x, y, and z directions" - -typedef ^ ParameterType ReKi delta 4 - - "size between 2 consecutive grid points in each grid direction" "s,m,m,m" -typedef ^ ParameterType ReKi pZero 4 - - "fixed position of the XYZ grid (i.e., XYZ coordinates of m%V(:,1,1,1,:))" "m" -typedef ^ ParameterType ReKi Z_Depth - - - "grid depth" m - typedef ^ MiscVarType SiKi N3D {8} - - "this is the weighting function for 3-d velocity field" - typedef ^ MiscVarType SiKi N4D {16} - - "this is the weighting function for 4-d velocity field" - typedef ^ MiscVarType integer Indx_Lo 4 - - "this is the index into the 4-d velocity field for each wave component" - @@ -54,7 +50,8 @@ typedef ^ ^ SiKi PWaveVel0 typedef ^ ^ SiKi WaveElev0 {:} - - "Instantaneous elevation time-series of incident waves at the platform reference point (NOTE THAT THIS CAN GET MODIFIED IN WAMIT)" (m) typedef ^ ^ SiKi WaveElev1 {:}{:}{:} - - "First order wave elevation" (m) typedef ^ ^ SiKi WaveElev2 {:}{:}{:} - - "Second order wave elevation" (m) -typedef ^ ^ SeaSt_WaveField_ParameterType GridParams - - - "Parameters for grid spacing" (-) +typedef ^ ^ GridInterp_ParameterType SrfGridParams - - - "Parameters of the wave free surface grid needed for interpolation" - +typedef ^ ^ GridInterp_ParameterType VolGridParams - - - "Parameters of the wave field volume grid needed for interpolation" - typedef ^ ^ IntKi WaveStMod - - - "Wave stretching model" typedef ^ ^ ReKi EffWtrDpth - - - "Water depth" (-) typedef ^ ^ ReKi MSL2SWL - - - "Vertical distance from mean sea level to still water level" (m) @@ -84,4 +81,6 @@ typedef ^ ^ INTEGER WvCrntMod typedef ^ ^ INTEGER NStepWave - - - "Total number of frequency components = total number of time steps in the incident wave" - typedef ^ ^ INTEGER NStepWave2 - - - "NStepWave / 2" - +typedef ^ ^ SiKi GridDepth - - - "Depth (>0) of wave grid below SWL" m +typedef ^ ^ DbKi WaveTimeShift - 0 - "Add this to the time to effectively phase shift the wave (useful for hybrid tank testing). Positive value only (advance time)" (s) typedef ^ ^ Current_InitInputType Current_InitInput - - - "InitInputs in the Current Module. For coupling with MD." - diff --git a/modules/seastate/src/SeaSt_WaveField_Types.f90 b/modules/seastate/src/SeaSt_WaveField_Types.f90 index ba4d49bc50..cba2c94b4c 100644 --- a/modules/seastate/src/SeaSt_WaveField_Types.f90 +++ b/modules/seastate/src/SeaSt_WaveField_Types.f90 @@ -32,6 +32,7 @@ MODULE SeaSt_WaveField_Types !--------------------------------------------------------------------------------------------------------------------------------- USE Current_Types +USE GridInterp_Types USE IfW_FlowField_Types USE NWTC_Library IMPLICIT NONE @@ -52,14 +53,6 @@ MODULE SeaSt_WaveField_Types INTEGER(IntKi), PUBLIC, PARAMETER :: WvCrntMod_Superpose = 0 ! WvCrntMod = 0 [Simpler superposition] [-] INTEGER(IntKi), PUBLIC, PARAMETER :: WvCrntMod_Doppler = 1 ! WvCrntMod = 1 [Doppler effect] [-] INTEGER(IntKi), PUBLIC, PARAMETER :: WvCrntMod_Full = 2 ! WvCrntMod = 2 [Doppler effect and amplitude/spectrum scaling] [-] -! ========= SeaSt_WaveField_ParameterType ======= - TYPE, PUBLIC :: SeaSt_WaveField_ParameterType - INTEGER(IntKi) , DIMENSION(1:4) :: n = 0_IntKi !< number of evenly-spaced grid points in the t, x, y, and z directions [-] - REAL(ReKi) , DIMENSION(1:4) :: delta = 0.0_ReKi !< size between 2 consecutive grid points in each grid direction [s,m,m,m] - REAL(ReKi) , DIMENSION(1:4) :: pZero = 0.0_ReKi !< fixed position of the XYZ grid (i.e., XYZ coordinates of m%V(:,1,1,1,:)) [m] - REAL(ReKi) :: Z_Depth = 0.0_ReKi !< grid depth [m] - END TYPE SeaSt_WaveField_ParameterType -! ======================= ! ========= SeaSt_WaveField_MiscVarType ======= TYPE, PUBLIC :: SeaSt_WaveField_MiscVarType REAL(SiKi) , DIMENSION(1:8) :: N3D = 0.0_R4Ki !< this is the weighting function for 3-d velocity field [-] @@ -83,7 +76,8 @@ MODULE SeaSt_WaveField_Types REAL(SiKi) , DIMENSION(:), ALLOCATABLE :: WaveElev0 !< Instantaneous elevation time-series of incident waves at the platform reference point (NOTE THAT THIS CAN GET MODIFIED IN WAMIT) [(m)] REAL(SiKi) , DIMENSION(:,:,:), ALLOCATABLE :: WaveElev1 !< First order wave elevation [(m)] REAL(SiKi) , DIMENSION(:,:,:), ALLOCATABLE :: WaveElev2 !< Second order wave elevation [(m)] - TYPE(SeaSt_WaveField_ParameterType) :: GridParams !< Parameters for grid spacing [(-)] + TYPE(GridInterp_ParameterType) :: SrfGridParams !< Parameters of the wave free surface grid needed for interpolation [-] + TYPE(GridInterp_ParameterType) :: VolGridParams !< Parameters of the wave field volume grid needed for interpolation [-] INTEGER(IntKi) :: WaveStMod = 0_IntKi !< Wave stretching model [-] REAL(ReKi) :: EffWtrDpth = 0.0_ReKi !< Water depth [(-)] REAL(ReKi) :: MSL2SWL = 0.0_ReKi !< Vertical distance from mean sea level to still water level [(m)] @@ -111,59 +105,14 @@ MODULE SeaSt_WaveField_Types INTEGER(IntKi) :: WvCrntMod = 0_IntKi !< Wave-current modeling option. [-] INTEGER(IntKi) :: NStepWave = 0_IntKi !< Total number of frequency components = total number of time steps in the incident wave [-] INTEGER(IntKi) :: NStepWave2 = 0_IntKi !< NStepWave / 2 [-] + REAL(SiKi) :: GridDepth = 0.0_R4Ki !< Depth (>0) of wave grid below SWL [m] + REAL(DbKi) :: WaveTimeShift = 0 !< Add this to the time to effectively phase shift the wave (useful for hybrid tank testing). Positive value only (advance time) [(s)] TYPE(Current_InitInputType) :: Current_InitInput !< InitInputs in the Current Module. For coupling with MD. [-] END TYPE SeaSt_WaveFieldType ! ======================= contains -subroutine SeaSt_WaveField_CopyParam(SrcParamData, DstParamData, CtrlCode, ErrStat, ErrMsg) - type(SeaSt_WaveField_ParameterType), intent(in) :: SrcParamData - type(SeaSt_WaveField_ParameterType), intent(inout) :: DstParamData - integer(IntKi), intent(in ) :: CtrlCode - integer(IntKi), intent( out) :: ErrStat - character(*), intent( out) :: ErrMsg - character(*), parameter :: RoutineName = 'SeaSt_WaveField_CopyParam' - ErrStat = ErrID_None - ErrMsg = '' - DstParamData%n = SrcParamData%n - DstParamData%delta = SrcParamData%delta - DstParamData%pZero = SrcParamData%pZero - DstParamData%Z_Depth = SrcParamData%Z_Depth -end subroutine - -subroutine SeaSt_WaveField_DestroyParam(ParamData, ErrStat, ErrMsg) - type(SeaSt_WaveField_ParameterType), intent(inout) :: ParamData - integer(IntKi), intent( out) :: ErrStat - character(*), intent( out) :: ErrMsg - character(*), parameter :: RoutineName = 'SeaSt_WaveField_DestroyParam' - ErrStat = ErrID_None - ErrMsg = '' -end subroutine - -subroutine SeaSt_WaveField_PackParam(RF, Indata) - type(RegFile), intent(inout) :: RF - type(SeaSt_WaveField_ParameterType), intent(in) :: InData - character(*), parameter :: RoutineName = 'SeaSt_WaveField_PackParam' - if (RF%ErrStat >= AbortErrLev) return - call RegPack(RF, InData%n) - call RegPack(RF, InData%delta) - call RegPack(RF, InData%pZero) - call RegPack(RF, InData%Z_Depth) - if (RegCheckErr(RF, RoutineName)) return -end subroutine - -subroutine SeaSt_WaveField_UnPackParam(RF, OutData) - type(RegFile), intent(inout) :: RF - type(SeaSt_WaveField_ParameterType), intent(inout) :: OutData - character(*), parameter :: RoutineName = 'SeaSt_WaveField_UnPackParam' - if (RF%ErrStat /= ErrID_None) return - call RegUnpack(RF, OutData%n); if (RegCheckErr(RF, RoutineName)) return - call RegUnpack(RF, OutData%delta); if (RegCheckErr(RF, RoutineName)) return - call RegUnpack(RF, OutData%pZero); if (RegCheckErr(RF, RoutineName)) return - call RegUnpack(RF, OutData%Z_Depth); if (RegCheckErr(RF, RoutineName)) return -end subroutine - subroutine SeaSt_WaveField_CopyMisc(SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg) type(SeaSt_WaveField_MiscVarType), intent(in) :: SrcMiscData type(SeaSt_WaveField_MiscVarType), intent(inout) :: DstMiscData @@ -370,7 +319,10 @@ subroutine SeaSt_WaveField_CopySeaSt_WaveFieldType(SrcSeaSt_WaveFieldTypeData, D end if DstSeaSt_WaveFieldTypeData%WaveElev2 = SrcSeaSt_WaveFieldTypeData%WaveElev2 end if - call SeaSt_WaveField_CopyParam(SrcSeaSt_WaveFieldTypeData%GridParams, DstSeaSt_WaveFieldTypeData%GridParams, CtrlCode, ErrStat2, ErrMsg2) + call GridInterp_CopyParam(SrcSeaSt_WaveFieldTypeData%SrfGridParams, DstSeaSt_WaveFieldTypeData%SrfGridParams, CtrlCode, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + if (ErrStat >= AbortErrLev) return + call GridInterp_CopyParam(SrcSeaSt_WaveFieldTypeData%VolGridParams, DstSeaSt_WaveFieldTypeData%VolGridParams, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return DstSeaSt_WaveFieldTypeData%WaveStMod = SrcSeaSt_WaveFieldTypeData%WaveStMod @@ -433,6 +385,8 @@ subroutine SeaSt_WaveField_CopySeaSt_WaveFieldType(SrcSeaSt_WaveFieldTypeData, D DstSeaSt_WaveFieldTypeData%WvCrntMod = SrcSeaSt_WaveFieldTypeData%WvCrntMod DstSeaSt_WaveFieldTypeData%NStepWave = SrcSeaSt_WaveFieldTypeData%NStepWave DstSeaSt_WaveFieldTypeData%NStepWave2 = SrcSeaSt_WaveFieldTypeData%NStepWave2 + DstSeaSt_WaveFieldTypeData%GridDepth = SrcSeaSt_WaveFieldTypeData%GridDepth + DstSeaSt_WaveFieldTypeData%WaveTimeShift = SrcSeaSt_WaveFieldTypeData%WaveTimeShift call Current_CopyInitInput(SrcSeaSt_WaveFieldTypeData%Current_InitInput, DstSeaSt_WaveFieldTypeData%Current_InitInput, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return @@ -483,7 +437,9 @@ subroutine SeaSt_WaveField_DestroySeaSt_WaveFieldType(SeaSt_WaveFieldTypeData, E if (allocated(SeaSt_WaveFieldTypeData%WaveElev2)) then deallocate(SeaSt_WaveFieldTypeData%WaveElev2) end if - call SeaSt_WaveField_DestroyParam(SeaSt_WaveFieldTypeData%GridParams, ErrStat2, ErrMsg2) + call GridInterp_DestroyParam(SeaSt_WaveFieldTypeData%SrfGridParams, ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) + call GridInterp_DestroyParam(SeaSt_WaveFieldTypeData%VolGridParams, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (allocated(SeaSt_WaveFieldTypeData%WaveElevC)) then deallocate(SeaSt_WaveFieldTypeData%WaveElevC) @@ -517,7 +473,8 @@ subroutine SeaSt_WaveField_PackSeaSt_WaveFieldType(RF, Indata) call RegPackAlloc(RF, InData%WaveElev0) call RegPackAlloc(RF, InData%WaveElev1) call RegPackAlloc(RF, InData%WaveElev2) - call SeaSt_WaveField_PackParam(RF, InData%GridParams) + call GridInterp_PackParam(RF, InData%SrfGridParams) + call GridInterp_PackParam(RF, InData%VolGridParams) call RegPack(RF, InData%WaveStMod) call RegPack(RF, InData%EffWtrDpth) call RegPack(RF, InData%MSL2SWL) @@ -551,6 +508,8 @@ subroutine SeaSt_WaveField_PackSeaSt_WaveFieldType(RF, Indata) call RegPack(RF, InData%WvCrntMod) call RegPack(RF, InData%NStepWave) call RegPack(RF, InData%NStepWave2) + call RegPack(RF, InData%GridDepth) + call RegPack(RF, InData%WaveTimeShift) call Current_PackInitInput(RF, InData%Current_InitInput) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -577,7 +536,8 @@ subroutine SeaSt_WaveField_UnPackSeaSt_WaveFieldType(RF, OutData) call RegUnpackAlloc(RF, OutData%WaveElev0); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%WaveElev1); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%WaveElev2); if (RegCheckErr(RF, RoutineName)) return - call SeaSt_WaveField_UnpackParam(RF, OutData%GridParams) ! GridParams + call GridInterp_UnpackParam(RF, OutData%SrfGridParams) ! SrfGridParams + call GridInterp_UnpackParam(RF, OutData%VolGridParams) ! VolGridParams call RegUnpack(RF, OutData%WaveStMod); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%EffWtrDpth); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%MSL2SWL); if (RegCheckErr(RF, RoutineName)) return @@ -622,6 +582,8 @@ subroutine SeaSt_WaveField_UnPackSeaSt_WaveFieldType(RF, OutData) call RegUnpack(RF, OutData%WvCrntMod); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%NStepWave); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%NStepWave2); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%GridDepth); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%WaveTimeShift); if (RegCheckErr(RF, RoutineName)) return call Current_UnpackInitInput(RF, OutData%Current_InitInput) ! Current_InitInput end subroutine diff --git a/modules/seastate/src/SeaState.f90 b/modules/seastate/src/SeaState.f90 index ed656cf6a5..f57de4396e 100644 --- a/modules/seastate/src/SeaState.f90 +++ b/modules/seastate/src/SeaState.f90 @@ -32,6 +32,7 @@ MODULE SeaState USE SeaState_Output USE Current USE Waves2 + USE GridInterp IMPLICIT NONE PRIVATE @@ -184,6 +185,13 @@ SUBROUTINE SeaSt_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, Init ! Initialize Waves module (Note that this may change InputFileData%Waves%WaveDT) CALL Waves_Init(InputFileData%Waves, Waves_InitOut, p%WaveField, ErrStat2, ErrMsg2 ); if(Failed()) return; + ! Store the WaveTimeShift + p%WaveField%WaveTimeShift = InitInp%WaveTimeShift + if (p%WaveField%WaveTimeShift < 0.0_DbKi) then + call SetErrStat(ErrID_Fatal, 'WaveTimeShift from driver code cannot be negative', ErrStat, ErrMsg, RoutineName) + return + endif + ! Copy Waves initialization output into the initialization input type for the WAMIT module p%WaveDT = InputFileData%Waves%WaveDT @@ -274,16 +282,28 @@ SUBROUTINE SeaSt_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, Init CALL SeaStOut_WrSummaryFile(InitInp, InputFileData, p, ErrStat2, ErrMsg2); if(Failed()) return; - - ! Setup the 4D grid information for the Interpolation Module - p%WaveField%GridParams%n = (/p%WaveField%NStepWave,p%nGrid(1),p%nGrid(2),p%nGrid(3)/) - p%WaveField%GridParams%delta = (/real(p%WaveDT,ReKi),p%deltaGrid(1),p%deltaGrid(2),p%deltaGrid(3)/) - p%WaveField%GridParams%pZero(1) = 0.0 !Time - p%WaveField%GridParams%pZero(2) = -InputFileData%X_HalfWidth - p%WaveField%GridParams%pZero(3) = -InputFileData%Y_HalfWidth - p%WaveField%GridParams%pZero(4) = -InputFileData%Z_Depth ! zi - p%WaveField%GridParams%Z_Depth = InputFileData%Z_Depth + ! Setup the 3D and 4D grid information for the Interpolation Module + p%WaveField%GridDepth = InputFileData%Z_Depth + + ! Set the parameters of the 3D free surface grid + CALL GridInterp_SetParams(3_IntKi, & ! dimension + (/p%WaveField%NStepWave,p%nGrid(1),p%nGrid(2)/), & ! n + (/real(p%WaveDT,ReKi),p%deltaGrid(1),p%deltaGrid(2)/), & ! delta + (/0.0,-InputFileData%X_HalfWidth,-InputFileData%Y_HalfWidth/), & ! pZero + (/.true.,.false.,.false./), & ! periodicity + p%WaveField%SrfGridParams, ErrStat2, ErrMsg2 ) + if(Failed()) return; + + ! Set the parameters of the 4D volume grid + CALL GridInterp_SetParams(4_IntKi, & ! dimension + (/p%WaveField%NStepWave,p%nGrid(1),p%nGrid(2),p%nGrid(3)/), & ! n + (/real(p%WaveDT,ReKi),p%deltaGrid(1),p%deltaGrid(2),p%deltaGrid(3)/), & ! delta + (/0.0,-InputFileData%X_HalfWidth,-InputFileData%Y_HalfWidth,0.0/), & ! pZero + (/.true.,.false.,.false.,.false./), & ! periodicity + p%WaveField%VolGridParams, ErrStat2, ErrMsg2 ) + if(Failed()) return; + IF ( p%OutSwtch == 1 ) THEN ! Only SeaSt-level output writing ! HACK WE can tell FAST not to write any SeaState outputs by simply deallocating the WriteOutputHdr array! @@ -404,15 +424,15 @@ subroutine SurfaceVisGenerate(ErrStat3, ErrMsg3) ErrMsg3 = "" ! Grid half width from the WaveField - HWidX = (real(p%WaveField%GridParams%n(2)-1,SiKi)) / 2.0_SiKi * p%WaveField%GridParams%delta(2) - HWidY = (real(p%WaveField%GridParams%n(3)-1,SiKi)) / 2.0_SiKi * p%WaveField%GridParams%delta(3) + HWidX = (real(p%WaveField%SrfGridParams%n(2)-1,SiKi)) / 2.0_SiKi * p%WaveField%SrfGridParams%delta(2) + HWidY = (real(p%WaveField%SrfGridParams%n(3)-1,SiKi)) / 2.0_SiKi * p%WaveField%SrfGridParams%delta(3) if ((InitInp%SurfaceVisNx <= 0) .or. (InitInp%SurfaceVisNy <= 0))then ! use the SeaState points exactly ! Set number of points to the number of seastate grid points in each direction - Nx = p%WaveField%GridParams%n(2) - Ny = p%WaveField%GridParams%n(3) - dx = p%WaveField%GridParams%delta(2) - dy = p%WaveField%GridParams%delta(3) + Nx = p%WaveField%SrfGridParams%n(2) + Ny = p%WaveField%SrfGridParams%n(3) + dx = p%WaveField%SrfGridParams%delta(2) + dy = p%WaveField%SrfGridParams%delta(3) call SetErrStat(ErrID_Info,"Setting wavefield visualization grid to "//trim(Num2LStr(Nx))//" x "//trim(Num2LStr(Ny))//"points",ErrStat3,ErrMsg3,RoutineName) elseif ((InitInp%SurfaceVisNx < 3) .or. (InitInp%SurfaceVisNx < 3)) then ! Set to 3 for minimum Nx = 3 diff --git a/modules/seastate/src/SeaState.txt b/modules/seastate/src/SeaState.txt index a31a93a308..ce87472aea 100644 --- a/modules/seastate/src/SeaState.txt +++ b/modules/seastate/src/SeaState.txt @@ -76,6 +76,7 @@ typedef ^ ^ ReKi def typedef ^ ^ ReKi defMSL2SWL - - - "Default mean sea level to still water level from the driver; may be overwritten" "m" typedef ^ ^ IntKi MHK - - - "MHK flag" - typedef ^ ^ DbKi TMax - - - "Supplied by Driver: The total simulation time" "(sec)" +typedef ^ ^ DbKi WaveTimeShift - 0 - "Add this to the time to effectively phase shift the wave (useful for hybrid tank testing). Positive value only (advance time)" (s) typedef ^ ^ INTEGER WaveFieldMod - - - "Wave field handling (-) (switch) 0: use individual SeaState inputs without adjustment, 1: adjust wave phases based on turbine offsets from farm origin" - typedef ^ ^ ReKi PtfmLocationX - - - "Supplied by Driver: X coordinate of platform location in the wave field" "m" typedef ^ ^ ReKi PtfmLocationY - - - "Supplied by Driver: Y coordinate of platform location in the wave field" "m" @@ -167,9 +168,20 @@ typedef ^ OutputType ReKi Wri typedef ^ MiscVarType INTEGER Decimate - - - "The output decimation counter" - typedef ^ ^ DbKi LastOutTime - - - "Last time step which was written to the output file (sec)" - typedef ^ ^ INTEGER LastIndWave - - - "The last index used in the wave kinematics arrays, used to optimize interpolation" - -typedef ^ ^ SeaSt_WaveField_MiscVarType WaveField_m - - - "misc var information from the SeaState Interpolation module" - +typedef ^ ^ GridInterp_MiscVarType WaveField_m - - - "misc var information from the SeaState Interpolation module" - # .... Linearization ....................................................................................................... typedef ^ ^ ModJacType Jac - - - "Values corresponding to module variables" - typedef ^ ^ SeaSt_InputType u_perturb - - - "Input type for linearization perturbation" - typedef ^ ^ SeaSt_OutputType y_lin - - - "Output type for linearization perturbation" - + +# typedef ^ Jac_u_idxStarts IntKi Extended - 1 - "Index to first point in u jacobian for Extended" - +# typedef ^ Jac_y_idxStarts IntKi Extended - 1 - "Index to first point in y jacobian for Extended" - +# typedef ^ Jac_y_idxStarts IntKi WrOuts - 2 - "Index to first point in y jacobian for WrOuts" - +# typedef ^ SeaSt_LinParams IntKi NumExtendedInputs - 1 - "number of extended inputs" - +# typedef ^ ^ IntKi NumExtendedOutputs - 1 - "number of extended outputs" - +# typedef ^ ^ Jac_u_idxStarts Jac_u_idxStartList - - - "Starting indices for all Jac_u components" - +# typedef ^ ^ Jac_y_idxStarts Jac_y_idxStartList - - - "Starting indices for all Jac_y components" - +# typedef ^ ^ ReKi du {:} - - "vector that determines size of perturbation for u (inputs)" +# typedef ^ ^ IntKi Jac_nu - - - "number of inputs in jacobian matrix" - +# typedef ^ ^ IntKi Jac_ny - - - "number of outputs in jacobian matrix" - diff --git a/modules/seastate/src/SeaState_C_Binding.f90 b/modules/seastate/src/SeaState_C_Binding.f90 index e7910afc58..0eccbe33bd 100644 --- a/modules/seastate/src/SeaState_C_Binding.f90 +++ b/modules/seastate/src/SeaState_C_Binding.f90 @@ -19,291 +19,1020 @@ !********************************************************************************************************************************** MODULE SeaState_C_Binding - USE ISO_C_BINDING - USE SeaState - USE SeaState_Types - USE SeaState_Output - USE NWTC_Library - USE NWTC_C_Binding, ONLY: ErrMsgLen_C, IntfStrLen, SetErrStat_F2C, FileNameFromCString - USE VersionInfo - - IMPLICIT NONE - SAVE - - PUBLIC :: SeaSt_C_Init - PUBLIC :: SeaSt_C_CalcOutput - PUBLIC :: SeaSt_C_End - - !------------------------------------------------------------------------------------ - ! Version info for display - TYPE(ProgDesc), PARAMETER :: version = SeaSt_ProgDesc - - !------------------------------------------------------------------------------------ - ! Debugging: DebugLevel -- passed at PreInit - ! 0 - none - ! 1 - some summary info - ! 2 - above + all position/orientation info - ! 3 - above + input files (if direct passed) - ! 4 - above + meshes - INTEGER(IntKi) :: DebugLevel = 0 - - !------------------------------ - ! Primary derived types - TYPE(SeaSt_InputType) :: InputData !< Inputs to SeaState - TYPE(SeaSt_InitInputType) :: InitInp - TYPE(SeaSt_InitOutputType) :: InitOutData !< Initial output data -- Names, units, and version info. - TYPE(SeaSt_ParameterType) :: p !< Parameters - TYPE(SeaSt_OutputType) :: y !< Initial output (outputs are not calculated; only the output mesh is initialized) - TYPE(SeaSt_MiscVarType) :: m !< Misc variables for optimization (not copied in glue code) - -CONTAINS - - -SUBROUTINE SeaSt_C_Init(InputFile_C, OutRootName_C, Gravity_C, WtrDens_C, WtrDpth_C, MSL2SWL_C, NSteps_C, TimeInterval_C, WaveElevSeriesFlag_C, WrWvKinMod_C, NumChannels_C, OutputChannelNames_C, OutputChannelUnits_C, ErrStat_C, ErrMsg_C) BIND (C, NAME='SeaSt_C_Init') -IMPLICIT NONE + USE ISO_C_BINDING + USE SeaSt_WaveField + USE SeaState + USE SeaState_Types + USE SeaState_Output + USE NWTC_Library + USE NWTC_C_Binding, ONLY: ErrMsgLen_C, IntfStrLen, SetErrStat_F2C, FileNameFromCString + USE VersionInfo + + implicit none + save + + PUBLIC :: SeaSt_C_PreInit + PUBLIC :: SeaSt_C_Init + PUBLIC :: SeaSt_C_CalcOutput + PUBLIC :: SeaSt_C_End + PUBLIC :: SeaSt_C_GetWaveFieldPointer + PUBLIC :: SeaSt_C_SetWaveFieldPointer + PUBLIC :: SeaSt_C_GetFluidVelAcc + PUBLIC :: SeaSt_C_GetSurfElev + PUBLIC :: SeaSt_C_GetSurfNorm + PUBLIC :: SeaSt_C_GetElevMinMaxEstimate + PUBLIC :: SeaSt_C_GetDens + PUBLIC :: SeaSt_C_GetDpth + PUBLIC :: SeaSt_C_GetMSL2SWL + + !------------------------------------------------------------------------------------ + ! Debugging: DebugLevel -- passed at PreInit + ! 0 - none + ! 1 - some summary info + ! 2 - above + all position/orientation info + ! 3 - above + input files (if direct passed) + ! 4 - above + meshes + integer(IntKi) :: DebugLevel + logical :: PreInitDone = .false. + + !------------------------------------------------------------------------------------ + ! Visualization + type VTKvis + character(1024) :: outdir + character(1024) :: OutRootName ! includes directory + integer(IntKi) :: write ! 0 off, 1 init, 2 animate + real(DbKi) :: dt + integer(IntKi) :: NWaveElevPts(2) ! number of points in x/y directions + real(SiKi), allocatable :: WaveElevVisX(:),WaveElevVisY(:) ! x, y locations of points + real(SiKi), allocatable :: WaveElevVisGrid(:,:,:) ! the actual surface data for full time series + integer(IntKi) :: tWidth = 5 ! Should calculate this, but not going to + integer(IntKi) :: LastWaveIndx + integer(IntKi) :: lastCount = -1 + end type VTKvis + type(VTKvis) :: vtk + + !------------------------------ + ! Primary derived types + type(SeaSt_InputType) :: u !< inputs to SS + type(SeaSt_InitInputType) :: InitInp !< initialization input + type(SeaSt_InitOutputType) :: InitOutData !< Initial output data + type(SeaSt_ParameterType), target :: p !< Parameters + type(SeaSt_OutputType) :: y !< Initial output (outputs are not calculated; only the output mesh is initialized) + type(SeaSt_MiscVarType) :: m !< Misc variables for optimization (not copied in glue code) + type(SeaSt_ContinuousStateType) :: x !< Initial continuous states + type(SeaSt_DiscreteStateType) :: xd !< Initial discrete states + type(SeaSt_ConstraintStateType) :: z !< Initial guess of the constraint states + type(SeaSt_OtherStateType) :: OtherState !< Initial other states + +contains + + +!> Set environment variables +subroutine SeaSt_C_PreInit(Gravity_C, WtrDens_C, WtrDpth_C, MSL2SWL_C, DebugLevel_C, OutVTKDir_C, WrVTK_in, WrVTK_inDT, ErrStat_C, ErrMsg_C) BIND (C, NAME='SeaSt_C_PreInit') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_PreInit +!GCC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_PreInit +#endif + real(c_float), intent(in ) :: Gravity_C + real(c_float), intent(in ) :: WtrDens_C + real(c_float), intent(in ) :: WtrDpth_C + real(c_float), intent(in ) :: MSL2SWL_C + integer(c_int), intent(in ) :: DebugLevel_C + character(kind=c_char), intent(in ) :: OutVTKDir_C(IntfStrLen) !< Directory to put all vtk output + integer(c_int), intent(in ) :: WrVTK_in !< Write VTK outputs [0: none, 1: init only, 2: animation] + real(c_double), intent(in ) :: WrVTK_inDT !< Timestep between VTK writes + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + + integer :: ErrStat, ErrStat2 + character(ErrMsgLen) :: ErrMsg, ErrMsg2 + integer :: i,j,k + character(*), parameter :: RoutineName = 'SeaSt_C_PreInit' + + ! Initialize error handling + ErrStat = ErrID_None + ErrMsg = "" + + call NWTC_Init( ProgNameIn= SeaSt_ProgDesc%Name ) + call DispCopyrightLicense( SeaSt_ProgDesc%Name ) + call DispCompileRuntimeInfo( SeaSt_ProgDesc%Name ) + + ! Store the out root dir - do this before ShowPassedData call + vtk%outdir = TRANSFER( OutVTKDir_C, vtk%outdir ) + i = INDEX(vtk%outdir,C_NULL_CHAR) - 1 ! if this has a c null character at the end... + if ( i > 0 ) vtk%outdir = vtk%outdir(1:I) ! remove it + + ! interface debugging + DebugLevel = int(DebugLevel_C,IntKi) + + ! check valid debug level, show passed data if >0 + if (DebugLevel < 0_IntKi) then + ErrStat2 = ErrID_Fatal + ErrMsg2 = "Interface debug level must be 0 or greater"//NewLine// & + " 0 - none"//NewLine// & + " 1 - some summary info and variables passed through interface (init only)"//NewLine// & + " 2 - above + all position info on all calls" + call ShowPassedData() + if (Failed()) return; + elseif (DebugLevel > 0_IntKi) THEN + call WrScr(" Interface debugging level "//trim(Num2Lstr(DebugLevel))//" requested.") + call ShowPassedData() + endif + + ! clear memory of anything we allocate locally + call ClearMem() ! ignoring any error handling from this + + ! store environment values + InitInp%Gravity = Gravity_C + InitInp%defWtrDens = WtrDens_C + InitInp%defWtrDpth = WtrDpth_C + InitInp%defMSL2SWL = MSL2SWL_C + + !---------------------- + ! store VTK output info + vtk%write = int(WrVTK_in, IntKi) + vtk%dt = real(WrVTK_inDT, DbKi) + + if (vtk%write < 0_IntKi .or. vtk%write > 2_IntKi) then + ErrStat2 = ErrID_Warn + ErrMSg2 = "WrVTK_in must be 0 (off), 1 (init), 2 (animation), but "//trim(Num2LStr(vtk%write))//" was passed. Turning off VTK surface export." + vtk%write = 0_IntKi + if (Failed()) return + endif + + if (vtk%write > 0_IntKi) then + ! Tell SeaState to generate the visualization using default grid + InitInp%SurfaceVis = .true. + InitInp%SurfaceVisNx = 0 ! use the WaveField grid resolution + InitInp%SurfaceVisNy = 0 ! use the WaveField grid resolution + endif + + + ! If we got this far, we are initialized + PreInitDone = .true. + + call Cleanup() + return +contains + logical function Failed() + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + Failed = ErrStat >= AbortErrLev + if (Failed) call Cleanup() + end function Failed + subroutine Cleanup() ! NOTE: we are ignoring any error reporting from here + call SetErrStat_F2C(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + end subroutine Cleanup + subroutine ShowPassedData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: SeaSt_C_PreInit") + call WrScr(" --------------------------------------------------------") + call WrScr(" Gravity_C -> "//trim(Num2LStr(Gravity_C))) + call WrScr(" WtrDens_C -> "//trim(Num2LStr(WtrDens_C))) + call WrScr(" WtrDpth_C -> "//trim(Num2LStr(WtrDpth_C))) + call WrScr(" MSL2SWL_C -> "//trim(Num2LStr(MSL2SWL_C))) + call WrScr(" DebugLevel_C -> "//trim(Num2LStr(DebugLevel_C))) + call WrScr(" OutVTKDir_C -> "//trim(vtk%outdir)) + call WrScr(" WrVTK_in -> "//trim(Num2LStr(WrVTK_in))) + call WrScr(" WrVTK_inDT -> "//trim(Num2LStr(WrVTK_inDT))) + call WrScr("-----------------------------------------------------------") + end subroutine ShowPassedData +end subroutine SeaSt_C_PreInit + + +!> Initialize the library (PreInit must be called first) +subroutine SeaSt_C_Init(InputFile_C, OutRootName_C, TimeInterval_C, TMax_C, WaveTimeShift_C, NumChannels_C, OutputChannelNames_C, OutputChannelUnits_C, ErrStat_C, ErrMsg_C) BIND (C, NAME='SeaSt_C_Init') #ifndef IMPLICIT_DLLEXPORT !DEC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_Init !GCC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_Init #endif - TYPE(C_PTR), INTENT(IN ) :: InputFile_C - TYPE(C_PTR), INTENT(IN ) :: OutRootName_C - REAL(C_FLOAT), INTENT(IN ) :: Gravity_C - REAL(C_FLOAT), INTENT(IN ) :: WtrDens_C - REAL(C_FLOAT), INTENT(IN ) :: WtrDpth_C - REAL(C_FLOAT), INTENT(IN ) :: MSL2SWL_C - INTEGER(C_INT), INTENT(IN ) :: NSteps_C - REAL(C_FLOAT), INTENT(IN ) :: TimeInterval_C - INTEGER(C_INT), INTENT(IN ) :: WaveElevSeriesFlag_C - INTEGER(C_INT), INTENT(IN ) :: WrWvKinMod_C - INTEGER(C_INT), INTENT( OUT) :: NumChannels_C - CHARACTER(KIND=C_CHAR), INTENT( OUT) :: OutputChannelNames_C(ChanLen*MaxOutPts+1) - CHARACTER(KIND=C_CHAR), INTENT( OUT) :: OutputChannelUnits_C(ChanLen*MaxOutPts+1) - INTEGER(C_INT), INTENT( OUT) :: ErrStat_C - CHARACTER(KIND=C_CHAR), INTENT( OUT) :: ErrMsg_C(ErrMsgLen_C) - - ! Local variables - CHARACTER(KIND=C_CHAR, len=IntfStrLen), POINTER :: InputFileString !< Input file as a single string with NULL chracter separating lines - CHARACTER(KIND=C_CHAR, len=IntfStrLen), POINTER :: OutputFileString !< Input file as a single string with NULL chracter separating lines - CHARACTER(IntfStrLen) :: InputFileName - CHARACTER(IntfStrLen) :: OutRootName - TYPE(SeaSt_InputType) :: u !< An initial guess for the input; input mesh must be defined - TYPE(SeaSt_ContinuousStateType) :: x !< Initial continuous states - TYPE(SeaSt_DiscreteStateType) :: xd !< Initial discrete states - TYPE(SeaSt_ConstraintStateType) :: z !< Initial guess of the constraint states - TYPE(SeaSt_OtherStateType) :: OtherState !< Initial other states - REAL(DbKi) :: Interval !< Coupling interval in seconds: the rate that - !! (1) SeaSt_UpdateStates() is called in loose coupling & - !! (2) SeaSt_UpdateDiscState() is called in tight coupling. - !! Input is the suggested time from the glue code; - !! Output is the actual coupling interval that will be used - !! by the glue code. - - INTEGER :: ErrStat_F !< aggregated error status - CHARACTER(ErrMsgLen) :: ErrMsg_F !< aggregated error message - INTEGER :: ErrStat_F2 !< temporary error status from a call - CHARACTER(ErrMsgLen) :: ErrMsg_F2 !< temporary error message from a call - INTEGER :: i,j,k - CHARACTER(*), PARAMETER :: RoutineName = 'SeaSt_C_Init' !< for error handling - - ! Initialize error handling - ErrStat_F = ErrID_None - ErrMsg_F = "" - - CALL NWTC_Init( ProgNameIn=version%Name ) - CALL DispCopyrightLicense( version%Name ) - CALL DispCompileRuntimeInfo( version%Name ) - - ! interface debugging - ! DebugLevel = int(DebugLevel_in,IntKi) - - ! Input files - CALL C_F_POINTER(InputFile_C, InputFileString) ! Get a pointer to the input file string - InputFileName = FileNameFromCString(InputFileString, IntfStrLen) ! convert the input file name from c_char to fortran character - - CALL C_F_POINTER(OutRootName_C, OutputFileString) ! Get a pointer to the input file string - OutRootName = FileNameFromCString(OutputFileString, IntfStrLen) ! convert the input file name from c_char to fortran character - - ! if non-zero, show all passed data here. Then check valid values - IF (DebugLevel /= 0_IntKi) THEN - CALL WrScr(" Interface debugging level "//trim(Num2Lstr(DebugLevel))//" requested.") - CALL ShowPassedData() - ENDIF - ! check valid debug level - IF (DebugLevel < 0_IntKi) THEN - ErrStat_F2 = ErrID_Fatal - ErrMsg_F2 = "Interface debug level must be 0 or greater"//NewLine// & - " 0 - none"//NewLine// & - " 1 - some summary info and variables passed through interface"//NewLine// & - " 2 - above + all position/orientation info"//NewLine// & - " 3 - above + input files (if direct passed)"//NewLine// & - " 4 - above + meshes" - IF (Failed()) RETURN; - ENDIF - - ! For debugging the interface: - IF (DebugLevel > 0) THEN - CALL ShowPassedData() - ENDIF - - ! Set other inputs for calling SeaSt_Init - InitInp%InputFile = InputFileName - InitInp%UseInputFile = .TRUE. - InitInp%OutRootName = OutRootName - InitInp%Gravity = Gravity_C - InitInp%defWtrDens = WtrDens_C - InitInp%defWtrDpth = WtrDpth_C - InitInp%defMSL2SWL = MSL2SWL_C - InitInp%TMax = (NSteps_C - 1) * TimeInterval_C ! Using this to match the SeaState driver; could otherwise get TMax directly - InitInp%WaveFieldMod = WaveElevSeriesFlag_C - ! REAL(ReKi) :: PtfmLocationX = 0.0_ReKi !< Supplied by Driver: X coordinate of platform location in the wave field [m] - ! REAL(ReKi) :: PtfmLocationY = 0.0_ReKi !< Supplied by Driver: Y coordinate of platform location in the wave field [m] - InitInp%WrWvKinMod = WrWvKinMod_C - ! LOGICAL :: HasIce = .false. !< Supplied by Driver: Whether this simulation has ice loading (flag) [-] - ! LOGICAL :: Linearize = .FALSE. !< Flag that tells this module if the glue code wants to linearize. [-] - ! LOGICAL :: SurfaceVis = .FALSE. !< Turn on grid surface visualization outputs [-] - ! INTEGER(IntKi) :: SurfaceVisNx = 0 !< Number of points in X direction to output for visualization grid. Use 0 or negative to set to SeaState resolution. [-] - ! INTEGER(IntKi) :: SurfaceVisNy = 0 !< Number of points in Y direction to output for visualization grid. Use 0 or negative to set to SeaState resolution. [-] - - CALL SeaSt_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOutData, ErrStat_F2, ErrMsg_F2 ) - IF (Failed()) RETURN - - ! Number of channels - NumChannels_C = size(InitOutData%WriteOutputHdr) - - ! transfer the output channel names and units to c_char arrays for returning - k=1 - DO i=1,NumChannels_C - DO j=1,ChanLen ! max length of channel name. Same for units - OutputChannelNames_C(k)=InitOutData%WriteOutputHdr(i)(j:j) - OutputChannelUnits_C(k)=InitOutData%WriteOutputUnt(i)(j:j) - k=k+1 - ENDDO - ENDDO - - ! null terminate the string - OutputChannelNames_C(k) = C_NULL_CHAR - OutputChannelUnits_C(k) = C_NULL_CHAR - - CALL Cleanup() - -CONTAINS - LOGICAL FUNCTION Failed() - CALL SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - Failed = ErrStat_F >= AbortErrLev - IF (Failed) CALL Cleanup() - END FUNCTION Failed - - SUBROUTINE Cleanup() ! NOTE: we are ignoring any error reporting from here - CALL SetErrStat_F2C(ErrStat_F,ErrMsg_F,ErrStat_C,ErrMsg_C) - END SUBROUTINE Cleanup - - SUBROUTINE ShowPassedData() - ! CHARACTER(1) :: TmpFlag - ! integer :: i,j - CALL WrSCr("") - CALL WrScr("-----------------------------------------------------------") - CALL WrScr("Interface debugging: Variables passed in through interface") - CALL WrScr(" SeaSt_C_Init") - CALL WrScr(" --------------------------------------------------------") - CALL WrScr(" FileInfo") - CALL WrScr("-----------------------------------------------------------") - END SUBROUTINE ShowPassedData -END SUBROUTINE SeaSt_C_Init - -SUBROUTINE SeaSt_C_CalcOutput(Time_C, OutputChannelValues_C, ErrStat_C, ErrMsg_C) BIND (C, NAME='SeaSt_C_CalcOutput') -IMPLICIT NONE + character(kind=c_char), intent(in ) :: InputFile_C(IntfStrLen) + character(kind=c_char), intent(in ) :: OutRootName_C(IntfStrLen) + real(c_double), intent(in ) :: TimeInterval_C + real(c_double), intent(in ) :: TMax_c + real(c_double), intent(in ) :: WaveTimeShift_C + integer(c_int), intent( out) :: NumChannels_C + character(kind=c_char), intent( out) :: OutputChannelNames_C(ChanLen*MaxOutPts+1) + character(kind=c_char), intent( out) :: OutputChannelUnits_C(ChanLen*MaxOutPts+1) + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + + ! Local variables + character(IntfStrLen) :: OutRootName + real(DbKi) :: Interval !< DT for calling + integer :: ErrStat, ErrStat2 + character(ErrMsgLen) :: ErrMsg, ErrMsg2 + integer :: i,j,k + character(*), parameter :: RoutineName = 'SeaSt_C_Init' !< for error handling + + if (.not. PreInitDone) then + ErrStat = ErrID_Fatal + ErrMSg = "SeaSt_C_PreInit must be called before SeaSt_C_Init" + call Cleanup() + endif + + ! Initialize error handling + ErrStat = ErrID_None + ErrMsg = "" + ErrStat_C = ErrID_None + ErrMsg_C = c_null_char + + ! Initialize vars in case of early return + NumChannels_C = 0_IntKi + OutputChannelNames_C = c_null_char + OutputChannelUnits_C = c_null_char + + call NWTC_Init( ProgNameIn= SeaSt_ProgDesc%Name ) + call DispCopyrightLicense( SeaSt_ProgDesc%Name ) + call DispCompileRuntimeInfo( SeaSt_ProgDesc%Name ) + + + ! Input file + InitInp%InputFile = TRANSFER( InputFile_C, InitInp%InputFile ) + i = INDEX(InitInp%InputFile,C_NULL_CHAR) - 1 ! if this has a c null character at the end... + if ( i > 0 ) InitInp%InputFile = InitInp%InputFile(1:I) ! remove it + + ! OutRootName - this should be relative to current location + InitInp%OutRootName = TRANSFER( OutRootName_C, InitInp%OutRootName ) + i = INDEX(InitInp%OutRootName,C_NULL_CHAR) - 1 ! if this has a c null character at the end... + if ( i > 0 ) InitInp%OutRootName = InitInp%OutRootName(1:I) ! remove it + vtk%OutRootName = InitInp%OutRootName ! store for vtk (will modify below) + + ! Debugging interface + if (DebugLevel > 0_IntKi) call ShowPassedData() + + ! Set other inputs for calling SeaSt_Init + InitInp%UseInputFile = .TRUE. ! don't allow passing of full file contents as a string + InitInp%TMax = real(TMax_c, DbKi) + InitInp%WaveFieldMod = 0_IntKi + InitInp%WrWvKinMod = 0_IntKi + InitInp%Linearize = .false. + InitInp%hasIce = .false. + InitInp%WaveFieldMod = 0 ! does not currently support moving platform. Not really necessary though since can directly get data in absolute coords + InitInp%PtfmLocationX = 0.0_ReKi + InitInp%PtfmLocationY = 0.0_ReKi + InitInp%WaveTimeShift = real(WaveTimeShift_C,DbKi) + + call SeaSt_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOutData, ErrStat2, ErrMsg2 ) + if (Failed()) return + + ! Number of channels + NumChannels_C = size(InitOutData%WriteOutputHdr) + + ! transfer the output channel names and units to c_char arrays for returning + k=1 + do i=1,NumChannels_C + do j=1,ChanLen ! max length of channel name. Same for units + OutputChannelNames_C(k)=InitOutData%WriteOutputHdr(i)(j:j) + OutputChannelUnits_C(k)=InitOutData%WriteOutputUnt(i)(j:j) + k=k+1 + enddo + enddo + + ! null terminate the string + OutputChannelNames_C(k) = C_NULL_CHAR + OutputChannelUnits_C(k) = C_NULL_CHAR + + if (vtk%write > 0_IntKi) then + call VTKsetup() + endif + + + + call Cleanup() + +contains + logical function Failed() + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + Failed = ErrStat >= AbortErrLev + if (Failed) call Cleanup() + end function Failed + subroutine Cleanup() ! NOTE: we are ignoring any error reporting from here + call SetErrStat_F2C(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + end subroutine Cleanup + subroutine ShowPassedData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: SeaSt_C_Init") + call WrScr(" --------------------------------------------------------") + call WrScr(" InputFile_C -> "//trim(InitInp%InputFile)) + call WrScr(" OutRootName_C -> "//trim(InitInp%OutRootName)) + call WrScr(" TMax_C -> "//trim(Num2LStr(TMax_C))) + call WrScr(" TimeInterval_C -> "//trim(Num2LStr(TimeInterval_C))) + call WrScr(" WaveTimeShift_C -> "//trim(Num2LStr(WaveTimeShift_C))) + call WrScr("-----------------------------------------------------------") + end subroutine ShowPassedData + + subroutine VTKsetup() + ! check dt (can't check against Interval since that is never set, so just make sure it is positive) + if (vtk%dt <= 0.0) vtk%dt = 0.25 + ! move data + if (allocated(InitOutData%WaveElevVisGrid)) then + vtk%NWaveElevPts(1) = size(InitOutData%WaveElevVisX) + vtk%NWaveElevPts(2) = size(InitOutData%WaveElevVisY) + call move_alloc(InitOutData%WaveElevVisX, vtk%WaveElevVisX) + call move_alloc(InitOutData%WaveElevVisY, vtk%WaveElevVisY) + call move_alloc(InitOutData%WaveElevVisGrid,vtk%WaveElevVisGrid ) + else + vtk%NWaveElevPts = 0 + vtk%write = 0 ! FIXME throw warning if we do this + endif + ! get the name of the output directory for vtk files (in a subdirectory called "vtk" of the output directory), and + ! create the VTK directory if it does not exist + call MKDIR( trim(vtk%outdir) ) + vtk%OutRootName = trim(vtk%outdir) // PathSep //trim( vtk%OutRootName ) + call WrVTK_WaveElevVisGrid (0.0_DbKi, vtk, ErrStat2, ErrMsg2) + if (Failed()) return + end subroutine VTKsetup +end subroutine SeaSt_C_Init + +subroutine SeaSt_C_CalcOutput(Time_C, OutputChannelValues_C, ErrStat_C, ErrMsg_C) BIND (C, NAME='SeaSt_C_CalcOutput') #ifndef IMPLICIT_DLLEXPORT !DEC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_CalcOutput !GCC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_CalcOutput #endif - REAL(C_DOUBLE), INTENT(IN ) :: Time_C - REAL(C_FLOAT), INTENT( OUT) :: OutputChannelValues_C(p%NumOuts) - INTEGER(C_INT), INTENT( OUT) :: ErrStat_C - CHARACTER(KIND=C_CHAR), INTENT( OUT) :: ErrMsg_C(ErrMsgLen_C) - - ! Local variables - TYPE(SeaSt_InputType) :: u !< An initial guess for the input; input mesh must be defined - TYPE(SeaSt_ContinuousStateType) :: x !< Initial continuous states - TYPE(SeaSt_DiscreteStateType) :: xd !< Initial discrete states - TYPE(SeaSt_ConstraintStateType) :: z !< Initial guess of the constraint states - TYPE(SeaSt_OtherStateType) :: OtherState !< Initial other states - - REAL(DbKi) :: Time - INTEGER :: ErrStat_F !< aggregated error status - CHARACTER(ErrMsgLen) :: ErrMsg_F !< aggregated error message - INTEGER :: ErrStat_F2 !< temporary error status from a call - CHARACTER(ErrMsgLen) :: ErrMsg_F2 !< temporary error message from a call - CHARACTER(*), PARAMETER :: RoutineName = 'SeaSt_C_End' !< for error handling - - ! Initialize error handling - ErrStat_F = ErrID_None - ErrMsg_F = "" - - ! Convert the inputs from C to Fortran - Time = REAL(Time_C,DbKi) - - CALL SeaSt_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat_F2, ErrMsg_F2 ) - IF (Failed()) RETURN - - ! Get the output channel info out of y - OutputChannelValues_C = REAL(y%WriteOutput, C_FLOAT) - - CALL Cleanup() - -CONTAINS - LOGICAL FUNCTION Failed() - CALL SetErrStat( ErrStat_F2, ErrMsg_F2, ErrStat_F, ErrMsg_F, RoutineName ) - Failed = ErrStat_F >= AbortErrLev - IF (Failed) CALL Cleanup() - END FUNCTION Failed - - SUBROUTINE Cleanup() ! NOTE: we are ignoring any error reporting from here - CALL SetErrStat_F2C(ErrStat_F,ErrMsg_F,ErrStat_C,ErrMsg_C) - END SUBROUTINE Cleanup -END SUBROUTINE - -SUBROUTINE SeaSt_C_End(ErrStat_C,ErrMsg_C) BIND (C, NAME='SeaSt_C_End') -IMPLICIT NONE + real(c_double), intent(in ) :: Time_C + real(c_float), intent( out) :: OutputChannelValues_C(p%NumOuts) + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + + ! Local variables + type(SeaSt_InputType) :: u !< An initial guess for the input; input mesh must be defined + type(SeaSt_ContinuousStateType) :: x !< Initial continuous states + type(SeaSt_DiscreteStateType) :: xd !< Initial discrete states + type(SeaSt_ConstraintStateType) :: z !< Initial guess of the constraint states + type(SeaSt_OtherStateType) :: OtherState !< Initial other states + + real(DbKi) :: Time + integer :: ErrStat, ErrStat2 + character(ErrMsgLen) :: ErrMsg, ErrMsg2 + character(*), parameter :: RoutineName = 'SeaSt_C_CalcOutput' !< for error handling + + ! Initialize error handling + ErrStat = ErrID_None + ErrMsg = "" + + ! Debugging + if (DebugLevel > 1) call ShowPassedData() + + ! Convert the inputs from C to Fortran + Time = REAL(Time_C,DbKi) + + call SeaSt_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat2, ErrMsg2 ) + if (Failed()) return + + ! Get the output channel info out of y + OutputChannelValues_C = REAL(y%WriteOutput, C_FLOAT) + + if (vtk%write > 1_IntKi) then + call WrVTK_WaveElevVisGrid (Time, vtk, ErrStat2, ErrMsg2) + if (Failed()) return + endif + + call Cleanup() + + ! Debugging + if (DebugLevel > 1) call ShowReturnData() + +contains + logical function Failed() + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + Failed = ErrStat >= AbortErrLev + if (Failed) call Cleanup() + end function Failed + subroutine Cleanup() ! NOTE: we are ignoring any error reporting from here + CALL SetErrStat_F2C(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + end subroutine Cleanup + subroutine ShowPassedData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: SeaSt_C_CalcOutput") + call WrScr(" --------------------------------------------------------") + call WrScr(" Time_C -> "//trim(Num2LStr(Time_C))) + end subroutine ShowPassedData + subroutine ShowReturnData() + call WrScr(" OutputChannelValues_C <-") + call WrMatrix(OutputChannelValues_C,CU,'g15.6') + call WrScr("-----------------------------------------------------------") + end subroutine ShowReturnData +end subroutine + +subroutine SeaSt_C_End(ErrStat_C,ErrMsg_C) BIND (C, NAME='SeaSt_C_End') #ifndef IMPLICIT_DLLEXPORT !DEC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_End !GCC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_End #endif - INTEGER(C_INT), INTENT( OUT) :: ErrStat_C - CHARACTER(KIND=C_CHAR), INTENT( OUT) :: ErrMsg_C(ErrMsgLen_C) - - ! Local variables - TYPE(SeaSt_InputType) :: u !< An initial guess for the input; input mesh must be defined - TYPE(SeaSt_ContinuousStateType) :: x !< Initial continuous states - TYPE(SeaSt_DiscreteStateType) :: xd !< Initial discrete states - TYPE(SeaSt_ConstraintStateType) :: z !< Initial guess of the constraint states - TYPE(SeaSt_OtherStateType) :: OtherState !< Initial other states - - INTEGER :: ErrStat !< aggregated error status - CHARACTER(ErrMsgLen) :: ErrMsg !< aggregated error message - INTEGER :: ErrStat2 !< temporary error status from a call - CHARACTER(ErrMsgLen) :: ErrMsg2 !< temporary error message from a call - CHARACTER(*), PARAMETER :: RoutineName = 'SeaSt_C_End' !< for error handling - - ! Initialize error handling - ErrStat = ErrID_None - ErrMsg = "" - CALL SeaSt_End(u, p, x, xd, z, OtherState, y, m, ErrStat2, ErrMsg2) - - CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) - CALL SetErrStat_F2C( ErrStat, ErrMsg, ErrStat_C, ErrMsg_C ) - -END SUBROUTINE - -FUNCTION SeaSt_GetWaveFieldPointer_C() BIND (C, NAME='SeaSt_GetWaveFieldPointer_C') -IMPLICIT NONE + integer(C_INT), intent( out) :: ErrStat_C + character(kind=C_CHAR), intent( out) :: ErrMsg_C(ErrMsgLen_C) + + integer :: ErrStat !< aggregated error status + character(ErrMsgLen) :: ErrMsg !< aggregated error message + integer :: ErrStat2 !< temporary error status from a call + character(ErrMsgLen) :: ErrMsg2 !< temporary error message from a call + character(*), parameter :: RoutineName = 'SeaSt_C_End' !< for error handling + ErrStat = ErrID_None + ErrMsg = "" + call SeaSt_End(u, p, x, xd, z, OtherState, y, m, ErrStat2, ErrMsg2) + call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + call ClearMem() ! ignoring any error handling from this + call SetErrStat_F2C( ErrStat, ErrMsg, ErrStat_C, ErrMsg_C ) +end subroutine + + +!> return the pointer to the WaveField data +subroutine SeaSt_C_GetWaveFieldPointer(WaveFieldPointer_C,ErrStat_C,ErrMsg_C) BIND (C, NAME='SeaSt_C_GetWaveFieldPointer') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetWaveFieldPointer +!GCC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetWaveFieldPointer +#endif + type(c_ptr), intent( out) :: WaveFieldPointer_C + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer :: ErrStat + character(ErrMsgLen) :: ErrMsg + character(*), parameter :: RoutineName = 'SeaSt_C_GetWaveFieldPointer' + logical :: valid + ErrStat = ErrID_None + ErrMSg = "" + WaveFieldPointer_C = C_NULL_PTR + call CheckWaveFieldPtr(RoutineName, valid, ErrStat, ErrMsg) + if (valid) WaveFieldPointer_C = C_LOC(p%WaveField) + call SetErrStat_F2C( ErrStat, ErrMsg, ErrStat_C, ErrMsg_C ) + if (DebugLevel > 1) call ShowPassedData() + return +contains + subroutine ShowPassedData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: SeaSt_C_GetWaveFieldPointer returns") + call WrScr(" --------------------------------------------------------") + call WrScr(" WaveFieldPointer_C <- "//trim(Num2LStr(loc(p%WaveField)))) + call WrScr("-----------------------------------------------------------") + end subroutine ShowPassedData +end subroutine + + +!> set the pointer to the WaveField data +subroutine SeaSt_C_SetWaveFieldPointer(WaveFieldPointer_C,ErrStat_C,ErrMsg_C) BIND (C, NAME='SeaSt_C_SetWaveFieldPointer') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_SetWaveFieldPointer +!GCC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_SetWaveFieldPointer +#endif + type(c_ptr), intent(in ) :: WaveFieldPointer_C + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer :: ErrStat + character(ErrMsgLen) :: ErrMsg + character(*), parameter :: RoutineName = 'SeaSt_C_SetWaveFieldPointer' + logical :: valid + ErrStat = ErrID_None + ErrMSg = "" + call C_F_POINTER(WaveFieldPointer_C, p%WaveField) + call CheckWaveFieldPtr(RoutineName, valid, ErrStat, ErrMsg) + call SetErrStat_F2C( ErrStat, ErrMsg, ErrStat_C, ErrMsg_C ) + if (DebugLevel > 1) call ShowPassedData() + return +contains + subroutine ShowPassedData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: SeaSt_C_SetWaveFieldPointer inputs") + call WrScr(" --------------------------------------------------------") + call WrScr(" WaveFieldPointer_C -> "//trim(Num2LStr(loc(p%WaveField)))) + call WrScr("-----------------------------------------------------------") + end subroutine ShowPassedData +end subroutine + + +!> Get the fluid velocity, acceleration, and node-in-water status at time+position coordinate +!! NOTE: if wave stretching is turned off, the SWL is used as the cutoff for the nodeInWater and for Vel / Acc values +subroutine SeaSt_C_GetFluidVelAcc(Time_C, Pos_C, Vel_C, Acc_C, NodeInWater_C, ErrStat_C,ErrMsg_C) BIND (C, NAME='SeaSt_C_GetFluidVelAcc') #ifndef IMPLICIT_DLLEXPORT -!DEC$ ATTRIBUTES DLLEXPORT :: SeaSt_GetWaveFieldPointer_C -!GCC$ ATTRIBUTES DLLEXPORT :: SeaSt_GetWaveFieldPointer_C +!DEC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetFluidVelAcc +!GCC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetFluidVelAcc #endif - TYPE(C_PTR) :: SeaSt_GetWaveFieldPointer_C - SeaSt_GetWaveFieldPointer_C = C_LOC(p%WaveField) - RETURN -END FUNCTION + real(c_double), intent(in ) :: Time_C + real(c_float), intent(in ) :: Pos_c(3) + real(c_float), intent( out) :: Vel_c(3) + real(c_float), intent( out) :: Acc_c(3) + integer(c_int), intent( out) :: NodeInWater_C + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + real(DbKi) :: Time + real(ReKi) :: Pos(3) + real(SiKi) :: Vel(3) + real(SiKi) :: Acc(3) + logical :: forceNodeInWater + logical, parameter :: fetchDynCurrent = .true. + integer(IntKi) :: nodeInWater + integer :: ErrStat, ErrStat2 + character(ErrMsgLen) :: ErrMsg, ErrMsg2 + character(*), parameter :: RoutineName = 'SeaSt_C_GetFluidVelAcc' + logical :: valid + + ! Initialize + ErrStat = ErrID_None + ErrMsg = "" + Vel_c = 0.0_c_float + Acc_c = 0.0_c_float + + forceNodeInWater = .false. + + if (DebugLevel > 1) call ShowPassedData() + + ! convert position and time to fortran types + Time = real(Time_C, DbKi) + Pos = real(Pos_C, ReKi) + + ! verify there is actually wavefield data + call CheckWaveFieldPtr(RoutineName, valid, ErrStat, ErrMsg) + if (.not. valid) then + call SetErrStat_F2C(ErrStat, ErrMsg, ErrStat_C, ErrMsg_C) + return + endif + + ! get wave field velocity and acceleration (current is included in this) + ! Notes: + ! - if node is out of water, velocity and acceleration are zero + ! - if position is outside the wave field boundary, it will simply return boundary edge value + ! - time must be positive or a fatal error occurs + call WaveField_GetNodeWaveVelAcc( p%WaveField, m%WaveField_m, Time, pos, forceNodeInWater, fetchDynCurrent, nodeInWater, Vel, Acc, ErrStat, ErrMsg ) + + ! Store resulting velocity and acceleration as C type + Vel_c = real(Vel,c_float) + Acc_c = real(Acc,c_float) + + ! Density value and node status to return + if (nodeInWater == 1_IntKi) then + NodeInWater_C = 1_c_int + else + NodeInWater_C = 0_c_int + endif + + call SetErrStat_F2C( ErrStat, ErrMsg, ErrStat_C, ErrMsg_C ) ! convert error from fortran to C for return + if (DebugLevel > 1) call ShowReturnData() + return +contains + subroutine ShowPassedData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: SeaSt_C_GetFluidVelAccDens") + call WrScr(" --------------------------------------------------------") + call WrScr(" Time_C -> "//trim(Num2LStr(Time_C))) + call WrScr(" Pos_C -> ("//trim(Num2LStr(Pos_C(1)))//","//trim(Num2LStr(Pos_C(2)))//","//trim(Num2LStr(Pos_C(3)))//")") + end subroutine ShowPassedData + subroutine ShowReturnData() + call WrScr(" Vel_C <- ("//trim(Num2LStr(Vel_C(1)))//","//trim(Num2LStr(Vel_C(2)))//","//trim(Num2LStr(Vel_C(3)))//")") + call WrScr(" Acc_C <- ("//trim(Num2LStr(Acc_C(1)))//","//trim(Num2LStr(Acc_C(2)))//","//trim(Num2LStr(Acc_C(3)))//")") + call WrScr(" NodeInWater_C <- "//trim(Num2LStr(NodeInWater_C))) + call WrScr("-----------------------------------------------------------") + end subroutine ShowReturnData +end subroutine SeaSt_C_GetFluidVelAcc + + + +!> return the surface elevation and normal vector at a point. +subroutine SeaSt_C_GetSurfElev(Time_C, Pos_C, Elev_C, ErrStat_C,ErrMsg_C) BIND (C, NAME='SeaSt_C_GetSurfElev') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetSurfElev +!GCC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetSurfElev +#endif + real(c_double), intent(in ) :: Time_C + real(c_float), intent(in ) :: Pos_c(2) + real(c_float), intent( out) :: Elev_C + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + + real(DbKi) :: Time + real(ReKi) :: Pos(2) + real(SiKi) :: Elev + integer :: ErrStat + character(ErrMsgLen) :: ErrMsg + character(*), parameter :: RoutineName = 'SeaSt_C_GetSurfElev' + logical :: valid + + if (DebugLevel > 1) call ShowPassedData() + + ! convert position and time to fortran types + Time = real(Time_C, DbKi) + Pos = real(Pos_C(1:2), ReKi) + + ! verify there is actually wavefield data + call CheckWaveFieldPtr(RoutineName, valid, ErrStat, ErrMsg) + if (.not. valid) then + call Cleanup() + return + endif + + ! get wave elevation (total combined first and second order) + ! Notes: + ! - if position is outside the wave field boundary, it will simply return boundary edge value + ! - time must be positive or a fatal error occurs + Elev = WaveField_GetNodeTotalWaveElev( p%WaveField, m%WaveField_m, Time, pos, ErrStat, ErrMsg ) + + ! Store resulting elevation as C type + Elev_C = real(Elev,c_float) + + if (DebugLevel > 1) call ShowReturnData() + call Cleanup() + return +contains + subroutine Cleanup() ! NOTE: we are ignoring any error reporting from here + CALL SetErrStat_F2C(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + end subroutine Cleanup + subroutine ShowPassedData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: SeaSt_C_GetSurfElev") + call WrScr(" --------------------------------------------------------") + call WrScr(" Time_C -> "//trim(Num2LStr(Time_C))) + call WrScr(" Pos_C -> ("//trim(Num2LStr(Pos_C(1)))//","//trim(Num2LStr(Pos_C(2)))//")") + end subroutine ShowPassedData + subroutine ShowReturnData() + call WrScr(" Elev_C <- "//trim(Num2LStr(Elev_C))) + call WrScr("-----------------------------------------------------------") + end subroutine ShowReturnData +end subroutine SeaSt_C_GetSurfElev + + + +!> return the surface normal vector at a point. +subroutine SeaSt_C_GetSurfNorm(Time_C, Pos_C, NormVec_C, ErrStat_C,ErrMsg_C) BIND (C, NAME='SeaSt_C_GetSurfNorm') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetSurfNorm +!GCC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetSurfNorm +#endif + real(c_double), intent(in ) :: Time_C + real(c_float), intent(in ) :: Pos_c(2) + real(c_float), intent( out) :: NormVec_C(3) + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + + real(DbKi) :: Time + real(ReKi) :: Pos(2) + real(ReKi) :: NormVec(3) + integer :: ErrStat + character(ErrMsgLen) :: ErrMsg + character(*), parameter :: RoutineName = 'SeaSt_C_GetSurfNorm' + logical :: valid + + if (DebugLevel > 1) call ShowPassedData() + + ! verify there is actually wavefield data + call CheckWaveFieldPtr(RoutineName, valid, ErrStat, ErrMsg) + if (.not. valid) then + call Cleanup() + return + endif + + ! convert position and time to fortran types + Time = real(Time_C, DbKi) + Pos = real(Pos_C(1:2), ReKi) + + ! get the normal vector at the point (set to vertical if outside region) + call WaveField_GetNodeWaveNormal( p%WaveField, m%WaveField_m, Time, pos, NormVec, ErrStat, ErrMsg ) + + ! Store resulting normal vector as C type + NormVec_C = real(NormVec,c_float) + + if (DebugLevel > 1) call ShowReturnData() + call Cleanup() + return +contains + subroutine Cleanup() ! NOTE: we are ignoring any error reporting from here + CALL SetErrStat_F2C(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + end subroutine Cleanup + subroutine ShowPassedData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: SeaSt_C_GetSurfNorm") + call WrScr(" --------------------------------------------------------") + call WrScr(" Time_C -> "//trim(Num2LStr(Time_C))) + call WrScr(" Pos_C -> ("//trim(Num2LStr(Pos_C(1)))//","//trim(Num2LStr(Pos_C(2)))//")") + end subroutine ShowPassedData + subroutine ShowReturnData() + call WrScr(" NormVec_C <- ("//trim(Num2LStr(NormVec_C(1)))//","//trim(Num2LStr(NormVec_C(2)))//","//trim(Num2LStr(NormVec_C(3)))//")") + call WrScr("-----------------------------------------------------------") + end subroutine ShowReturnData +end subroutine SeaSt_C_GetSurfNorm + + +!> return the min and max levels across entire wavefield. This only needs to be called once at the +!! start if desired +subroutine SeaSt_C_GetElevMinMaxEstimate(Min_C, Max_C, ErrStat_C,ErrMsg_C) BIND (C, NAME='SeaSt_C_GetElevMinMaxEstimate') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetElevMinMaxEstimate +!GCC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetElevMinMaxEstimate +#endif + real(c_float), intent( out) :: Min_C + real(c_float), intent( out) :: Max_C + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + + real(SiKi) :: MinElev + real(SiKi) :: MaxElev + integer :: ErrStat + character(ErrMsgLen) :: ErrMsg + character(*), parameter :: RoutineName = 'SeaSt_C_GetElevMinMaxEstimate' + logical :: valid + + if (DebugLevel > 1) call ShowPassedData() + + ! verify there is actually wavefield data + call CheckWaveFieldPtr(RoutineName, valid, ErrStat, ErrMsg) + if (.not. valid) then + call Cleanup() + return + endif + + ! Measure directly from the data set (this is not ideal and will break if the layout changes) + call WaveField_GetMinMaxWaveElevEstimate( p%WaveField, MinElev, MaxElev, ErrStat, ErrMsg) + Min_C = real(MinElev, c_float) + Max_C = real(MaxElev, c_float) + + if (DebugLevel > 1) call ShowReturnData() + call Cleanup() + return +contains + subroutine Cleanup() ! NOTE: we are ignoring any error reporting from here + CALL SetErrStat_F2C(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + end subroutine Cleanup + subroutine ShowPassedData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: SeaSt_C_GetElevMinMaxEstimate") + call WrScr(" --------------------------------------------------------") + end subroutine ShowPassedData + subroutine ShowReturnData() + call WrScr(" Min_C <- "//trim(Num2LStr(Min_C))) + call WrScr(" Max_C <- "//trim(Num2LStr(Max_C))) + call WrScr("-----------------------------------------------------------") + end subroutine ShowReturnData +end subroutine SeaSt_C_GetElevMinMaxEstimate + + +!---------------------------------------------------------------------------------------------------------------------------------- +! Routines to return environment vars +!---------------------------------------------------------------------------------------------------------------------------------- +!> retrieve the water density +subroutine SeaSt_C_GetDens(Dens_C, ErrStat_C, ErrMsg_C) BIND (C, NAME='SeaSt_C_GetDens') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetDens +!GCC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetDens +#endif + real(c_float), intent( out) :: Dens_C + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer :: ErrStat + character(ErrMsgLen) :: ErrMsg + character(*), parameter :: RoutineName = 'SeaSt_C_GetDens' + logical :: valid + + ! verify there is actually wavefield data + call CheckWaveFieldPtr(RoutineName, valid, ErrStat, ErrMsg) + if (.not. valid) then + call SetErrStat_F2C(ErrStat, ErrMsg, ErrStat_C, ErrMsg_C) + Dens_C = 0.0_c_float + return + endif + + Dens_C = real(p%WaveField%WtrDens, c_float) + if (DebugLevel > 1) call ShowReturnData() +contains + subroutine ShowReturnData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: SeaSt_C_GetDens returns") + call WrScr(" --------------------------------------------------------") + call WrScr(" Dens_C <- "//trim(Num2LStr(Dens_C))) + call WrScr(" --------------------------------------------------------") + end subroutine ShowReturnData +end subroutine + + +!> retrieve the water depth +subroutine SeaSt_C_GetDpth(Dpth_C, ErrStat_C, ErrMsg_C) BIND (C, NAME='SeaSt_C_GetDpth') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetDpth +!GCC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetDpth +#endif + real(c_float), intent( out) :: Dpth_C + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer :: ErrStat + character(ErrMsgLen) :: ErrMsg + character(*), parameter :: RoutineName = 'SeaSt_C_GetDpth' + logical :: valid + + ! verify there is actually wavefield data + call CheckWaveFieldPtr(RoutineName, valid, ErrStat, ErrMsg) + if (.not. valid) then + call SetErrStat_F2C(ErrStat, ErrMsg, ErrStat_C, ErrMsg_C) + Dpth_C = 0.0_c_float + return + endif + + Dpth_C = real(p%WaveField%WtrDpth, c_float) + if (DebugLevel > 1) call ShowReturnData() +contains + subroutine ShowReturnData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: SeaSt_C_GetDpth returns") + call WrScr(" --------------------------------------------------------") + call WrScr(" Dpth_C <- "//trim(Num2LStr(Dpth_C))) + call WrScr(" --------------------------------------------------------") + end subroutine ShowReturnData +end subroutine + + +!> retrieve MSL to SWL distance +subroutine SeaSt_C_GetMSL2SWL(MSL2SWL_C, ErrStat_C, ErrMsg_C) BIND (C, NAME='SeaSt_C_GetMSL2SWL') +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetMSL2SWL +!GCC$ ATTRIBUTES DLLEXPORT :: SeaSt_C_GetMSL2SWL +#endif + real(c_float), intent( out) :: MSL2SWL_C + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + integer :: ErrStat + character(ErrMsgLen) :: ErrMsg + character(*), parameter :: RoutineName = 'SeaSt_C_GetMSL2SWL' + logical :: valid + + ! verify there is actually wavefield data + call CheckWaveFieldPtr(RoutineName, valid, ErrStat, ErrMsg) + if (.not. valid) then + call SetErrStat_F2C(ErrStat, ErrMsg, ErrStat_C, ErrMsg_C) + MSL2SWL_C = 0.0_c_float + return + endif + + MSL2SWL_C = real(p%WaveField%MSL2SWL, c_float) + if (DebugLevel > 1) call ShowReturnData() +contains + subroutine ShowReturnData() + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: SeaSt_C_GetMSL2SWL returns") + call WrScr(" --------------------------------------------------------") + call WrScr(" MSL2SWL_C <- "//trim(Num2LStr(MSL2SWL_C))) + call WrScr(" --------------------------------------------------------") + end subroutine ShowReturnData +end subroutine + + +!> routine to check if the WaveField pointer is valid. ErrStat==ErrID_None is a valid pointer +subroutine CheckWaveFieldPtr(callingRoutine,valid,ErrStat,ErrMsg) + character(*), intent(in ) :: callingRoutine + logical, intent( out) :: valid + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + ErrStat = ErrID_None + ErrMsg = "" + valid = .true. + if (associated(p%WaveField)) then + ! basic sanity check + if (.not. allocated(p%WaveField%WaveTime)) then + ErrStat = ErrID_Fatal + ErrMsg = trim(callingRoutine)//":: Invalid pointer passed in, or WaveField not initialized" + valid = .false. + endif + else + ErrStat = ErrID_Fatal + ErrMsg = trim(callingRoutine)//":: Invalid pointer passed in, or WaveField not initialized" + valid = .false. + endif +end subroutine + + +!FIXME: the following visualization writer should be merged into the library vtk.f90 +! this is a modified duplicate of the routine from FAST_Subs by the same name. +!FIXME: this routine currently only grabs the closest timestep and does not do any interpolation +!---------------------------------------------------------------------------------------------------------------------------------- +!> This subroutine writes the wave elevation data for a given time step +subroutine WrVTK_WaveElevVisGrid(t_global, vtk, ErrStat, ErrMsg) + real(DbKi), intent(in ) :: t_global !< Current global time + type(VTKvis), intent(inout) :: vtk + integer(IntKi), intent( out) :: ErrStat + character(ErrMsgLen), intent( out) :: ErrMsg + integer(IntKi) :: Un ! fortran unit number + integer(IntKi) :: n, iy, ix ! loop counters + real(SiKi) :: t + integer(IntKi) :: count ! which file is this? + character(1024) :: FileName + integer(IntKi) :: NumberOfPoints + integer(IntKi), parameter :: NumberOfLines = 0 + integer(IntKi) :: NumberOfPolys + character(1024) :: Tstr + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + character(*),parameter :: RoutineName = 'WrVTK_WaveElevVisGrid' + + ! Initialize error handling + ErrStat = ErrID_None + ErrMsg = "" + + NumberOfPoints = vtk%NWaveElevPts(1) * vtk%NWaveElevPts(2) + ! I'm going to make triangles for now. we should probably just make this a structured file at some point + NumberOfPolys = ( vtk%NWaveElevPts(1) - 1 ) * & + ( vtk%NWaveElevPts(2) - 1 ) * 2 + + count = nint(t_global / vtk%dt) + if (count == vtk%lastCount) return ! already wrote this one + vtk%lastCount = count ! store the current number to make sure we don't write it twice + + !................................................................. + ! write the data that potentially changes each time step: + !................................................................. + ! construct the string for the zero-padded VTK write-out step + write(Tstr, '(i' // trim(Num2LStr(vtk%tWidth)) //'.'// trim(Num2LStr(vtk%tWidth)) // ')') count + + ! PolyData (.vtp) - Serial vtkPolyData (unstructured) file + FileName = TRIM(vtk%OutRootName)//'.WaveSurface.'//TRIM(Tstr)//'.vtp' + + call WrVTK_header( FileName, NumberOfPoints, NumberOfLines, NumberOfPolys, Un, ErrStat, ErrMsg ) + if (ErrStat >= AbortErrLev) return + +! points (nodes, augmented with NumSegments): + write(Un,'(A)') ' ' + write(Un,'(A)') ' ' + + ! I'm not going to interpolate in time; I'm just going to get the index of the closest wave time value + t = REAL(t_global,SiKi) + call GetWaveElevIndx( t, p%WaveField%WaveTime, vtk%LastWaveIndx ) + + do ix=1,vtk%NWaveElevPts(1) + do iy=1,vtk%NWaveElevPts(2) + write(Un,VTK_AryFmt) vtk%WaveElevVisX(ix), vtk%WaveElevVisY(iy), vtk%WaveElevVisGrid(vtk%LastWaveIndx,ix,iy) + end do + end do + + write(Un,'(A)') ' ' + write(Un,'(A)') ' ' + write(Un,'(A)') ' ' + write(Un,'(A)') ' ' + + do ix=1,vtk%NWaveElevPts(1)-1 + do iy=1,vtk%NWaveElevPts(2)-1 + n = vtk%NWaveElevPts(2)*(ix-1)+iy - 1 ! points start at 0 + write(Un,'(3(i7))') n, n+1, n+vtk%NWaveElevPts(2) + write(Un,'(3(i7))') n+1, n+1+vtk%NWaveElevPts(2), n+vtk%NWaveElevPts(2) + end do + end do + write(Un,'(A)') ' ' + write(Un,'(A)') ' ' + do n=1,NumberOfPolys + WRITE(Un,'(i7)') 3*n + end do + write(Un,'(A)') ' ' + write(Un,'(A)') ' ' + call WrVTK_footer( Un ) +contains + !---------------------------------------------------------------------------------------------------------------------------------- + !> This function returns the index, Ind, of the XAry closest to XValIn, where XAry is assumed to be periodic. It starts + !! searching at the value of Ind from a previous step. + subroutine GetWaveElevIndx( XValIn, XAry, Ind ) + integer, intent(inout) :: Ind ! Initial and final index into the arrays. + real(SiKi), intent(in) :: XAry (:) !< Array of X values to be interpolated. + real(SiKi), intent(in) :: XValIn !< X value to be found + integer :: AryLen ! Length of the arrays. + real(SiKi) :: XVal !< X to be found (wrapped/periodic) + + AryLen = size(XAry) + + ! Wrap XValIn into the range XAry(1) to XAry(AryLen) + XVal = MOD(XValIn, XAry(AryLen)) + + ! Let's check the limits first. + if ( XVal <= XAry(1) ) then + Ind = 1 + return + else if ( XVal >= XAry(AryLen) ) then + Ind = AryLen + return + else + ! Set the Ind to the first index if we are at the beginning of XAry + if ( XVal <= XAry(2) ) then + Ind = 1 + end if + end if + + ! Let's interpolate! + Ind = MAX( MIN( Ind, AryLen-1 ), 1 ) + do + if ( XVal < XAry(Ind) ) then + Ind = Ind - 1 + else if ( XVal >= XAry(Ind+1) ) then + Ind = Ind + 1 + else + ! XAry(Ind) <= XVal < XAry(Ind+1) + ! this would make it the "closest" node, but I'm not going to worry about that for visualization purposes + !if ( XVal > (XAry(Ind+1) + XAry(Ind))/2.0_SiKi ) Ind = Ind + 1 + return + end if + end do + return + end subroutine GetWaveElevIndx +end subroutine WrVTK_WaveElevVisGrid +!> clear any memory that the _End routine would not clear. Note we are ignoring any errors +subroutine ClearMem() + integer(IntKi) :: ErrStat2 + character(ErrMsgLen) :: ErrMsg2 + if (allocated(vtk%WaveElevVisX )) deallocate(vtk%WaveElevVisX ) + if (allocated(vtk%WaveElevVisY )) deallocate(vtk%WaveElevVisY ) + if (allocated(vtk%WaveElevVisGrid)) deallocate(vtk%WaveElevVisGrid) + call SeaSt_DestroyInitInput( InitInp, ErrStat2, ErrMsg2) + call SeaSt_DestroyInitOutput(InitOutData, ErrStat2, ErrMsg2) +end subroutine ClearMem end module SeaState_C_Binding diff --git a/modules/seastate/src/SeaState_DriverCode.f90 b/modules/seastate/src/SeaState_DriverCode.f90 index d3f3790075..f1ae3fb7a0 100644 --- a/modules/seastate/src/SeaState_DriverCode.f90 +++ b/modules/seastate/src/SeaState_DriverCode.f90 @@ -161,6 +161,7 @@ program SeaStateDriver InitInData%OutRootName = drvrInitInp%OutRootName InitInData%TMax = (drvrInitInp%NSteps-1) * drvrInitInp%TimeInterval ! Starting time is always t = 0.0 InitInData%HasIce = .false. + InitInData%WaveTimeShift = 0.0_DbKi ! for phase shifting wave field in time (positive value only) ! Get the current time call date_and_time ( Values=StrtTime ) ! Let's time the whole simulation diff --git a/modules/seastate/src/SeaState_Input.f90 b/modules/seastate/src/SeaState_Input.f90 index b90aae273c..23fda77435 100644 --- a/modules/seastate/src/SeaState_Input.f90 +++ b/modules/seastate/src/SeaState_Input.f90 @@ -927,10 +927,10 @@ subroutine SeaStateInput_ProcessInitData( InitInp, p, InputFileData, ErrStat, Er return end if - if ( ( InputFileData%Current%CurrMod /= 0 ) .AND. ( InitInp%MHK /= MHK_None ) ) then - call SetErrStat( ErrID_Fatal,'CurrMod must be set to 0 for an MHK turbine.',ErrStat,ErrMsg,RoutineName) - return - end if + ! if ( ( InputFileData%Current%CurrMod /= 0 ) .AND. ( InitInp%MHK /= MHK_None ) ) then + ! call SetErrStat( ErrID_Fatal,'CurrMod must be set to 0 for an MHK turbine.',ErrStat,ErrMsg,RoutineName) + ! return + ! end if if ( InputFileData%Current%CurrMod == 1 ) then ! .TRUE if we have standard current. diff --git a/modules/seastate/src/SeaState_Output.f90 b/modules/seastate/src/SeaState_Output.f90 index db07f41aea..2f596ed51a 100644 --- a/modules/seastate/src/SeaState_Output.f90 +++ b/modules/seastate/src/SeaState_Output.f90 @@ -272,7 +272,7 @@ SUBROUTINE SeaStOut_WriteWvKinFiles( Rootname, SeaSt_Prog, WaveField, WaveDT, X_ y_gridPts(i+1) = -Y_HalfWidth + deltaGrid(2)*i end do do i = 0, NGrid(3)-1 - z_gridPts(i+1) = - ( 1.0 - cos( real((NGrid(3) - 1) - i, ReKi) * deltaGrid(3) ) ) * WaveField%GridParams%Z_Depth + z_gridPts(i+1) = - ( 1.0 - cos( real((NGrid(3) - 1) - i, ReKi) * deltaGrid(3) ) ) * WaveField%GridDepth end do ! Write the increments from [0, NStepWave] even though for OpenFAST data, NStepWave = 0, but for arbitrary user data this may not be true. @@ -1033,6 +1033,7 @@ SUBROUTINE SeaStOut_WrSummaryFile(InitInp, InputFileData, p, ErrStat, ErrMsg ) p%WaveField%EffWtrDpth, '(m) relative to SWL' WRITE( UnSum, '(1X,A15,F8.2,A20,F8.2,A19/)' ) 'Grid Z_Depth : ', InputFileData%Z_Depth - p%WaveField%MSL2SWL, '(m) relative to MSL; ', & InputFileData%Z_Depth, '(m) relative to SWL' + WRITE( UnSum, '(1X,A50,F10.5,A4)' ) 'WaveTimeShift: (applied at WaveField data access) ', p%WaveField%WaveTimeShift,' (s)' end if Frmt = '(1X,ES18.4e2,2x,ES18.4e2,2x,ES18.4e2,2x,ES18.4e2)' diff --git a/modules/seastate/src/SeaState_Types.f90 b/modules/seastate/src/SeaState_Types.f90 index 53fac26332..b15181ef0c 100644 --- a/modules/seastate/src/SeaState_Types.f90 +++ b/modules/seastate/src/SeaState_Types.f90 @@ -97,6 +97,7 @@ MODULE SeaState_Types REAL(ReKi) :: defMSL2SWL = 0.0_ReKi !< Default mean sea level to still water level from the driver; may be overwritten [m] INTEGER(IntKi) :: MHK = 0_IntKi !< MHK flag [-] REAL(DbKi) :: TMax = 0.0_R8Ki !< Supplied by Driver: The total simulation time [(sec)] + REAL(DbKi) :: WaveTimeShift = 0 !< Add this to the time to effectively phase shift the wave (useful for hybrid tank testing). Positive value only (advance time) [(s)] INTEGER(IntKi) :: WaveFieldMod = 0_IntKi !< Wave field handling (-) (switch) 0: use individual SeaState inputs without adjustment, 1: adjust wave phases based on turbine offsets from farm origin [-] REAL(ReKi) :: PtfmLocationX = 0.0_ReKi !< Supplied by Driver: X coordinate of platform location in the wave field [m] REAL(ReKi) :: PtfmLocationY = 0.0_ReKi !< Supplied by Driver: Y coordinate of platform location in the wave field [m] @@ -183,7 +184,7 @@ MODULE SeaState_Types INTEGER(IntKi) :: Decimate = 0_IntKi !< The output decimation counter [-] REAL(DbKi) :: LastOutTime = 0.0_R8Ki !< Last time step which was written to the output file (sec) [-] INTEGER(IntKi) :: LastIndWave = 0_IntKi !< The last index used in the wave kinematics arrays, used to optimize interpolation [-] - TYPE(SeaSt_WaveField_MiscVarType) :: WaveField_m !< misc var information from the SeaState Interpolation module [-] + TYPE(GridInterp_MiscVarType) :: WaveField_m !< misc var information from the SeaState Interpolation module [-] TYPE(ModJacType) :: Jac !< Values corresponding to module variables [-] TYPE(SeaSt_InputType) :: u_perturb !< Input type for linearization perturbation [-] TYPE(SeaSt_OutputType) :: y_lin !< Output type for linearization perturbation [-] @@ -484,6 +485,7 @@ subroutine SeaSt_CopyInitInput(SrcInitInputData, DstInitInputData, CtrlCode, Err DstInitInputData%defMSL2SWL = SrcInitInputData%defMSL2SWL DstInitInputData%MHK = SrcInitInputData%MHK DstInitInputData%TMax = SrcInitInputData%TMax + DstInitInputData%WaveTimeShift = SrcInitInputData%WaveTimeShift DstInitInputData%WaveFieldMod = SrcInitInputData%WaveFieldMod DstInitInputData%PtfmLocationX = SrcInitInputData%PtfmLocationX DstInitInputData%PtfmLocationY = SrcInitInputData%PtfmLocationY @@ -528,6 +530,7 @@ subroutine SeaSt_PackInitInput(RF, Indata) call RegPack(RF, InData%defMSL2SWL) call RegPack(RF, InData%MHK) call RegPack(RF, InData%TMax) + call RegPack(RF, InData%WaveTimeShift) call RegPack(RF, InData%WaveFieldMod) call RegPack(RF, InData%PtfmLocationX) call RegPack(RF, InData%PtfmLocationY) @@ -569,6 +572,7 @@ subroutine SeaSt_UnPackInitInput(RF, OutData) call RegUnpack(RF, OutData%defMSL2SWL); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%MHK); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%TMax); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%WaveTimeShift); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%WaveFieldMod); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%PtfmLocationX); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%PtfmLocationY); if (RegCheckErr(RF, RoutineName)) return @@ -1302,7 +1306,7 @@ subroutine SeaSt_CopyMisc(SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg) DstMiscData%Decimate = SrcMiscData%Decimate DstMiscData%LastOutTime = SrcMiscData%LastOutTime DstMiscData%LastIndWave = SrcMiscData%LastIndWave - call SeaSt_WaveField_CopyMisc(SrcMiscData%WaveField_m, DstMiscData%WaveField_m, CtrlCode, ErrStat2, ErrMsg2) + call GridInterp_CopyMisc(SrcMiscData%WaveField_m, DstMiscData%WaveField_m, CtrlCode, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return call NWTC_Library_CopyModJacType(SrcMiscData%Jac, DstMiscData%Jac, CtrlCode, ErrStat2, ErrMsg2) @@ -1325,7 +1329,7 @@ subroutine SeaSt_DestroyMisc(MiscData, ErrStat, ErrMsg) character(*), parameter :: RoutineName = 'SeaSt_DestroyMisc' ErrStat = ErrID_None ErrMsg = '' - call SeaSt_WaveField_DestroyMisc(MiscData%WaveField_m, ErrStat2, ErrMsg2) + call GridInterp_DestroyMisc(MiscData%WaveField_m, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) call NWTC_Library_DestroyModJacType(MiscData%Jac, ErrStat2, ErrMsg2) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) @@ -1343,7 +1347,7 @@ subroutine SeaSt_PackMisc(RF, Indata) call RegPack(RF, InData%Decimate) call RegPack(RF, InData%LastOutTime) call RegPack(RF, InData%LastIndWave) - call SeaSt_WaveField_PackMisc(RF, InData%WaveField_m) + call GridInterp_PackMisc(RF, InData%WaveField_m) call NWTC_Library_PackModJacType(RF, InData%Jac) call SeaSt_PackInput(RF, InData%u_perturb) call SeaSt_PackOutput(RF, InData%y_lin) @@ -1358,7 +1362,7 @@ subroutine SeaSt_UnPackMisc(RF, OutData) call RegUnpack(RF, OutData%Decimate); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%LastOutTime); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%LastIndWave); if (RegCheckErr(RF, RoutineName)) return - call SeaSt_WaveField_UnpackMisc(RF, OutData%WaveField_m) ! WaveField_m + call GridInterp_UnpackMisc(RF, OutData%WaveField_m) ! WaveField_m call NWTC_Library_UnpackModJacType(RF, OutData%Jac) ! Jac call SeaSt_UnpackInput(RF, OutData%u_perturb) ! u_perturb call SeaSt_UnpackOutput(RF, OutData%y_lin) ! y_lin diff --git a/modules/servodyn/src/BladedInterface_EX.f90 b/modules/servodyn/src/BladedInterface_EX.f90 index b2f08be893..b4f3b8383d 100644 --- a/modules/servodyn/src/BladedInterface_EX.f90 +++ b/modules/servodyn/src/BladedInterface_EX.f90 @@ -64,7 +64,7 @@ MODULE BladedInterface_EX integer(IntKi), parameter :: CableCtrl_MaxChan = 200 !< Maximum channels in cable control group integer(IntKi), parameter :: StCCtrl_StartIdx = 2801 !< Starting index for the StC control integer(IntKi), parameter :: StCCtrl_MaxChan = 200 !< Maximum channels in StC control group - integer(IntKi), parameter :: StCCtrl_ChanPerSet = 20 !< Channels needed per set (10 sets for total channels) + integer(IntKi), parameter :: StCCtrl_ChanPerSet = 25 !< Channels needed per set (8 sets for total channels) CONTAINS @@ -277,15 +277,15 @@ end subroutine InitCableCtrl subroutine InitStCCtrl() integer(IntKi) :: I,J ! Generic counters - ! Error check the Cable Ctrl + ! Error check the StC Ctrl if (.not. allocated(StC_CtrlChanInitInfo%Requestor)) then ErrStat2=ErrID_Fatal - ErrMsg2='StC control string array indicating which module requested cable controls is missing (StC_CtrlChanInitInfo%Requestor)' + ErrMsg2='StC control string array indicating which module requested StC controls is missing (StC_CtrlChanInitInfo%Requestor)' if (Failed()) return endif if (size(StC_CtrlChanInitInfo%Requestor) /= p%NumStC_Control) then ErrStat2=ErrID_Fatal - ErrMsg2='Size of StC control string array (StC_CtrlChanInitInfo%Requestor) does not match the number of requested cable control channels.' + ErrMsg2='Size of StC control string array (StC_CtrlChanInitInfo%Requestor) does not match the number of requested StC control channels.' if (Failed()) return endif if ( (size(StC_CtrlChanInitInfo%InitMeasDisp,2) /= p%NumStC_Control) .or. & @@ -293,9 +293,10 @@ subroutine InitStCCtrl() (size(StC_CtrlChanInitInfo%InitStiff ,2) /= p%NumStC_Control) .or. & (size(StC_CtrlChanInitInfo%InitDamp ,2) /= p%NumStC_Control) .or. & (size(StC_CtrlChanInitInfo%InitBrake ,2) /= p%NumStC_Control) .or. & - (size(StC_CtrlChanInitInfo%InitForce ,2) /= p%NumStC_Control) ) then + (size(StC_CtrlChanInitInfo%InitForce ,2) /= p%NumStC_Control) .or. & + (size(StC_CtrlChanInitInfo%InitMoment ,2) /= p%NumStC_Control) ) then ErrStat2=ErrID_Fatal - ErrMsg2='Size of StC control initialization arrays (StC_CtrlChanInitInfo%Init*) do not match the number of requested cable control channels. Programming error somewhere.' + ErrMsg2='Size of StC control initialization arrays (StC_CtrlChanInitInfo%Init*) do not match the number of requested StC control channels. Programming error somewhere.' if (Failed()) return endif if ( p%NumStC_Control*StCCtrl_ChanPerSet > StCCtrl_MaxChan ) then @@ -324,6 +325,8 @@ subroutine InitStCCtrl() if (Failed()) return call AllocAry( dll_data%PrevStCCmdForce, 3, p%NumStC_Control, 'PrevStCCmdForce', ErrStat2, ErrMsg2 ) if (Failed()) return + call AllocAry( dll_data%PrevStCCmdMoment,3, p%NumStC_Control, 'PrevStCCmdMoment',ErrStat2, ErrMsg2 ) + if (Failed()) return call AllocAry( dll_data%StCCmdStiff, 3, p%NumStC_Control, 'StCCmdStiff', ErrStat2, ErrMsg2 ) if (Failed()) return call AllocAry( dll_data%StCCmdDamp, 3, p%NumStC_Control, 'StCCmdDamp', ErrStat2, ErrMsg2 ) @@ -332,6 +335,8 @@ subroutine InitStCCtrl() if (Failed()) return call AllocAry( dll_data%StCCmdForce, 3, p%NumStC_Control, 'StCCmdForce', ErrStat2, ErrMsg2 ) if (Failed()) return + call AllocAry( dll_data%StCCmdMoment, 3, p%NumStC_Control, 'StCCmdMoment', ErrStat2, ErrMsg2 ) + if (Failed()) return ! Initialize to values passed in dll_data%StCMeasDisp = real(StC_CtrlChanInitInfo%InitMeasDisp,SiKi) dll_data%StCMeasVel = real(StC_CtrlChanInitInfo%InitMeasVel ,SiKi) @@ -339,10 +344,12 @@ subroutine InitStCCtrl() dll_data%PrevStCCmdDamp = real(StC_CtrlChanInitInfo%InitDamp ,SiKi) dll_data%PrevStCCmdBrake = real(StC_CtrlChanInitInfo%InitBrake ,SiKi) dll_data%PrevStCCmdForce = real(StC_CtrlChanInitInfo%InitForce ,SiKi) + dll_data%PrevStCCmdMoment = real(StC_CtrlChanInitInfo%InitMoment ,SiKi) dll_data%StCCmdStiff = real(StC_CtrlChanInitInfo%InitStiff ,SiKi) dll_data%StCCmdDamp = real(StC_CtrlChanInitInfo%InitDamp ,SiKi) dll_data%StCCmdBrake = real(StC_CtrlChanInitInfo%InitBrake ,SiKi) dll_data%StCCmdForce = real(StC_CtrlChanInitInfo%InitForce ,SiKi) + dll_data%StCCmdMoment = real(StC_CtrlChanInitInfo%InitMoment ,SiKi) ! Create info for summary file about channels if (UnSum > 0) then @@ -366,8 +373,13 @@ subroutine InitStCCtrl() call WrSumInfoRcvd( J+16,StC_CtrlChanInitInfo%Requestor(I),'StC control channel group '//trim(Num2LStr(I))//' -- StC_Force_X (additional force)') call WrSumInfoRcvd( J+17,StC_CtrlChanInitInfo%Requestor(I),'StC control channel group '//trim(Num2LStr(I))//' -- StC_Force_Y (additional force)') call WrSumInfoRcvd( J+18,StC_CtrlChanInitInfo%Requestor(I),'StC control channel group '//trim(Num2LStr(I))//' -- StC_Force_Z (additional force)') - call WrSumInfoRcvd( J+19,StC_CtrlChanInitInfo%Requestor(I),'StC control channel group '//trim(Num2LStr(I))//' -- Reserved for future') - call WrSumInfoRcvd( J+20,StC_CtrlChanInitInfo%Requestor(I),'StC control channel group '//trim(Num2LStr(I))//' -- Reserved for future') + call WrSumInfoRcvd( J+19,StC_CtrlChanInitInfo%Requestor(I),'StC control channel group '//trim(Num2LStr(I))//' -- StC_Moment_X (additional moment)') + call WrSumInfoRcvd( J+20,StC_CtrlChanInitInfo%Requestor(I),'StC control channel group '//trim(Num2LStr(I))//' -- StC_Moment_Y (additional moment)') + call WrSumInfoRcvd( J+21,StC_CtrlChanInitInfo%Requestor(I),'StC control channel group '//trim(Num2LStr(I))//' -- StC_Moment_Z (additional moment)') + call WrSumInfoRcvd( J+22,StC_CtrlChanInitInfo%Requestor(I),'StC control channel group '//trim(Num2LStr(I))//' -- Reserved for future') + call WrSumInfoRcvd( J+23,StC_CtrlChanInitInfo%Requestor(I),'StC control channel group '//trim(Num2LStr(I))//' -- Reserved for future') + call WrSumInfoRcvd( J+24,StC_CtrlChanInitInfo%Requestor(I),'StC control channel group '//trim(Num2LStr(I))//' -- Reserved for future') + call WrSumInfoRcvd( J+25,StC_CtrlChanInitInfo%Requestor(I),'StC control channel group '//trim(Num2LStr(I))//' -- Reserved for future') enddo endif end subroutine InitStCCtrl @@ -569,7 +581,8 @@ subroutine SetEXavrStC_Sensors() dll_data%avrswap(J+ 7:J+ 9) = dll_data%PrevStCCmdStiff(1:3,I) ! StC initial stiffness -- StC_Stiff_X, StC_Stiff_Y, StC_Stiff_Z (N/m) dll_data%avrswap(J+10:J+12) = dll_data%PrevStCCmdDamp( 1:3,I) ! StC initial damping -- StC_Damp_X, StC_Damp_Y, StC_Damp_Z (N/(m/s)) dll_data%avrswap(J+13:J+15) = dll_data%PrevStCCmdBrake(1:3,I) ! StC initial brake -- StC_Brake_X, StC_Brake_Y, StC_Brake_Z (N) - dll_data%avrswap(J+16:J+18) = dll_data%PrevStCCmdForce(1:3,I) ! StC initial brake -- StC_Force_X, StC_Force_Y, StC_Force_Z (N) + dll_data%avrswap(J+16:J+18) = dll_data%PrevStCCmdForce(1:3,I) ! StC initial force -- StC_Force_X, StC_Force_Y, StC_Force_Z (N) + dll_data%avrswap(J+19:J+21) = dll_data%PrevStCCmdMoment(1:3,I) ! StC initial moment -- StC_Moment_X, StC_Moment_Y, StC_Moment_Z (N) enddo endif end subroutine SetEXavrStC_Sensors @@ -645,7 +658,8 @@ subroutine Retrieve_EXavrSWAP_StControls () dll_data%StCCmdStiff(1:3,I) = dll_data%avrswap(J+ 7:J+ 9) ! StC commanded stiffness -- StC_Stiff_X, StC_Stiff_Y, StC_Stiff_Z (N/m) dll_data%StCCmdDamp( 1:3,I) = dll_data%avrswap(J+10:J+12) ! StC commanded damping -- StC_Damp_X, StC_Damp_Y, StC_Damp_Z (N/(m/s)) dll_data%StCCmdBrake(1:3,I) = dll_data%avrswap(J+13:J+15) ! StC commanded brake -- StC_Brake_X, StC_Brake_Y, StC_Brake_Z (N) - dll_data%StCCmdForce(1:3,I) = dll_data%avrswap(J+16:J+18) ! StC commanded brake -- StC_Force_X, StC_Force_Y, StC_Force_Z (N) + dll_data%StCCmdForce(1:3,I) = dll_data%avrswap(J+16:J+18) ! StC commanded force -- StC_Force_X, StC_Force_Y, StC_Force_Z (N) + dll_data%StCCmdMoment(1:3,I)= dll_data%avrswap(J+19:J+21) ! StC commanded moment -- StC_Moment_X, StC_Moment_Y, StC_Moment_Z (N) enddo end subroutine Retrieve_EXavrSWAP_StControls diff --git a/modules/servodyn/src/ServoDyn.f90 b/modules/servodyn/src/ServoDyn.f90 index 55f2848f97..ecf953e3e4 100644 --- a/modules/servodyn/src/ServoDyn.f90 +++ b/modules/servodyn/src/ServoDyn.f90 @@ -1346,7 +1346,7 @@ subroutine CheckInfo() do i=1,size(InitOut%LinNames_y) Flag='F' if (InitOut%RotFrame_y(i)) Flag='T' - call WrFileNR(CU,' '//Num2LStr(i)//Flag//' '//InitOut%LinNames_y(i)//NewLine) + call WrScr(' '//Num2LStr(i)//Flag//' '//InitOut%LinNames_y(i)) enddo endif if (allocated(InitOut%LinNames_x)) then @@ -1368,13 +1368,13 @@ subroutine CheckInfo() do i=1,size(InitOut%LinNames_x) Flag='F' if (InitOut%RotFrame_x(i)) Flag='T' - call WrFileNR(CU,' '//Num2LStr(i)//Flag//' '//trim(Num2LStr(InitOut%DerivOrder_x(i)))//' '//InitOut%LinNames_x(i)//NewLine) + call WrScr(' '//Num2LStr(i)//Flag//' '//trim(Num2LStr(InitOut%DerivOrder_x(i)))//' '//InitOut%LinNames_x(i)) enddo endif if (allocated(InitOut%LinNames_u)) then call WrScr('Perturb Size u') do i=1,size(p%du) - call WrFileNR(CU,' '//trim(Num2LStr(i))//' '//trim(Num2LStr(p%du(i)))//NewLine) + call WrScr(' '//trim(Num2LStr(i))//' '//trim(Num2LStr(p%du(i)))) enddo call WrScr('LinNames_u') do j=1,p%NumBStC @@ -1396,9 +1396,9 @@ subroutine CheckInfo() FlagLoad='F' if (InitOut%RotFrame_u(i)) Flag='T' if (InitOut%IsLoad_u(i)) FlagLoad='T' - call WrFileNR(CU,' '//Num2LStr(i)//Flag//' '//FlagLoad//' ('// & + call WrScr(' '//Num2LStr(i)//Flag//' '//FlagLoad//' ('// & trim(Num2LStr(p%Jac_u_indx(i,1)))//','//trim(Num2LStr(p%Jac_u_indx(i,2)))//','//trim(Num2LStr(p%Jac_u_indx(i,3)))// & - ') '//InitOut%LinNames_u(i)//NewLine) + ') '//InitOut%LinNames_u(i)) enddo endif end subroutine CheckInfo @@ -1977,6 +1977,7 @@ subroutine StC_CtrlChan_Setup(m,p,CtrlChanInitInfo,UnSum,ErrStat,ErrMsg) allocate(CtrlChanInitInfo%InitDamp( 3,p%NumStC_Control), STAT=ErrStat2); if ( AllErr('Could not allocate InitDamp array') ) return; allocate(CtrlChanInitInfo%InitBrake( 3,p%NumStC_Control), STAT=ErrStat2); if ( AllErr('Could not allocate InitBrake array') ) return; allocate(CtrlChanInitInfo%InitForce( 3,p%NumStC_Control), STAT=ErrStat2); if ( AllErr('Could not allocate InitForce array') ) return; + allocate(CtrlChanInitInfo%InitMoment( 3,p%NumStC_Control), STAT=ErrStat2); if ( AllErr('Could not allocate InitMoment array') ) return; allocate(CtrlChanInitInfo%InitMeasDisp(3,p%NumStC_Control), STAT=ErrStat2); if ( AllErr('Could not allocate InitMeasDisp array') ) return; allocate(CtrlChanInitInfo%InitMeasVel( 3,p%NumStC_Control), STAT=ErrStat2); if ( AllErr('Could not allocate InitMeasVel array') ) return; CtrlChanInitInfo%Requestor = "" @@ -1984,6 +1985,7 @@ subroutine StC_CtrlChan_Setup(m,p,CtrlChanInitInfo,UnSum,ErrStat,ErrMsg) CtrlChanInitInfo%InitDamp = 0.0_SiKi CtrlChanInitInfo%InitBrake = 0.0_SiKi CtrlChanInitInfo%InitForce = 0.0_SiKi + CtrlChanInitInfo%InitMoment = 0.0_SiKi CtrlChanInitInfo%InitMeasDisp = 0.0_SiKi CtrlChanInitInfo%InitMeasVel = 0.0_SiKi @@ -2032,7 +2034,7 @@ subroutine StC_CtrlChan_Setup(m,p,CtrlChanInitInfo,UnSum,ErrStat,ErrMsg) ! Set all the initial values to pass to the controller for first call and ensure all inputs/outputs for control are sized same call StC_SetDLLinputs(p,m,CtrlChanInitInfo%InitMeasDisp,CtrlChanInitInfo%InitMeasVel,ErrStat2,ErrMsg2,.TRUE.) ! Do resizing if needed if (Failed()) return; - call StC_SetInitDLLinputs(p,m,CtrlChanInitInfo%InitStiff,CtrlChanInitInfo%InitDamp,CtrlChanInitInfo%InitBrake,CtrlChanInitInfo%InitForce,ErrStat2,ErrMsg2) + call StC_SetInitDLLinputs(p,m,CtrlChanInitInfo%InitStiff,CtrlChanInitInfo%InitDamp,CtrlChanInitInfo%InitBrake,CtrlChanInitInfo%InitForce,CtrlChanInitInfo%InitMoment,ErrStat2,ErrMsg2) if (Failed()) return; ! Duplicates the Cmd channel data (so that they are allocated for first UpdateStates routine) call StC_InitExtrapInputs(p,m,ErrStat2,ErrMsg2) @@ -2170,10 +2172,11 @@ SUBROUTINE SrvD_UpdateStates( t, n, Inputs, InputTimes, p, x, xd, z, OtherState, INTEGER(IntKi) :: k ! loop counter for blade in BStC INTEGER(IntKi) :: order TYPE(SrvD_InputType) :: u_interp ! interpolated input - REAL(ReKi), ALLOCATABLE :: StC_CmdStiff(:,:) !< StC_CmdStiff command signals (3,p%NumStC_Control) -- used only if p%NumStC_Ctrl > 0 - REAL(ReKi), ALLOCATABLE :: StC_CmdDamp(:,:) !< StC_CmdDamp command signals (3,p%NumStC_Control) -- used only if p%NumStC_Ctrl > 0 - REAL(ReKi), ALLOCATABLE :: StC_CmdBrake(:,:) !< StC_CmdBrake command signals (3,p%NumStC_Control) -- used only if p%NumStC_Ctrl > 0 - REAL(ReKi), ALLOCATABLE :: StC_CmdForce(:,:) !< StC_CmdForce command signals (3,p%NumStC_Control) -- used only if p%NumStC_Ctrl > 0 + REAL(ReKi), ALLOCATABLE :: StC_CmdStiff(:,:) !< StC_CmdStiff command signals (3,p%NumStC_Control) -- used only if p%NumStC_Ctrl > 0 + REAL(ReKi), ALLOCATABLE :: StC_CmdDamp(:,:) !< StC_CmdDamp command signals (3,p%NumStC_Control) -- used only if p%NumStC_Ctrl > 0 + REAL(ReKi), ALLOCATABLE :: StC_CmdBrake(:,:) !< StC_CmdBrake command signals (3,p%NumStC_Control) -- used only if p%NumStC_Ctrl > 0 + REAL(ReKi), ALLOCATABLE :: StC_CmdForce(:,:) !< StC_CmdForce command signals (3,p%NumStC_Control) -- used only if p%NumStC_Ctrl > 0 + REAL(ReKi), ALLOCATABLE :: StC_CmdMoment(:,:) !< StC_CmdMoment command signals (3,p%NumStC_Control) -- used only if p%NumStC_Ctrl > 0 INTEGER(IntKi) :: ErrStat2 ! Error status of the operation (occurs after initial error) CHARACTER(ErrMsgLen) :: ErrMsg2 ! Error message if ErrStat2 /= ErrID_None @@ -2245,7 +2248,8 @@ SUBROUTINE SrvD_UpdateStates( t, n, Inputs, InputTimes, p, x, xd, z, OtherState, call AllocAry(StC_CmdDamp, 3, p%NumStC_Control, 'StC_CmdDamp' , ErrStat2, ErrMsg2 ); if (Failed()) return; call AllocAry(StC_CmdBrake, 3, p%NumStC_Control, 'StC_CmdBrake', ErrStat2, ErrMsg2 ); if (Failed()) return; call AllocAry(StC_CmdForce, 3, p%NumStC_Control, 'StC_CmdForce', ErrStat2, ErrMsg2 ); if (Failed()) return; - call StCControl_CalcOutput( t_next, p, StC_CmdStiff, StC_CmdDamp, StC_CmdBrake, StC_CmdForce, m, ErrStat2, ErrMsg2 ) + call AllocAry(StC_CmdMoment,3, p%NumStC_Control, 'StC_CmdMoment',ErrStat2, ErrMsg2 ); if (Failed()) return; + call StCControl_CalcOutput( t_next, p, StC_CmdStiff, StC_CmdDamp, StC_CmdBrake, StC_CmdForce, StC_CmdMoment, m, ErrStat2, ErrMsg2 ) if (Failed()) return; endif @@ -2339,11 +2343,12 @@ SUBROUTINE Cleanup() if (allocated(StC_CmdDamp)) deallocate(StC_CmdDamp) if (allocated(StC_CmdBrake)) deallocate(StC_CmdBrake) if (allocated(StC_CmdForce)) deallocate(StC_CmdForce) + if (allocated(StC_CmdMoment)) deallocate(StC_CmdMoment) END SUBROUTINE Cleanup subroutine SetStCInput_CtrlChans(u_StC) type(StC_InputType), intent(inout) :: u_StC(:) !< Inputs at InputTimes ! -- not all StC instances will necessarily be looking for this, so these inputs might not be allocated) - if (allocated(u_StC(1)%CmdStiff) .and. allocated(u_StC(1)%CmdDamp) .and. allocated(u_StC(1)%CmdBrake) .and. allocated(u_StC(1)%CmdForce)) then + if (allocated(u_StC(1)%CmdStiff) .and. allocated(u_StC(1)%CmdDamp) .and. allocated(u_StC(1)%CmdBrake) .and. allocated(u_StC(1)%CmdForce) .and. allocated(u_StC(1)%CmdMoment)) then if ( n > m%PrevTstepNcall ) then ! Cycle u%CmdStiff and others -- we are at a new timestep. do i=p%InterpOrder,1,-1 ! shift back in time in reverse order -- oldest (InterpOrder+1) to newest (1) @@ -2351,6 +2356,7 @@ subroutine SetStCInput_CtrlChans(u_StC) u_StC(i+1)%CmdDamp = u_StC(i)%CmdDamp u_StC(i+1)%CmdBrake = u_StC(i)%CmdBrake u_StC(i+1)%CmdForce = u_StC(i)%CmdForce + u_StC(i+1)%CmdMoment= u_StC(i)%CmdMoment enddo endif ! Now set the current commanded values @@ -2358,6 +2364,7 @@ subroutine SetStCInput_CtrlChans(u_StC) u_StC(1)%CmdDamp( 1:3,1:p%NumStC_Control) = StC_CmdDamp( 1:3,1:p%NumStC_Control) u_StC(1)%CmdBrake(1:3,1:p%NumStC_Control) = StC_CmdBrake(1:3,1:p%NumStC_Control) u_StC(1)%CmdForce(1:3,1:p%NumStC_Control) = StC_CmdForce(1:3,1:p%NumStC_Control) + u_StC(1)%CmdMoment(1:3,1:p%NumStC_Control)= StC_CmdMoment(1:3,1:p%NumStC_Control) endif end subroutine SetStCInput_CtrlChans @@ -2465,6 +2472,7 @@ SUBROUTINE SrvD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg REAL(ReKi), ALLOCATABLE :: StC_CmdDamp(:,:) !< StC_CmdDamp command signals (3,p%NumStC_Control) -- used only if p%NumStC_Ctrl > 0 REAL(ReKi), ALLOCATABLE :: StC_CmdBrake(:,:) !< StC_CmdBrake command signals (3,p%NumStC_Control) -- used only if p%NumStC_Ctrl > 0 REAL(ReKi), ALLOCATABLE :: StC_CmdForce(:,:) !< StC_CmdForce command signals (3,p%NumStC_Control) -- used only if p%NumStC_Ctrl > 0 + REAL(ReKi), ALLOCATABLE :: StC_CmdMoment(:,:) !< StC_CmdMoment command signals (3,p%NumStC_Control) -- used only if p%NumStC_Ctrl > 0 INTEGER(IntKi) :: ErrStat2 CHARACTER(ErrMsgLen) :: ErrMsg2 CHARACTER(*), PARAMETER :: RoutineName = 'SrvD_CalcOutput' @@ -2532,7 +2540,8 @@ SUBROUTINE SrvD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg call AllocAry(StC_CmdDamp, 3, p%NumStC_Control, 'StC_CmdDamp' , ErrStat2, ErrMsg2 ); if (Failed()) return; call AllocAry(StC_CmdBrake, 3, p%NumStC_Control, 'StC_CmdBrake', ErrStat2, ErrMsg2 ); if (Failed()) return; call AllocAry(StC_CmdForce, 3, p%NumStC_Control, 'StC_CmdForce', ErrStat2, ErrMsg2 ); if (Failed()) return; - call StCControl_CalcOutput( t, p, StC_CmdStiff, StC_CmdDamp, StC_CmdBrake, StC_CmdForce, m, ErrStat2, ErrMsg2 ) + call AllocAry(StC_CmdMoment,3, p%NumStC_Control, 'StC_CmdMoment',ErrStat2, ErrMsg2 ); if (Failed()) return; + call StCControl_CalcOutput( t, p, StC_CmdStiff, StC_CmdDamp, StC_CmdBrake, StC_CmdForce, StC_CmdMoment, m, ErrStat2, ErrMsg2 ) if (Failed()) return; endif do j=1,p%NumBStC ! Blades @@ -2633,15 +2642,17 @@ SUBROUTINE Cleanup() if (allocated(StC_CmdDamp)) deallocate(StC_CmdDamp) if (allocated(StC_CmdBrake)) deallocate(StC_CmdBrake) if (allocated(StC_CmdForce)) deallocate(StC_CmdForce) + if (allocated(StC_CmdMoment)) deallocate(StC_CmdMoment) END SUBROUTINE Cleanup subroutine SetStCInput_CtrlChans(u_StC) type(StC_InputType), intent(inout) :: u_StC !< Inputs at InputTimes ! -- not all StC instances will necessarily be looking for this, so these inputs might not be allocated) - if (allocated(u_StC%CmdStiff) .and. allocated(u_StC%CmdDamp) .and. allocated(u_StC%CmdBrake) .and. allocated(u_StC%CmdForce)) then + if (allocated(u_StC%CmdStiff) .and. allocated(u_StC%CmdDamp) .and. allocated(u_StC%CmdBrake) .and. allocated(u_StC%CmdForce) .and. allocated(u_StC%CmdMoment)) then u_StC%CmdStiff(1:3,1:p%NumStC_Control) = StC_CmdStiff(1:3,1:p%NumStC_Control) u_StC%CmdDamp( 1:3,1:p%NumStC_Control) = StC_CmdDamp( 1:3,1:p%NumStC_Control) u_StC%CmdBrake(1:3,1:p%NumStC_Control) = StC_CmdBrake(1:3,1:p%NumStC_Control) u_StC%CmdForce(1:3,1:p%NumStC_Control) = StC_CmdForce(1:3,1:p%NumStC_Control) + u_StC%CmdMoment(1:3,1:p%NumStC_Control)= StC_CmdMoment(1:3,1:p%NumStC_Control) endif end subroutine SetStCInput_CtrlChans @@ -6476,13 +6487,14 @@ END SUBROUTINE CableControl_CalcOutput ! Commanded Airfoil UserProp for blade (must be same units as given in AD15 airfoil tables) ! This is passed to AD15 to be interpolated with the airfoil table userprop column ! (might be used for airfoil flap angles for example) -SUBROUTINE StCControl_CalcOutput( t, p, StC_CmdStiff, StC_CmdDamp, StC_CmdBrake, StC_CmdForce, m, ErrStat, ErrMsg ) +SUBROUTINE StCControl_CalcOutput( t, p, StC_CmdStiff, StC_CmdDamp, StC_CmdBrake, StC_CmdForce, StC_CmdMoment, m, ErrStat, ErrMsg ) REAL(DbKi), INTENT(IN ) :: t !< Current simulation time in seconds TYPE(SrvD_ParameterType), INTENT(IN ) :: p !< Parameters REAL(ReKi), ALLOCATABLE, INTENT(INOUT) :: StC_CmdStiff(:,:) !< StC_CmdStiff command signals (3,p%NumStC_Control) REAL(ReKi), ALLOCATABLE, INTENT(INOUT) :: StC_CmdDamp(:,:) !< StC_CmdDamp command signals (3,p%NumStC_Control) REAL(ReKi), ALLOCATABLE, INTENT(INOUT) :: StC_CmdBrake(:,:) !< StC_CmdBrake command signals (3,p%NumStC_Control) REAL(ReKi), ALLOCATABLE, INTENT(INOUT) :: StC_CmdForce(:,:) !< StC_CmdForce command signals (3,p%NumStC_Control) + REAL(ReKi), ALLOCATABLE, INTENT(INOUT) :: StC_CmdMoment(:,:) !< StC_CmdMoment command signals (3,p%NumStC_Control) TYPE(SrvD_MiscVarType), INTENT(INOUT) :: m !< Misc (optimization) variables INTEGER(IntKi), INTENT( OUT) :: ErrStat !< Error status of the operation CHARACTER(*), INTENT( OUT) :: ErrMsg !< Error message if ErrStat /= ErrID_None @@ -6494,7 +6506,7 @@ SUBROUTINE StCControl_CalcOutput( t, p, StC_CmdStiff, StC_CmdDamp, StC_CmdBrake, ! Only proceed if we have have StC controls with the extended swap and legacy interface if ((p%NumStC_Control <= 0) .or. (.not. p%EXavrSWAP)) return - if (.not. allocated(StC_CmdStiff) .or. .not. allocated(StC_CmdDamp) .or. .not. allocated(StC_CmdBrake) .or. .not. allocated(StC_CmdForce)) then + if (.not. allocated(StC_CmdStiff) .or. .not. allocated(StC_CmdDamp) .or. .not. allocated(StC_CmdBrake) .or. .not. allocated(StC_CmdForce) .or. .not. allocated(StC_CmdMoment)) then ErrStat = ErrID_Fatal ErrMsg = "StC control signal matrices not allocated. Programming error somewhere." return @@ -6522,11 +6534,16 @@ SUBROUTINE StCControl_CalcOutput( t, p, StC_CmdStiff, StC_CmdDamp, StC_CmdBrake, StC_CmdForce(1:3,1:p%NumStC_Control) = m%dll_data%PrevStCCmdForce(1:3,1:p%NumStC_Control) + & factor * ( m%dll_data%StCCmdForce(1:3,1:p%NumStC_Control) - m%dll_data%PrevStCCmdForce(1:3,1:p%NumStC_Control) ) endif + if (allocated(StC_CmdMoment)) then + StC_CmdMoment(1:3,1:p%NumStC_Control) = m%dll_data%PrevStCCmdMoment(1:3,1:p%NumStC_Control) + & + factor * ( m%dll_data%StCCmdMoment(1:3,1:p%NumStC_Control) - m%dll_data%PrevStCCmdMoment(1:3,1:p%NumStC_Control) ) + endif else if (allocated(StC_CmdStiff)) StC_CmdStiff(1:3,1:p%NumStC_Control) = m%dll_data%StCCmdStiff(1:3,1:p%NumStC_Control) if (allocated(StC_CmdDamp)) StC_CmdDamp( 1:3,1:p%NumStC_Control) = m%dll_data%StCCmdDamp( 1:3,1:p%NumStC_Control) if (allocated(StC_CmdBrake)) StC_CmdBrake(1:3,1:p%NumStC_Control) = m%dll_data%StCCmdBrake(1:3,1:p%NumStC_Control) if (allocated(StC_CmdForce)) StC_CmdForce(1:3,1:p%NumStC_Control) = m%dll_data%StCCmdForce(1:3,1:p%NumStC_Control) + if (allocated(StC_CmdMoment)) StC_CmdMoment(1:3,1:p%NumStC_Control)= m%dll_data%StCCmdMoment(1:3,1:p%NumStC_Control) end if END SUBROUTINE StCControl_CalcOutput @@ -6661,13 +6678,14 @@ subroutine GetMeas(iNum,CChan,y) ! Assemble info about who requested which ch end subroutine GetMeas end subroutine StC_SetDLLinputs -subroutine StC_SetInitDLLinputs(p,m,InitStiff,InitDamp,InitBrake,InitForce,ErrStat,ErrMsg) +subroutine StC_SetInitDLLinputs(p,m,InitStiff,InitDamp,InitBrake,InitForce,InitMoment,ErrStat,ErrMsg) type(SrvD_ParameterType), intent(in ) :: p !< Parameters type(SrvD_MiscVarType), intent(inout) :: m !< Misc (optimization) variables real(SiKi), allocatable, intent(inout) :: InitStiff(:,:) !< initial stiffness -- from input file normally output of DLL (3,p%NumStC_Control) real(SiKi), allocatable, intent(inout) :: InitDamp(:,:) !< Initial damping -- from input file normally output of DLL (3,p%NumStC_Control) real(SiKi), allocatable, intent(inout) :: InitBrake(:,:) !< Initial brake -- from input file (?) normally output of DLL (3,p%NumStC_Control) - real(SiKi), allocatable, intent(inout) :: InitForce(:,:) !< Initial brake -- from input file (?) normally output of DLL (3,p%NumStC_Control) + real(SiKi), allocatable, intent(inout) :: InitForce(:,:) !< Initial force -- from input file (?) normally output of DLL (3,p%NumStC_Control) + real(SiKi), allocatable, intent(inout) :: InitMoment(:,:) !< Initial moment -- from input file (?) normally output of DLL (3,p%NumStC_Control) integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None @@ -6684,7 +6702,7 @@ subroutine StC_SetInitDLLinputs(p,m,InitStiff,InitDamp,InitBrake,InitForce,ErrSt ! Only proceed if we have have StC controls with the extended swap if ((p%NumStC_Control <= 0) .or. (.not. p%EXavrSWAP)) return - if ((.not. allocated(InitStiff)) .or. (.not. allocated(InitDamp)) .or. (.not. allocated(InitBrake)) .or. (.not. allocated(InitForce))) then + if ((.not. allocated(InitStiff)) .or. (.not. allocated(InitDamp)) .or. (.not. allocated(InitBrake)) .or. (.not. allocated(InitForce)) .or. (.not. allocated(InitMoment))) then ErrStat2 = ErrID_Fatal ErrMsg2 = "StC control signal matrices not allocated. Programming error somewhere." if (Failed()) return @@ -6728,6 +6746,7 @@ subroutine StC_SetInitDLLinputs(p,m,InitStiff,InitDamp,InitBrake,InitForce,ErrSt endif enddo InitForce = 0.0_ReKi + InitMoment = 0.0_ReKi contains subroutine ResizeStCinput(iNum,u) ! Assemble info about who requested which channel @@ -6735,8 +6754,8 @@ subroutine ResizeStCinput(iNum,u) ! Assemble info about who requested which c type(StC_InputType), intent(inout) :: u ! inputs from the StC instance -- will contain allocated Cmd input values if used type(StC_InputType) :: u_tmp ! copy of u -- for resizing as needed integer(IntKi) :: i_local - if (allocated(u%CmdStiff) .and. allocated(u%CmdDamp) .and. allocated(u%CmdBrake) .and. allocated(u%CmdForce)) then ! either all or none will be allocated - if (p%NumStC_Control > min(size(u%CmdStiff,2),size(u%CmdDamp,2),size(u%CmdBrake,2),size(u%CmdForce,2))) then + if (allocated(u%CmdStiff) .and. allocated(u%CmdDamp) .and. allocated(u%CmdBrake) .and. allocated(u%CmdForce) .and. allocated(u%CmdMoment)) then ! either all or none will be allocated + if (p%NumStC_Control > min(size(u%CmdStiff,2),size(u%CmdDamp,2),size(u%CmdBrake,2),size(u%CmdForce,2),size(u%CmdMoment,2))) then call StC_CopyInput(u,u_tmp,MESH_NEWCOPY,ErrStat2,ErrMsg2); if (Failed()) return; if (allocated(u%CmdStiff)) deallocate(u%CmdStiff) @@ -6767,6 +6786,13 @@ subroutine ResizeStCinput(iNum,u) ! Assemble info about who requested which c u%CmdForce(1:3,i_local) = u_tmp%CmdForce(1:3,i_local) enddo + if (allocated(u%CmdMoment)) deallocate(u%CmdMoment) + call AllocAry(u%CmdMoment,3,p%NumStC_Control,"u%CmdMoment",ErrStat2,ErrMsg2); if (Failed()) return; + u%CmdMoment = 0.0_ReKi + do i_local=1,min(p%NumStC_Control,size(u_tmp%CmdMoment,2)) + u%CmdMoment(1:3,i_local) = u_tmp%CmdMoment(1:3,i_local) + enddo + call Cleanup() endif else @@ -6786,6 +6812,10 @@ subroutine ResizeStCinput(iNum,u) ! Assemble info about who requested which c call AllocAry(u%CmdForce,3,p%NumStC_Control,"u%CmdForce",ErrStat2,ErrMsg2); if (Failed()) return; u%CmdForce = 0.0_ReKi endif + if (.not. allocated(u%CmdMoment)) then + call AllocAry(u%CmdMoment,3,p%NumStC_Control,"u%CmdMoment",ErrStat2,ErrMsg2); if (Failed()) return; + u%CmdMoment = 0.0_ReKi + endif endif end subroutine ResizeStCinput subroutine GetMeas(iNum,CChan,u) ! Assemble info about who requested which channel @@ -6798,6 +6828,7 @@ subroutine GetMeas(iNum,CChan,u) ! Assemble info about who requested which ch InitDamp( 1:3,CChan(j)) = InitDamp( 1:3,CChan(j)) + real(u%CmdDamp( 1:3,CChan(j)),SiKi) InitBrake(1:3,CChan(j)) = InitBrake(1:3,CChan(j)) + real(u%CmdBrake(1:3,CChan(j)),SiKi) InitForce(1:3,CChan(j)) = InitForce(1:3,CChan(j)) + real(u%CmdForce(1:3,CChan(j)),SiKi) + InitMoment(1:3,CChan(j))= InitMoment(1:3,CChan(j))+ real(u%CmdMoment(1:3,CChan(j)),SiKi) endif enddo end subroutine GetMeas diff --git a/modules/servodyn/src/ServoDyn_Registry.txt b/modules/servodyn/src/ServoDyn_Registry.txt index e7a6eaa8bb..405e73c47b 100644 --- a/modules/servodyn/src/ServoDyn_Registry.txt +++ b/modules/servodyn/src/ServoDyn_Registry.txt @@ -262,12 +262,14 @@ typedef ^ BladedDLLType SiKi CableDeltaL {:} - - "The swap array: used to pass typedef ^ BladedDLLType SiKi CableDeltaLdot {:} - - "The swap array: used to pass data from the DLL controller for cable tensioning DeltaLdot using extended avrSWAP [see EXavrSWAP documentation in BladededInterface_EX]" m/s typedef ^ BladedDLLType SiKi PrevStCCmdStiff {:}{:} - - "Previous value for ramping StC stiffness from controller (3,NumStC_Control)" "N/m" typedef ^ BladedDLLType SiKi PrevStCCmdDamp {:}{:} - - "Previous value for ramping StC damping from controller (3,NumStC_Control)" "N/(m/s)" -typedef ^ BladedDLLType SiKi PrevStCCmdBrake {:}{:} - - "Previous value for ramping StC braking signal (3,NumStC_Control)" "N/(m/s)" -typedef ^ BladedDLLType SiKi PrevStCCmdForce {:}{:} - - "Previous value for ramping StC force signal (3,NumStC_Control)" "N/(m/s)" +typedef ^ BladedDLLType SiKi PrevStCCmdBrake {:}{:} - - "Previous value for ramping StC braking signal (3,NumStC_Control)" "N" +typedef ^ BladedDLLType SiKi PrevStCCmdForce {:}{:} - - "Previous value for ramping StC force signal (3,NumStC_Control)" "N" +typedef ^ BladedDLLType SiKi PrevStCCmdMoment {:}{:} - - "Previous value for ramping StC moment signal (3,NumStC_Control)" "N-m" typedef ^ BladedDLLType SiKi StCCmdStiff {:}{:} - - "StC stiffness from controller (3,NumStC_Control)" "N/m" typedef ^ BladedDLLType SiKi StCCmdDamp {:}{:} - - "StC damping from controller (3,NumStC_Control)" "N/(m/s)" typedef ^ BladedDLLType SiKi StCCmdBrake {:}{:} - - "StC braking signal (3,NumStC_Control)" "N" typedef ^ BladedDLLType SiKi StCCmdForce {:}{:} - - "StC commanded force signal (3,NumStC_Control)" "N" +typedef ^ BladedDLLType SiKi StCCmdMoment {:}{:} - - "StC commanded moment signal (3,NumStC_Control)" "N-m" typedef ^ BladedDLLType SiKi StCMeasDisp {:}{:} - - "StC measured local displacement signal from StC (3,NumStC_Control)" "m" typedef ^ BladedDLLType SiKi StCMeasVel {:}{:} - - "StC measured local velocity signal from StC (3,NumStC_Control)" "m/s" diff --git a/modules/servodyn/src/ServoDyn_Types.f90 b/modules/servodyn/src/ServoDyn_Types.f90 index 7985794050..997e5be481 100644 --- a/modules/servodyn/src/ServoDyn_Types.f90 +++ b/modules/servodyn/src/ServoDyn_Types.f90 @@ -273,12 +273,14 @@ MODULE ServoDyn_Types REAL(SiKi) , DIMENSION(:), ALLOCATABLE :: CableDeltaLdot !< The swap array: used to pass data from the DLL controller for cable tensioning DeltaLdot using extended avrSWAP [see EXavrSWAP documentation in BladededInterface_EX] [m/s] REAL(SiKi) , DIMENSION(:,:), ALLOCATABLE :: PrevStCCmdStiff !< Previous value for ramping StC stiffness from controller (3,NumStC_Control) [N/m] REAL(SiKi) , DIMENSION(:,:), ALLOCATABLE :: PrevStCCmdDamp !< Previous value for ramping StC damping from controller (3,NumStC_Control) [N/(m/s)] - REAL(SiKi) , DIMENSION(:,:), ALLOCATABLE :: PrevStCCmdBrake !< Previous value for ramping StC braking signal (3,NumStC_Control) [N/(m/s)] - REAL(SiKi) , DIMENSION(:,:), ALLOCATABLE :: PrevStCCmdForce !< Previous value for ramping StC force signal (3,NumStC_Control) [N/(m/s)] + REAL(SiKi) , DIMENSION(:,:), ALLOCATABLE :: PrevStCCmdBrake !< Previous value for ramping StC braking signal (3,NumStC_Control) [N] + REAL(SiKi) , DIMENSION(:,:), ALLOCATABLE :: PrevStCCmdForce !< Previous value for ramping StC force signal (3,NumStC_Control) [N] + REAL(SiKi) , DIMENSION(:,:), ALLOCATABLE :: PrevStCCmdMoment !< Previous value for ramping StC moment signal (3,NumStC_Control) [N-m] REAL(SiKi) , DIMENSION(:,:), ALLOCATABLE :: StCCmdStiff !< StC stiffness from controller (3,NumStC_Control) [N/m] REAL(SiKi) , DIMENSION(:,:), ALLOCATABLE :: StCCmdDamp !< StC damping from controller (3,NumStC_Control) [N/(m/s)] REAL(SiKi) , DIMENSION(:,:), ALLOCATABLE :: StCCmdBrake !< StC braking signal (3,NumStC_Control) [N] REAL(SiKi) , DIMENSION(:,:), ALLOCATABLE :: StCCmdForce !< StC commanded force signal (3,NumStC_Control) [N] + REAL(SiKi) , DIMENSION(:,:), ALLOCATABLE :: StCCmdMoment !< StC commanded moment signal (3,NumStC_Control) [N-m] REAL(SiKi) , DIMENSION(:,:), ALLOCATABLE :: StCMeasDisp !< StC measured local displacement signal from StC (3,NumStC_Control) [m] REAL(SiKi) , DIMENSION(:,:), ALLOCATABLE :: StCMeasVel !< StC measured local velocity signal from StC (3,NumStC_Control) [m/s] END TYPE BladedDLLType @@ -1834,6 +1836,18 @@ subroutine SrvD_CopyBladedDLLType(SrcBladedDLLTypeData, DstBladedDLLTypeData, Ct end if DstBladedDLLTypeData%PrevStCCmdForce = SrcBladedDLLTypeData%PrevStCCmdForce end if + if (allocated(SrcBladedDLLTypeData%PrevStCCmdMoment)) then + LB(1:2) = lbound(SrcBladedDLLTypeData%PrevStCCmdMoment) + UB(1:2) = ubound(SrcBladedDLLTypeData%PrevStCCmdMoment) + if (.not. allocated(DstBladedDLLTypeData%PrevStCCmdMoment)) then + allocate(DstBladedDLLTypeData%PrevStCCmdMoment(LB(1):UB(1),LB(2):UB(2)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstBladedDLLTypeData%PrevStCCmdMoment.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstBladedDLLTypeData%PrevStCCmdMoment = SrcBladedDLLTypeData%PrevStCCmdMoment + end if if (allocated(SrcBladedDLLTypeData%StCCmdStiff)) then LB(1:2) = lbound(SrcBladedDLLTypeData%StCCmdStiff) UB(1:2) = ubound(SrcBladedDLLTypeData%StCCmdStiff) @@ -1882,6 +1896,18 @@ subroutine SrvD_CopyBladedDLLType(SrcBladedDLLTypeData, DstBladedDLLTypeData, Ct end if DstBladedDLLTypeData%StCCmdForce = SrcBladedDLLTypeData%StCCmdForce end if + if (allocated(SrcBladedDLLTypeData%StCCmdMoment)) then + LB(1:2) = lbound(SrcBladedDLLTypeData%StCCmdMoment) + UB(1:2) = ubound(SrcBladedDLLTypeData%StCCmdMoment) + if (.not. allocated(DstBladedDLLTypeData%StCCmdMoment)) then + allocate(DstBladedDLLTypeData%StCCmdMoment(LB(1):UB(1),LB(2):UB(2)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstBladedDLLTypeData%StCCmdMoment.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstBladedDLLTypeData%StCCmdMoment = SrcBladedDLLTypeData%StCCmdMoment + end if if (allocated(SrcBladedDLLTypeData%StCMeasDisp)) then LB(1:2) = lbound(SrcBladedDLLTypeData%StCMeasDisp) UB(1:2) = ubound(SrcBladedDLLTypeData%StCMeasDisp) @@ -1979,6 +2005,9 @@ subroutine SrvD_DestroyBladedDLLType(BladedDLLTypeData, ErrStat, ErrMsg) if (allocated(BladedDLLTypeData%PrevStCCmdForce)) then deallocate(BladedDLLTypeData%PrevStCCmdForce) end if + if (allocated(BladedDLLTypeData%PrevStCCmdMoment)) then + deallocate(BladedDLLTypeData%PrevStCCmdMoment) + end if if (allocated(BladedDLLTypeData%StCCmdStiff)) then deallocate(BladedDLLTypeData%StCCmdStiff) end if @@ -1991,6 +2020,9 @@ subroutine SrvD_DestroyBladedDLLType(BladedDLLTypeData, ErrStat, ErrMsg) if (allocated(BladedDLLTypeData%StCCmdForce)) then deallocate(BladedDLLTypeData%StCCmdForce) end if + if (allocated(BladedDLLTypeData%StCCmdMoment)) then + deallocate(BladedDLLTypeData%StCCmdMoment) + end if if (allocated(BladedDLLTypeData%StCMeasDisp)) then deallocate(BladedDLLTypeData%StCMeasDisp) end if @@ -2100,10 +2132,12 @@ subroutine SrvD_PackBladedDLLType(RF, Indata) call RegPackAlloc(RF, InData%PrevStCCmdDamp) call RegPackAlloc(RF, InData%PrevStCCmdBrake) call RegPackAlloc(RF, InData%PrevStCCmdForce) + call RegPackAlloc(RF, InData%PrevStCCmdMoment) call RegPackAlloc(RF, InData%StCCmdStiff) call RegPackAlloc(RF, InData%StCCmdDamp) call RegPackAlloc(RF, InData%StCCmdBrake) call RegPackAlloc(RF, InData%StCCmdForce) + call RegPackAlloc(RF, InData%StCCmdMoment) call RegPackAlloc(RF, InData%StCMeasDisp) call RegPackAlloc(RF, InData%StCMeasVel) if (RegCheckErr(RF, RoutineName)) return @@ -2216,10 +2250,12 @@ subroutine SrvD_UnPackBladedDLLType(RF, OutData) call RegUnpackAlloc(RF, OutData%PrevStCCmdDamp); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%PrevStCCmdBrake); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%PrevStCCmdForce); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%PrevStCCmdMoment); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%StCCmdStiff); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%StCCmdDamp); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%StCCmdBrake); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%StCCmdForce); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%StCCmdMoment); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%StCMeasDisp); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%StCMeasVel); if (RegCheckErr(RF, RoutineName)) return end subroutine diff --git a/modules/servodyn/src/StrucCtrl.f90 b/modules/servodyn/src/StrucCtrl.f90 index 0121622dc2..2daabff9aa 100644 --- a/modules/servodyn/src/StrucCtrl.f90 +++ b/modules/servodyn/src/StrucCtrl.f90 @@ -347,6 +347,8 @@ SUBROUTINE StC_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu if (Failed()) return; call AllocAry( u%CmdForce, 3, maxval(p%StC_CChan), 'u%CmdForce', ErrStat2, ErrMsg2 ) if (Failed()) return; + call AllocAry( u%CmdMoment,3, maxval(p%StC_CChan), 'u%CmdMoment', ErrStat2, ErrMsg2 ) + if (Failed()) return; call AllocAry( y%MeasDisp, 3, maxval(p%StC_CChan), 'y%MeasDisp', ErrStat2, ErrMsg2 ) if (Failed()) return; call AllocAry( y%MeasVel, 3, maxval(p%StC_CChan), 'y%MeasVel', ErrStat2, ErrMsg2 ) @@ -356,6 +358,7 @@ SUBROUTINE StC_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu u%CmdDamp = 0.0_ReKi u%CmdBrake = 0.0_ReKi u%CmdForce = 0.0_ReKi + u%CmdMoment = 0.0_ReKi y%MeasDisp = 0.0_ReKi y%MeasVel = 0.0_ReKi ! Check that dimensions of x are what we expect @@ -369,7 +372,7 @@ SUBROUTINE StC_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOu if (p%StC_CChan(i) > 0) then u%CmdStiff(1:3,p%StC_CChan(i)) = (/ p%K_X, p%K_Y, p%K_Z /) u%CmdDamp( 1:3,p%StC_CChan(i)) = (/ p%C_X, p%C_Y, p%C_Z /) - !u%CmdBrake and u%CmdForce--- leave these at zero for now (no input file method to set it) + !u%CmdBrake, u%CmdForce and u%CmdMoment -- leave these at zero for now (no input file method to set it) ! The states are sized by (6,NumMeshPts). NumMeshPts is then used to set ! size of StC_CChan as well. For safety, we will check it here. y%MeasDisp(1:3,p%StC_CChan(i)) = (/ x%StC_x(1,i), x%StC_x(3,i), x%StC_x(5,i) /) @@ -407,6 +410,7 @@ subroutine Init_Misc( p, m, ErrStat, ErrMsg ) ! they have been moved into MiscVars so that we don so we don't reallocate all the time. call AllocAry(m%F_stop , 3, p%NumMeshPts, 'F_stop' , ErrStat, ErrMsg); if (ErrStat >= AbortErrLev) return; m%F_stop = 0.0_ReKi call AllocAry(m%F_ext , 3, p%NumMeshPts, 'F_ext' , ErrStat, ErrMsg); if (ErrStat >= AbortErrLev) return; m%F_ext = 0.0_ReKi + call AllocAry(m%M_ext , 3, p%NumMeshPts, 'M_ext' , ErrStat, ErrMsg); if (ErrStat >= AbortErrLev) return; m%M_ext = 0.0_ReKi call AllocAry(m%F_fr , 3, p%NumMeshPts, 'F_fr' , ErrStat, ErrMsg); if (ErrStat >= AbortErrLev) return; m%F_fr = 0.0_ReKi call AllocAry(m%C_ctrl , 3, p%NumMeshPts, 'C_ctrl' , ErrStat, ErrMsg); if (ErrStat >= AbortErrLev) return; m%C_ctrl = 0.0_ReKi call AllocAry(m%C_Brake, 3, p%NumMeshPts, 'C_Brake', ErrStat, ErrMsg); if (ErrStat >= AbortErrLev) return; m%C_Brake = 0.0_ReKi @@ -997,20 +1001,11 @@ SUBROUTINE StC_CalcOutput( Time, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrM ELSEIF ( p%StC_DOF_MODE == DOFMode_ForceDLL ) THEN ! Note that the prescribed force is applied the same to all Mesh pts ! that are passed into this instance of the StC - if (p%PrescribedForcesCoordSys == PRESCRIBED_FORCE_GLOBAL) then - ! Global coords - do i_pt=1,p%NumMeshPts - y%Mesh(i_pt)%Force(1:3,1) = m%F_ext(1:3,i_pt) - y%Mesh(i_pt)%Moment(1:3,1) = 0 - enddo - ! Leave in for now just in case we decide there is a use case for a follower force from the DLL - ! elseif (p%PrescribedForcesCoordSys == PRESCRIBED_FORCE_LOCAL) then - ! ! local coords - ! do i_pt=1,p%NumMeshPts - ! y%Mesh(i_pt)%Force(1:3,1) = matmul(transpose(u%Mesh(i_pt)%Orientation(:,:,1)), m%F_P(1:3,i_pt)) - ! y%Mesh(i_pt)%Moment(1:3,1) = matmul(transpose(u%Mesh(i_pt)%Orientation(:,:,1)), m%M_P(1:3,i_pt)) - ! enddo - endif + ! Global coords only + do i_pt=1,p%NumMeshPts + y%Mesh(i_pt)%Force(1:3,1) = m%F_ext(1:3,i_pt) + y%Mesh(i_pt)%Moment(1:3,1) = m%M_ext(1:3,i_pt) + enddo END IF ! Set output values for the measured displacements for @@ -1196,7 +1191,7 @@ SUBROUTINE StC_CalcContStateDeriv( Time, u, p, x, xd, z, OtherState, m, dxdt, Er ELSE IF (p%StC_CMODE == CMODE_Semi) THEN ! ground hook control CALL StC_GroundHookDamp(dxdt,x,u,p,m%rdisp_P,m%rdot_P,m%C_ctrl,m%C_Brake,m%F_fr) ELSE IF (p%StC_CMODE == CMODE_ActiveDLL) THEN ! Active control from DLL - call StC_ActiveCtrl_StiffDamp(u,p,m%K,m%C_ctrl,m%C_Brake,m%F_ext) + call StC_ActiveCtrl_StiffDamp(u,p,m%K,m%C_ctrl,m%C_Brake,m%F_ext,m%M_ext) m%F_fr = 0.0_ReKi if (.not. p%Use_F_TBL) then K(1:3,:) = m%K(1:3,:) @@ -1676,13 +1671,14 @@ SUBROUTINE StC_GroundHookDamp(dxdt,x,u,p,rdisp_P,rdot_P,C_ctrl,C_Brake,F_fr) enddo END SUBROUTINE StC_GroundHookDamp !---------------------------------------------------------------------------------------------------------------------------------- -SUBROUTINE StC_ActiveCtrl_StiffDamp(u,p,K_ctrl,C_ctrl,C_Brake,F_ctrl) +SUBROUTINE StC_ActiveCtrl_StiffDamp(u,p,K_ctrl,C_ctrl,C_Brake,F_ctrl,M_ctrl) TYPE(StC_InputType), INTENT(IN ) :: u !< Inputs at Time TYPE(StC_ParameterType), INTENT(IN ) :: p !< The module's parameter data real(ReKi), intent(inout) :: K_ctrl(:,:) !< stiffness commanded by dll -- leave alone if no ctrl real(ReKi), intent(inout) :: C_ctrl(:,:) !< damping commanded by dll real(ReKi), intent(inout) :: C_Brake(:,:) !< brake commanded by dll - real(ReKi), intent(inout) :: F_ctrl(:,:) !< brake commanded by dll + real(ReKi), intent(inout) :: F_ctrl(:,:) !< force commanded by dll + real(ReKi), intent(inout) :: M_ctrl(:,:) !< moment commanded by dll integer(IntKi) :: i_pt ! counter for mesh points do i_pt=1,p%NumMeshPts if (p%StC_CChan(i_pt) > 0) then ! This index should have been checked at init, so won't check bounds here @@ -1690,12 +1686,14 @@ SUBROUTINE StC_ActiveCtrl_StiffDamp(u,p,K_ctrl,C_ctrl,C_Brake,F_ctrl) C_ctrl( 1:3,i_pt) = u%CmdDamp( 1:3,p%StC_CChan(i_pt)) C_Brake(1:3,i_pt) = u%CmdBrake(1:3,p%StC_CChan(i_pt)) F_ctrl(1:3,i_pt) = u%CmdForce(1:3,p%StC_CChan(i_pt)) + M_ctrl(1:3,i_pt) = u%CmdMoment(1:3,p%StC_CChan(i_pt)) else ! Use parameters from file (as if no control) -- leave K value as that may be set by table prior C_ctrl(1,:) = p%C_X C_ctrl(2,:) = p%C_Y C_ctrl(3,:) = p%C_Z C_Brake = 0.0_ReKi F_ctrl = 0.0_ReKi + M_ctrl = 0.0_ReKi endif enddo END SUBROUTINE StC_ActiveCtrl_StiffDamp @@ -2230,6 +2228,9 @@ subroutine StC_ValidatePrimaryData( InputFileData, InitInp, ErrStat, ErrMsg ) if ( InputFileData%StC_CChan(i) < 0 .or. InputFileData%StC_CChan(i) > 10 ) then call SetErrStat( ErrID_Fatal, 'Control channel (StC_CChan) must be between 0 (off) and 10 when StC_CMode=5.', ErrStat, ErrMsg, RoutineName ) endif + if ( InputFileData%StC_CChan(i) == 0 ) then + call SetErrStat( ErrID_Warn, 'Control mode 5 (active with DLL control) requested, but no control channel specified. No external force will be applied.', ErrStat, ErrMsg, RoutineName ) + endif enddo endif diff --git a/modules/servodyn/src/StrucCtrl_Registry.txt b/modules/servodyn/src/StrucCtrl_Registry.txt index 9a48c94ae9..d6d9fab594 100644 --- a/modules/servodyn/src/StrucCtrl_Registry.txt +++ b/modules/servodyn/src/StrucCtrl_Registry.txt @@ -105,6 +105,7 @@ typedef ^ StC_CtrlChanInitInfoType SiKi InitStiff {:}{:} - - "StC stiffness at typedef ^ StC_CtrlChanInitInfoType SiKi InitDamp {:}{:} - - "StC damping at initialization (3,NumStC_Control) -- passed from StC to let controller know the value during init" "N/(m/s)" typedef ^ StC_CtrlChanInitInfoType SiKi InitBrake {:}{:} - - "StC braking signal at initialization (3,NumStC_Control) -- passed from StC to let controller know the value during init" "N" typedef ^ StC_CtrlChanInitInfoType SiKi InitForce {:}{:} - - "StC external force signal at initialization (3,NumStC_Control) -- passed from StC to let controller know the value during init (should be zero)" "N" +typedef ^ StC_CtrlChanInitInfoType SiKi InitMoment {:}{:} - - "StC external moment signal at initialization (3,NumStC_Control) -- passed from StC to let controller know the value during init (should be zero)" "N-m" typedef ^ StC_CtrlChanInitInfoType SiKi InitMeasDisp {:}{:} - - "StC measured local displacement signal from StC at initialization (3,NumStC_Control)" "m" typedef ^ StC_CtrlChanInitInfoType SiKi InitMeasVel {:}{:} - - "StC measured local velocity signal from StC at initialization (3,NumStC_Control)" "m/s" @@ -126,6 +127,7 @@ typedef ^ OtherStateType Reki DummyOtherState - - - "Remove this variable if # at a given time, etc.) or other data that do not depend on time typedef ^ MiscVarType Reki F_stop {:}{:} - - "Stop forces" - typedef ^ MiscVarType ReKi F_ext {:}{:} - - "External forces (user defined or from controller)" - +typedef ^ MiscVarType ReKi M_ext {:}{:} - - "External moments (user defined or from controller)" - typedef ^ MiscVarType ReKi F_fr {:}{:} - - "Friction forces" - typedef ^ MiscVarType ReKi K {:}{:} - - "Stiffness -- might be changed if controller controls this" N/m typedef ^ MiscVarType ReKi C_ctrl {:}{:} - - "Controlled Damping (On/Off)" - @@ -205,6 +207,7 @@ typedef ^ ^ ReKi CmdStiff {:}{:} - - "StC stiffness from controller" "N/m" typedef ^ ^ ReKi CmdDamp {:}{:} - - "StC damping from controller" "N/(m/s)" typedef ^ ^ ReKi CmdBrake {:}{:} - - "StC braking from controller" "N/(m/s)" typedef ^ ^ ReKi CmdForce {:}{:} - - "StC force from controller" "N" +typedef ^ ^ ReKi CmdMoment {:}{:} - - "StC moment from controller" "N-m" # ..... Outputs ................................................................................................................... # Define outputs that are contained on the mesh here: typedef ^ OutputType MeshType Mesh {:} - - "Loads at the StC reference points in the inertial frame" - diff --git a/modules/servodyn/src/StrucCtrl_Types.f90 b/modules/servodyn/src/StrucCtrl_Types.f90 index 956d664327..649ef9183b 100644 --- a/modules/servodyn/src/StrucCtrl_Types.f90 +++ b/modules/servodyn/src/StrucCtrl_Types.f90 @@ -131,6 +131,7 @@ MODULE StrucCtrl_Types REAL(SiKi) , DIMENSION(:,:), ALLOCATABLE :: InitDamp !< StC damping at initialization (3,NumStC_Control) -- passed from StC to let controller know the value during init [N/(m/s)] REAL(SiKi) , DIMENSION(:,:), ALLOCATABLE :: InitBrake !< StC braking signal at initialization (3,NumStC_Control) -- passed from StC to let controller know the value during init [N] REAL(SiKi) , DIMENSION(:,:), ALLOCATABLE :: InitForce !< StC external force signal at initialization (3,NumStC_Control) -- passed from StC to let controller know the value during init (should be zero) [N] + REAL(SiKi) , DIMENSION(:,:), ALLOCATABLE :: InitMoment !< StC external moment signal at initialization (3,NumStC_Control) -- passed from StC to let controller know the value during init (should be zero) [N-m] REAL(SiKi) , DIMENSION(:,:), ALLOCATABLE :: InitMeasDisp !< StC measured local displacement signal from StC at initialization (3,NumStC_Control) [m] REAL(SiKi) , DIMENSION(:,:), ALLOCATABLE :: InitMeasVel !< StC measured local velocity signal from StC at initialization (3,NumStC_Control) [m/s] END TYPE StC_CtrlChanInitInfoType @@ -164,6 +165,7 @@ MODULE StrucCtrl_Types TYPE, PUBLIC :: StC_MiscVarType REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: F_stop !< Stop forces [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: F_ext !< External forces (user defined or from controller) [-] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: M_ext !< External moments (user defined or from controller) [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: F_fr !< Friction forces [-] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: K !< Stiffness -- might be changed if controller controls this [N/m] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: C_ctrl !< Controlled Damping (On/Off) [-] @@ -244,6 +246,7 @@ MODULE StrucCtrl_Types REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: CmdDamp !< StC damping from controller [N/(m/s)] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: CmdBrake !< StC braking from controller [N/(m/s)] REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: CmdForce !< StC force from controller [N] + REAL(ReKi) , DIMENSION(:,:), ALLOCATABLE :: CmdMoment !< StC moment from controller [N-m] END TYPE StC_InputType ! ======================= ! ========= StC_OutputType ======= @@ -259,9 +262,10 @@ MODULE StrucCtrl_Types integer(IntKi), public, parameter :: StC_u_CmdDamp = 4 ! StC%CmdDamp integer(IntKi), public, parameter :: StC_u_CmdBrake = 5 ! StC%CmdBrake integer(IntKi), public, parameter :: StC_u_CmdForce = 6 ! StC%CmdForce - integer(IntKi), public, parameter :: StC_y_Mesh = 7 ! StC%Mesh(DL%i1) - integer(IntKi), public, parameter :: StC_y_MeasDisp = 8 ! StC%MeasDisp - integer(IntKi), public, parameter :: StC_y_MeasVel = 9 ! StC%MeasVel + integer(IntKi), public, parameter :: StC_u_CmdMoment = 7 ! StC%CmdMoment + integer(IntKi), public, parameter :: StC_y_Mesh = 8 ! StC%Mesh(DL%i1) + integer(IntKi), public, parameter :: StC_y_MeasDisp = 9 ! StC%MeasDisp + integer(IntKi), public, parameter :: StC_y_MeasVel = 10 ! StC%MeasVel contains @@ -869,6 +873,18 @@ subroutine StC_CopyCtrlChanInitInfoType(SrcCtrlChanInitInfoTypeData, DstCtrlChan end if DstCtrlChanInitInfoTypeData%InitForce = SrcCtrlChanInitInfoTypeData%InitForce end if + if (allocated(SrcCtrlChanInitInfoTypeData%InitMoment)) then + LB(1:2) = lbound(SrcCtrlChanInitInfoTypeData%InitMoment) + UB(1:2) = ubound(SrcCtrlChanInitInfoTypeData%InitMoment) + if (.not. allocated(DstCtrlChanInitInfoTypeData%InitMoment)) then + allocate(DstCtrlChanInitInfoTypeData%InitMoment(LB(1):UB(1),LB(2):UB(2)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstCtrlChanInitInfoTypeData%InitMoment.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstCtrlChanInitInfoTypeData%InitMoment = SrcCtrlChanInitInfoTypeData%InitMoment + end if if (allocated(SrcCtrlChanInitInfoTypeData%InitMeasDisp)) then LB(1:2) = lbound(SrcCtrlChanInitInfoTypeData%InitMeasDisp) UB(1:2) = ubound(SrcCtrlChanInitInfoTypeData%InitMeasDisp) @@ -917,6 +933,9 @@ subroutine StC_DestroyCtrlChanInitInfoType(CtrlChanInitInfoTypeData, ErrStat, Er if (allocated(CtrlChanInitInfoTypeData%InitForce)) then deallocate(CtrlChanInitInfoTypeData%InitForce) end if + if (allocated(CtrlChanInitInfoTypeData%InitMoment)) then + deallocate(CtrlChanInitInfoTypeData%InitMoment) + end if if (allocated(CtrlChanInitInfoTypeData%InitMeasDisp)) then deallocate(CtrlChanInitInfoTypeData%InitMeasDisp) end if @@ -935,6 +954,7 @@ subroutine StC_PackCtrlChanInitInfoType(RF, Indata) call RegPackAlloc(RF, InData%InitDamp) call RegPackAlloc(RF, InData%InitBrake) call RegPackAlloc(RF, InData%InitForce) + call RegPackAlloc(RF, InData%InitMoment) call RegPackAlloc(RF, InData%InitMeasDisp) call RegPackAlloc(RF, InData%InitMeasVel) if (RegCheckErr(RF, RoutineName)) return @@ -953,6 +973,7 @@ subroutine StC_UnPackCtrlChanInitInfoType(RF, OutData) call RegUnpackAlloc(RF, OutData%InitDamp); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%InitBrake); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%InitForce); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%InitMoment); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%InitMeasDisp); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%InitMeasVel); if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -1220,6 +1241,18 @@ subroutine StC_CopyMisc(SrcMiscData, DstMiscData, CtrlCode, ErrStat, ErrMsg) end if DstMiscData%F_ext = SrcMiscData%F_ext end if + if (allocated(SrcMiscData%M_ext)) then + LB(1:2) = lbound(SrcMiscData%M_ext) + UB(1:2) = ubound(SrcMiscData%M_ext) + if (.not. allocated(DstMiscData%M_ext)) then + allocate(DstMiscData%M_ext(LB(1):UB(1),LB(2):UB(2)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstMiscData%M_ext.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstMiscData%M_ext = SrcMiscData%M_ext + end if if (allocated(SrcMiscData%F_fr)) then LB(1:2) = lbound(SrcMiscData%F_fr) UB(1:2) = ubound(SrcMiscData%F_fr) @@ -1416,6 +1449,9 @@ subroutine StC_DestroyMisc(MiscData, ErrStat, ErrMsg) if (allocated(MiscData%F_ext)) then deallocate(MiscData%F_ext) end if + if (allocated(MiscData%M_ext)) then + deallocate(MiscData%M_ext) + end if if (allocated(MiscData%F_fr)) then deallocate(MiscData%F_fr) end if @@ -1470,6 +1506,7 @@ subroutine StC_PackMisc(RF, Indata) if (RF%ErrStat >= AbortErrLev) return call RegPackAlloc(RF, InData%F_stop) call RegPackAlloc(RF, InData%F_ext) + call RegPackAlloc(RF, InData%M_ext) call RegPackAlloc(RF, InData%F_fr) call RegPackAlloc(RF, InData%K) call RegPackAlloc(RF, InData%C_ctrl) @@ -1499,6 +1536,7 @@ subroutine StC_UnPackMisc(RF, OutData) if (RF%ErrStat /= ErrID_None) return call RegUnpackAlloc(RF, OutData%F_stop); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%F_ext); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%M_ext); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%F_fr); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%K); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%C_ctrl); if (RegCheckErr(RF, RoutineName)) return @@ -1868,6 +1906,18 @@ subroutine StC_CopyInput(SrcInputData, DstInputData, CtrlCode, ErrStat, ErrMsg) end if DstInputData%CmdForce = SrcInputData%CmdForce end if + if (allocated(SrcInputData%CmdMoment)) then + LB(1:2) = lbound(SrcInputData%CmdMoment) + UB(1:2) = ubound(SrcInputData%CmdMoment) + if (.not. allocated(DstInputData%CmdMoment)) then + allocate(DstInputData%CmdMoment(LB(1):UB(1),LB(2):UB(2)), stat=ErrStat2) + if (ErrStat2 /= 0) then + call SetErrStat(ErrID_Fatal, 'Error allocating DstInputData%CmdMoment.', ErrStat, ErrMsg, RoutineName) + return + end if + end if + DstInputData%CmdMoment = SrcInputData%CmdMoment + end if end subroutine subroutine StC_DestroyInput(InputData, ErrStat, ErrMsg) @@ -1902,6 +1952,9 @@ subroutine StC_DestroyInput(InputData, ErrStat, ErrMsg) if (allocated(InputData%CmdForce)) then deallocate(InputData%CmdForce) end if + if (allocated(InputData%CmdMoment)) then + deallocate(InputData%CmdMoment) + end if end subroutine subroutine StC_PackInput(RF, Indata) @@ -1924,6 +1977,7 @@ subroutine StC_PackInput(RF, Indata) call RegPackAlloc(RF, InData%CmdDamp) call RegPackAlloc(RF, InData%CmdBrake) call RegPackAlloc(RF, InData%CmdForce) + call RegPackAlloc(RF, InData%CmdMoment) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -1953,6 +2007,7 @@ subroutine StC_UnPackInput(RF, OutData) call RegUnpackAlloc(RF, OutData%CmdDamp); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%CmdBrake); if (RegCheckErr(RF, RoutineName)) return call RegUnpackAlloc(RF, OutData%CmdForce); if (RegCheckErr(RF, RoutineName)) return + call RegUnpackAlloc(RF, OutData%CmdMoment); if (RegCheckErr(RF, RoutineName)) return end subroutine subroutine StC_CopyOutput(SrcOutputData, DstOutputData, CtrlCode, ErrStat, ErrMsg) @@ -2202,6 +2257,9 @@ SUBROUTINE StC_Input_ExtrapInterp1(u1, u2, tin, u_out, tin_out, ErrStat, ErrMsg IF (ALLOCATED(u_out%CmdForce) .AND. ALLOCATED(u1%CmdForce)) THEN u_out%CmdForce = a1*u1%CmdForce + a2*u2%CmdForce END IF ! check if allocated + IF (ALLOCATED(u_out%CmdMoment) .AND. ALLOCATED(u1%CmdMoment)) THEN + u_out%CmdMoment = a1*u1%CmdMoment + a2*u2%CmdMoment + END IF ! check if allocated END SUBROUTINE SUBROUTINE StC_Input_ExtrapInterp2(u1, u2, u3, tin, u_out, tin_out, ErrStat, ErrMsg ) @@ -2279,6 +2337,9 @@ SUBROUTINE StC_Input_ExtrapInterp2(u1, u2, u3, tin, u_out, tin_out, ErrStat, Err IF (ALLOCATED(u_out%CmdForce) .AND. ALLOCATED(u1%CmdForce)) THEN u_out%CmdForce = a1*u1%CmdForce + a2*u2%CmdForce + a3*u3%CmdForce END IF ! check if allocated + IF (ALLOCATED(u_out%CmdMoment) .AND. ALLOCATED(u1%CmdMoment)) THEN + u_out%CmdMoment = a1*u1%CmdMoment + a2*u2%CmdMoment + a3*u3%CmdMoment + END IF ! check if allocated END SUBROUTINE subroutine StC_Output_ExtrapInterp(y, t, y_out, t_out, ErrStat, ErrMsg) @@ -2594,6 +2655,8 @@ subroutine StC_VarPackInput(V, u, ValAry) VarVals = u%CmdBrake(V%iLB:V%iUB,V%j) ! Rank 2 Array case (StC_u_CmdForce) VarVals = u%CmdForce(V%iLB:V%iUB,V%j) ! Rank 2 Array + case (StC_u_CmdMoment) + VarVals = u%CmdMoment(V%iLB:V%iUB,V%j) ! Rank 2 Array case default VarVals = 0.0_R8Ki end select @@ -2626,6 +2689,8 @@ subroutine StC_VarUnpackInput(V, ValAry, u) u%CmdBrake(V%iLB:V%iUB, V%j) = VarVals ! Rank 2 Array case (StC_u_CmdForce) u%CmdForce(V%iLB:V%iUB, V%j) = VarVals ! Rank 2 Array + case (StC_u_CmdMoment) + u%CmdMoment(V%iLB:V%iUB, V%j) = VarVals ! Rank 2 Array end select end associate end subroutine @@ -2644,6 +2709,8 @@ function StC_InputFieldName(DL) result(Name) Name = "u%CmdBrake" case (StC_u_CmdForce) Name = "u%CmdForce" + case (StC_u_CmdMoment) + Name = "u%CmdMoment" case default Name = "Unknown Field" end select diff --git a/modules/turbsim/src/TSsubs.f90 b/modules/turbsim/src/TSsubs.f90 index f869387b6e..d0bc39fa27 100644 --- a/modules/turbsim/src/TSsubs.f90 +++ b/modules/turbsim/src/TSsubs.f90 @@ -39,20 +39,24 @@ MODULE TSSubs !! real array) of the simulated velocity (wind/water speed). It returns !! values FOR ONLY the velocity components that use the IEC method for !! computing spatial coherence; i.e., for i where SCMod(i) == CohMod_IEC -SUBROUTINE CalcFourierCoeffs_IEC( p, U, PhaseAngles, S, V, TRH, ErrStat, ErrMsg ) +!! +!! OpenMP: This makes a copy of the TRH array for each thread to use, which +!! is a little inefficient, but the speedup from parallelization +!! should outweigh the memory overhead. In the single threaded case, +!! a single copy is made, which is relatively negligible. +SUBROUTINE CalcFourierCoeffs_IEC( p, U, PhaseAngles, S, V, TRH_in, ErrStat, ErrMsg ) TYPE(TurbSim_ParameterType), INTENT(IN ) :: p !< TurbSim parameters REAL(ReKi), INTENT(IN) :: U (:) !< The steady u-component wind speeds for the grid (NPoints). REAL(ReKi), INTENT(IN) :: PhaseAngles (:,:,:) !< The array that holds the random phases [number of points, number of frequencies, number of wind components=3]. REAL(ReKi), INTENT(IN) :: S (:,:,:) !< The turbulence PSD array (NumFreq,NPoints,3). REAL(ReKi), INTENT(INOUT) :: V (:,:,:) !< An array containing the summations of the rows of H (NumSteps,NPoints,3). -REAL(ReKi), INTENT(INOUT) :: TRH (:) !< The transfer function matrix. just used as a work array +REAL(ReKi), INTENT(INOUT) :: TRH_in(:) !< The transfer function matrix. just used as a work array INTEGER(IntKi), INTENT(OUT) :: ErrStat CHARACTER(*), INTENT(OUT) :: ErrMsg - - ! Internal variables - +REAL(ReKi), allocatable, save :: TRH(:) ! Each OMP thread gets its own copy of this array +!$OMP THREADPRIVATE(TRH) REAL(ReKi), ALLOCATABLE :: Dist(:) ! The distance between points REAL(ReKi), ALLOCATABLE :: DistU(:) @@ -64,25 +68,22 @@ SUBROUTINE CalcFourierCoeffs_IEC( p, U, PhaseAngles, S, V, TRH, ErrStat, ErrMsg INTEGER(IntKi) :: ErrStat2 CHARACTER(MaxMsgLen) :: ErrMsg2 - - - + ErrStat = ErrID_None ErrMsg = "" - IF (.NOT. ANY(p%met%SCMod == CohMod_IEC) ) RETURN + IF (.NOT. ANY(p%met%SCMod == CohMod_IEC)) RETURN !-------------------------------------------------------------------------------- ! allocate arrays !-------------------------------------------------------------------------------- - CALL AllocAry( Dist, p%grid%NPacked, 'Dist coherence array', ErrStat2, ErrMsg2 ); CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'CalcFourierCoeffs_IEC') - CALL AllocAry( DistU, p%grid%NPacked, 'DistU coherence array', ErrStat2, ErrMsg2 ); CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'CalcFourierCoeffs_IEC') - IF (ErrStat >= AbortErrLev) THEN - CALL Cleanup() - RETURN - END IF - - + + CALL AllocAry( Dist, p%grid%NPacked, 'Dist coherence array', ErrStat2, ErrMsg2 ); CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'CalcFourierCoeffs_IEC') + CALL AllocAry( DistU, p%grid%NPacked, 'DistU coherence array', ErrStat2, ErrMsg2 ); CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'CalcFourierCoeffs_IEC') + IF (ErrStat >= AbortErrLev) RETURN + + TRH = TRH_in ! point the PRIVATE array to the passed in array for single thread case + !-------------------------------------------------------------------------------- ! Calculate the distances and other parameters that don't change with frequency !--------------------------------------------------------------------------------- @@ -115,7 +116,13 @@ SUBROUTINE CalcFourierCoeffs_IEC( p, U, PhaseAngles, S, V, TRH, ErrStat, ErrMsg ! Calculate the coherence, Veers' H matrix (CSDs), and the fourier coefficients !--------------------------------------------------------------------------------- - DO IFREQ = 1,p%grid%NumFreq + !$OMP PARALLEL DO & + !$OMP DEFAULT(None) & + !$OMP SHARED(p, PhaseAngles, S, V, Dist, DistU, IVec, ErrStat, ErrMsg, AbortErrLev) & + !$OMP PRIVATE(Indx, I, J, ErrStat2, ErrMsg2) & + !$OMP COPYIN(TRH) + DO IFREQ = 1, p%grid%NumFreq + ! ----------------------------------------------- ! Create the coherence matrix for this frequency ! ----------------------------------------------- @@ -149,27 +156,18 @@ SUBROUTINE CalcFourierCoeffs_IEC( p, U, PhaseAngles, S, V, TRH, ErrStat, ErrMsg ! use H matrix to calculate coefficients ! ----------------------------------------------- - CALL Coh2H( p, IVec, IFreq, TRH, S, ErrStat2, ErrMsg2 ) + CALL Coh2H(p, IVec, IFreq, TRH, S, ErrStat2, ErrMsg2) + if (ErrStat2 >= AbortErrLev) then + !$OMP CRITICAL CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, 'CalcFourierCoeffs_IEC') - IF (ErrStat >= AbortErrLev) THEN - CALL Cleanup() - RETURN - END IF - CALL H2Coeffs( IVec, IFreq, TRH, PhaseAngles, V, p%grid%NPoints ) - END DO !IFreq + !$OMP END CRITICAL + else + CALL H2Coeffs( IVec, IFreq, TRH, PhaseAngles, V, p%grid%NPoints ) + endif + END DO !IFreq END DO !IVec - CALL Cleanup() - RETURN - -!............................................ -CONTAINS - SUBROUTINE Cleanup() - - IF ( ALLOCATED( Dist ) ) DEALLOCATE( Dist ) - IF ( ALLOCATED( DistU ) ) DEALLOCATE( DistU ) - END SUBROUTINE Cleanup !............................................ END SUBROUTINE CalcFourierCoeffs_IEC !======================================================================= @@ -720,18 +718,17 @@ SUBROUTINE EyeCoh2H( IVec, IFreq, TRH, S, NPoints ) ! ----------------------------------------------------------------------------------- Indx = 1 + TRH = 0.0 DO J = 1,NPoints ! The column number ! The diagonal entries of the matrix: TRH(Indx) = SQRT( ABS( S(IFreq,J,IVec) ) ) - ! The off-diagonal values: - Indx = Indx + 1 - DO I = J+1,NPoints ! The row number - TRH(Indx) = 0.0 - Indx = Indx + 1 - ENDDO ! I + ! skip rest of row (NPoints-1) -- these are off diagonal elements that are zero. + ! Then add 1 to get to next diagonal entry + Indx = Indx + (NPoints - J) + 1 + ENDDO ! J END SUBROUTINE EyeCoh2H @@ -751,8 +748,9 @@ SUBROUTINE Coh2H( p, IVec, IFreq, TRH, S, ErrStat, ErrMsg ) integer :: Indx, J, I, NPts - +integer :: old_max_levels ! maximum nesting levels for OPENMP + ! ------------------------------------------------------------- ! Calculate the Cholesky factorization for the coherence matrix ! ------------------------------------------------------------- @@ -872,115 +870,81 @@ END SUBROUTINE H2Coeffs !> This routine takes the Fourier coefficients and converts them to velocity !! note that the resulting time series has zero mean. SUBROUTINE Coeffs2TimeSeries( V, NumSteps, NPoints, NUsrPoints, ErrStat, ErrMsg ) - - - !USE NWTC_FFTPACK - - IMPLICIT NONE - - - ! passed variables INTEGER(IntKi), INTENT(IN) :: NumSteps !< Size of dimension 1 of V (number of time steps) INTEGER(IntKi), INTENT(IN) :: NPoints !< Size of dimension 2 of V (number of grid points) INTEGER(IntKi), INTENT(IN) :: NUsrPoints !< number of user-defined time series - REAL(ReKi), INTENT(INOUT) :: V (NumSteps,NPoints,3) !< An array containing the summations of the rows of H (NumSteps,NPoints,3). - INTEGER(IntKi), intent( out) :: ErrStat !< Error level CHARACTER(*), intent( out) :: ErrMsg !< Message describing error - - ! local variables TYPE(FFT_DataType) :: FFT_Data ! data for applying FFT REAL(SiKi), ALLOCATABLE :: Work ( : ) ! working array to hold coefficients of fft !bjj: made it allocatable so it doesn't take stack space - - - INTEGER(IntKi) :: ITime ! loop counter for time step/frequency INTEGER(IntKi) :: IVec ! loop counter for velocity components INTEGER(IntKi) :: IPoint ! loop counter for grid points - + logical :: ExitOMPlooping ! flag to indicate skipping other loops INTEGER(IntKi) :: ErrStat2 ! Error level (local) - !CHARACTER(MaxMsgLen) :: ErrMsg2 ! Message describing error (local) - ! initialize variables - - !ErrStat = ErrID_None - !ErrMsg = "" - CALL AllocAry(Work, NumSteps, 'Work',ErrStat,ErrMsg) if (ErrStat >= AbortErrLev) return - ! Allocate the FFT working storage and initialize its variables - -CALL InitFFT( NumSteps, FFT_Data, ErrStat=ErrStat2 ) - CALL SetErrStat(ErrStat2, 'Error in InitFFT', ErrStat, ErrMsg, 'Coeffs2TimeSeries' ) - IF (ErrStat >= AbortErrLev) THEN - CALL Cleanup() - RETURN - END IF - - ! Get the stationary-point time series. -CALL WrScr ( ' Generating time series for all points:' ) + CALL WrScr ( ' Generating time series for all points:' ) -DO IVec=1,3 + ! Since we are potentially using OpenMP here, we cannot + ExitOMPlooping = .false. - CALL WrScr ( ' '//Comp(IVec)//'-component' ) + DO IVec=1,3 - DO IPoint=1,NPoints !NTotB + ! make sure we didn't have a failure on prior OMP loop + if (ExitOMPlooping) cycle - ! Overwrite the first point with zero. This sets the real (and - ! imaginary) part of the steady-state value to zero so that we - ! can add in the mean value later. + CALL WrScr ( ' '//Comp(IVec)//'-component' ) - Work(1) = 0.0_ReKi + ! The FFT_Data is not thread safe with the allocation inside. + CALL InitFFT( NumSteps, FFT_Data, ErrStat=ErrStat2 ) + CALL SetErrStat(ErrStat2, 'Error in InitFFT', ErrStat, ErrMsg, 'Coeffs2TimeSeries' ) -! DO ITime = 2,NumSteps-1 - DO ITime = 2,NumSteps - Work(ITime) = V(ITime-1, IPoint, IVec) - ENDDO ! ITime - - IF (iPoint > NUsrPoints) THEN - ! BJJ: we can't override this for the user-input spectra or we don't get the correct time series out. - ! Per JMJ, I will keep this here for the other points, but I personally think it could be skipped, too. + ! Proceed only if the InitFFT worked. + ! NOTE: this is to allow for OpenMP - can't return from inside loop + if (ErrStat2 < AbortErrLev) then ! check ErrStat2 for this OMPthread + DO IPoint=1,NPoints !NTotB + ! Overwrite the first point with zero. This sets the real (and + ! imaginary) part of the steady-state value to zero so that we + ! can add in the mean value later. + Work(1) = 0.0_ReKi + Work(2:NumSteps) = V(1:NumSteps-1, IPoint, IVec) - ! Now, let's add a complex zero to the end to set the power in the Nyquist - ! frequency to zero. + IF (iPoint > NUsrPoints) THEN + ! BJJ: we can't override this for the user-input spectra or we don't get the correct time series out. + ! Per JMJ, I will keep this here for the other points, but I personally think it could be skipped, too. + + ! Now, let's add a complex zero to the end to set the power in the Nyquist + ! frequency to zero. + Work(NumSteps) = 0.0 + END IF - Work(NumSteps) = 0.0 - END IF - + ! perform FFT + CALL ApplyFFT( Work, FFT_Data, ErrStat2 ) + IF (ErrStat2 /= ErrID_None ) THEN + CALL SetErrStat(ErrStat2, 'Error in ApplyFFT for point '//TRIM(Num2LStr(IPoint))//'.', ErrStat, ErrMsg, 'Coeffs2TimeSeries' ) + IF (ErrStat >= AbortErrLev) EXIT + END IF + + V(:,IPoint,IVec) = Work + ENDDO ! IPoint + ! Clean up memory from FFT_Data. + CALL ExitFFT( FFT_Data, ErrStat2 ) + CALL SetErrStat(ErrStat2, 'Error in ExitFFT', ErrStat, ErrMsg, 'Coeffs2TimeSeries' ) - ! perform FFT - - CALL ApplyFFT( Work, FFT_Data, ErrStat2 ) - IF (ErrStat2 /= ErrID_None ) THEN - CALL SetErrStat(ErrStat2, 'Error in ApplyFFT for point '//TRIM(Num2LStr(IPoint))//'.', ErrStat, ErrMsg, 'Coeffs2TimeSeries' ) - IF (ErrStat >= AbortErrLev) EXIT - END IF - - V(:,IPoint,IVec) = Work - - ENDDO ! IPoint - -ENDDO ! IVec - -CALL Cleanup() - -RETURN -CONTAINS -!........................................... -SUBROUTINE Cleanup() - - CALL ExitFFT( FFT_Data, ErrStat2 ) - CALL SetErrStat(ErrStat2, 'Error in ExitFFT', ErrStat, ErrMsg, 'Coeffs2TimeSeries' ) + ! skip further OMP loops if any sequential (or if OMP not used). + ! NOTE: OMP doesn't allow return inside OMP thread + if (ErrStat2 >= AbortErrLev) ExitOMPlooping = .true. + endif + ENDDO ! IVec - if (allocated(work)) deallocate(work) - - END SUBROUTINE Cleanup END SUBROUTINE Coeffs2TimeSeries !======================================================================= !> This routine calculates the two-sided Fourier amplitudes of the frequencies @@ -2030,28 +1994,28 @@ SUBROUTINE AddMeanAndRotate(p, V, U, HWindDir, VWindDir) REAL(ReKi) :: v3(3) ! temporary 3-component array containing velocity INTEGER(IntKi) :: ITime ! loop counter for time step INTEGER(IntKi) :: IPoint ! loop counter for grid points - - - - + !.............................................................................. - ! Add mean wind to u' components and rotate to inertial reference + ! Add mean wind to u' components and rotate to inertial reference ! frame coordinate system - !.............................................................................. + !.............................................................................. + + !$OMP PARALLEL DO & + !$OMP COLLAPSE(2) & + !$OMP DEFAULT(None) & + !$OMP PRIVATE(v3) & + !$OMP SHARED(p, U, V, HWindDir, VWindDir) DO IPoint=1,p%grid%Npoints DO ITime=1,p%grid%NumSteps - ! Add mean wind speed to the streamwise component and - ! Rotate the wind to the X-Y-Z (inertial) reference frame coordinates: - + ! Add mean wind speed to the streamwise component and + ! Rotate the wind to the X-Y-Z (inertial) reference frame coordinates: v3 = V(ITime,IPoint,:) CALL CalculateWindComponents( v3, U(IPoint), HWindDir(IPoint), VWindDir(IPoint), V(ITime,IPoint,:) ) ENDDO ! ITime - ENDDO ! IPoint - - + END SUBROUTINE AddMeanAndRotate !======================================================================= SUBROUTINE TS_ValidateInput(P, ErrStat, ErrMsg) diff --git a/modules/version/tests/VersionInfo_test_tools.F90 b/modules/version/tests/VersionInfo_test_tools.F90 index 30e67ae0f7..de6a95e11b 100644 --- a/modules/version/tests/VersionInfo_test_tools.F90 +++ b/modules/version/tests/VersionInfo_test_tools.F90 @@ -12,16 +12,14 @@ module versioninfo_test_tools character(11), parameter :: terminal="/dev/stdout" #endif -integer, parameter :: stdout=CU - contains subroutine hide_terminal_output() - open(unit=stdout, file=trim(nullfile)) + open(unit=CU, file=trim(nullfile)) end subroutine subroutine show_terminal_output() - open(unit=stdout, file=terminal, status="old") + open(unit=CU, file=terminal, status="old") end subroutine end module diff --git a/modules/wakedynamics/src/WakeDynamics.f90 b/modules/wakedynamics/src/WakeDynamics.f90 index dcf119c18a..6664c6e44f 100644 --- a/modules/wakedynamics/src/WakeDynamics.f90 +++ b/modules/wakedynamics/src/WakeDynamics.f90 @@ -888,7 +888,7 @@ subroutine WD_UpdateStates( t, n, u, p, x, xd, z, OtherState, m, errStat, errMsg dx = 0.0_ReKi dy_HWkDfl = GetYawCorrection(xd%YawErr_filt(0), xd%xhat_plane(:,0), dx, p, errStat2, errMsg2) call SetErrStat(ErrStat2, ErrMsg2, errStat, errMsg, RoutineName) - if (errStat /= ErrID_None) then + if (errStat >= AbortErrLev) then ! TEST: E3 call Cleanup() return @@ -911,7 +911,13 @@ subroutine WD_UpdateStates( t, n, u, p, x, xd, z, OtherState, m, errStat, errMsg if (p%Mod_Wake == Mod_Wake_Polar) then ! Compute wake deficit of first plane based on rotor loading, outputs: Vx_Wake, m - call NearWakeCorrection( xd%Ct_azavg_filt, xd%Cq_azavg_filt, xd%Vx_rel_disk_filt, p, m, xd%Vx_wake(:,0), m%Vt_wake, xd%D_rotor_filt(0), errStat, errMsg ) + call NearWakeCorrection( xd%Ct_azavg_filt, xd%Cq_azavg_filt, xd%Vx_rel_disk_filt, p, m, xd%Vx_wake(:,0), m%Vt_wake, xd%D_rotor_filt(0), errStat2, errMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, errStat, errMsg, RoutineName) + if (errStat >= AbortErrLev) then + call Cleanup() + return + end if + m%Ct_avg = get_Ctavg(p%r, xd%Ct_azavg_filt, xd%D_rotor_filt(0)) else if (p%Mod_Wake == Mod_Wake_Cartesian .or. p%Mod_Wake == Mod_Wake_Curl) then @@ -922,7 +928,12 @@ subroutine WD_UpdateStates( t, n, u, p, x, xd, z, OtherState, m, errStat, errMsg ! --- Compute Vx ! Compute Vx(r) - call NearWakeCorrection( xd%Ct_azavg_filt, xd%Cq_azavg_filt, xd%Vx_rel_disk_filt, p, m, m%Vx_polar(:), m%Vt_wake, xd%D_rotor_filt(0), errStat, errMsg ) + call NearWakeCorrection( xd%Ct_azavg_filt, xd%Cq_azavg_filt, xd%Vx_rel_disk_filt, p, m, m%Vx_polar(:), m%Vt_wake, xd%D_rotor_filt(0), errStat2, errMsg2 ) + call SetErrStat(ErrStat2, ErrMsg2, errStat, errMsg, RoutineName) + if (errStat >= AbortErrLev) then + call Cleanup() + return + end if ! Convert to Cartesian call Axisymmetric2CartesianVx(m%Vx_polar, p%r, p%y, p%z, xd%Vx_wake2(:,:,0)) call FilterVx(xd%Vx_wake2(:,:,0), p%FilterInit) ! don't filter if FilterInit is 0 diff --git a/modules/wakedynamics/src/WakeDynamics_Registry.txt b/modules/wakedynamics/src/WakeDynamics_Registry.txt index f2ea856b2c..56ae0024ef 100644 --- a/modules/wakedynamics/src/WakeDynamics_Registry.txt +++ b/modules/wakedynamics/src/WakeDynamics_Registry.txt @@ -56,7 +56,6 @@ typedef ^ WD_InputFileType ReKi k_vCurl - - typedef ^ WD_InputFileType Logical OutAllPlanes - - - "Output all planes" - # wake added turbulence (WAT) inputs typedef ^ WD_InputFileType Logical WAT - - - "Switch for turning on and off wake-added turbulence" - -#typedef ^ WD_InputFileType ReKi WAT_k_Def {5} - - "Calibrated parameters for the influence of the maximum wake deficit on wake-added turbulence (set of 5 parameters: k_Def , FMin, DMin, DMax, Exp) (-) [>=0.0, >=0.0 and <=1.0, >=0.0, >DMin, >0.0] or DEFAULT [DEFAULT=[0.6, 0.0, 0.0, 2.0, 1.0 ]]" - typedef ^ WD_InputFileType ReKi WAT_k_Def_k_c - - - "Calibrated parameter for the influence of the maximum wake deficit on wake-added turblence (-) [>=0] or DEFAULT [DEFAULT=0.6]" - typedef ^ WD_InputFileType ReKi WAT_k_Def_FMin - - - "Calibrated parameter in the eddy viscosity filter function for the WAT maximum wake deficit defining the value in the minimum region [>=0.0 and <=1.0] or DEFAULT [DEFAULT=0.0]" - typedef ^ WD_InputFileType ReKi WAT_k_Def_DMin - - - "Calibrated parameter in the eddy viscosity filter function for the WAT maximum wake deficit defining the transitional diameter fraction between the minimum and exponential regions [>=0.0] or DEFAULT [DEFAULT=0.0]" - @@ -189,7 +188,6 @@ typedef ^ ParameterType CHARACTER(1024) OutFileVTKDir - - - "The parent di typedef ^ ParameterType IntKi TurbNum - 0 - "Turbine ID number (start with 1; end with number of turbines)" - # wake added turbulence (WAT) parameters typedef ^ ParameterType Logical WAT - - - "Switch for turning on and off wake-added turbulence" - -#typedef ^ ParameterType ReKi WAT_k_Def {5} - - "Calibrated parameters for the influence of the maximum wake deficit on wake-added turbulence (set of 5 parameters: k_Def , FMin, DMin, DMax, Exp) (-) [>=0.0, >=0.0 and <=1.0, >=0.0, >DMin, >0.0] or DEFAULT [DEFAULT=[0.6, 0.0, 0.0, 2.0, 1.0 ]]" - typedef ^ ParameterType ReKi WAT_k_Def_k_c - - - "Calibrated parameter for the influence of the maximum wake deficit on wake-added turblence (-) [>=0] or DEFAULT [DEFAULT=0.6]" - typedef ^ ParameterType ReKi WAT_k_Def_FMin - - - "Calibrated parameter in the eddy viscosity filter function for the WAT maximum wake deficit defining the value in the minimum region [>=0.0 and <=1.0] or DEFAULT [DEFAULT=0.0]" - typedef ^ ParameterType ReKi WAT_k_Def_DMin - - - "Calibrated parameter in the eddy viscosity filter function for the WAT maximum wake deficit defining the transitional diameter fraction between the minimum and exponential regions [>=0.0] or DEFAULT [DEFAULT=0.0]" - diff --git a/openfast_io/README.md b/openfast_io/README.md index 4d1d77dfda..2d1849c858 100644 --- a/openfast_io/README.md +++ b/openfast_io/README.md @@ -38,7 +38,7 @@ These instructions are for interaction directly with the `openfast_io` source co ``` ### Extra options -[ROSCO](https://github.com/NREL/ROSCO) can be installed as an optional dependency. Run either +[ROSCO](https://github.com/NatLabRockies/ROSCO) can be installed as an optional dependency. Run either ```shell pip install openfast_io[rosco] ``` diff --git a/openfast_io/openfast_io/FAST_reader.py b/openfast_io/openfast_io/FAST_reader.py index 55127ca4da..681491afcd 100644 --- a/openfast_io/openfast_io/FAST_reader.py +++ b/openfast_io/openfast_io/FAST_reader.py @@ -550,6 +550,7 @@ def read_ElastoDyn(self, ed_file): self.fst_vt['ElastoDyn']['BlPIner(3)'] = float_read(f.readline().split()[0]) self.fst_vt['ElastoDyn']['HubMass'] = float_read(f.readline().split()[0]) self.fst_vt['ElastoDyn']['HubIner'] = float_read(f.readline().split()[0]) + self.fst_vt['ElastoDyn']['HubIner_Teeter'] = float_read(f.readline().split()[0]) self.fst_vt['ElastoDyn']['GenIner'] = float_read(f.readline().split()[0]) self.fst_vt['ElastoDyn']['NacMass'] = float_read(f.readline().split()[0]) self.fst_vt['ElastoDyn']['NacYIner'] = float_read(f.readline().split()[0]) @@ -1696,7 +1697,7 @@ def read_ServoDyn(self): for i in range(self.fst_vt['ServoDyn']['DLL_NumTrq']): data = f.readline().split() self.fst_vt['ServoDyn']['GenSpd_TLU'][i] = float_read(data[0]) - self.fst_vt['ServoDyn']['GenTrq_TLU'][i] = float_read(data[0]) + self.fst_vt['ServoDyn']['GenTrq_TLU'][i] = float_read(data[1]) # ServoDyn Output Params (sd_out_params) f.readline() @@ -3546,6 +3547,8 @@ def execute(self): if not os.path.isabs(self.fst_vt['ElastoDyn']['TwrFile']): ed_tower_file = os.path.join(os.path.dirname(ed_file), self.fst_vt['ElastoDyn']['TwrFile']) + else: + ed_tower_file = self.fst_vt['ElastoDyn']['TwrFile'] self.read_ElastoDynTower(ed_tower_file) if self.fst_vt['Fst']['CompInflow'] == 1: diff --git a/openfast_io/openfast_io/FAST_writer.py b/openfast_io/openfast_io/FAST_writer.py index 68b115064b..2c40bc4e32 100644 --- a/openfast_io/openfast_io/FAST_writer.py +++ b/openfast_io/openfast_io/FAST_writer.py @@ -465,7 +465,8 @@ def write_ElastoDyn(self): f.write('{:<22} {:<11} {:}'.format(self.fst_vt['ElastoDyn']['BlPIner(2)'], 'BlPIner(2)', '- Blade pitch inertia, blade 2 (kg m^2)\n')) f.write('{:<22} {:<11} {:}'.format(self.fst_vt['ElastoDyn']['BlPIner(3)'], 'BlPIner(3)', '- Blade pitch inertia, blade 3 (kg m^2) [unused for 2 blades]\n')) f.write('{:<22} {:<11} {:}'.format(self.fst_vt['ElastoDyn']['HubMass'], 'HubMass', '- Hub mass (kg)\n')) - f.write('{:<22} {:<11} {:}'.format(self.fst_vt['ElastoDyn']['HubIner'], 'HubIner', '- Hub inertia about rotor axis [3 blades] or teeter axis [2 blades] (kg m^2)\n')) + f.write('{:<22} {:<11} {:}'.format(self.fst_vt['ElastoDyn']['HubIner'], 'HubIner', 'Hub inertia about rotor axis (2 or 3-blades) (kg m^2)\n')) + f.write('{:<22} {:<11} {:}'.format(self.fst_vt['ElastoDyn']['HubIner_Teeter'], 'HubIner_Teeter', 'Hub inertia about teeter axis (2-blades) (kg m^2)\n')) f.write('{:<22} {:<11} {:}'.format(self.fst_vt['ElastoDyn']['GenIner'], 'GenIner', '- Generator inertia about HSS (kg m^2)\n')) f.write('{:<22} {:<11} {:}'.format(self.fst_vt['ElastoDyn']['NacMass'], 'NacMass', '- Nacelle mass (kg)\n')) f.write('{:<22} {:<11} {:}'.format(self.fst_vt['ElastoDyn']['NacYIner'], 'NacYIner', '- Nacelle inertia about yaw axis (kg m^2)\n')) @@ -840,6 +841,9 @@ def write_BeamDynBlade(self, bldInd = None): f.write('\n') f.write('\n') + f.flush() + os.fsync(f) + f.close() def write_InflowWind(self): self.fst_vt['Fst']['InflowFile'] = self.FAST_namingOut + '_InflowWind.dat' diff --git a/openfast_io/openfast_io/turbsim_util.py b/openfast_io/openfast_io/turbsim_util.py index 73e1c0cc31..a47dd3ab56 100644 --- a/openfast_io/openfast_io/turbsim_util.py +++ b/openfast_io/openfast_io/turbsim_util.py @@ -90,6 +90,8 @@ def read_input_file(self, input_file_name): self.CTLz = inpf.readline().split()[0] self.CTStartTime = inpf.readline().split()[0] + inpf.close() + class TurbsimWriter(object): diff --git a/openfast_io/pyproject.toml b/openfast_io/pyproject.toml index b942dade9f..460e3bf782 100644 --- a/openfast_io/pyproject.toml +++ b/openfast_io/pyproject.toml @@ -9,15 +9,15 @@ version = "4.1.2" description = "Readers and writers for OpenFAST files." license = {file = "../LICENSE"} authors = [ - {name = "Mayank Chetan", email = "mayank.chetan@nrel.gov" }, - {name = "Andy Platt", email = "andy.platt@nrel.gov" }, - {name = "Derek Slaughter", email = "derek.slaughter@nrel.gov" }, - { name = "NREL WISDEM Team", email = "systems.engineering@nrel.gov" }, + {name = "Mayank Chetan", email = "mayank.chetan@nlr.gov" }, + {name = "Andy Platt", email = "andy.platt@nlr.gov" }, + {name = "Derek Slaughter", email = "derek.slaughter@nlr.gov" }, + { name = "NLR WISDEM Team", email = "systems.engineering@nlr.gov" }, ] maintainers = [ - {name = "Mayank Chetan", email = "mayank.chetan@nrel.gov" }, - {name = "Andy Platt", email = "andy.platt@nrel.gov" }, - {name = "Derek Slaughter", email = "derek.slaughter@nrel.gov" }, + {name = "Mayank Chetan", email = "mayank.chetan@nlr.gov" }, + {name = "Andy Platt", email = "andy.platt@nlr.gov" }, + {name = "Derek Slaughter", email = "derek.slaughter@nlr.gov" }, ] readme = "README.md" requires-python = ">3.10" @@ -42,7 +42,11 @@ classifiers = [ # Optional "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3", + "Programming Language :: Python", ] dependencies = [ @@ -63,8 +67,8 @@ Issues = "https://github.com/OpenFAST/openfast/issues" [project.optional-dependencies] rosco = ["rosco>2.9.2"] -xlrd = ["xlrd>2"] -all = ["rosco>2.9.2", "xlrd>2"] +xlrd = ["xlrd>2.0"] +all = ["rosco>2.9.2", "xlrd>2.0"] [tool.hatch.version] source = "vcs" diff --git a/reg_tests/CMakeLists.txt b/reg_tests/CMakeLists.txt index 52e79de065..ddc7ad8ace 100644 --- a/reg_tests/CMakeLists.txt +++ b/reg_tests/CMakeLists.txt @@ -93,22 +93,27 @@ file(MAKE_DIRECTORY ${CTEST_BINARY_DIR}) foreach(regTest glue-codes/openfast glue-codes/openfast-cpp modules/aerodyn modules/beamdyn modules/hydrodyn modules/inflowwind modules/moordyn modules/subdyn openfast_io) file(MAKE_DIRECTORY ${CTEST_BINARY_DIR}/${regTest}) endforeach() +foreach(srcDir BAR_Baseline) + file(COPY "${CMAKE_CURRENT_LIST_DIR}/r-test/modules/aerodyn/${srcDir}" + DESTINATION "${CTEST_BINARY_DIR}/modules/aerodyn/") +endforeach() -## openfast seed -foreach(turbineDirectory 5MW_Baseline AOC AWT27 SWRT UAE_VI WP_Baseline) - file(COPY "${CMAKE_CURRENT_LIST_DIR}/r-test/glue-codes/openfast/${turbineDirectory}" - DESTINATION "${CTEST_BINARY_DIR}/glue-codes/openfast/") +## openfast test directories +foreach(srcDir 5MW_Baseline AOC AWT27 SWRT UAE_VI WP_Baseline) + file(COPY "${CMAKE_CURRENT_LIST_DIR}/r-test/glue-codes/openfast/${srcDir}" + DESTINATION "${CTEST_BINARY_DIR}/glue-codes/openfast/") endforeach() -foreach(turbineDirectory 5MW_Baseline) - file(COPY "${CMAKE_CURRENT_LIST_DIR}/r-test/glue-codes/openfast/${turbineDirectory}" - DESTINATION "${CTEST_BINARY_DIR}/glue-codes/python/") +## python test directories +foreach(srcDir 5MW_Baseline) + file(COPY "${CMAKE_CURRENT_LIST_DIR}/r-test/glue-codes/openfast/${srcDir}" + DESTINATION "${CTEST_BINARY_DIR}/glue-codes/python/") endforeach() ## fastfarm seed -foreach(turbineDirectory 5MW_Baseline) - file(COPY "${CMAKE_CURRENT_LIST_DIR}/r-test/glue-codes/fast-farm/${turbineDirectory}" - DESTINATION "${CTEST_BINARY_DIR}/glue-codes/fast-farm/") +foreach(srcDir 5MW_Baseline WAT_MannBoxDB) + file(COPY "${CMAKE_CURRENT_LIST_DIR}/r-test/glue-codes/fast-farm/${srcDir}" + DESTINATION "${CTEST_BINARY_DIR}/glue-codes/fast-farm/") endforeach() # add the tests diff --git a/reg_tests/CTestList.cmake b/reg_tests/CTestList.cmake index f7df881a5d..2ecae0b163 100644 --- a/reg_tests/CTestList.cmake +++ b/reg_tests/CTestList.cmake @@ -250,6 +250,15 @@ function(seast_regression TESTNAME LABEL) regression(${TEST_SCRIPT} ${SEASTATE_EXECUTABLE} ${SOURCE_DIRECTORY} ${BUILD_DIRECTORY} " " ${TESTNAME} "${LABEL}" " ") endfunction(seast_regression) +# py_seastate +function(py_seast_regression TESTNAME LABEL) + set(TEST_SCRIPT "${CMAKE_CURRENT_LIST_DIR}/executeSeaStatePyRegressionCase.py") + set(SEASTATE_EXECUTABLE "${Python_EXECUTABLE}") + set(SOURCE_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/..") + set(BUILD_DIRECTORY "${CTEST_BINARY_DIR}/modules/seastate") + regression(${TEST_SCRIPT} ${SEASTATE_EXECUTABLE} ${SOURCE_DIRECTORY} ${BUILD_DIRECTORY} " " ${TESTNAME} "${LABEL}" " ") +endfunction(py_seast_regression) + # moordyn function(md_regression TESTNAME LABEL) set(TEST_SCRIPT "${CMAKE_CURRENT_LIST_DIR}/executeMoordynRegressionCase.py") @@ -307,6 +316,15 @@ function(py_openfast_io_library_pytest TESTNAME LABEL) endfunction(py_openfast_io_library_pytest) +# py_wavetank +function(py_wavetank_regression TESTNAME LABEL) + set(TEST_SCRIPT "${CMAKE_CURRENT_LIST_DIR}/executeWavetankPyRegressionCase.py") + set(SEASTATE_EXECUTABLE "${Python_EXECUTABLE}") + set(SOURCE_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/..") + set(BUILD_DIRECTORY "${CTEST_BINARY_DIR}/glue-codes/other") + regression(${TEST_SCRIPT} ${SEASTATE_EXECUTABLE} ${SOURCE_DIRECTORY} ${BUILD_DIRECTORY} " " ${TESTNAME} "${LABEL}" " ") +endfunction(py_wavetank_regression) + #=============================================================================== # Regression tests #=============================================================================== @@ -320,6 +338,8 @@ of_regression("AWT_WSt_StartUpShutDown" "openfast;elastodyn;aerod of_regression("AOC_WSt" "openfast;elastodyn;aerodyn;servodyn") of_regression("AOC_YFree_WTurb" "openfast;elastodyn;aerodyn;servodyn") of_regression("AOC_YFix_WSt" "openfast;elastodyn;aerodyn;servodyn") +of_regression("AOC_YFriction_Loading" "openfast;elastodyn;aerodyn;servodyn") +of_regression("AOC_YFriction_Stiffness" "openfast;elastodyn;aerodyn;servodyn") of_regression("UAE_Dnwind_YRamp_WSt" "openfast;elastodyn;aerodyn;servodyn") of_regression("UAE_Upwind_Rigid_WRamp_PwrCurve" "openfast;elastodyn;aerodyn;servodyn") of_regression("WP_VSP_WTurb_PitchFail" "openfast;elastodyn;aerodyn;servodyn") @@ -353,6 +373,7 @@ of_regression("MHK_RM1_Fixed" "openfast;elastodyn;aerod of_regression("MHK_RM1_Floating" "openfast;elastodyn;aerodyn;hydrodyn;moordyn;mhk;offshore") of_regression("MHK_RM1_Floating_MR" "openfast;elastodyn;aerodyn;servodyn;hydrodyn;moordyn;multirotor;offshore") of_regression("MHK_RM1_Floating_wNacDrag" "openfast;elastodyn;aerodyn;hydrodyn;moordyn;mhk;offshore") +of_regression("MHK_RM1_Floating_Tank-scaled" "openfast;elastodyn;aerodyn;hydrodyn;moordyn;mhk;offshore;scaled") of_regression("Tailfin_FreeYaw1DOF_PolarBased" "openfast;elastodyn;aerodyn") of_regression("Tailfin_FreeYaw1DOF_Unsteady" "openfast;elastodyn;aerodyn") of_regression("5MW_Land_DLL_WTurb_ADsk" "openfast;elastodyn;aerodisk") @@ -360,6 +381,8 @@ of_regression("5MW_Land_DLL_WTurb_ADsk_SED" "openfast;simple-elastody of_regression("5MW_Land_DLL_WTurb_SED" "openfast;simple-elastodyn;aerodyn") of_regression("OC6_phaseII" "openfast;soildyn;subdyn;hydrodyn;offshore;stc") +of_regression("MinimalExample" "openfast;elastodyn") + of_aeromap_regression("5MW_Land_AeroMap" "aeromap;elastodyn;aerodyn") # OpenFAST C++ API test @@ -521,6 +544,7 @@ seast_regression("seastate_WaveMod7_WaveStMod3" "seastate") seast_regression("seastate_WvCrntMod1" "seastate") seast_regression("seastate_WvCrntMod2" "seastate") seast_regression("seastate_wavemod5" "seastate") # place at end since it reads outputs generated by seastate_wr_kin1 +py_seast_regression("py_seastate_1" "seastate;python") # MoorDyn regression tests md_regression("md_5MW_OC4Semi" "moordyn") @@ -551,3 +575,6 @@ adsk_regression("adsk_timeseries_shutdown" "aerodisk") # SimplifiedElastoDyn regression tests sed_regression("sed_test_HSSbrk" "simple-elastodyn") sed_regression("sed_test_freewheel" "simple-elastodyn") + +# Wavetank library interface (MD + SS + AD) +py_wavetank_regression("py_wavetank_test1" "wavetank;aerodyn;moordyn;seastate;python;scaled") diff --git a/reg_tests/executeSeaStatePyRegressionCase.py b/reg_tests/executeSeaStatePyRegressionCase.py new file mode 100644 index 0000000000..2d2f8b875e --- /dev/null +++ b/reg_tests/executeSeaStatePyRegressionCase.py @@ -0,0 +1,146 @@ +# +# Copyright 2017 National Renewable Energy Laboratory +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +""" + This program executes SeaSate through the python interface for a single test case. + The test data is contained in a git submodule, r-test, which must be initialized + prior to running. See the r-test README or OpenFAST documentation for more info. + + Get usage with: `executeSeaStatePyRegressionCase.py -h` +""" + +import os +import sys +basepath = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.sep.join([basepath, "lib"])) +import argparse +import numpy as np +import shutil +import glob +import subprocess +import rtestlib as rtl +import openfastDrivers +import pass_fail +from errorPlotting import exportCaseSummary + +##### Main program + +### Store the python executable for future python calls +pythonCommand = sys.executable + +### Verify input arguments +parser = argparse.ArgumentParser(description="Executes SeaState c-bindings library interface with a regression test for a single test case.") +parser.add_argument("caseName", metavar="Case-Name", type=str, nargs=1, help="The name of the test case.") +parser.add_argument("executable", metavar="SeaState-Python", type=str, nargs=1, help="The path to the InflowWind driver executable.") +parser.add_argument("sourceDirectory", metavar="path/to/openfast_repo", type=str, nargs=1, help="The path to the OpenFAST repository.") +parser.add_argument("buildDirectory", metavar="path/to/openfast_repo/build", type=str, nargs=1, help="The path to the OpenFAST repository build directory.") +parser.add_argument("rtol", metavar="Relative-Tolerance", type=float, nargs=1, help="Relative tolerance to allow the solution to deviate; expressed as order of magnitudes less than baseline.") +parser.add_argument("atol", metavar="Absolute-Tolerance", type=float, nargs=1, help="Absolute tolerance to allow small values to pass; expressed as order of magnitudes less than baseline.") +parser.add_argument("-p", "-plot", dest="plot", action='store_true', help="bool to include plots in failed cases") +parser.add_argument("-n", "-no-exec", dest="noExec", action='store_true', help="bool to prevent execution of the test cases") +parser.add_argument("-v", "-verbose", dest="verbose", action='store_true', help="bool to include verbose system output") + +args = parser.parse_args() + +caseName = args.caseName[0] +executable = args.executable[0] +sourceDirectory = args.sourceDirectory[0] +buildDirectory = args.buildDirectory[0] +rtol = args.rtol[0] +atol = args.atol[0] +plotError = args.plot if args.plot is False else True +noExec = args.noExec if args.noExec is False else True +verbose = args.verbose if args.verbose is False else True + +# validate inputs +rtl.validateExeOrExit(executable) +rtl.validateDirOrExit(sourceDirectory) +if not os.path.isdir(buildDirectory): + os.makedirs(buildDirectory, exist_ok=True) + +### Build the filesystem navigation variables for running the test case +regtests = os.path.join(sourceDirectory, "reg_tests") +lib = os.path.join(regtests, "lib") +rtest = os.path.join(regtests, "r-test") +moduleDirectory = os.path.join(rtest, "modules", "seastate") +inputsDirectory = os.path.join(moduleDirectory, caseName) +targetOutputDirectory = os.path.join(inputsDirectory) +testBuildDirectory = os.path.join(buildDirectory, caseName) + +# verify all the required directories exist +if not os.path.isdir(rtest): + rtl.exitWithError("The test data directory, {}, does not exist. If you haven't already, run `git submodule update --init --recursive`".format(rtest)) +if not os.path.isdir(targetOutputDirectory): + rtl.exitWithError("The test data outputs directory, {}, does not exist. Try running `git submodule update`".format(targetOutputDirectory)) +if not os.path.isdir(inputsDirectory): + rtl.exitWithError("The test data inputs directory, {}, does not exist. Verify your local repository is up to date.".format(inputsDirectory)) + +# create the local output directory if it does not already exist +# and initialize it with input files for all test cases +if not os.path.isdir(testBuildDirectory): + os.makedirs(testBuildDirectory) + for file in glob.glob(os.path.join(inputsDirectory,"*py")): + filename = file.split(os.path.sep)[-1] + shutil.copy(os.path.join(inputsDirectory,filename), os.path.join(testBuildDirectory,filename)) + for file in glob.glob(os.path.join(inputsDirectory,"*inp")): + filename = file.split(os.path.sep)[-1] + shutil.copy(os.path.join(inputsDirectory,filename), os.path.join(testBuildDirectory,filename)) + for file in glob.glob(os.path.join(inputsDirectory,"*dat")): + filename = file.split(os.path.sep)[-1] + shutil.copy(os.path.join(inputsDirectory,filename), os.path.join(testBuildDirectory,filename)) + +### Run inflowwind on the test case +if not noExec: + caseInputFile = os.path.join(testBuildDirectory, "py_seastate_driver.py") + returnCode = openfastDrivers.runInflowwindDriverCase(caseInputFile, executable) + if returnCode != 0: + sys.exit(returnCode*10) + +### Build the filesystem navigation variables for running the regression test. +# the "Points.Results.dat" file is from calls to routines other than CalcOutput +localOutFile = os.path.join(testBuildDirectory, "Points.Results.dat") +baselineOutFile = os.path.join(targetOutputDirectory, "Points.Results.dat") +rtl.validateFileOrExit(localOutFile) +rtl.validateFileOrExit(baselineOutFile) + +testData, testInfo, _ = pass_fail.readFASTOut(localOutFile) +baselineData, baselineInfo, _ = pass_fail.readFASTOut(baselineOutFile) + +passing_channels = pass_fail.passing_channels(testData.T, baselineData.T, rtol, atol) +passing_channels = passing_channels.T + +norms = pass_fail.calculateNorms(testData, baselineData) + +# export all case summaries +channel_names = testInfo["attribute_names"] +exportCaseSummary(testBuildDirectory, caseName, channel_names, passing_channels, norms) + +# passing case +if np.all(passing_channels): + sys.exit(0) + +# failing case +if plotError: + from errorPlotting import finalizePlotDirectory, plotOpenfastError + for channel in testInfo["attribute_names"]: + try: + plotOpenfastError(localOutFile, baselineOutFile, channel, rtol, atol) + except: + error = sys.exc_info()[1] + print("Error generating plots: {}".format(error)) + finalizePlotDirectory(localOutFile, testInfo["attribute_names"], caseName) + +sys.exit(1) diff --git a/reg_tests/executeWavetankPyRegressionCase.py b/reg_tests/executeWavetankPyRegressionCase.py new file mode 100644 index 0000000000..506702716f --- /dev/null +++ b/reg_tests/executeWavetankPyRegressionCase.py @@ -0,0 +1,149 @@ +# +# Copyright 2025 National Renewable Energy Laboratory +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +""" + This program executes the WaveTank library interface through python interface + for a single test case. The test data is contained in a git submodule, r-test, + which must be initialized prior to running. See the r-test README or OpenFAST + documentation for more info. + + Get usage with: `executeWavetankPyRegressionCase.py -h` +""" + +import os +import sys +basepath = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.sep.join([basepath, "lib"])) +import argparse +import numpy as np +import shutil +import glob +import subprocess +import rtestlib as rtl +import openfastDrivers +import pass_fail +from errorPlotting import exportCaseSummary + +##### Helper functions +excludeExt=['.out','.outb','.ech','.yaml','.sum','.log'] + +##### Main program + +### Store the python executable for future python calls +pythonCommand = sys.executable + +### Verify input arguments +parser = argparse.ArgumentParser(description="Executes wavetank c-bindings library interface with a regression test for a single test case.") +parser.add_argument("caseName", metavar="Case-Name", type=str, nargs=1, help="The name of the test case.") +parser.add_argument("executable", metavar="WaveTank-Python", type=str, nargs=1, help="The path to the wavetank python driver case.") +parser.add_argument("sourceDirectory", metavar="path/to/openfast_repo", type=str, nargs=1, help="The path to the OpenFAST repository.") +parser.add_argument("buildDirectory", metavar="path/to/openfast_repo/build", type=str, nargs=1, help="The path to the OpenFAST repository build directory.") +parser.add_argument("rtol", metavar="Relative-Tolerance", type=float, nargs=1, help="Relative tolerance to allow the solution to deviate; expressed as order of magnitudes less than baseline.") +parser.add_argument("atol", metavar="Absolute-Tolerance", type=float, nargs=1, help="Absolute tolerance to allow small values to pass; expressed as order of magnitudes less than baseline.") +parser.add_argument("-p", "-plot", dest="plot", action='store_true', help="bool to include plots in failed cases") +parser.add_argument("-n", "-no-exec", dest="noExec", action='store_true', help="bool to prevent execution of the test cases") +parser.add_argument("-v", "-verbose", dest="verbose", action='store_true', help="bool to include verbose system output") + +args = parser.parse_args() + +caseName = args.caseName[0] +executable = args.executable[0] +sourceDirectory = args.sourceDirectory[0] +buildDirectory = args.buildDirectory[0] +rtol = args.rtol[0] +atol = args.atol[0] +plotError = args.plot if args.plot is False else True +noExec = args.noExec if args.noExec is False else True +verbose = args.verbose if args.verbose is False else True + +# validate inputs +rtl.validateExeOrExit(executable) +rtl.validateDirOrExit(sourceDirectory) +if not os.path.isdir(buildDirectory): + os.makedirs(buildDirectory, exist_ok=True) + +### Build the filesystem navigation variables for running the test case +regtests = os.path.join(sourceDirectory, "reg_tests") +lib = os.path.join(regtests, "lib") +rtest = os.path.join(regtests, "r-test") +moduleDirectory = os.path.join(rtest, "glue-codes", "other") +inputsDirectory = os.path.join(moduleDirectory, caseName) +targetOutputDirectory = os.path.join(inputsDirectory) +testBuildDirectory = os.path.join(buildDirectory, caseName) + +dependsDir = os.path.join("..", "..", "openfast", "MHK_RM1_Floating_Tank-scaled") + +# verify all the required directories exist +if not os.path.isdir(rtest): + rtl.exitWithError("The test data directory, {}, does not exist. If you haven't already, run `git submodule update --init --recursive`".format(rtest)) +if not os.path.isdir(targetOutputDirectory): + rtl.exitWithError("The test data outputs directory, {}, does not exist. Try running `git submodule update`".format(targetOutputDirectory)) +if not os.path.isdir(inputsDirectory): + rtl.exitWithError("The test data inputs directory, {}, does not exist. Verify your local repository is up to date.".format(inputsDirectory)) + +# create the local output directory if it does not already exist +# and initialize it with input files for all test cases +if not os.path.isdir(testBuildDirectory): + rtl.copyTree(inputsDirectory, testBuildDirectory, excludeExt) + +# Dependency +src = os.path.join(inputsDirectory, dependsDir) +dst = os.path.join(testBuildDirectory, dependsDir) +if not os.path.isdir(dst): + rtl.copyTree(src, dst, excludeExt) + +### Run inflowwind on the test case +if not noExec: + caseInputFile = os.path.join(testBuildDirectory, "py_wavetank_driver.py") + returnCode = openfastDrivers.runInflowwindDriverCase(caseInputFile, executable) + if returnCode != 0: + sys.exit(returnCode*10) + +### Build the filesystem navigation variables for running the regression test. +# the "Points.Results.dat" file is from calls to routines other than CalcOutput +localOutFile = os.path.join(testBuildDirectory, "FRM1Q_Floating_tank_test.out") +baselineOutFile = os.path.join(targetOutputDirectory, "FRM1Q_Floating_tank_test.out") +rtl.validateFileOrExit(localOutFile) +rtl.validateFileOrExit(baselineOutFile) + +testData, testInfo, _ = pass_fail.readFASTOut(localOutFile) +baselineData, baselineInfo, _ = pass_fail.readFASTOut(baselineOutFile) + +passing_channels = pass_fail.passing_channels(testData.T, baselineData.T, rtol, atol) +passing_channels = passing_channels.T + +norms = pass_fail.calculateNorms(testData, baselineData) + +# export all case summaries +channel_names = testInfo["attribute_names"] +exportCaseSummary(testBuildDirectory, caseName, channel_names, passing_channels, norms) + +# passing case +if np.all(passing_channels): + sys.exit(0) + +# failing case +if plotError: + from errorPlotting import finalizePlotDirectory, plotOpenfastError + for channel in testInfo["attribute_names"]: + try: + plotOpenfastError(localOutFile, baselineOutFile, channel, rtol, atol) + except: + error = sys.exc_info()[1] + print("Error generating plots: {}".format(error)) + finalizePlotDirectory(localOutFile, testInfo["attribute_names"], caseName) + +sys.exit(1) diff --git a/reg_tests/r-test b/reg_tests/r-test index 659764f127..67fd5a4b50 160000 --- a/reg_tests/r-test +++ b/reg_tests/r-test @@ -1 +1 @@ -Subproject commit 659764f1276bab92c678405572cbcd0053a68614 +Subproject commit 67fd5a4b508d9c8cb1c2f1c7118aa357f692edee diff --git a/requirements.txt b/requirements.txt index ad406e0555..1006eeb363 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,5 @@ numpy vtk Bokeh>=2.4,!=3.0.0,!=3.0.1,!=3.0.2,!=3.0.3 -pytest \ No newline at end of file +pytest +nptdms diff --git a/vs-build/OpenFAST.sln b/vs-build/OpenFAST.sln index cfddc88254..90b4e29564 100644 --- a/vs-build/OpenFAST.sln +++ b/vs-build/OpenFAST.sln @@ -1745,7 +1745,6 @@ Global {E32296E3-72E8-435B-9BF3-2FAE02189CA5}.Matlab_Release|x86.ActiveCfg = Release|x64 {E32296E3-72E8-435B-9BF3-2FAE02189CA5}.Matlab_Release|x86.Build.0 = Release|x64 {E32296E3-72E8-435B-9BF3-2FAE02189CA5}.OpenMP_Release|x64.ActiveCfg = OpenMP_Release|x64 - {E32296E3-72E8-435B-9BF3-2FAE02189CA5}.OpenMP_Release|x64.Build.0 = OpenMP_Release|x64 {E32296E3-72E8-435B-9BF3-2FAE02189CA5}.OpenMP_Release|x86.ActiveCfg = OpenMP_Release|x64 {E32296E3-72E8-435B-9BF3-2FAE02189CA5}.OpenMP_Release|x86.Build.0 = OpenMP_Release|x64 {E32296E3-72E8-435B-9BF3-2FAE02189CA5}.Release|x64.ActiveCfg = Release|x64 @@ -1775,7 +1774,6 @@ Global {9E1FBABD-B8BD-450F-B53D-72FEE6E78C53}.Matlab_Release|x86.ActiveCfg = Release|x64 {9E1FBABD-B8BD-450F-B53D-72FEE6E78C53}.Matlab_Release|x86.Build.0 = Release|x64 {9E1FBABD-B8BD-450F-B53D-72FEE6E78C53}.OpenMP_Release|x64.ActiveCfg = OpenMP_Release|x64 - {9E1FBABD-B8BD-450F-B53D-72FEE6E78C53}.OpenMP_Release|x64.Build.0 = OpenMP_Release|x64 {9E1FBABD-B8BD-450F-B53D-72FEE6E78C53}.OpenMP_Release|x86.ActiveCfg = OpenMP_Release|x64 {9E1FBABD-B8BD-450F-B53D-72FEE6E78C53}.OpenMP_Release|x86.Build.0 = OpenMP_Release|x64 {9E1FBABD-B8BD-450F-B53D-72FEE6E78C53}.Release|x64.ActiveCfg = Release|x64 @@ -1805,7 +1803,6 @@ Global {4CE0CEBB-4A29-4E09-8EF5-240C46343ECD}.Matlab_Release|x86.ActiveCfg = Release|x64 {4CE0CEBB-4A29-4E09-8EF5-240C46343ECD}.Matlab_Release|x86.Build.0 = Release|x64 {4CE0CEBB-4A29-4E09-8EF5-240C46343ECD}.OpenMP_Release|x64.ActiveCfg = OpenMP_Release|x64 - {4CE0CEBB-4A29-4E09-8EF5-240C46343ECD}.OpenMP_Release|x64.Build.0 = OpenMP_Release|x64 {4CE0CEBB-4A29-4E09-8EF5-240C46343ECD}.OpenMP_Release|x86.ActiveCfg = OpenMP_Release|x64 {4CE0CEBB-4A29-4E09-8EF5-240C46343ECD}.OpenMP_Release|x86.Build.0 = OpenMP_Release|x64 {4CE0CEBB-4A29-4E09-8EF5-240C46343ECD}.Release|x64.ActiveCfg = Release|x64 @@ -1835,7 +1832,6 @@ Global {F861FB71-8FE4-42A5-8FB4-684F60D50B9C}.Matlab_Release|x86.ActiveCfg = Release|x64 {F861FB71-8FE4-42A5-8FB4-684F60D50B9C}.Matlab_Release|x86.Build.0 = Release|x64 {F861FB71-8FE4-42A5-8FB4-684F60D50B9C}.OpenMP_Release|x64.ActiveCfg = OpenMP_Release|x64 - {F861FB71-8FE4-42A5-8FB4-684F60D50B9C}.OpenMP_Release|x64.Build.0 = OpenMP_Release|x64 {F861FB71-8FE4-42A5-8FB4-684F60D50B9C}.OpenMP_Release|x86.ActiveCfg = OpenMP_Release|x64 {F861FB71-8FE4-42A5-8FB4-684F60D50B9C}.OpenMP_Release|x86.Build.0 = OpenMP_Release|x64 {F861FB71-8FE4-42A5-8FB4-684F60D50B9C}.Release|x64.ActiveCfg = Release|x64 @@ -1865,7 +1861,6 @@ Global {09919696-2DC4-48A3-B862-7BBF5CFD59CE}.Matlab_Release|x86.ActiveCfg = Release|x64 {09919696-2DC4-48A3-B862-7BBF5CFD59CE}.Matlab_Release|x86.Build.0 = Release|x64 {09919696-2DC4-48A3-B862-7BBF5CFD59CE}.OpenMP_Release|x64.ActiveCfg = OpenMP_Release|x64 - {09919696-2DC4-48A3-B862-7BBF5CFD59CE}.OpenMP_Release|x64.Build.0 = OpenMP_Release|x64 {09919696-2DC4-48A3-B862-7BBF5CFD59CE}.OpenMP_Release|x86.ActiveCfg = OpenMP_Release|x64 {09919696-2DC4-48A3-B862-7BBF5CFD59CE}.OpenMP_Release|x86.Build.0 = OpenMP_Release|x64 {09919696-2DC4-48A3-B862-7BBF5CFD59CE}.Release|x64.ActiveCfg = Release|x64 @@ -1896,7 +1891,6 @@ Global {C271833A-06D0-441D-A5A8-DDAB0AA4740C}.Matlab_Release|x86.ActiveCfg = Release|x64 {C271833A-06D0-441D-A5A8-DDAB0AA4740C}.Matlab_Release|x86.Build.0 = Release|x64 {C271833A-06D0-441D-A5A8-DDAB0AA4740C}.OpenMP_Release|x64.ActiveCfg = OpenMP_Release|x64 - {C271833A-06D0-441D-A5A8-DDAB0AA4740C}.OpenMP_Release|x64.Build.0 = OpenMP_Release|x64 {C271833A-06D0-441D-A5A8-DDAB0AA4740C}.OpenMP_Release|x86.ActiveCfg = OpenMP_Release|x64 {C271833A-06D0-441D-A5A8-DDAB0AA4740C}.OpenMP_Release|x86.Build.0 = OpenMP_Release|x64 {C271833A-06D0-441D-A5A8-DDAB0AA4740C}.Release|x64.ActiveCfg = Release|x64 @@ -1956,7 +1950,6 @@ Global {6AAA9C6D-9884-476E-B868-40A1C08BF0C3}.Matlab_Release|x86.ActiveCfg = Release|x64 {6AAA9C6D-9884-476E-B868-40A1C08BF0C3}.Matlab_Release|x86.Build.0 = Release|x64 {6AAA9C6D-9884-476E-B868-40A1C08BF0C3}.OpenMP_Release|x64.ActiveCfg = OpenMP_Release|x64 - {6AAA9C6D-9884-476E-B868-40A1C08BF0C3}.OpenMP_Release|x64.Build.0 = OpenMP_Release|x64 {6AAA9C6D-9884-476E-B868-40A1C08BF0C3}.OpenMP_Release|x86.ActiveCfg = OpenMP_Release|x64 {6AAA9C6D-9884-476E-B868-40A1C08BF0C3}.OpenMP_Release|x86.Build.0 = OpenMP_Release|x64 {6AAA9C6D-9884-476E-B868-40A1C08BF0C3}.Release|x64.ActiveCfg = Release|x64 @@ -1986,7 +1979,6 @@ Global {2153CAE1-90AC-4F06-A62C-EB162CED1192}.Matlab_Release|x86.ActiveCfg = Release|x64 {2153CAE1-90AC-4F06-A62C-EB162CED1192}.Matlab_Release|x86.Build.0 = Release|x64 {2153CAE1-90AC-4F06-A62C-EB162CED1192}.OpenMP_Release|x64.ActiveCfg = OpenMP_Release|x64 - {2153CAE1-90AC-4F06-A62C-EB162CED1192}.OpenMP_Release|x64.Build.0 = OpenMP_Release|x64 {2153CAE1-90AC-4F06-A62C-EB162CED1192}.OpenMP_Release|x86.ActiveCfg = OpenMP_Release|x64 {2153CAE1-90AC-4F06-A62C-EB162CED1192}.OpenMP_Release|x86.Build.0 = OpenMP_Release|x64 {2153CAE1-90AC-4F06-A62C-EB162CED1192}.Release|x64.ActiveCfg = Release|x64 @@ -2046,7 +2038,6 @@ Global {FD3D570E-D4D4-4EB3-8894-8C1190EC7989}.Matlab_Release|x86.ActiveCfg = Release|x64 {FD3D570E-D4D4-4EB3-8894-8C1190EC7989}.Matlab_Release|x86.Build.0 = Release|x64 {FD3D570E-D4D4-4EB3-8894-8C1190EC7989}.OpenMP_Release|x64.ActiveCfg = OpenMP_Release|x64 - {FD3D570E-D4D4-4EB3-8894-8C1190EC7989}.OpenMP_Release|x64.Build.0 = OpenMP_Release|x64 {FD3D570E-D4D4-4EB3-8894-8C1190EC7989}.OpenMP_Release|x86.ActiveCfg = OpenMP_Release|x64 {FD3D570E-D4D4-4EB3-8894-8C1190EC7989}.OpenMP_Release|x86.Build.0 = OpenMP_Release|x64 {FD3D570E-D4D4-4EB3-8894-8C1190EC7989}.Release|x64.ActiveCfg = Release|x64 @@ -2108,7 +2099,6 @@ Global {49A73297-8D2F-4C60-BDC8-B20533516368}.Matlab_Release|x86.ActiveCfg = Release|x64 {49A73297-8D2F-4C60-BDC8-B20533516368}.Matlab_Release|x86.Build.0 = Release|x64 {49A73297-8D2F-4C60-BDC8-B20533516368}.OpenMP_Release|x64.ActiveCfg = OpenMP_Release|x64 - {49A73297-8D2F-4C60-BDC8-B20533516368}.OpenMP_Release|x64.Build.0 = OpenMP_Release|x64 {49A73297-8D2F-4C60-BDC8-B20533516368}.OpenMP_Release|x86.ActiveCfg = OpenMP_Release|x64 {49A73297-8D2F-4C60-BDC8-B20533516368}.OpenMP_Release|x86.Build.0 = OpenMP_Release|x64 {49A73297-8D2F-4C60-BDC8-B20533516368}.Release|x64.ActiveCfg = Release|x64 @@ -2138,7 +2128,6 @@ Global {457CA498-DEB4-43D6-97A3-73FA2DFE99D8}.Matlab_Release|x86.ActiveCfg = Release|x64 {457CA498-DEB4-43D6-97A3-73FA2DFE99D8}.Matlab_Release|x86.Build.0 = Release|x64 {457CA498-DEB4-43D6-97A3-73FA2DFE99D8}.OpenMP_Release|x64.ActiveCfg = OpenMP_Release|x64 - {457CA498-DEB4-43D6-97A3-73FA2DFE99D8}.OpenMP_Release|x64.Build.0 = OpenMP_Release|x64 {457CA498-DEB4-43D6-97A3-73FA2DFE99D8}.OpenMP_Release|x86.ActiveCfg = OpenMP_Release|x64 {457CA498-DEB4-43D6-97A3-73FA2DFE99D8}.OpenMP_Release|x86.Build.0 = OpenMP_Release|x64 {457CA498-DEB4-43D6-97A3-73FA2DFE99D8}.Release|x64.ActiveCfg = Release|x64 @@ -2168,7 +2157,6 @@ Global {EA3CEB08-28EE-4A74-B09F-34CBC5CD9418}.Matlab_Release|x86.ActiveCfg = Release|x64 {EA3CEB08-28EE-4A74-B09F-34CBC5CD9418}.Matlab_Release|x86.Build.0 = Release|x64 {EA3CEB08-28EE-4A74-B09F-34CBC5CD9418}.OpenMP_Release|x64.ActiveCfg = OpenMP_Release|x64 - {EA3CEB08-28EE-4A74-B09F-34CBC5CD9418}.OpenMP_Release|x64.Build.0 = OpenMP_Release|x64 {EA3CEB08-28EE-4A74-B09F-34CBC5CD9418}.OpenMP_Release|x86.ActiveCfg = OpenMP_Release|x64 {EA3CEB08-28EE-4A74-B09F-34CBC5CD9418}.OpenMP_Release|x86.Build.0 = OpenMP_Release|x64 {EA3CEB08-28EE-4A74-B09F-34CBC5CD9418}.Release|x64.ActiveCfg = Release|x64 @@ -2198,7 +2186,6 @@ Global {71E6A09C-4569-4C24-87AF-8C9EE0C545BA}.Matlab_Release|x86.ActiveCfg = Release|x64 {71E6A09C-4569-4C24-87AF-8C9EE0C545BA}.Matlab_Release|x86.Build.0 = Release|x64 {71E6A09C-4569-4C24-87AF-8C9EE0C545BA}.OpenMP_Release|x64.ActiveCfg = OpenMP_Release|x64 - {71E6A09C-4569-4C24-87AF-8C9EE0C545BA}.OpenMP_Release|x64.Build.0 = OpenMP_Release|x64 {71E6A09C-4569-4C24-87AF-8C9EE0C545BA}.OpenMP_Release|x86.ActiveCfg = OpenMP_Release|x64 {71E6A09C-4569-4C24-87AF-8C9EE0C545BA}.OpenMP_Release|x86.Build.0 = OpenMP_Release|x64 {71E6A09C-4569-4C24-87AF-8C9EE0C545BA}.Release|x64.ActiveCfg = Release|x64 @@ -2286,7 +2273,6 @@ Global {AC22FAE4-AD5D-40CA-9AAB-6F7AB2AA915D}.Matlab_Release|x86.ActiveCfg = Release|x64 {AC22FAE4-AD5D-40CA-9AAB-6F7AB2AA915D}.Matlab_Release|x86.Build.0 = Release|x64 {AC22FAE4-AD5D-40CA-9AAB-6F7AB2AA915D}.OpenMP_Release|x64.ActiveCfg = Release|x64 - {AC22FAE4-AD5D-40CA-9AAB-6F7AB2AA915D}.OpenMP_Release|x64.Build.0 = Release|x64 {AC22FAE4-AD5D-40CA-9AAB-6F7AB2AA915D}.OpenMP_Release|x86.ActiveCfg = Release|x64 {AC22FAE4-AD5D-40CA-9AAB-6F7AB2AA915D}.OpenMP_Release|x86.Build.0 = Release|x64 {AC22FAE4-AD5D-40CA-9AAB-6F7AB2AA915D}.Release|x64.ActiveCfg = Release|x64 @@ -2316,7 +2302,6 @@ Global {9425AE4C-5879-4ED4-AA29-6A07F7C1B62E}.Matlab_Release|x86.ActiveCfg = Release|x64 {9425AE4C-5879-4ED4-AA29-6A07F7C1B62E}.Matlab_Release|x86.Build.0 = Release|x64 {9425AE4C-5879-4ED4-AA29-6A07F7C1B62E}.OpenMP_Release|x64.ActiveCfg = Release|x64 - {9425AE4C-5879-4ED4-AA29-6A07F7C1B62E}.OpenMP_Release|x64.Build.0 = Release|x64 {9425AE4C-5879-4ED4-AA29-6A07F7C1B62E}.OpenMP_Release|x86.ActiveCfg = Release|x64 {9425AE4C-5879-4ED4-AA29-6A07F7C1B62E}.OpenMP_Release|x86.Build.0 = Release|x64 {9425AE4C-5879-4ED4-AA29-6A07F7C1B62E}.Release|x64.ActiveCfg = Release|x64 @@ -2346,7 +2331,6 @@ Global {4AB5A895-5B6B-406D-AE36-85F5F5940974}.Matlab_Release|x86.ActiveCfg = Release|x64 {4AB5A895-5B6B-406D-AE36-85F5F5940974}.Matlab_Release|x86.Build.0 = Release|x64 {4AB5A895-5B6B-406D-AE36-85F5F5940974}.OpenMP_Release|x64.ActiveCfg = Release|x64 - {4AB5A895-5B6B-406D-AE36-85F5F5940974}.OpenMP_Release|x64.Build.0 = Release|x64 {4AB5A895-5B6B-406D-AE36-85F5F5940974}.OpenMP_Release|x86.ActiveCfg = Release|x64 {4AB5A895-5B6B-406D-AE36-85F5F5940974}.OpenMP_Release|x86.Build.0 = Release|x64 {4AB5A895-5B6B-406D-AE36-85F5F5940974}.Release|x64.ActiveCfg = Release|x64 @@ -2408,7 +2392,6 @@ Global {9779535B-8DE4-4484-8B59-6E78344D658A}.Matlab_Release|x86.ActiveCfg = Release|x64 {9779535B-8DE4-4484-8B59-6E78344D658A}.Matlab_Release|x86.Build.0 = Release|x64 {9779535B-8DE4-4484-8B59-6E78344D658A}.OpenMP_Release|x64.ActiveCfg = OpenMP_Release|x64 - {9779535B-8DE4-4484-8B59-6E78344D658A}.OpenMP_Release|x64.Build.0 = OpenMP_Release|x64 {9779535B-8DE4-4484-8B59-6E78344D658A}.OpenMP_Release|x86.ActiveCfg = OpenMP_Release|x64 {9779535B-8DE4-4484-8B59-6E78344D658A}.OpenMP_Release|x86.Build.0 = OpenMP_Release|x64 {9779535B-8DE4-4484-8B59-6E78344D658A}.Release|x64.ActiveCfg = Release|x64 diff --git a/vs-build/RunRegistry.bat b/vs-build/RunRegistry.bat index d21db165b7..f413323c6e 100644 --- a/vs-build/RunRegistry.bat +++ b/vs-build/RunRegistry.bat @@ -72,6 +72,12 @@ SET Output_Loc=%CURR_LOC% %REGISTRY% "%CURR_LOC%\Registry_NWTC_Library_base.txt" %ALL_FAST_Includes% -O "%Output_Loc%" -noextrap GOTO checkError +:GridInterp +SET CURR_LOC=%NWTC_Lib_Loc% +SET Output_Loc=%CURR_LOC% +%REGISTRY% "%CURR_LOC%\GridInterp.txt " -I "%NWTC_Lib_Loc%" -I "%CURR_LOC%" -O "%Output_Loc%" -noextrap +GOTO checkError + :MAP SET CURR_LOC=%MAP_Loc% SET Output_Loc=%CURR_LOC% diff --git a/vs-build/SoilDyn/SoilDyn-w-registry.sln b/vs-build/SoilDyn/SoilDyn-w-registry.sln deleted file mode 100644 index 7b448a55d4..0000000000 --- a/vs-build/SoilDyn/SoilDyn-w-registry.sln +++ /dev/null @@ -1,64 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.1022 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{6989167D-11E4-40FE-8C1A-2192A86A7E90}") = "SoilDyn", "SoilDyn.vfproj", "{815C302F-A93D-4C22-9329-7112345113C0}" - ProjectSection(ProjectDependencies) = postProject - {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16} = {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FAST_Registry", "..\Registry\FAST_Registry.vcxproj", "{DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug_Double|Win32 = Debug_Double|Win32 - Debug_Double|x64 = Debug_Double|x64 - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release_Double|Win32 = Release_Double|Win32 - Release_Double|x64 = Release_Double|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {815C302F-A93D-4C22-9329-7112345113C0}.Debug_Double|Win32.ActiveCfg = Debug_Double|Win32 - {815C302F-A93D-4C22-9329-7112345113C0}.Debug_Double|Win32.Build.0 = Debug_Double|Win32 - {815C302F-A93D-4C22-9329-7112345113C0}.Debug_Double|x64.ActiveCfg = Debug_Double|x64 - {815C302F-A93D-4C22-9329-7112345113C0}.Debug_Double|x64.Build.0 = Debug_Double|x64 - {815C302F-A93D-4C22-9329-7112345113C0}.Debug|Win32.ActiveCfg = Debug|Win32 - {815C302F-A93D-4C22-9329-7112345113C0}.Debug|Win32.Build.0 = Debug|Win32 - {815C302F-A93D-4C22-9329-7112345113C0}.Debug|x64.ActiveCfg = Debug|x64 - {815C302F-A93D-4C22-9329-7112345113C0}.Debug|x64.Build.0 = Debug|x64 - {815C302F-A93D-4C22-9329-7112345113C0}.Release_Double|Win32.ActiveCfg = Release_Double|Win32 - {815C302F-A93D-4C22-9329-7112345113C0}.Release_Double|Win32.Build.0 = Release_Double|Win32 - {815C302F-A93D-4C22-9329-7112345113C0}.Release_Double|x64.ActiveCfg = Release_Double|x64 - {815C302F-A93D-4C22-9329-7112345113C0}.Release_Double|x64.Build.0 = Release_Double|x64 - {815C302F-A93D-4C22-9329-7112345113C0}.Release|Win32.ActiveCfg = Release|Win32 - {815C302F-A93D-4C22-9329-7112345113C0}.Release|Win32.Build.0 = Release|Win32 - {815C302F-A93D-4C22-9329-7112345113C0}.Release|x64.ActiveCfg = Release|x64 - {815C302F-A93D-4C22-9329-7112345113C0}.Release|x64.Build.0 = Release|x64 - {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug_Double|Win32.ActiveCfg = Debug|Win32 - {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug_Double|Win32.Build.0 = Debug|Win32 - {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug_Double|x64.ActiveCfg = Debug|x64 - {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug_Double|x64.Build.0 = Debug|x64 - {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug|Win32.ActiveCfg = Release|Win32 - {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug|Win32.Build.0 = Release|Win32 - {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug|x64.ActiveCfg = Debug|x64 - {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug|x64.Build.0 = Debug|x64 - {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release_Double|Win32.ActiveCfg = Release|Win32 - {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release_Double|Win32.Build.0 = Release|Win32 - {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release_Double|x64.ActiveCfg = Release|x64 - {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release_Double|x64.Build.0 = Release|x64 - {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release|Win32.ActiveCfg = Release|Win32 - {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release|Win32.Build.0 = Release|Win32 - {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release|x64.ActiveCfg = Release|x64 - {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {A0376D01-250D-4BCF-8D81-F82B933958E7} - EndGlobalSection -EndGlobal diff --git a/vs-build/SoilDyn/SoilDyn.vfproj b/vs-build/SoilDyn/SoilDyn.vfproj deleted file mode 100644 index 53fa2e93e0..0000000000 --- a/vs-build/SoilDyn/SoilDyn.vfproj +++ /dev/null @@ -1,296 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vs-build/modules/NWTC-Library.vfproj b/vs-build/modules/NWTC-Library.vfproj index ad6d1771ac..abce4b81ca 100644 --- a/vs-build/modules/NWTC-Library.vfproj +++ b/vs-build/modules/NWTC-Library.vfproj @@ -91,7 +91,28 @@ - + + + + + + + + + + + + + + + + + + + + + + @@ -211,6 +232,8 @@ + +