diff --git a/.github/workflows/sharpy_no_test_needed.yaml b/.github/workflows/sharpy_no_test_needed.yaml new file mode 100644 index 000000000..f5fa4656d --- /dev/null +++ b/.github/workflows/sharpy_no_test_needed.yaml @@ -0,0 +1,18 @@ +name: Python package + +on: + pull_request: + branches: + - master + - develop + - 'rc*' + paths-ignore: + - '*.py' + - 'lib/**' + - '.github/workflows/**' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - run: 'echo "No changes to python files, submodules or workflows, no run required" ' diff --git a/.github/workflows/sharpy_tests.yaml b/.github/workflows/sharpy_tests.yaml index 91b663354..77754c028 100644 --- a/.github/workflows/sharpy_tests.yaml +++ b/.github/workflows/sharpy_tests.yaml @@ -1,6 +1,16 @@ name: Python package -on: [push] +on: + push: + paths: + - '*.py' + - 'lib/*' + - '.github/workflows/*' + pull_request: + branches: + - master + - develop + - 'rc*' jobs: build: @@ -8,7 +18,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6] + python-version: [3.7] steps: - uses: actions/checkout@v2 @@ -24,7 +34,7 @@ jobs: - name: Setup conda uses: s-weigand/setup-conda@v1 with: - python-version: 3.6 + python-version: 3.7 - name: Pre-Install dependencies run: | gfortran --version @@ -45,9 +55,9 @@ jobs: git submodule init git submodule update git fetch -t - source bin/sharpy_vars.sh mkdir build && cd build cmake .. && make install -j 4 && cd .. + pip install . pip install coverage coverage run -m unittest discover - name: Upload Coverage to Codecov diff --git a/.gitignore b/.gitignore index 68198c66a..d43e0f710 100644 --- a/.gitignore +++ b/.gitignore @@ -40,7 +40,7 @@ htmlcov/ .cache nosetests.xml coverage.xml -*,cover +*.cover .hypothesis/ # Translations @@ -89,10 +89,9 @@ ENV/ # Vim stuff *.*~ *.swp -*.txt *.h5 -# project dependant stuff +# project dependent stuff lib/*.so output/* .idea @@ -100,43 +99,21 @@ output/* # figure folders *figs/* -*.csv *.eps *.vtu -utils/eigen_debug/__init__\.py - -utils/eigen_debug/printers\.py - -tests/uvlm/\.DS_Store - -*/.DS_Store output*/ snapshots/ - -.DS_Store - -tests/coupled/dynamic/horten/BFF/ - -tests/coupled/dynamic/horten/BFF2/ - -tests/coupled/prescribed/goland_linear/model_diff/ - +# OSX files +*.DS_Store # linear tools / tests cases *.prof -sharpy/linear/test/test_cases/* -tests/linear/uvlm/figs/* -tests/linear/uvlm/res/* -cases/linear/coupled/figs/* -cases/linear/coupled/res/* figs/* -tests/linear/rom/figs/ - \.spyproject/ # sharpy extension diff --git a/.version.json b/.version.json index f0d3d5195..5640fc512 100644 --- a/.version.json +++ b/.version.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, "label": "release version", - "message": "1.3", + "message": "2.0", "color": "green" } diff --git a/bin/sharpy b/bin/sharpy deleted file mode 100755 index 0cee4a694..000000000 --- a/bin/sharpy +++ /dev/null @@ -1,9 +0,0 @@ -#! /usr/bin/env python -import sys -import sharpy.sharpy_main -import warnings - -data = None -with warnings.catch_warnings(): - warnings.simplefilter('ignore') - data = sharpy.sharpy_main.main(sys.argv) diff --git a/bin/sharpy_vars.sh b/bin/sharpy_vars.sh deleted file mode 100755 index b4c7163a2..000000000 --- a/bin/sharpy_vars.sh +++ /dev/null @@ -1,14 +0,0 @@ -# Alfonso del Carre -# This script loads the required paths for sharpy - -if test -n "$ZSH_VERSION"; then - SCRIPTPATH=${0:a:h} -elif test -n "$BASH_VERSION"; then - SCRIPTPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -fi - -export PATH=$SCRIPTPATH:$PATH -echo "SHARPy added to PATH from the directory: "$SCRIPTPATH - -SCRIPTPATH=$SCRIPTPATH"/.." -export PYTHONPATH=$SCRIPTPATH:$PYTHONPATH diff --git a/docs/source/conf.py b/docs/source/conf.py index 57e4710fb..08e53a2c1 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -74,7 +74,7 @@ # General information about the project. project = 'SHARPy' -copyright = '2021, LoCA Lab ICL' +copyright = '2022, LoCA Lab ICL' author = 'Aeroelastics Lab, Imperial College London' # The version info for the project you're documenting, acts as replacement for @@ -82,9 +82,9 @@ # built documents. # # The short X.Y version. -version = '1.3' +version = '2.0' # The full version, including alpha/beta/rc tags. -release = '1.3' +release = '2.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/source/content/contributing.md b/docs/source/content/contributing.md index c79252053..84a8457a5 100644 --- a/docs/source/content/contributing.md +++ b/docs/source/content/contributing.md @@ -201,3 +201,37 @@ If you contribute, please make sure you know what branch to work from. If in dou Commit names are also important since they are the backbone of the code's change log. Please write concise commit titles and explain the main changes in the body of the commit message. An excellent guide on writing good commit messages can be found [here](https://chris.beams.io/posts/git-commit/). + +# For developers: + +## Releasing a new SHARPy version + +In the release candidate branch: + +1. Update the version number in the docs configuration file `docs/source/conf.py`. Update variables `version` and `release` + +2. Update `version.json` file + +3. Update version in `sharpy/__init__.py` file + +4. Commit, push and wait for tests to pass + +5. Merge release candidate branch into `master` branch + +In the `master` branch: + +1. Run the [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator) tool locally with the following parameters: + ``` + github_changelog_generator -u imperialcollegelondon -p sharpy -t --future-release + ``` + +2. Push the changes to the `CHANGELOG.md` file + +3. Create a release tag. IMPORTANT: ensure it is an *annotated* tag, otherwise the version and commit number in SHARPy will not display properly + ``` + git tag -a + git push origin --tags -f + ``` + where `` is something like `2.0`. + +4. Create the GitHub release, choosing the newly created tag from the dropdown menu. Do not create a tag from the dropdown menu directly because it will not be an annotated tag diff --git a/docs/source/content/example_notebooks/linear_goland_flutter.ipynb b/docs/source/content/example_notebooks/linear_goland_flutter.ipynb index c9d873265..fb5d45c12 100644 --- a/docs/source/content/example_notebooks/linear_goland_flutter.ipynb +++ b/docs/source/content/example_notebooks/linear_goland_flutter.ipynb @@ -1,1507 +1,1507 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Flutter Analysis of a Goland Wing using the SHARPy Linear Solver\n", - "\n", - "This is an example using SHARPy to find the flutter speed of a Goland wing by:\n", - "\n", - "* Calculating aerodynamic forces and deflections using a nonlinear solver\n", - "\n", - "* Linearising about this reference condition\n", - "\n", - "* Creating a reduced order model of the linearised aerodynamics\n", - "\n", - "* Evaluate the stability of the linearised aeroelastic system at different velocities\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### References\n", - "\n", - "Maraniello, S., & Palacios, R. (2019). State-Space Realizations and Internal Balancing in Potential-Flow Aerodynamics with Arbitrary Kinematics. AIAA Journal, 57(6), 1–14. https://doi.org/10.2514/1.J058153\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Required Packages" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "import os\n", - "import sys\n", - "import cases.templates.flying_wings as wings # See this package for the Goland wing structural and aerodynamic definition\n", - "import sharpy.sharpy_main # used to run SHARPy from Jupyter" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Problem Set-up" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Velocity\n", - "\n", - "The UVLM is assembled in normalised time at a velocity of $1 m/s$. The only matrices that need updating then with free stream velocity are the structural matrices, which is significantly cheaper to do than to update the UVLM." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "u_inf = 1.\n", - "alpha_deg = 0.\n", - "rho = 1.02\n", - "num_modes = 4" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Discretisation\n", - "\n", - "Note: To achieve convergence of the flutter results with the ones found in the literature, a significant discretisation may be required. If you are running this notebook for the first time, set `M = 4` initially to verify that your system can perform!" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "M = 16\n", - "N = 32\n", - "M_star_fact = 10" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### ROM\n", - "\n", - "A moment-matching (Krylov subspace) model order reduction technique is employed. This ROM method offers the ability to interpolate the transfer functions at a desired point in the complex plane. See the ROM documentation pages for more info.\n", - "\n", - "Note: this ROM method matches the transfer function but does not guarantee stability. Therefore the resulting system may be unstable. These unstable modes may appear far in the right hand plane but will not affect the flutter speed calculations." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "c_ref = 1.8288 # Goland wing reference chord. Used for frequency normalisation\n", - "rom_settings = dict()\n", - "rom_settings['algorithm'] = 'mimo_rational_arnoldi' # reduction algorithm\n", - "rom_settings['r'] = 6 # Krylov subspace order\n", - "frequency_continuous_k = np.array([0.]) # Interpolation point in the complex plane with reduced frequency units\n", - "frequency_continuous_w = 2 * u_inf * frequency_continuous_k / c_ref\n", - "rom_settings['frequency'] = frequency_continuous_w" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Case Admin" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The case to run will be: goland_csM16N32Ms10_nmodes4rom_MIMORA_r6_sig0000_0000j\n", - "Case files will be saved in ./cases/goland_csM16N32Ms10_nmodes4rom_MIMORA_r6_sig0000_0000j\n", - "Output files will be saved in ./output/goland_csM16N32Ms10_nmodes4rom_MIMORA_r6_sig0000_0000j/\n" - ] - } - ], - "source": [ - "case_name = 'goland_cs'\n", - "case_nlin_info = 'M%dN%dMs%d_nmodes%d' % (M, N, M_star_fact, num_modes)\n", - "case_rom_info = 'rom_MIMORA_r%d_sig%04d_%04dj' % (rom_settings['r'], frequency_continuous_k[-1].real * 100,\n", - " frequency_continuous_k[-1].imag * 100)\n", - "\n", - "case_name += case_nlin_info + case_rom_info\n", - "\n", - "route_test_dir = os.path.abspath('')\n", - "\n", - "print('The case to run will be: %s' % case_name)\n", - "print('Case files will be saved in ./cases/%s' %case_name)\n", - "print('Output files will be saved in ./output/%s/' %case_name)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Simulation Set-Up" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Goland Wing\n", - "\n", - "`ws` is an instance of a Goland wing with a control surface. Reference the template file `cases.templates.flying_wings.GolandControlSurface` for more info on the geometrical, structural and aerodynamic definition of the Goland wing here used." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "ws = wings.GolandControlSurface(M=M,\n", - " N=N,\n", - " Mstar_fact=M_star_fact,\n", - " u_inf=u_inf,\n", - " alpha=alpha_deg,\n", - " cs_deflection=[0, 0],\n", - " rho=rho,\n", - " sweep=0,\n", - " physical_time=2,\n", - " n_surfaces=2,\n", - " route=route_test_dir + '/cases',\n", - " case_name=case_name)\n", - "\n", - "ws.clean_test_files()\n", - "ws.update_derived_params()\n", - "ws.set_default_config_dict()\n", - "\n", - "ws.generate_aero_file()\n", - "ws.generate_fem_file()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Simulation Settings\n", - "\n", - "The settings for each of the solvers are now set. For a detailed description on them please reference their respective documentation pages" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### SHARPy Settings\n", - "\n", - "The most important setting is the `flow` list. It tells SHARPy which solvers to run and in which order." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "ws.config['SHARPy'] = {\n", - " 'flow':\n", - " ['BeamLoader', 'AerogridLoader',\n", - " 'StaticCoupled',\n", - " 'AerogridPlot',\n", - " 'BeamPlot',\n", - " 'Modal',\n", - " 'LinearAssembler',\n", - " 'AsymptoticStability',\n", - " ],\n", - " 'case': ws.case_name, 'route': ws.route,\n", - " 'write_screen': 'on', 'write_log': 'on',\n", - " 'log_folder': route_test_dir + '/output/',\n", - " 'log_file': ws.case_name + '.log'}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Beam Loader Settings" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "ws.config['BeamLoader'] = {\n", - " 'unsteady': 'off',\n", - " 'orientation': ws.quat}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Aerogrid Loader Settings" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "ws.config['AerogridLoader'] = {\n", - " 'unsteady': 'off',\n", - " 'aligned_grid': 'on',\n", - " 'mstar': ws.Mstar_fact * ws.M,\n", - " 'freestream_dir': ws.u_inf_direction,\n", - " 'wake_shape_generator': 'StraightWake',\n", - " 'wake_shape_generator_input': {'u_inf': ws.u_inf,\n", - " 'u_inf_direction': ws.u_inf_direction,\n", - " 'dt': ws.dt}}\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Static Coupled Solver" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "ws.config['StaticCoupled'] = {\n", - " 'print_info': 'on',\n", - " 'max_iter': 200,\n", - " 'n_load_steps': 1,\n", - " 'tolerance': 1e-10,\n", - " 'relaxation_factor': 0.,\n", - " 'aero_solver': 'StaticUvlm',\n", - " 'aero_solver_settings': {\n", - " 'rho': ws.rho,\n", - " 'print_info': 'off',\n", - " 'horseshoe': 'off',\n", - " 'num_cores': 4,\n", - " 'n_rollup': 0,\n", - " 'rollup_dt': ws.dt,\n", - " 'rollup_aic_refresh': 1,\n", - " 'rollup_tolerance': 1e-4,\n", - " 'velocity_field_generator': 'SteadyVelocityField',\n", - " 'velocity_field_input': {\n", - " 'u_inf': ws.u_inf,\n", - " 'u_inf_direction': ws.u_inf_direction}},\n", - " 'structural_solver': 'NonLinearStatic',\n", - " 'structural_solver_settings': {'print_info': 'off',\n", - " 'max_iterations': 150,\n", - " 'num_load_steps': 4,\n", - " 'delta_curved': 1e-1,\n", - " 'min_delta': 1e-10,\n", - " 'gravity_on': 'on',\n", - " 'gravity': 9.81}}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### AerogridPlot Settings" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "ws.config['AerogridPlot'] = {'include_rbm': 'off',\n", - " 'include_applied_forces': 'on',\n", - " 'minus_m_star': 0}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### BeamPlot Settings" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "ws.config['BeamPlot'] = {'include_rbm': 'off',\n", - " 'include_applied_forces': 'on'}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Modal Solver Settings" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "ws.config['Modal'] = {'NumLambda': 20,\n", - " 'rigid_body_modes': 'off',\n", - " 'print_matrices': 'on',\n", - " 'keep_linear_matrices': 'on',\n", - " 'write_dat': 'off',\n", - " 'rigid_modes_cg': 'off',\n", - " 'continuous_eigenvalues': 'off',\n", - " 'dt': 0,\n", - " 'plot_eigenvalues': False,\n", - " 'max_rotation_deg': 15.,\n", - " 'max_displacement': 0.15,\n", - " 'write_modes_vtk': True,\n", - " 'use_undamped_modes': True}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Linear System Assembly Settings" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "ws.config['LinearAssembler'] = {'linear_system': 'LinearAeroelastic',\n", - " 'linear_system_settings': {\n", - " 'beam_settings': {'modal_projection': 'on',\n", - " 'inout_coords': 'modes',\n", - " 'discrete_time': 'on',\n", - " 'newmark_damp': 0.5e-4,\n", - " 'discr_method': 'newmark',\n", - " 'dt': ws.dt,\n", - " 'proj_modes': 'undamped',\n", - " 'use_euler': 'off',\n", - " 'num_modes': num_modes,\n", - " 'print_info': 'on',\n", - " 'gravity': 'on',\n", - " 'remove_sym_modes': 'on',\n", - " 'remove_dofs': []},\n", - " 'aero_settings': {'dt': ws.dt,\n", - " 'ScalingDict': {'length': 0.5 * ws.c_ref,\n", - " 'speed': u_inf,\n", - " 'density': rho},\n", - " 'integr_order': 2,\n", - " 'density': ws.rho,\n", - " 'remove_predictor': 'on',\n", - " 'use_sparse': 'on',\n", - " 'remove_inputs': ['u_gust'],\n", - " 'rom_method': ['Krylov'],\n", - " 'rom_method_settings': {'Krylov': rom_settings}},\n", - " }}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Asymptotic Stability Analysis Settings" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "ws.config['AsymptoticStability'] = {'print_info': True,\n", - " 'velocity_analysis': [100, 180, 81],\n", - " 'modes_to_plot': []}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Write solver settings config file" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "ws.config.write()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Run SHARPy" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--------------------------------------------------------------------------------\u001b[0m\n", - " ###### ## ## ### ######## ######## ## ##\u001b[0m\n", - " ## ## ## ## ## ## ## ## ## ## ## ##\u001b[0m\n", - " ## ## ## ## ## ## ## ## ## ####\u001b[0m\n", - " ###### ######### ## ## ######## ######## ##\u001b[0m\n", - " ## ## ## ######### ## ## ## ##\u001b[0m\n", - " ## ## ## ## ## ## ## ## ## ##\u001b[0m\n", - " ###### ## ## ## ## ## ## ## ##\u001b[0m\n", - "--------------------------------------------------------------------------------\u001b[0m\n", - "Aeroelastics Lab, Aeronautics Department.\u001b[0m\n", - " Copyright (c), Imperial College London.\u001b[0m\n", - " All rights reserved.\u001b[0m\n", - " License available at https://github.com/imperialcollegelondon/sharpy\u001b[0m\n", - "\u001b[36mRunning SHARPy from /home/ng213/2TB/pazy_code/pazy-sharpy/lib/sharpy/docs/source/content/example_notebooks\u001b[0m\n", - "\u001b[36mSHARPy being run is in /home/ng213/2TB/pazy_code/pazy-sharpy/lib/sharpy\u001b[0m\n", - "\u001b[36mThe branch being run is dev_setting_error\u001b[0m\n", - "\u001b[36mThe version and commit hash are: v1.2.1-339-g156c731-156c731\u001b[0m\n", - "SHARPy output folder set\u001b[0m\n", - "\u001b[34m\t/home/ng213/2TB/pazy_code/pazy-sharpy/lib/sharpy/docs/source/content/example_notebooks/output//goland_csM16N32Ms10_nmodes4rom_MIMORA_r6_sig0000_0000j/\u001b[0m\n", - "\u001b[36mGenerating an instance of BeamLoader\u001b[0m\n", - "Variable for_pos has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: [0.0, 0, 0]\u001b[0m\n", - "\u001b[36mGenerating an instance of AerogridLoader\u001b[0m\n", - "Variable control_surface_deflection has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: []\u001b[0m\n", - "Variable control_surface_deflection_generator_settings has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: {}\u001b[0m\n", - "Variable dx1 has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: -1.0\u001b[0m\n", - "Variable ndx1 has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1\u001b[0m\n", - "Variable r has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1.0\u001b[0m\n", - "Variable dxmax has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: -1.0\u001b[0m\n", - "\u001b[34mThe aerodynamic grid contains 2 surfaces\u001b[0m\n", - "\u001b[34m Surface 0, M=16, N=16\u001b[0m\n", - " Wake 0, M=160, N=16\u001b[0m\n", - "\u001b[34m Surface 1, M=16, N=16\u001b[0m\n", - " Wake 1, M=160, N=16\u001b[0m\n", - " In total: 512 bound panels\u001b[0m\n", - " In total: 5120 wake panels\u001b[0m\n", - " Total number of panels = 5632\u001b[0m\n", - "\u001b[36mGenerating an instance of StaticCoupled\u001b[0m\n", - "Variable correct_forces_method has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: \u001b[0m\n", - "Variable runtime_generators has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: {}\u001b[0m\n", - "\u001b[36mGenerating an instance of NonLinearStatic\u001b[0m\n", - "Variable newmark_damp has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0.0001\u001b[0m\n", - "Variable gravity_dir has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: [0.0, 0.0, 1.0]\u001b[0m\n", - "Variable relaxation_factor has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0.3\u001b[0m\n", - "Variable dt has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0.01\u001b[0m\n", - "Variable num_steps has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 500\u001b[0m\n", - "Variable initial_position has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: [0. 0. 0.]\u001b[0m\n", - "\u001b[36mGenerating an instance of StaticUvlm\u001b[0m\n", - "Variable iterative_solver has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable iterative_tol has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0.0001\u001b[0m\n", - "Variable iterative_precond has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable cfl1 has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: True\u001b[0m\n", - "Variable vortex_radius has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1e-06\u001b[0m\n", - "Variable vortex_radius_wake_ind has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1e-06\u001b[0m\n", - "Variable rbm_vel_g has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]\u001b[0m\n", - "Variable centre_rot_g has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: [0.0, 0.0, 0.0]\u001b[0m\n", - "\u001b[0m\n", - "\u001b[0m\n", - "\u001b[0m\n", - "|=====|=====|============|==========|==========|==========|==========|==========|==========|\u001b[0m\n", - "|iter |step | log10(res) | Fx | Fy | Fz | Mx | My | Mz |\u001b[0m\n", - "|=====|=====|============|==========|==========|==========|==========|==========|==========|\u001b[0m\n", - "| 0 | 0 | 0.00000 | 0.0000 | -0.0000 |-4271.0417| 0.0000 | 781.0842 | 0.0000 |\u001b[0m\n", - "| 1 | 0 | -11.89144 | 0.0000 | -0.0000 |-4271.0039| 0.0000 | 781.0906 | 0.0000 |\u001b[0m\n", - "\u001b[36mGenerating an instance of AerogridPlot\u001b[0m\n", - "Variable include_forward_motion has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable include_unsteady_applied_forces has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable name_prefix has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: \u001b[0m\n", - "Variable u_inf has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0.0\u001b[0m\n", - "Variable dt has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0.0\u001b[0m\n", - "Variable include_velocities has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable include_incidence_angle has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable num_cores has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1\u001b[0m\n", - "Variable vortex_radius has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1e-06\u001b[0m\n", - "\u001b[34m...Finished\u001b[0m\n", - "\u001b[36mGenerating an instance of BeamPlot\u001b[0m\n", - "Variable include_FoR has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable include_applied_moments has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: True\u001b[0m\n", - "Variable name_prefix has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: \u001b[0m\n", - "Variable output_rbm has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: True\u001b[0m\n", - "\u001b[34m...Finished\u001b[0m\n", - "\u001b[36mGenerating an instance of Modal\u001b[0m\n", - "Variable print_info has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: True\u001b[0m\n", - "Variable delta_curved has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0.01\u001b[0m\n", - "Variable use_custom_timestep has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: -1\u001b[0m\n", - "Structural eigenvalues\u001b[0m\n", - "\u001b[0m\n", - "\u001b[0m\n", - "\u001b[0m\n", - "|==============|==============|==============|==============|==============|==============|==============|\u001b[0m\n", - "| mode | eval_real | eval_imag | freq_n (Hz) | freq_d (Hz) | damping | period (s) |\u001b[0m\n", - "|==============|==============|==============|==============|==============|==============|==============|\u001b[0m\n", - "| 0 | 0.000000 | 48.067396 | 7.650164 | 7.650164 | -0.000000 | 0.130716 |\u001b[0m\n", - "| 1 | 0.000000 | 48.067398 | 7.650164 | 7.650164 | -0.000000 | 0.130716 |\u001b[0m\n", - "| 2 | 0.000000 | 95.685736 | 15.228858 | 15.228858 | -0.000000 | 0.065665 |\u001b[0m\n", - "| 3 | 0.000000 | 95.685754 | 15.228861 | 15.228861 | -0.000000 | 0.065665 |\u001b[0m\n", - "| 4 | 0.000000 | 243.144471 | 38.697644 | 38.697644 | -0.000000 | 0.025841 |\u001b[0m\n", - "| 5 | 0.000000 | 243.144477 | 38.697645 | 38.697645 | -0.000000 | 0.025841 |\u001b[0m\n", - "| 6 | 0.000000 | 343.801136 | 54.717650 | 54.717650 | -0.000000 | 0.018276 |\u001b[0m\n", - "| 7 | 0.000000 | 343.801137 | 54.717650 | 54.717650 | -0.000000 | 0.018276 |\u001b[0m\n", - "| 8 | 0.000000 | 443.324608 | 70.557303 | 70.557303 | -0.000000 | 0.014173 |\u001b[0m\n", - "| 9 | 0.000000 | 443.324619 | 70.557304 | 70.557304 | -0.000000 | 0.014173 |\u001b[0m\n", - "| 10 | 0.000000 | 461.992869 | 73.528449 | 73.528449 | -0.000000 | 0.013600 |\u001b[0m\n", - "| 11 | 0.000000 | 461.992869 | 73.528449 | 73.528449 | -0.000000 | 0.013600 |\u001b[0m\n", - "| 12 | 0.000000 | 601.126871 | 95.672313 | 95.672313 | -0.000000 | 0.010452 |\u001b[0m\n", - "| 13 | 0.000000 | 601.126873 | 95.672313 | 95.672313 | -0.000000 | 0.010452 |\u001b[0m\n", - "| 14 | 0.000000 | 782.997645 | 124.617946 | 124.617946 | -0.000000 | 0.008025 |\u001b[0m\n", - "| 15 | 0.000000 | 782.997649 | 124.617946 | 124.617946 | -0.000000 | 0.008025 |\u001b[0m\n", - "| 16 | 0.000000 | 917.191257 | 145.975522 | 145.975522 | -0.000000 | 0.006850 |\u001b[0m\n", - "| 17 | 0.000000 | 917.191259 | 145.975523 | 145.975523 | -0.000000 | 0.006850 |\u001b[0m\n", - "| 18 | 0.000000 | 975.005694 | 155.176976 | 155.176976 | -0.000000 | 0.006444 |\u001b[0m\n", - "| 19 | 0.000000 | 975.005699 | 155.176977 | 155.176977 | -0.000000 | 0.006444 |\u001b[0m\n", - "\u001b[36mGenerating an instance of LinearAssembler\u001b[0m\n", - "Variable linearisation_tstep has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: -1\u001b[0m\n", - "Variable modal_tstep has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: -1\u001b[0m\n", - "Variable inout_coordinates has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: \u001b[0m\n", - "Variable retain_inputs has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: []\u001b[0m\n", - "Variable retain_outputs has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: []\u001b[0m\n", - "\u001b[36mGenerating an instance of LinearAeroelastic\u001b[0m\n", - "Variable uvlm_filename has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: \u001b[0m\n", - "Variable track_body has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: True\u001b[0m\n", - "Variable use_euler has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "\u001b[36mGenerating an instance of LinearUVLM\u001b[0m\n", - "Variable gust_assembler has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: \u001b[0m\n", - "Variable vortex_radius has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1e-06\u001b[0m\n", - "Variable cfl1 has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: True\u001b[0m\n", - "Variable velocity_field_generator has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: SteadyVelocityField\u001b[0m\n", - "Variable velocity_field_input has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: {}\u001b[0m\n", - "Variable physical_model has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: True\u001b[0m\n", - "Variable track_body has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable track_body_number has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: -1\u001b[0m\n", - "Initialising Static linear UVLM solver class...\u001b[0m\n", - "\t\t\t...done in 1.35 sec\u001b[0m\n", - "\u001b[36mGenerating an instance of Krylov\u001b[0m\n", - "Variable print_info has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: True\u001b[0m\n", - "Variable single_side has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: \u001b[0m\n", - "Variable tangent_input_file has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: \u001b[0m\n", - "Variable restart_arnoldi has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Initialising Krylov Model Order Reduction\u001b[0m\n", - "State-space realisation of UVLM equations started...\u001b[0m\n", - "\u001b[34mComputing wake propagation matrix with CFL1=True\u001b[0m\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/ng213/anaconda3/envs/sharpy_env/lib/python3.7/site-packages/scipy/sparse/_index.py:126: SparseEfficiencyWarning: Changing the sparsity structure of a csc_matrix is expensive. lil_matrix is more efficient.\n", - " self._set_arrayXarray(i, j, x)\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[34m\tstate-space model produced in form:\u001b[0m\n", - "\u001b[34m\t\t\th_{n+1} = A h_{n} + B u_{n}\u001b[0m\n", - "\u001b[34m\t\t\twith:\u001b[0m\n", - "\u001b[34m\tx_n = h_n + Bp u_n\u001b[0m\n", - "\t\t\t...done in 17.25 sec\u001b[0m\n", - "Scaling UVLM system with reference time 0.914400s\u001b[0m\n", - "\u001b[34mNon-dimensional time step set (0.125000)\u001b[0m\n", - "System scaled in 31.698308s\u001b[0m\n", - "\u001b[36mGenerating an instance of LinearBeam\u001b[0m\n", - "Warning, projecting system with damping onto undamped modes\u001b[0m\n", - "\u001b[0m\n", - "Linearising gravity terms...\u001b[0m\n", - "\u001b[34m\tM = 7.26 kg\u001b[0m\n", - "\u001b[34m\tX_CG A -> 0.00 0.00 -0.00\u001b[0m\n", - "\u001b[36mNode 1 \t-> B -0.000 -0.116 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 0.381 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 0.381 -0.000\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", - "\u001b[36mNode 2 \t-> B -0.000 -0.116 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 0.762 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 0.762 -0.000\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", - "\u001b[36mNode 3 \t-> B -0.000 -0.116 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 1.143 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 1.143 -0.000\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", - "\u001b[36mNode 4 \t-> B -0.000 -0.116 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 1.524 -0.001\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 1.524 -0.001\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", - "\u001b[36mNode 5 \t-> B -0.000 -0.116 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 1.905 -0.001\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 1.905 -0.001\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", - "\u001b[36mNode 6 \t-> B -0.000 -0.116 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 2.286 -0.001\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 2.286 -0.001\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", - "\u001b[36mNode 7 \t-> B -0.000 -0.116 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 2.667 -0.002\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 2.667 -0.002\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", - "\u001b[36mNode 8 \t-> B -0.000 -0.116 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 3.048 -0.002\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 3.048 -0.002\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", - "\u001b[36mNode 9 \t-> B -0.000 -0.116 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 3.429 -0.003\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 3.429 -0.003\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", - "\u001b[36mNode 10 \t-> B -0.000 -0.116 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 3.810 -0.003\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 3.810 -0.003\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", - "\u001b[36mNode 11 \t-> B -0.000 -0.116 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 4.191 -0.004\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 4.191 -0.004\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", - "\u001b[36mNode 12 \t-> B -0.000 -0.116 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 4.572 -0.004\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 4.572 -0.004\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", - "\u001b[36mNode 13 \t-> B -0.000 -0.116 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 4.953 -0.005\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 4.953 -0.005\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", - "\u001b[36mNode 14 \t-> B -0.000 -0.116 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 5.334 -0.005\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 5.334 -0.005\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", - "\u001b[36mNode 15 \t-> B -0.000 -0.116 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 5.715 -0.006\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 5.715 -0.006\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", - "\u001b[36mNode 16 \t-> B -0.000 -0.116 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 6.096 -0.006\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 6.096 -0.006\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 3.6281\u001b[0m\n", - "\u001b[36mNode 17 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 -6.096 -0.006\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 -6.096 -0.006\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 3.6281\u001b[0m\n", - "\u001b[36mNode 18 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 -5.715 -0.006\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 -5.715 -0.006\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", - "\u001b[36mNode 19 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 -5.334 -0.005\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 -5.334 -0.005\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", - "\u001b[36mNode 20 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 -4.953 -0.005\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 -4.953 -0.005\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", - "\u001b[36mNode 21 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 -4.572 -0.004\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 -4.572 -0.004\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", - "\u001b[36mNode 22 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 -4.191 -0.004\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 -4.191 -0.004\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", - "\u001b[36mNode 23 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 -3.810 -0.003\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 -3.810 -0.003\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", - "\u001b[36mNode 24 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 -3.429 -0.003\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 -3.429 -0.003\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", - "\u001b[36mNode 25 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 -3.048 -0.002\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 -3.048 -0.002\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", - "\u001b[36mNode 26 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 -2.667 -0.002\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 -2.667 -0.002\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", - "\u001b[36mNode 27 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 -2.286 -0.002\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 -2.286 -0.002\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", - "\u001b[36mNode 28 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 -1.905 -0.001\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 -1.905 -0.001\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", - "\u001b[36mNode 29 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 -1.524 -0.001\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 -1.524 -0.001\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", - "\u001b[36mNode 30 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 -1.143 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 -1.143 -0.000\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", - "\u001b[36mNode 31 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 -0.762 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 -0.762 -0.000\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", - "\u001b[36mNode 32 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.116 -0.381 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.116 -0.381 -0.000\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", - " Updated the beam C, modal C and K matrices with the terms from the\n", - "gravity linearisation\u001b[0m\n", - "\u001b[0m\n", - "Scaling beam according to reduced time...\u001b[0m\n", - "\u001b[34m\tSetting the beam time step to (0.1250)\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "Model Order Reduction in progress...\u001b[0m\n", - "Moment Matching Krylov Model Reduction\u001b[0m\n", - "\tConstruction Algorithm:\u001b[0m\n", - "\u001b[34m\t\tmimo_rational_arnoldi\u001b[0m\n", - "\tInterpolation points:\u001b[0m\n", - "\u001b[34m\t\tsigma = 0.000000 + 0.000000j [rad/s]\u001b[0m\n", - "\u001b[34m\u001b[0m\n", - "\tKrylov order:\u001b[0m\n", - "\u001b[34m\t\tr = 6\u001b[0m\n", - "\u001b[34m\tConstructing controllability space\u001b[0m\n", - "\u001b[34m\tConstructing observability space\u001b[0m\n", - "\u001b[34m\tDeflating column 23\u001b[0m\n", - "\u001b[34m\tDeflating column 25\u001b[0m\n", - "\u001b[34m\tDeflating column 28\u001b[0m\n", - "\u001b[34m\tDeflating column 33\u001b[0m\n", - "ROM is stable\u001b[0m\n", - "\tDT Eigenvalues:\u001b[0m\n", - "\t\tmu = 0.992076 + -0.000000j\u001b[0m\n", - "\t\tmu = 0.971409 + -0.000000j\u001b[0m\n", - "\t\tmu = 0.957534 + -0.038927j\u001b[0m\n", - "\t\tmu = 0.957534 + 0.038927j\u001b[0m\n", - "\t\tmu = 0.954926 + -0.072820j\u001b[0m\n", - "\t\tmu = 0.954926 + 0.072820j\u001b[0m\n", - "\t\tmu = 0.954282 + -0.000000j\u001b[0m\n", - "\t\tmu = 0.935272 + 0.000000j\u001b[0m\n", - "\t\tmu = 0.927865 + 0.092012j\u001b[0m\n", - "\t\tmu = 0.927865 + -0.092012j\u001b[0m\n", - "\t\tmu = 0.927075 + -0.066636j\u001b[0m\n", - "\t\tmu = 0.927075 + 0.066636j\u001b[0m\n", - "\t\tmu = 0.925588 + 0.038813j\u001b[0m\n", - "\t\tmu = 0.925588 + -0.038813j\u001b[0m\n", - "\t\tmu = 0.922836 + -0.004482j\u001b[0m\n", - "\t\tmu = 0.922836 + 0.004482j\u001b[0m\n", - "\t\tmu = 0.915491 + -0.025196j\u001b[0m\n", - "\t\tmu = 0.915491 + 0.025196j\u001b[0m\n", - "\t\tmu = 0.908662 + 0.053066j\u001b[0m\n", - "\t\tmu = 0.908662 + -0.053066j\u001b[0m\n", - "\t\tmu = 0.881127 + -0.056743j\u001b[0m\n", - "\t\tmu = 0.881127 + 0.056743j\u001b[0m\n", - "\t\tmu = 0.882642 + -0.000000j\u001b[0m\n", - "\t\tmu = 0.867726 + -0.000000j\u001b[0m\n", - "\t\tmu = 0.717587 + 0.263893j\u001b[0m\n", - "\t\tmu = 0.717587 + -0.263893j\u001b[0m\n", - "\t\tmu = 0.697184 + 0.000000j\u001b[0m\n", - "\t\tmu = 0.360807 + 0.000000j\u001b[0m\n", - "\t\tmu = -0.000018 + -0.002663j\u001b[0m\n", - "\t\tmu = -0.000018 + 0.002663j\u001b[0m\n", - "\t\tmu = 0.000155 + -0.000000j\u001b[0m\n", - "\t\tmu = -0.000154 + 0.000000j\u001b[0m\n", - "\u001b[0m\n", - "System reduced from order 6656 to \u001b[0m\n", - "\u001b[34m\tn = 32 states\u001b[0m\n", - "...Completed Model Order Reduction in 5.16 s\u001b[0m\n", - "Aeroelastic system assembled:\u001b[0m\n", - "\u001b[34m\tAerodynamic states: 32\u001b[0m\n", - "\u001b[34m\tStructural states: 4\u001b[0m\n", - "\u001b[34m\tTotal states: 36\u001b[0m\n", - "\u001b[34m\tInputs: 8\u001b[0m\n", - "\u001b[34m\tOutputs: 6\u001b[0m\n", - "\u001b[34mFinal system is:\u001b[0m\n", - "\u001b[34mState-space system\u001b[0m\n", - "\u001b[34mStates: 36\u001b[0m\n", - "\u001b[34mInputs: 8\u001b[0m\n", - "\u001b[34mOutputs: 6\u001b[0m\n", - "\u001b[34m\u001b[0m\n", - "\u001b[36mGenerating an instance of AsymptoticStability\u001b[0m\n", - "Variable reference_velocity has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1.0\u001b[0m\n", - "Variable frequency_cutoff has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0.0\u001b[0m\n", - "Variable export_eigenvalues has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable display_root_locus has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable iterative_eigvals has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable num_evals has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 200\u001b[0m\n", - "Variable postprocessors has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: []\u001b[0m\n", - "Variable postprocessors_settings has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: {}\u001b[0m\n", - "Dynamical System Eigenvalues\u001b[0m\n", - "Calculating eigenvalues using direct method\u001b[0m\n", - "\u001b[0m\n", - "\u001b[0m\n", - "\u001b[0m\n", - "|==============|==============|==============|==============|==============|==============|==============|\u001b[0m\n", - "| mode | eval_real | eval_imag | freq_n (Hz) | freq_d (Hz) | damping | period (s) |\u001b[0m\n", - "|==============|==============|==============|==============|==============|==============|==============|\u001b[0m\n", - "| 0 | -0.021637 | 24.315428 | 3.869922 | 3.869921 | 0.000890 | 0.258403 |\u001b[0m\n", - "| 1 | -0.021637 | -24.315428 | 3.869922 | 3.869921 | 0.000890 | 0.258403 |\u001b[0m\n", - "| 2 | -0.069601 | -0.000000 | 0.011077 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 3 | -0.105223 | -21.320535 | 3.393310 | 3.393269 | 0.004935 | 0.294701 |\u001b[0m\n", - "| 4 | -0.105223 | 21.320535 | 3.393310 | 3.393269 | 0.004935 | 0.294701 |\u001b[0m\n", - "| 5 | -0.253787 | -0.000000 | 0.040391 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 6 | -0.372425 | 0.355473 | 0.081940 | 0.056575 | 0.723379 | 17.675568 |\u001b[0m\n", - "| 7 | -0.372425 | -0.355473 | 0.081940 | 0.056575 | 0.723379 | 17.675568 |\u001b[0m\n", - "| 8 | -0.378143 | 0.665882 | 0.121875 | 0.105978 | 0.493812 | 9.435879 |\u001b[0m\n", - "| 9 | -0.378143 | -0.665882 | 0.121875 | 0.105978 | 0.493812 | 9.435879 |\u001b[0m\n", - "| 10 | -0.409413 | -0.000000 | 0.065160 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 11 | -0.585461 | 0.000000 | 0.093179 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 12 | -0.612214 | -0.864763 | 0.168631 | 0.137631 | 0.577812 | 7.265789 |\u001b[0m\n", - "| 13 | -0.612214 | 0.864763 | 0.168631 | 0.137631 | 0.577812 | 7.265789 |\u001b[0m\n", - "| 14 | -0.639935 | 0.627769 | 0.142673 | 0.099913 | 0.713860 | 10.008752 |\u001b[0m\n", - "| 15 | -0.639935 | -0.627769 | 0.142673 | 0.099913 | 0.713860 | 10.008752 |\u001b[0m\n", - "| 16 | -0.668833 | 0.366654 | 0.121394 | 0.058355 | 0.876881 | 17.136529 |\u001b[0m\n", - "| 17 | -0.668833 | -0.366654 | 0.121394 | 0.058355 | 0.876881 | 17.136529 |\u001b[0m\n", - "| 18 | -0.702465 | 0.042487 | 0.112005 | 0.006762 | 0.998176 | 147.883816 |\u001b[0m\n", - "| 19 | -0.702465 | -0.042487 | 0.112005 | 0.006762 | 0.998176 | 147.883816 |\u001b[0m\n", - "| 20 | -0.769174 | 0.240726 | 0.128273 | 0.038313 | 0.954353 | 26.100974 |\u001b[0m\n", - "| 21 | -0.769174 | -0.240726 | 0.128273 | 0.038313 | 0.954353 | 26.100974 |\u001b[0m\n", - "| 22 | -0.823096 | 0.510355 | 0.154138 | 0.081226 | 0.849886 | 12.311395 |\u001b[0m\n", - "| 23 | -0.823096 | -0.510355 | 0.154138 | 0.081226 | 0.849886 | 12.311395 |\u001b[0m\n", - "| 24 | -1.089098 | 0.562639 | 0.195099 | 0.089547 | 0.888447 | 11.167353 |\u001b[0m\n", - "| 25 | -1.089098 | -0.562639 | 0.195099 | 0.089547 | 0.888447 | 11.167353 |\u001b[0m\n", - "| 26 | -1.092180 | -0.000000 | 0.173826 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 27 | -1.241288 | 0.000000 | 0.197557 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 28 | -2.348569 | 3.083089 | 0.616840 | 0.490689 | 0.605970 | 2.037951 |\u001b[0m\n", - "| 29 | -2.348569 | -3.083089 | 0.616840 | 0.490689 | 0.605970 | 2.037951 |\u001b[0m\n", - "| 30 | -3.155780 | -0.000000 | 0.502258 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 31 | -8.918115 | -0.000000 | 1.419362 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 32 | -26.700406 | -27.485500 | 6.098697 | 4.374453 | 0.696788 | 0.228600 |\u001b[0m\n", - "| 33 | -28.824123 | 0.000000 | 4.587502 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 34 | -35.766803 | -27.485500 | 7.179135 | 4.374453 | 0.792918 | 0.228600 |\u001b[0m\n", - "| 35 | -36.676675 | -0.000000 | 5.837274 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "\u001b[34mVelocity Asymptotic Stability Analysis\u001b[0m\n", - "\u001b[34mInitial velocity: 100.00 m/s\u001b[0m\n", - "\u001b[34mFinal velocity: 180.00 m/s\u001b[0m\n", - "\u001b[34mNumber of evaluations: 81\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 100.00 m/2\tmax. CT eig. real: -3.925687\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 101.00 m/2\tmax. CT eig. real: -3.951244\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 102.00 m/2\tmax. CT eig. real: -3.975951\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 103.00 m/2\tmax. CT eig. real: -3.999782\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 104.00 m/2\tmax. CT eig. real: -4.022709\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 105.00 m/2\tmax. CT eig. real: -4.044705\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 106.00 m/2\tmax. CT eig. real: -4.065744\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 107.00 m/2\tmax. CT eig. real: -4.085798\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 108.00 m/2\tmax. CT eig. real: -4.104841\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 109.00 m/2\tmax. CT eig. real: -4.122845\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 110.00 m/2\tmax. CT eig. real: -4.139781\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 111.00 m/2\tmax. CT eig. real: -4.155617\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 112.00 m/2\tmax. CT eig. real: -4.170322\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 113.00 m/2\tmax. CT eig. real: -4.183860\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 114.00 m/2\tmax. CT eig. real: -4.196192\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 115.00 m/2\tmax. CT eig. real: -4.207276\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 116.00 m/2\tmax. CT eig. real: -4.217065\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 117.00 m/2\tmax. CT eig. real: -4.225507\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 118.00 m/2\tmax. CT eig. real: -4.232544\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 119.00 m/2\tmax. CT eig. real: -4.238111\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 120.00 m/2\tmax. CT eig. real: -4.242138\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 121.00 m/2\tmax. CT eig. real: -4.244545\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 122.00 m/2\tmax. CT eig. real: -4.245246\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 123.00 m/2\tmax. CT eig. real: -4.244143\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 124.00 m/2\tmax. CT eig. real: -4.241130\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 125.00 m/2\tmax. CT eig. real: -4.236092\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 126.00 m/2\tmax. CT eig. real: -4.228899\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 127.00 m/2\tmax. CT eig. real: -4.219413\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 128.00 m/2\tmax. CT eig. real: -4.207482\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 129.00 m/2\tmax. CT eig. real: -4.192940\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 130.00 m/2\tmax. CT eig. real: -4.175607\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 131.00 m/2\tmax. CT eig. real: -4.155291\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 132.00 m/2\tmax. CT eig. real: -4.131780\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 133.00 m/2\tmax. CT eig. real: -4.104848\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 134.00 m/2\tmax. CT eig. real: -4.074252\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 135.00 m/2\tmax. CT eig. real: -4.039730\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 136.00 m/2\tmax. CT eig. real: -4.001000\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 137.00 m/2\tmax. CT eig. real: -3.957763\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 138.00 m/2\tmax. CT eig. real: -3.909697\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 139.00 m/2\tmax. CT eig. real: -3.856462\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 140.00 m/2\tmax. CT eig. real: -3.797697\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 141.00 m/2\tmax. CT eig. real: -3.733020\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 142.00 m/2\tmax. CT eig. real: -3.662031\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 143.00 m/2\tmax. CT eig. real: -3.584314\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 144.00 m/2\tmax. CT eig. real: -3.499436\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 145.00 m/2\tmax. CT eig. real: -3.406959\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 146.00 m/2\tmax. CT eig. real: -3.306437\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 147.00 m/2\tmax. CT eig. real: -3.197433\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 148.00 m/2\tmax. CT eig. real: -3.079522\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 149.00 m/2\tmax. CT eig. real: -2.952307\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 150.00 m/2\tmax. CT eig. real: -2.815436\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 151.00 m/2\tmax. CT eig. real: -2.668615\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 152.00 m/2\tmax. CT eig. real: -2.511634\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 153.00 m/2\tmax. CT eig. real: -2.344382\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 154.00 m/2\tmax. CT eig. real: -2.166866\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 155.00 m/2\tmax. CT eig. real: -1.979231\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 156.00 m/2\tmax. CT eig. real: -1.781770\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 157.00 m/2\tmax. CT eig. real: -1.574929\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 158.00 m/2\tmax. CT eig. real: -1.359301\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 159.00 m/2\tmax. CT eig. real: -1.135613\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 160.00 m/2\tmax. CT eig. real: -0.904707\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 161.00 m/2\tmax. CT eig. real: -0.667505\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 162.00 m/2\tmax. CT eig. real: -0.424982\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 163.00 m/2\tmax. CT eig. real: -0.178127\t\u001b[0m\n", - "\tN unstab.: 000\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 164.00 m/2\tmax. CT eig. real: 0.072086\t\u001b[0m\n", - "\tN unstab.: 002\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\t70.64\t70.64\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 165.00 m/2\tmax. CT eig. real: 0.324729\t\u001b[0m\n", - "\tN unstab.: 002\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\t70.41\t70.41\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 166.00 m/2\tmax. CT eig. real: 0.578936\t\u001b[0m\n", - "\tN unstab.: 002\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\t70.20\t70.20\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 167.00 m/2\tmax. CT eig. real: 0.833925\t\u001b[0m\n", - "\tN unstab.: 002\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\t69.99\t69.99\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 168.00 m/2\tmax. CT eig. real: 1.088998\t\u001b[0m\n", - "\tN unstab.: 002\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\t69.79\t69.79\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 169.00 m/2\tmax. CT eig. real: 1.343552\t\u001b[0m\n", - "\tN unstab.: 002\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\t69.61\t69.61\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 170.00 m/2\tmax. CT eig. real: 1.597068\t\u001b[0m\n", - "\tN unstab.: 002\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\t69.43\t69.43\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 171.00 m/2\tmax. CT eig. real: 1.849117\t\u001b[0m\n", - "\tN unstab.: 002\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\t69.26\t69.26\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 172.00 m/2\tmax. CT eig. real: 2.099343\t\u001b[0m\n", - "\tN unstab.: 002\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\t69.10\t69.10\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 173.00 m/2\tmax. CT eig. real: 2.347461\t\u001b[0m\n", - "\tN unstab.: 002\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\t68.94\t68.94\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 174.00 m/2\tmax. CT eig. real: 2.593246\t\u001b[0m\n", - "\tN unstab.: 002\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\t68.79\t68.79\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 175.00 m/2\tmax. CT eig. real: 2.836529\t\u001b[0m\n", - "\tN unstab.: 002\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\t68.65\t68.65\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 176.00 m/2\tmax. CT eig. real: 3.077180\t\u001b[0m\n", - "\tN unstab.: 002\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\t68.51\t68.51\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 177.00 m/2\tmax. CT eig. real: 3.315113\t\u001b[0m\n", - "\tN unstab.: 002\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\t68.38\t68.38\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 178.00 m/2\tmax. CT eig. real: 3.550268\t\u001b[0m\n", - "\tN unstab.: 002\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\t68.25\t68.25\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 179.00 m/2\tmax. CT eig. real: 3.782616\t\u001b[0m\n", - "\tN unstab.: 002\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\t68.13\t68.13\u001b[0m\n", - "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", - "LTI\tu: 180.00 m/2\tmax. CT eig. real: 4.012145\t\u001b[0m\n", - "\tN unstab.: 002\u001b[0m\n", - "\tUnstable aeroelastic natural frequency CT(rad/s):\t68.01\t68.01\u001b[0m\n", - "Saving velocity analysis results...\u001b[0m\n", - "\u001b[34m\tSuccessful\u001b[0m\n", - "\u001b[36mFINISHED - Elapsed time = 59.1569176 seconds\u001b[0m\n", - "\u001b[36mFINISHED - CPU process time = 94.3774846 seconds\u001b[0m\n" - ] - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "sharpy.sharpy_main.main(['', ws.route + ws.case_name + '.sharpy'])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Analysis" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Nonlinear equilibrium\n", - "\n", - "The nonlinear equilibrium condition can be visualised and analysed by opening, with Paraview, the files in the `/output//aero` and `/output//beam` folders to see the deflection and aerodynamic forces acting" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Stability \n", - "\n", - "The stability of the Goland wing is now analysed under changing free stream velocity. The aeroelastic system is projected onto 2 structural modes (1st bending and 1st torsion). The two modes are seen quite separated at 100 m/s. As speed is increased, the damping of the torsion mode decreases until it crosses the imaginary axis onto the right hand plane and flutter begins. This flutter mode is a bending-torsion mode, as seen from the natural frequency plot where the frequencies of each coalesce into this mode." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "file_name = './output/%s/stability/velocity_analysis_min1000_max1800_nvel0081.dat' % case_name\n", - "\n", - "velocity_analysis = np.loadtxt(file_name)\n", - "u_inf = velocity_analysis[:, 0]\n", - "eigs_r = velocity_analysis[:, 1]\n", - "eigs_i = velocity_analysis[:, 2]" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "fig = plt.figure()\n", - "plt.scatter(eigs_r, eigs_i, c=u_inf, cmap='Blues')\n", - "cbar = plt.colorbar()\n", - "cbar.set_label('Free Stream Velocity, $u_\\infty$ [m/s]')\n", - "\n", - "plt.grid()\n", - "plt.xlim(-10, 10)\n", - "plt.ylim(-150, 150)\n", - "plt.xlabel('Real Part, $\\lambda$ [rad/s]')\n", - "plt.ylabel('Imag Part, $\\lambda$ [rad/s]');" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "fig = plt.figure()\n", - "natural_frequency = np.sqrt(eigs_r ** 2 + eigs_i ** 2)\n", - "damping_ratio = eigs_r / natural_frequency\n", - "cond = (eigs_r>-25) * (eigs_r<10) * (natural_frequency<100) # filter unwanted eigenvalues for this plot (mostly aero modes)\n", - "\n", - "plt.scatter(u_inf[cond], damping_ratio[cond], color='k', marker='s', s=9)\n", - "\n", - "plt.grid()\n", - "plt.ylim(-0.25, 0.25)\n", - "plt.xlabel('Free Stream Velocity, $u_\\infty$ [m/s]')\n", - "plt.ylabel('Damping Ratio, $\\zeta$ [-]');" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "fig = plt.figure()\n", - "cond = (eigs_r>-25) * (eigs_r<10) # filter unwanted eigenvalues for this plot (mostly aero modes)\n", - "plt.scatter(u_inf[cond], natural_frequency[cond], color='k', marker='s', s=9)\n", - "\n", - "plt.grid()\n", - "plt.ylim(40, 100)\n", - "plt.xlabel('Free Stream Velocity, $u_\\infty$ [m/s]')\n", - "plt.ylabel('Natural Frequency, $\\omega_n$ [rad/s]');" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.5" - } - }, - "nbformat": 4, - "nbformat_minor": 4 + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Flutter Analysis of a Goland Wing using the SHARPy Linear Solver\n", + "\n", + "This is an example using SHARPy to find the flutter speed of a Goland wing by:\n", + "\n", + "* Calculating aerodynamic forces and deflections using a nonlinear solver\n", + "\n", + "* Linearising about this reference condition\n", + "\n", + "* Creating a reduced order model of the linearised aerodynamics\n", + "\n", + "* Evaluate the stability of the linearised aeroelastic system at different velocities\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### References\n", + "\n", + "Maraniello, S., & Palacios, R. (2019). State-Space Realizations and Internal Balancing in Potential-Flow Aerodynamics with Arbitrary Kinematics. AIAA Journal, 57(6), 1–14. https://doi.org/10.2514/1.J058153\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Required Packages" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import os\n", + "import sys\n", + "import sharpy.cases.templates.flying_wings as wings # See this package for the Goland wing structural and aerodynamic definition\n", + "import sharpy.sharpy_main # used to run SHARPy from Jupyter" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Problem Set-up" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Velocity\n", + "\n", + "The UVLM is assembled in normalised time at a velocity of $1 m/s$. The only matrices that need updating then with free stream velocity are the structural matrices, which is significantly cheaper to do than to update the UVLM." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "u_inf = 1.\n", + "alpha_deg = 0.\n", + "rho = 1.02\n", + "num_modes = 4" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Discretisation\n", + "\n", + "Note: To achieve convergence of the flutter results with the ones found in the literature, a significant discretisation may be required. If you are running this notebook for the first time, set `M = 4` initially to verify that your system can perform!" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "M = 16\n", + "N = 32\n", + "M_star_fact = 10" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ROM\n", + "\n", + "A moment-matching (Krylov subspace) model order reduction technique is employed. This ROM method offers the ability to interpolate the transfer functions at a desired point in the complex plane. See the ROM documentation pages for more info.\n", + "\n", + "Note: this ROM method matches the transfer function but does not guarantee stability. Therefore the resulting system may be unstable. These unstable modes may appear far in the right hand plane but will not affect the flutter speed calculations." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "c_ref = 1.8288 # Goland wing reference chord. Used for frequency normalisation\n", + "rom_settings = dict()\n", + "rom_settings['algorithm'] = 'mimo_rational_arnoldi' # reduction algorithm\n", + "rom_settings['r'] = 6 # Krylov subspace order\n", + "frequency_continuous_k = np.array([0.]) # Interpolation point in the complex plane with reduced frequency units\n", + "frequency_continuous_w = 2 * u_inf * frequency_continuous_k / c_ref\n", + "rom_settings['frequency'] = frequency_continuous_w" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Case Admin" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The case to run will be: goland_csM16N32Ms10_nmodes4rom_MIMORA_r6_sig0000_0000j\n", + "Case files will be saved in ./cases/goland_csM16N32Ms10_nmodes4rom_MIMORA_r6_sig0000_0000j\n", + "Output files will be saved in ./output/goland_csM16N32Ms10_nmodes4rom_MIMORA_r6_sig0000_0000j/\n" + ] + } + ], + "source": [ + "case_name = 'goland_cs'\n", + "case_nlin_info = 'M%dN%dMs%d_nmodes%d' % (M, N, M_star_fact, num_modes)\n", + "case_rom_info = 'rom_MIMORA_r%d_sig%04d_%04dj' % (rom_settings['r'], frequency_continuous_k[-1].real * 100,\n", + " frequency_continuous_k[-1].imag * 100)\n", + "\n", + "case_name += case_nlin_info + case_rom_info\n", + "\n", + "route_test_dir = os.path.abspath('')\n", + "\n", + "print('The case to run will be: %s' % case_name)\n", + "print('Case files will be saved in ./cases/%s' %case_name)\n", + "print('Output files will be saved in ./output/%s/' %case_name)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Simulation Set-Up" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Goland Wing\n", + "\n", + "`ws` is an instance of a Goland wing with a control surface. Reference the template file `sharpy.cases.templates.flying_wings.GolandControlSurface` for more info on the geometrical, structural and aerodynamic definition of the Goland wing here used." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "ws = wings.GolandControlSurface(M=M,\n", + " N=N,\n", + " Mstar_fact=M_star_fact,\n", + " u_inf=u_inf,\n", + " alpha=alpha_deg,\n", + " cs_deflection=[0, 0],\n", + " rho=rho,\n", + " sweep=0,\n", + " physical_time=2,\n", + " n_surfaces=2,\n", + " route=route_test_dir + '/cases',\n", + " case_name=case_name)\n", + "\n", + "ws.clean_test_files()\n", + "ws.update_derived_params()\n", + "ws.set_default_config_dict()\n", + "\n", + "ws.generate_aero_file()\n", + "ws.generate_fem_file()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Simulation Settings\n", + "\n", + "The settings for each of the solvers are now set. For a detailed description on them please reference their respective documentation pages" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### SHARPy Settings\n", + "\n", + "The most important setting is the `flow` list. It tells SHARPy which solvers to run and in which order." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "ws.config['SHARPy'] = {\n", + " 'flow':\n", + " ['BeamLoader', 'AerogridLoader',\n", + " 'StaticCoupled',\n", + " 'AerogridPlot',\n", + " 'BeamPlot',\n", + " 'Modal',\n", + " 'LinearAssembler',\n", + " 'AsymptoticStability',\n", + " ],\n", + " 'case': ws.case_name, 'route': ws.route,\n", + " 'write_screen': 'on', 'write_log': 'on',\n", + " 'log_folder': route_test_dir + '/output/',\n", + " 'log_file': ws.case_name + '.log'}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Beam Loader Settings" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "ws.config['BeamLoader'] = {\n", + " 'unsteady': 'off',\n", + " 'orientation': ws.quat}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Aerogrid Loader Settings" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "ws.config['AerogridLoader'] = {\n", + " 'unsteady': 'off',\n", + " 'aligned_grid': 'on',\n", + " 'mstar': ws.Mstar_fact * ws.M,\n", + " 'freestream_dir': ws.u_inf_direction,\n", + " 'wake_shape_generator': 'StraightWake',\n", + " 'wake_shape_generator_input': {'u_inf': ws.u_inf,\n", + " 'u_inf_direction': ws.u_inf_direction,\n", + " 'dt': ws.dt}}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Static Coupled Solver" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "ws.config['StaticCoupled'] = {\n", + " 'print_info': 'on',\n", + " 'max_iter': 200,\n", + " 'n_load_steps': 1,\n", + " 'tolerance': 1e-10,\n", + " 'relaxation_factor': 0.,\n", + " 'aero_solver': 'StaticUvlm',\n", + " 'aero_solver_settings': {\n", + " 'rho': ws.rho,\n", + " 'print_info': 'off',\n", + " 'horseshoe': 'off',\n", + " 'num_cores': 4,\n", + " 'n_rollup': 0,\n", + " 'rollup_dt': ws.dt,\n", + " 'rollup_aic_refresh': 1,\n", + " 'rollup_tolerance': 1e-4,\n", + " 'velocity_field_generator': 'SteadyVelocityField',\n", + " 'velocity_field_input': {\n", + " 'u_inf': ws.u_inf,\n", + " 'u_inf_direction': ws.u_inf_direction}},\n", + " 'structural_solver': 'NonLinearStatic',\n", + " 'structural_solver_settings': {'print_info': 'off',\n", + " 'max_iterations': 150,\n", + " 'num_load_steps': 4,\n", + " 'delta_curved': 1e-1,\n", + " 'min_delta': 1e-10,\n", + " 'gravity_on': 'on',\n", + " 'gravity': 9.81}}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### AerogridPlot Settings" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "ws.config['AerogridPlot'] = {'include_rbm': 'off',\n", + " 'include_applied_forces': 'on',\n", + " 'minus_m_star': 0}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### BeamPlot Settings" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "ws.config['BeamPlot'] = {'include_rbm': 'off',\n", + " 'include_applied_forces': 'on'}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Modal Solver Settings" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "ws.config['Modal'] = {'NumLambda': 20,\n", + " 'rigid_body_modes': 'off',\n", + " 'print_matrices': 'on',\n", + " 'keep_linear_matrices': 'on',\n", + " 'write_dat': 'off',\n", + " 'rigid_modes_cg': 'off',\n", + " 'continuous_eigenvalues': 'off',\n", + " 'dt': 0,\n", + " 'plot_eigenvalues': False,\n", + " 'max_rotation_deg': 15.,\n", + " 'max_displacement': 0.15,\n", + " 'write_modes_vtk': True,\n", + " 'use_undamped_modes': True}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Linear System Assembly Settings" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "ws.config['LinearAssembler'] = {'linear_system': 'LinearAeroelastic',\n", + " 'linear_system_settings': {\n", + " 'beam_settings': {'modal_projection': 'on',\n", + " 'inout_coords': 'modes',\n", + " 'discrete_time': 'on',\n", + " 'newmark_damp': 0.5e-4,\n", + " 'discr_method': 'newmark',\n", + " 'dt': ws.dt,\n", + " 'proj_modes': 'undamped',\n", + " 'use_euler': 'off',\n", + " 'num_modes': num_modes,\n", + " 'print_info': 'on',\n", + " 'gravity': 'on',\n", + " 'remove_sym_modes': 'on',\n", + " 'remove_dofs': []},\n", + " 'aero_settings': {'dt': ws.dt,\n", + " 'ScalingDict': {'length': 0.5 * ws.c_ref,\n", + " 'speed': u_inf,\n", + " 'density': rho},\n", + " 'integr_order': 2,\n", + " 'density': ws.rho,\n", + " 'remove_predictor': 'on',\n", + " 'use_sparse': 'on',\n", + " 'remove_inputs': ['u_gust'],\n", + " 'rom_method': ['Krylov'],\n", + " 'rom_method_settings': {'Krylov': rom_settings}},\n", + " }}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Asymptotic Stability Analysis Settings" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "ws.config['AsymptoticStability'] = {'print_info': True,\n", + " 'velocity_analysis': [100, 180, 81],\n", + " 'modes_to_plot': []}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Write solver settings config file" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "ws.config.write()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Run SHARPy" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--------------------------------------------------------------------------------\u001b[0m\n", + " ###### ## ## ### ######## ######## ## ##\u001b[0m\n", + " ## ## ## ## ## ## ## ## ## ## ## ##\u001b[0m\n", + " ## ## ## ## ## ## ## ## ## ####\u001b[0m\n", + " ###### ######### ## ## ######## ######## ##\u001b[0m\n", + " ## ## ## ######### ## ## ## ##\u001b[0m\n", + " ## ## ## ## ## ## ## ## ## ##\u001b[0m\n", + " ###### ## ## ## ## ## ## ## ##\u001b[0m\n", + "--------------------------------------------------------------------------------\u001b[0m\n", + "Aeroelastics Lab, Aeronautics Department.\u001b[0m\n", + " Copyright (c), Imperial College London.\u001b[0m\n", + " All rights reserved.\u001b[0m\n", + " License available at https://github.com/imperialcollegelondon/sharpy\u001b[0m\n", + "\u001b[36mRunning SHARPy from /home/ng213/2TB/pazy_code/pazy-sharpy/lib/sharpy/docs/source/content/example_notebooks\u001b[0m\n", + "\u001b[36mSHARPy being run is in /home/ng213/2TB/pazy_code/pazy-sharpy/lib/sharpy\u001b[0m\n", + "\u001b[36mThe branch being run is dev_setting_error\u001b[0m\n", + "\u001b[36mThe version and commit hash are: v1.2.1-339-g156c731-156c731\u001b[0m\n", + "SHARPy output folder set\u001b[0m\n", + "\u001b[34m\t/home/ng213/2TB/pazy_code/pazy-sharpy/lib/sharpy/docs/source/content/example_notebooks/output//goland_csM16N32Ms10_nmodes4rom_MIMORA_r6_sig0000_0000j/\u001b[0m\n", + "\u001b[36mGenerating an instance of BeamLoader\u001b[0m\n", + "Variable for_pos has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: [0.0, 0, 0]\u001b[0m\n", + "\u001b[36mGenerating an instance of AerogridLoader\u001b[0m\n", + "Variable control_surface_deflection has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: []\u001b[0m\n", + "Variable control_surface_deflection_generator_settings has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: {}\u001b[0m\n", + "Variable dx1 has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: -1.0\u001b[0m\n", + "Variable ndx1 has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1\u001b[0m\n", + "Variable r has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1.0\u001b[0m\n", + "Variable dxmax has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: -1.0\u001b[0m\n", + "\u001b[34mThe aerodynamic grid contains 2 surfaces\u001b[0m\n", + "\u001b[34m Surface 0, M=16, N=16\u001b[0m\n", + " Wake 0, M=160, N=16\u001b[0m\n", + "\u001b[34m Surface 1, M=16, N=16\u001b[0m\n", + " Wake 1, M=160, N=16\u001b[0m\n", + " In total: 512 bound panels\u001b[0m\n", + " In total: 5120 wake panels\u001b[0m\n", + " Total number of panels = 5632\u001b[0m\n", + "\u001b[36mGenerating an instance of StaticCoupled\u001b[0m\n", + "Variable correct_forces_method has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: \u001b[0m\n", + "Variable runtime_generators has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: {}\u001b[0m\n", + "\u001b[36mGenerating an instance of NonLinearStatic\u001b[0m\n", + "Variable newmark_damp has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0.0001\u001b[0m\n", + "Variable gravity_dir has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: [0.0, 0.0, 1.0]\u001b[0m\n", + "Variable relaxation_factor has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0.3\u001b[0m\n", + "Variable dt has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0.01\u001b[0m\n", + "Variable num_steps has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 500\u001b[0m\n", + "Variable initial_position has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: [0. 0. 0.]\u001b[0m\n", + "\u001b[36mGenerating an instance of StaticUvlm\u001b[0m\n", + "Variable iterative_solver has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable iterative_tol has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0.0001\u001b[0m\n", + "Variable iterative_precond has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable cfl1 has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: True\u001b[0m\n", + "Variable vortex_radius has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1e-06\u001b[0m\n", + "Variable vortex_radius_wake_ind has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1e-06\u001b[0m\n", + "Variable rbm_vel_g has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]\u001b[0m\n", + "Variable centre_rot_g has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: [0.0, 0.0, 0.0]\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\n", + "|=====|=====|============|==========|==========|==========|==========|==========|==========|\u001b[0m\n", + "|iter |step | log10(res) | Fx | Fy | Fz | Mx | My | Mz |\u001b[0m\n", + "|=====|=====|============|==========|==========|==========|==========|==========|==========|\u001b[0m\n", + "| 0 | 0 | 0.00000 | 0.0000 | -0.0000 |-4271.0417| 0.0000 | 781.0842 | 0.0000 |\u001b[0m\n", + "| 1 | 0 | -11.89144 | 0.0000 | -0.0000 |-4271.0039| 0.0000 | 781.0906 | 0.0000 |\u001b[0m\n", + "\u001b[36mGenerating an instance of AerogridPlot\u001b[0m\n", + "Variable include_forward_motion has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable include_unsteady_applied_forces has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable name_prefix has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: \u001b[0m\n", + "Variable u_inf has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0.0\u001b[0m\n", + "Variable dt has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0.0\u001b[0m\n", + "Variable include_velocities has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable include_incidence_angle has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable num_cores has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1\u001b[0m\n", + "Variable vortex_radius has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1e-06\u001b[0m\n", + "\u001b[34m...Finished\u001b[0m\n", + "\u001b[36mGenerating an instance of BeamPlot\u001b[0m\n", + "Variable include_FoR has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable include_applied_moments has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: True\u001b[0m\n", + "Variable name_prefix has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: \u001b[0m\n", + "Variable output_rbm has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: True\u001b[0m\n", + "\u001b[34m...Finished\u001b[0m\n", + "\u001b[36mGenerating an instance of Modal\u001b[0m\n", + "Variable print_info has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: True\u001b[0m\n", + "Variable delta_curved has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0.01\u001b[0m\n", + "Variable use_custom_timestep has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: -1\u001b[0m\n", + "Structural eigenvalues\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\n", + "|==============|==============|==============|==============|==============|==============|==============|\u001b[0m\n", + "| mode | eval_real | eval_imag | freq_n (Hz) | freq_d (Hz) | damping | period (s) |\u001b[0m\n", + "|==============|==============|==============|==============|==============|==============|==============|\u001b[0m\n", + "| 0 | 0.000000 | 48.067396 | 7.650164 | 7.650164 | -0.000000 | 0.130716 |\u001b[0m\n", + "| 1 | 0.000000 | 48.067398 | 7.650164 | 7.650164 | -0.000000 | 0.130716 |\u001b[0m\n", + "| 2 | 0.000000 | 95.685736 | 15.228858 | 15.228858 | -0.000000 | 0.065665 |\u001b[0m\n", + "| 3 | 0.000000 | 95.685754 | 15.228861 | 15.228861 | -0.000000 | 0.065665 |\u001b[0m\n", + "| 4 | 0.000000 | 243.144471 | 38.697644 | 38.697644 | -0.000000 | 0.025841 |\u001b[0m\n", + "| 5 | 0.000000 | 243.144477 | 38.697645 | 38.697645 | -0.000000 | 0.025841 |\u001b[0m\n", + "| 6 | 0.000000 | 343.801136 | 54.717650 | 54.717650 | -0.000000 | 0.018276 |\u001b[0m\n", + "| 7 | 0.000000 | 343.801137 | 54.717650 | 54.717650 | -0.000000 | 0.018276 |\u001b[0m\n", + "| 8 | 0.000000 | 443.324608 | 70.557303 | 70.557303 | -0.000000 | 0.014173 |\u001b[0m\n", + "| 9 | 0.000000 | 443.324619 | 70.557304 | 70.557304 | -0.000000 | 0.014173 |\u001b[0m\n", + "| 10 | 0.000000 | 461.992869 | 73.528449 | 73.528449 | -0.000000 | 0.013600 |\u001b[0m\n", + "| 11 | 0.000000 | 461.992869 | 73.528449 | 73.528449 | -0.000000 | 0.013600 |\u001b[0m\n", + "| 12 | 0.000000 | 601.126871 | 95.672313 | 95.672313 | -0.000000 | 0.010452 |\u001b[0m\n", + "| 13 | 0.000000 | 601.126873 | 95.672313 | 95.672313 | -0.000000 | 0.010452 |\u001b[0m\n", + "| 14 | 0.000000 | 782.997645 | 124.617946 | 124.617946 | -0.000000 | 0.008025 |\u001b[0m\n", + "| 15 | 0.000000 | 782.997649 | 124.617946 | 124.617946 | -0.000000 | 0.008025 |\u001b[0m\n", + "| 16 | 0.000000 | 917.191257 | 145.975522 | 145.975522 | -0.000000 | 0.006850 |\u001b[0m\n", + "| 17 | 0.000000 | 917.191259 | 145.975523 | 145.975523 | -0.000000 | 0.006850 |\u001b[0m\n", + "| 18 | 0.000000 | 975.005694 | 155.176976 | 155.176976 | -0.000000 | 0.006444 |\u001b[0m\n", + "| 19 | 0.000000 | 975.005699 | 155.176977 | 155.176977 | -0.000000 | 0.006444 |\u001b[0m\n", + "\u001b[36mGenerating an instance of LinearAssembler\u001b[0m\n", + "Variable linearisation_tstep has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: -1\u001b[0m\n", + "Variable modal_tstep has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: -1\u001b[0m\n", + "Variable inout_coordinates has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: \u001b[0m\n", + "Variable retain_inputs has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: []\u001b[0m\n", + "Variable retain_outputs has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: []\u001b[0m\n", + "\u001b[36mGenerating an instance of LinearAeroelastic\u001b[0m\n", + "Variable uvlm_filename has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: \u001b[0m\n", + "Variable track_body has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: True\u001b[0m\n", + "Variable use_euler has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "\u001b[36mGenerating an instance of LinearUVLM\u001b[0m\n", + "Variable gust_assembler has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: \u001b[0m\n", + "Variable vortex_radius has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1e-06\u001b[0m\n", + "Variable cfl1 has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: True\u001b[0m\n", + "Variable velocity_field_generator has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: SteadyVelocityField\u001b[0m\n", + "Variable velocity_field_input has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: {}\u001b[0m\n", + "Variable physical_model has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: True\u001b[0m\n", + "Variable track_body has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable track_body_number has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: -1\u001b[0m\n", + "Initialising Static linear UVLM solver class...\u001b[0m\n", + "\t\t\t...done in 1.35 sec\u001b[0m\n", + "\u001b[36mGenerating an instance of Krylov\u001b[0m\n", + "Variable print_info has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: True\u001b[0m\n", + "Variable single_side has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: \u001b[0m\n", + "Variable tangent_input_file has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: \u001b[0m\n", + "Variable restart_arnoldi has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Initialising Krylov Model Order Reduction\u001b[0m\n", + "State-space realisation of UVLM equations started...\u001b[0m\n", + "\u001b[34mComputing wake propagation matrix with CFL1=True\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/ng213/anaconda3/envs/sharpy_env/lib/python3.7/site-packages/scipy/sparse/_index.py:126: SparseEfficiencyWarning: Changing the sparsity structure of a csc_matrix is expensive. lil_matrix is more efficient.\n", + " self._set_arrayXarray(i, j, x)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[34m\tstate-space model produced in form:\u001b[0m\n", + "\u001b[34m\t\t\th_{n+1} = A h_{n} + B u_{n}\u001b[0m\n", + "\u001b[34m\t\t\twith:\u001b[0m\n", + "\u001b[34m\tx_n = h_n + Bp u_n\u001b[0m\n", + "\t\t\t...done in 17.25 sec\u001b[0m\n", + "Scaling UVLM system with reference time 0.914400s\u001b[0m\n", + "\u001b[34mNon-dimensional time step set (0.125000)\u001b[0m\n", + "System scaled in 31.698308s\u001b[0m\n", + "\u001b[36mGenerating an instance of LinearBeam\u001b[0m\n", + "Warning, projecting system with damping onto undamped modes\u001b[0m\n", + "\u001b[0m\n", + "Linearising gravity terms...\u001b[0m\n", + "\u001b[34m\tM = 7.26 kg\u001b[0m\n", + "\u001b[34m\tX_CG A -> 0.00 0.00 -0.00\u001b[0m\n", + "\u001b[36mNode 1 \t-> B -0.000 -0.116 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 0.381 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 0.381 -0.000\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", + "\u001b[36mNode 2 \t-> B -0.000 -0.116 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 0.762 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 0.762 -0.000\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", + "\u001b[36mNode 3 \t-> B -0.000 -0.116 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 1.143 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 1.143 -0.000\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", + "\u001b[36mNode 4 \t-> B -0.000 -0.116 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 1.524 -0.001\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 1.524 -0.001\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", + "\u001b[36mNode 5 \t-> B -0.000 -0.116 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 1.905 -0.001\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 1.905 -0.001\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", + "\u001b[36mNode 6 \t-> B -0.000 -0.116 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 2.286 -0.001\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 2.286 -0.001\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", + "\u001b[36mNode 7 \t-> B -0.000 -0.116 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 2.667 -0.002\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 2.667 -0.002\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", + "\u001b[36mNode 8 \t-> B -0.000 -0.116 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 3.048 -0.002\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 3.048 -0.002\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", + "\u001b[36mNode 9 \t-> B -0.000 -0.116 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 3.429 -0.003\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 3.429 -0.003\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", + "\u001b[36mNode 10 \t-> B -0.000 -0.116 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 3.810 -0.003\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 3.810 -0.003\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", + "\u001b[36mNode 11 \t-> B -0.000 -0.116 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 4.191 -0.004\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 4.191 -0.004\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", + "\u001b[36mNode 12 \t-> B -0.000 -0.116 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 4.572 -0.004\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 4.572 -0.004\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", + "\u001b[36mNode 13 \t-> B -0.000 -0.116 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 4.953 -0.005\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 4.953 -0.005\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", + "\u001b[36mNode 14 \t-> B -0.000 -0.116 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 5.334 -0.005\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 5.334 -0.005\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", + "\u001b[36mNode 15 \t-> B -0.000 -0.116 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 5.715 -0.006\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 5.715 -0.006\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", + "\u001b[36mNode 16 \t-> B -0.000 -0.116 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 6.096 -0.006\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 6.096 -0.006\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 3.6281\u001b[0m\n", + "\u001b[36mNode 17 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 -6.096 -0.006\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 -6.096 -0.006\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 3.6281\u001b[0m\n", + "\u001b[36mNode 18 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 -5.715 -0.006\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 -5.715 -0.006\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", + "\u001b[36mNode 19 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 -5.334 -0.005\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 -5.334 -0.005\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", + "\u001b[36mNode 20 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 -4.953 -0.005\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 -4.953 -0.005\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", + "\u001b[36mNode 21 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 -4.572 -0.004\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 -4.572 -0.004\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", + "\u001b[36mNode 22 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 -4.191 -0.004\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 -4.191 -0.004\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", + "\u001b[36mNode 23 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 -3.810 -0.003\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 -3.810 -0.003\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", + "\u001b[36mNode 24 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 -3.429 -0.003\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 -3.429 -0.003\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", + "\u001b[36mNode 25 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 -3.048 -0.002\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 -3.048 -0.002\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", + "\u001b[36mNode 26 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 -2.667 -0.002\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 -2.667 -0.002\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", + "\u001b[36mNode 27 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 -2.286 -0.002\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 -2.286 -0.002\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", + "\u001b[36mNode 28 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 -1.905 -0.001\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 -1.905 -0.001\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", + "\u001b[36mNode 29 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 -1.524 -0.001\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 -1.524 -0.001\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", + "\u001b[36mNode 30 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 -1.143 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 -1.143 -0.000\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", + "\u001b[36mNode 31 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 -0.762 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 -0.762 -0.000\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 7.2563\u001b[0m\n", + "\u001b[36mNode 32 \t-> B -0.000 -0.116 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.116 -0.381 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.116 -0.381 -0.000\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 14.5125\u001b[0m\n", + " Updated the beam C, modal C and K matrices with the terms from the\n", + "gravity linearisation\u001b[0m\n", + "\u001b[0m\n", + "Scaling beam according to reduced time...\u001b[0m\n", + "\u001b[34m\tSetting the beam time step to (0.1250)\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "Model Order Reduction in progress...\u001b[0m\n", + "Moment Matching Krylov Model Reduction\u001b[0m\n", + "\tConstruction Algorithm:\u001b[0m\n", + "\u001b[34m\t\tmimo_rational_arnoldi\u001b[0m\n", + "\tInterpolation points:\u001b[0m\n", + "\u001b[34m\t\tsigma = 0.000000 + 0.000000j [rad/s]\u001b[0m\n", + "\u001b[34m\u001b[0m\n", + "\tKrylov order:\u001b[0m\n", + "\u001b[34m\t\tr = 6\u001b[0m\n", + "\u001b[34m\tConstructing controllability space\u001b[0m\n", + "\u001b[34m\tConstructing observability space\u001b[0m\n", + "\u001b[34m\tDeflating column 23\u001b[0m\n", + "\u001b[34m\tDeflating column 25\u001b[0m\n", + "\u001b[34m\tDeflating column 28\u001b[0m\n", + "\u001b[34m\tDeflating column 33\u001b[0m\n", + "ROM is stable\u001b[0m\n", + "\tDT Eigenvalues:\u001b[0m\n", + "\t\tmu = 0.992076 + -0.000000j\u001b[0m\n", + "\t\tmu = 0.971409 + -0.000000j\u001b[0m\n", + "\t\tmu = 0.957534 + -0.038927j\u001b[0m\n", + "\t\tmu = 0.957534 + 0.038927j\u001b[0m\n", + "\t\tmu = 0.954926 + -0.072820j\u001b[0m\n", + "\t\tmu = 0.954926 + 0.072820j\u001b[0m\n", + "\t\tmu = 0.954282 + -0.000000j\u001b[0m\n", + "\t\tmu = 0.935272 + 0.000000j\u001b[0m\n", + "\t\tmu = 0.927865 + 0.092012j\u001b[0m\n", + "\t\tmu = 0.927865 + -0.092012j\u001b[0m\n", + "\t\tmu = 0.927075 + -0.066636j\u001b[0m\n", + "\t\tmu = 0.927075 + 0.066636j\u001b[0m\n", + "\t\tmu = 0.925588 + 0.038813j\u001b[0m\n", + "\t\tmu = 0.925588 + -0.038813j\u001b[0m\n", + "\t\tmu = 0.922836 + -0.004482j\u001b[0m\n", + "\t\tmu = 0.922836 + 0.004482j\u001b[0m\n", + "\t\tmu = 0.915491 + -0.025196j\u001b[0m\n", + "\t\tmu = 0.915491 + 0.025196j\u001b[0m\n", + "\t\tmu = 0.908662 + 0.053066j\u001b[0m\n", + "\t\tmu = 0.908662 + -0.053066j\u001b[0m\n", + "\t\tmu = 0.881127 + -0.056743j\u001b[0m\n", + "\t\tmu = 0.881127 + 0.056743j\u001b[0m\n", + "\t\tmu = 0.882642 + -0.000000j\u001b[0m\n", + "\t\tmu = 0.867726 + -0.000000j\u001b[0m\n", + "\t\tmu = 0.717587 + 0.263893j\u001b[0m\n", + "\t\tmu = 0.717587 + -0.263893j\u001b[0m\n", + "\t\tmu = 0.697184 + 0.000000j\u001b[0m\n", + "\t\tmu = 0.360807 + 0.000000j\u001b[0m\n", + "\t\tmu = -0.000018 + -0.002663j\u001b[0m\n", + "\t\tmu = -0.000018 + 0.002663j\u001b[0m\n", + "\t\tmu = 0.000155 + -0.000000j\u001b[0m\n", + "\t\tmu = -0.000154 + 0.000000j\u001b[0m\n", + "\u001b[0m\n", + "System reduced from order 6656 to \u001b[0m\n", + "\u001b[34m\tn = 32 states\u001b[0m\n", + "...Completed Model Order Reduction in 5.16 s\u001b[0m\n", + "Aeroelastic system assembled:\u001b[0m\n", + "\u001b[34m\tAerodynamic states: 32\u001b[0m\n", + "\u001b[34m\tStructural states: 4\u001b[0m\n", + "\u001b[34m\tTotal states: 36\u001b[0m\n", + "\u001b[34m\tInputs: 8\u001b[0m\n", + "\u001b[34m\tOutputs: 6\u001b[0m\n", + "\u001b[34mFinal system is:\u001b[0m\n", + "\u001b[34mState-space system\u001b[0m\n", + "\u001b[34mStates: 36\u001b[0m\n", + "\u001b[34mInputs: 8\u001b[0m\n", + "\u001b[34mOutputs: 6\u001b[0m\n", + "\u001b[34m\u001b[0m\n", + "\u001b[36mGenerating an instance of AsymptoticStability\u001b[0m\n", + "Variable reference_velocity has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1.0\u001b[0m\n", + "Variable frequency_cutoff has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0.0\u001b[0m\n", + "Variable export_eigenvalues has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable display_root_locus has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable iterative_eigvals has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable num_evals has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 200\u001b[0m\n", + "Variable postprocessors has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: []\u001b[0m\n", + "Variable postprocessors_settings has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: {}\u001b[0m\n", + "Dynamical System Eigenvalues\u001b[0m\n", + "Calculating eigenvalues using direct method\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\n", + "|==============|==============|==============|==============|==============|==============|==============|\u001b[0m\n", + "| mode | eval_real | eval_imag | freq_n (Hz) | freq_d (Hz) | damping | period (s) |\u001b[0m\n", + "|==============|==============|==============|==============|==============|==============|==============|\u001b[0m\n", + "| 0 | -0.021637 | 24.315428 | 3.869922 | 3.869921 | 0.000890 | 0.258403 |\u001b[0m\n", + "| 1 | -0.021637 | -24.315428 | 3.869922 | 3.869921 | 0.000890 | 0.258403 |\u001b[0m\n", + "| 2 | -0.069601 | -0.000000 | 0.011077 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 3 | -0.105223 | -21.320535 | 3.393310 | 3.393269 | 0.004935 | 0.294701 |\u001b[0m\n", + "| 4 | -0.105223 | 21.320535 | 3.393310 | 3.393269 | 0.004935 | 0.294701 |\u001b[0m\n", + "| 5 | -0.253787 | -0.000000 | 0.040391 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 6 | -0.372425 | 0.355473 | 0.081940 | 0.056575 | 0.723379 | 17.675568 |\u001b[0m\n", + "| 7 | -0.372425 | -0.355473 | 0.081940 | 0.056575 | 0.723379 | 17.675568 |\u001b[0m\n", + "| 8 | -0.378143 | 0.665882 | 0.121875 | 0.105978 | 0.493812 | 9.435879 |\u001b[0m\n", + "| 9 | -0.378143 | -0.665882 | 0.121875 | 0.105978 | 0.493812 | 9.435879 |\u001b[0m\n", + "| 10 | -0.409413 | -0.000000 | 0.065160 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 11 | -0.585461 | 0.000000 | 0.093179 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 12 | -0.612214 | -0.864763 | 0.168631 | 0.137631 | 0.577812 | 7.265789 |\u001b[0m\n", + "| 13 | -0.612214 | 0.864763 | 0.168631 | 0.137631 | 0.577812 | 7.265789 |\u001b[0m\n", + "| 14 | -0.639935 | 0.627769 | 0.142673 | 0.099913 | 0.713860 | 10.008752 |\u001b[0m\n", + "| 15 | -0.639935 | -0.627769 | 0.142673 | 0.099913 | 0.713860 | 10.008752 |\u001b[0m\n", + "| 16 | -0.668833 | 0.366654 | 0.121394 | 0.058355 | 0.876881 | 17.136529 |\u001b[0m\n", + "| 17 | -0.668833 | -0.366654 | 0.121394 | 0.058355 | 0.876881 | 17.136529 |\u001b[0m\n", + "| 18 | -0.702465 | 0.042487 | 0.112005 | 0.006762 | 0.998176 | 147.883816 |\u001b[0m\n", + "| 19 | -0.702465 | -0.042487 | 0.112005 | 0.006762 | 0.998176 | 147.883816 |\u001b[0m\n", + "| 20 | -0.769174 | 0.240726 | 0.128273 | 0.038313 | 0.954353 | 26.100974 |\u001b[0m\n", + "| 21 | -0.769174 | -0.240726 | 0.128273 | 0.038313 | 0.954353 | 26.100974 |\u001b[0m\n", + "| 22 | -0.823096 | 0.510355 | 0.154138 | 0.081226 | 0.849886 | 12.311395 |\u001b[0m\n", + "| 23 | -0.823096 | -0.510355 | 0.154138 | 0.081226 | 0.849886 | 12.311395 |\u001b[0m\n", + "| 24 | -1.089098 | 0.562639 | 0.195099 | 0.089547 | 0.888447 | 11.167353 |\u001b[0m\n", + "| 25 | -1.089098 | -0.562639 | 0.195099 | 0.089547 | 0.888447 | 11.167353 |\u001b[0m\n", + "| 26 | -1.092180 | -0.000000 | 0.173826 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 27 | -1.241288 | 0.000000 | 0.197557 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 28 | -2.348569 | 3.083089 | 0.616840 | 0.490689 | 0.605970 | 2.037951 |\u001b[0m\n", + "| 29 | -2.348569 | -3.083089 | 0.616840 | 0.490689 | 0.605970 | 2.037951 |\u001b[0m\n", + "| 30 | -3.155780 | -0.000000 | 0.502258 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 31 | -8.918115 | -0.000000 | 1.419362 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 32 | -26.700406 | -27.485500 | 6.098697 | 4.374453 | 0.696788 | 0.228600 |\u001b[0m\n", + "| 33 | -28.824123 | 0.000000 | 4.587502 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 34 | -35.766803 | -27.485500 | 7.179135 | 4.374453 | 0.792918 | 0.228600 |\u001b[0m\n", + "| 35 | -36.676675 | -0.000000 | 5.837274 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "\u001b[34mVelocity Asymptotic Stability Analysis\u001b[0m\n", + "\u001b[34mInitial velocity: 100.00 m/s\u001b[0m\n", + "\u001b[34mFinal velocity: 180.00 m/s\u001b[0m\n", + "\u001b[34mNumber of evaluations: 81\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 100.00 m/2\tmax. CT eig. real: -3.925687\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 101.00 m/2\tmax. CT eig. real: -3.951244\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 102.00 m/2\tmax. CT eig. real: -3.975951\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 103.00 m/2\tmax. CT eig. real: -3.999782\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 104.00 m/2\tmax. CT eig. real: -4.022709\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 105.00 m/2\tmax. CT eig. real: -4.044705\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 106.00 m/2\tmax. CT eig. real: -4.065744\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 107.00 m/2\tmax. CT eig. real: -4.085798\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 108.00 m/2\tmax. CT eig. real: -4.104841\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 109.00 m/2\tmax. CT eig. real: -4.122845\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 110.00 m/2\tmax. CT eig. real: -4.139781\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 111.00 m/2\tmax. CT eig. real: -4.155617\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 112.00 m/2\tmax. CT eig. real: -4.170322\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 113.00 m/2\tmax. CT eig. real: -4.183860\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 114.00 m/2\tmax. CT eig. real: -4.196192\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 115.00 m/2\tmax. CT eig. real: -4.207276\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 116.00 m/2\tmax. CT eig. real: -4.217065\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 117.00 m/2\tmax. CT eig. real: -4.225507\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 118.00 m/2\tmax. CT eig. real: -4.232544\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 119.00 m/2\tmax. CT eig. real: -4.238111\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 120.00 m/2\tmax. CT eig. real: -4.242138\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 121.00 m/2\tmax. CT eig. real: -4.244545\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 122.00 m/2\tmax. CT eig. real: -4.245246\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 123.00 m/2\tmax. CT eig. real: -4.244143\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 124.00 m/2\tmax. CT eig. real: -4.241130\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 125.00 m/2\tmax. CT eig. real: -4.236092\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 126.00 m/2\tmax. CT eig. real: -4.228899\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 127.00 m/2\tmax. CT eig. real: -4.219413\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 128.00 m/2\tmax. CT eig. real: -4.207482\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 129.00 m/2\tmax. CT eig. real: -4.192940\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 130.00 m/2\tmax. CT eig. real: -4.175607\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 131.00 m/2\tmax. CT eig. real: -4.155291\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 132.00 m/2\tmax. CT eig. real: -4.131780\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 133.00 m/2\tmax. CT eig. real: -4.104848\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 134.00 m/2\tmax. CT eig. real: -4.074252\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 135.00 m/2\tmax. CT eig. real: -4.039730\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 136.00 m/2\tmax. CT eig. real: -4.001000\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 137.00 m/2\tmax. CT eig. real: -3.957763\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 138.00 m/2\tmax. CT eig. real: -3.909697\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 139.00 m/2\tmax. CT eig. real: -3.856462\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 140.00 m/2\tmax. CT eig. real: -3.797697\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 141.00 m/2\tmax. CT eig. real: -3.733020\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 142.00 m/2\tmax. CT eig. real: -3.662031\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 143.00 m/2\tmax. CT eig. real: -3.584314\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 144.00 m/2\tmax. CT eig. real: -3.499436\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 145.00 m/2\tmax. CT eig. real: -3.406959\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 146.00 m/2\tmax. CT eig. real: -3.306437\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 147.00 m/2\tmax. CT eig. real: -3.197433\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 148.00 m/2\tmax. CT eig. real: -3.079522\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 149.00 m/2\tmax. CT eig. real: -2.952307\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 150.00 m/2\tmax. CT eig. real: -2.815436\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 151.00 m/2\tmax. CT eig. real: -2.668615\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 152.00 m/2\tmax. CT eig. real: -2.511634\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 153.00 m/2\tmax. CT eig. real: -2.344382\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 154.00 m/2\tmax. CT eig. real: -2.166866\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 155.00 m/2\tmax. CT eig. real: -1.979231\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 156.00 m/2\tmax. CT eig. real: -1.781770\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 157.00 m/2\tmax. CT eig. real: -1.574929\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 158.00 m/2\tmax. CT eig. real: -1.359301\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 159.00 m/2\tmax. CT eig. real: -1.135613\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 160.00 m/2\tmax. CT eig. real: -0.904707\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 161.00 m/2\tmax. CT eig. real: -0.667505\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 162.00 m/2\tmax. CT eig. real: -0.424982\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 163.00 m/2\tmax. CT eig. real: -0.178127\t\u001b[0m\n", + "\tN unstab.: 000\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 164.00 m/2\tmax. CT eig. real: 0.072086\t\u001b[0m\n", + "\tN unstab.: 002\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\t70.64\t70.64\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 165.00 m/2\tmax. CT eig. real: 0.324729\t\u001b[0m\n", + "\tN unstab.: 002\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\t70.41\t70.41\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 166.00 m/2\tmax. CT eig. real: 0.578936\t\u001b[0m\n", + "\tN unstab.: 002\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\t70.20\t70.20\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 167.00 m/2\tmax. CT eig. real: 0.833925\t\u001b[0m\n", + "\tN unstab.: 002\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\t69.99\t69.99\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 168.00 m/2\tmax. CT eig. real: 1.088998\t\u001b[0m\n", + "\tN unstab.: 002\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\t69.79\t69.79\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 169.00 m/2\tmax. CT eig. real: 1.343552\t\u001b[0m\n", + "\tN unstab.: 002\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\t69.61\t69.61\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 170.00 m/2\tmax. CT eig. real: 1.597068\t\u001b[0m\n", + "\tN unstab.: 002\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\t69.43\t69.43\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 171.00 m/2\tmax. CT eig. real: 1.849117\t\u001b[0m\n", + "\tN unstab.: 002\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\t69.26\t69.26\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 172.00 m/2\tmax. CT eig. real: 2.099343\t\u001b[0m\n", + "\tN unstab.: 002\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\t69.10\t69.10\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 173.00 m/2\tmax. CT eig. real: 2.347461\t\u001b[0m\n", + "\tN unstab.: 002\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\t68.94\t68.94\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 174.00 m/2\tmax. CT eig. real: 2.593246\t\u001b[0m\n", + "\tN unstab.: 002\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\t68.79\t68.79\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 175.00 m/2\tmax. CT eig. real: 2.836529\t\u001b[0m\n", + "\tN unstab.: 002\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\t68.65\t68.65\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 176.00 m/2\tmax. CT eig. real: 3.077180\t\u001b[0m\n", + "\tN unstab.: 002\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\t68.51\t68.51\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 177.00 m/2\tmax. CT eig. real: 3.315113\t\u001b[0m\n", + "\tN unstab.: 002\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\t68.38\t68.38\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 178.00 m/2\tmax. CT eig. real: 3.550268\t\u001b[0m\n", + "\tN unstab.: 002\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\t68.25\t68.25\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 179.00 m/2\tmax. CT eig. real: 3.782616\t\u001b[0m\n", + "\tN unstab.: 002\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\t68.13\t68.13\u001b[0m\n", + "\u001b[34mUpdating C and K matrices and natural frequencies with new normalised time...\u001b[0m\n", + "LTI\tu: 180.00 m/2\tmax. CT eig. real: 4.012145\t\u001b[0m\n", + "\tN unstab.: 002\u001b[0m\n", + "\tUnstable aeroelastic natural frequency CT(rad/s):\t68.01\t68.01\u001b[0m\n", + "Saving velocity analysis results...\u001b[0m\n", + "\u001b[34m\tSuccessful\u001b[0m\n", + "\u001b[36mFINISHED - Elapsed time = 59.1569176 seconds\u001b[0m\n", + "\u001b[36mFINISHED - CPU process time = 94.3774846 seconds\u001b[0m\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sharpy.sharpy_main.main(['', ws.route + ws.case_name + '.sharpy'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Analysis" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Nonlinear equilibrium\n", + "\n", + "The nonlinear equilibrium condition can be visualised and analysed by opening, with Paraview, the files in the `/output//aero` and `/output//beam` folders to see the deflection and aerodynamic forces acting" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Stability \n", + "\n", + "The stability of the Goland wing is now analysed under changing free stream velocity. The aeroelastic system is projected onto 2 structural modes (1st bending and 1st torsion). The two modes are seen quite separated at 100 m/s. As speed is increased, the damping of the torsion mode decreases until it crosses the imaginary axis onto the right hand plane and flutter begins. This flutter mode is a bending-torsion mode, as seen from the natural frequency plot where the frequencies of each coalesce into this mode." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "file_name = './output/%s/stability/velocity_analysis_min1000_max1800_nvel0081.dat' % case_name\n", + "\n", + "velocity_analysis = np.loadtxt(file_name)\n", + "u_inf = velocity_analysis[:, 0]\n", + "eigs_r = velocity_analysis[:, 1]\n", + "eigs_i = velocity_analysis[:, 2]" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig = plt.figure()\n", + "plt.scatter(eigs_r, eigs_i, c=u_inf, cmap='Blues')\n", + "cbar = plt.colorbar()\n", + "cbar.set_label('Free Stream Velocity, $u_\\infty$ [m/s]')\n", + "\n", + "plt.grid()\n", + "plt.xlim(-10, 10)\n", + "plt.ylim(-150, 150)\n", + "plt.xlabel('Real Part, $\\lambda$ [rad/s]')\n", + "plt.ylabel('Imag Part, $\\lambda$ [rad/s]');" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig = plt.figure()\n", + "natural_frequency = np.sqrt(eigs_r ** 2 + eigs_i ** 2)\n", + "damping_ratio = eigs_r / natural_frequency\n", + "cond = (eigs_r>-25) * (eigs_r<10) * (natural_frequency<100) # filter unwanted eigenvalues for this plot (mostly aero modes)\n", + "\n", + "plt.scatter(u_inf[cond], damping_ratio[cond], color='k', marker='s', s=9)\n", + "\n", + "plt.grid()\n", + "plt.ylim(-0.25, 0.25)\n", + "plt.xlabel('Free Stream Velocity, $u_\\infty$ [m/s]')\n", + "plt.ylabel('Damping Ratio, $\\zeta$ [-]');" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig = plt.figure()\n", + "cond = (eigs_r>-25) * (eigs_r<10) # filter unwanted eigenvalues for this plot (mostly aero modes)\n", + "plt.scatter(u_inf[cond], natural_frequency[cond], color='k', marker='s', s=9)\n", + "\n", + "plt.grid()\n", + "plt.ylim(40, 100)\n", + "plt.xlabel('Free Stream Velocity, $u_\\infty$ [m/s]')\n", + "plt.ylabel('Natural Frequency, $\\omega_n$ [rad/s]');" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 } diff --git a/docs/source/content/example_notebooks/linear_horten.ipynb b/docs/source/content/example_notebooks/linear_horten.ipynb index 4b1ff9a72..94fee1dae 100644 --- a/docs/source/content/example_notebooks/linear_horten.ipynb +++ b/docs/source/content/example_notebooks/linear_horten.ipynb @@ -1,1456 +1,1456 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Asymptotic Stability of a Flying Wing in Cruise Trimmed Conditions\n", - "\n", - "A Horten flying wing is analysed. The nonlinear trim condition is found and the system is linearised. The eigenvalues of the linearised system are then used to evaluate the stability at the cruise trimmed flight conditions." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# required packages\n", - "import sharpy.utils.algebra as algebra\n", - "import sharpy.sharpy_main\n", - "from cases.hangar.richards_wing import Baseline\n", - "import numpy as np\n", - "import configobj\n", - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Flight Conditions\n", - "\n", - "Initial flight conditions. The values for angle of attack ``alpha``, control surface deflection ``cs_deflection`` and ``thrust`` are only initial values. The values required for trim will be calculated by the ``StaticTrim`` routine" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "u_inf = 28\n", - "alpha_deg = 4.5135\n", - "cs_deflection = 0.1814\n", - "thrust = 5.5129" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Discretisation" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "M = 4 # chordwise panels\n", - "N = 11 # spanwise panels\n", - "Msf = 5 # wake length in chord numbers" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Create Horten Wing" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0\n", - "Section Mass: 11.88 \n", - "Linear Mass: 11.88\n", - "Section Ixx: 1.8777\n", - "Section Iyy: 1.0137\n", - "Section Izz: 2.5496\n", - "Linear Ixx: 1.88\n", - "1\n", - "Section Mass: 10.99 \n", - "Linear Mass: 10.99\n", - "Section Ixx: 1.4694\n", - "Section Iyy: 0.9345\n", - "Section Izz: 2.1501\n", - "Linear Ixx: 1.74\n", - "2\n", - "Section Mass: 10.10 \n", - "Linear Mass: 10.10\n", - "Section Ixx: 1.1257\n", - "Section Iyy: 0.8561\n", - "Section Izz: 1.7993\n", - "Linear Ixx: 1.60\n", - "3\n", - "Section Mass: 9.21 \n", - "Linear Mass: 9.21\n", - "Section Ixx: 0.8410\n", - "Section Iyy: 0.7783\n", - "Section Izz: 1.4933\n", - "Linear Ixx: 1.46\n", - "4\n", - "Section Mass: 8.32 \n", - "Linear Mass: 8.32\n", - "Section Ixx: 0.6096\n", - "Section Iyy: 0.7011\n", - "Section Izz: 1.2280\n", - "Linear Ixx: 1.31\n", - "5\n", - "Section Mass: 7.43 \n", - "Linear Mass: 7.43\n", - "Section Ixx: 0.4260\n", - "Section Iyy: 0.6246\n", - "Section Izz: 0.9996\n", - "Linear Ixx: 1.17\n", - "6\n", - "Section Mass: 6.54 \n", - "Linear Mass: 6.54\n", - "Section Ixx: 0.2845\n", - "Section Iyy: 0.5485\n", - "Section Izz: 0.8040\n", - "Linear Ixx: 1.03\n", - "7\n", - "Section Mass: 5.64 \n", - "Linear Mass: 5.64\n", - "Section Ixx: 0.1796\n", - "Section Iyy: 0.4728\n", - "Section Izz: 0.6374\n", - "Linear Ixx: 0.89\n", - "8\n", - "Section Mass: 4.75 \n", - "Linear Mass: 4.75\n", - "Section Ixx: 0.1055\n", - "Section Iyy: 0.3975\n", - "Section Izz: 0.4959\n", - "Linear Ixx: 0.75\n", - "9\n", - "Section Mass: 3.86 \n", - "Linear Mass: 3.86\n", - "Section Ixx: 0.0567\n", - "Section Iyy: 0.3226\n", - "Section Izz: 0.3753\n", - "Linear Ixx: 0.61\n", - "10\n", - "Section Mass: 2.97 \n", - "Linear Mass: 2.97\n", - "Section Ixx: 0.0275\n", - "Section Iyy: 0.2479\n", - "Section Izz: 0.2719\n", - "Linear Ixx: 0.47\n" - ] - } - ], - "source": [ - "ws = Baseline(M=M,\n", - " N=N,\n", - " Mstarfactor=Msf,\n", - " u_inf=u_inf,\n", - " rho=1.02,\n", - " alpha_deg=alpha_deg,\n", - " roll_deg=0,\n", - " cs_deflection_deg=cs_deflection,\n", - " thrust=thrust,\n", - " physical_time=20,\n", - " case_name='horten',\n", - " case_name_format=4,\n", - " case_remarks='M%gN%gMsf%g' % (M, N, Msf))\n", - "\n", - "ws.set_properties()\n", - "ws.initialise()\n", - "ws.clean_test_files()\n", - "\n", - "ws.update_mass_stiffness(sigma=1., sigma_mass=2.5)\n", - "ws.update_fem_prop()\n", - "ws.generate_fem_file()\n", - "ws.update_aero_properties()\n", - "ws.generate_aero_file()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Simulation Information\n", - "\n", - "The ``flow`` setting tells SHARPy which solvers to run and in which order. You may be stranged by the presence of the ``DynamicCoupled`` solver but it is necessary to give an initial speed to the structure. This will allow proper linearisation of the structural and rigid body equations." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "flow = ['BeamLoader',\n", - " 'AerogridLoader',\n", - " 'StaticTrim',\n", - " 'BeamPlot',\n", - " 'AerogridPlot',\n", - " 'AeroForcesCalculator',\n", - " 'DynamicCoupled',\n", - " 'Modal',\n", - " 'LinearAssembler',\n", - " 'AsymptoticStability',\n", - " ]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### SHARPy Settings" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "settings = dict()\n", - "settings['SHARPy'] = {'case': ws.case_name,\n", - " 'route': ws.case_route,\n", - " 'flow': flow,\n", - " 'write_screen': 'on',\n", - " 'write_log': 'on',\n", - " 'log_folder': './output/',\n", - " 'log_file': ws.case_name + '.log'}\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Loaders" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "settings['BeamLoader'] = {'unsteady': 'off',\n", - " 'orientation': algebra.euler2quat(np.array([ws.roll,\n", - " ws.alpha,\n", - " ws.beta]))}\n", - "settings['AerogridLoader'] = {'unsteady': 'off',\n", - " 'aligned_grid': 'on',\n", - " 'mstar': int(ws.M * ws.Mstarfactor),\n", - " 'freestream_dir': ['1', '0', '0'],\n", - " 'control_surface_deflection': [''],\n", - " 'wake_shape_generator': 'StraightWake',\n", - " 'wake_shape_generator_input': {'u_inf': ws.u_inf,\n", - " 'u_inf_direction': ['1', '0', '0'],\n", - " 'dt': ws.dt}}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### StaticCoupled Solver" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "settings['StaticCoupled'] = {'print_info': 'on',\n", - " 'structural_solver': 'NonLinearStatic',\n", - " 'structural_solver_settings': {'print_info': 'off',\n", - " 'max_iterations': 200,\n", - " 'num_load_steps': 1,\n", - " 'delta_curved': 1e-5,\n", - " 'min_delta': ws.tolerance,\n", - " 'gravity_on': 'on',\n", - " 'gravity': 9.81},\n", - " 'aero_solver': 'StaticUvlm',\n", - " 'aero_solver_settings': {'print_info': 'on',\n", - " 'horseshoe': ws.horseshoe,\n", - " 'num_cores': 4,\n", - " 'n_rollup': int(0),\n", - " 'rollup_dt': ws.dt,\n", - " 'rollup_aic_refresh': 1,\n", - " 'rollup_tolerance': 1e-4,\n", - " 'velocity_field_generator': 'SteadyVelocityField',\n", - " 'velocity_field_input': {'u_inf': ws.u_inf,\n", - " 'u_inf_direction': [1., 0, 0]},\n", - " 'rho': ws.rho},\n", - " 'max_iter': 200,\n", - " 'n_load_steps': 1,\n", - " 'tolerance': ws.tolerance,\n", - " 'relaxation_factor': 0.2}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Trim solver" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "settings['StaticTrim'] = {'solver': 'StaticCoupled',\n", - " 'solver_settings': settings['StaticCoupled'],\n", - " 'thrust_nodes': ws.thrust_nodes,\n", - " 'initial_alpha': ws.alpha,\n", - " 'initial_deflection': ws.cs_deflection,\n", - " 'initial_thrust': ws.thrust,\n", - " 'max_iter': 200,\n", - " 'fz_tolerance': 1e-2,\n", - " 'fx_tolerance': 1e-2,\n", - " 'm_tolerance': 1e-2}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Nonlinear Equilibrium Post-process" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "settings['AerogridPlot'] = {\n", - " 'include_rbm': 'off',\n", - " 'include_applied_forces': 'on',\n", - " 'minus_m_star': 0,\n", - " 'u_inf': ws.u_inf\n", - " }\n", - "settings['AeroForcesCalculator'] = {\n", - " 'write_text_file': 'off',\n", - " 'text_file_name': ws.case_name + '_aeroforces.csv',\n", - " 'screen_output': 'on',\n", - " 'unsteady': 'off',\n", - " 'coefficients': True,\n", - " 'q_ref': 0.5 * ws.rho * ws.u_inf ** 2,\n", - " 'S_ref': 12.809,\n", - " }\n", - "\n", - "settings['BeamPlot'] = {\n", - " 'include_rbm': 'on',\n", - " 'include_applied_forces': 'on',\n", - " 'include_FoR': 'on'}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### DynamicCoupled Solver\n", - "\n", - "As mentioned before, a single time step of ``DynamicCoupled`` is required to give the structure the velocity required for the linearisation of the rigid body equations to be correct. Hence `n_time_steps = 1`" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "struct_solver_settings = {'print_info': 'off',\n", - " 'initial_velocity_direction': [-1., 0., 0.],\n", - " 'max_iterations': 950,\n", - " 'delta_curved': 1e-6,\n", - " 'min_delta': ws.tolerance,\n", - " 'newmark_damp': 5e-3,\n", - " 'gravity_on': True,\n", - " 'gravity': 9.81,\n", - " 'num_steps': ws.n_tstep,\n", - " 'dt': ws.dt,\n", - " 'initial_velocity': ws.u_inf * 1}\n", - "\n", - "step_uvlm_settings = {'print_info': 'on',\n", - " 'num_cores': 4,\n", - " 'convection_scheme': ws.wake_type,\n", - " 'velocity_field_generator': 'SteadyVelocityField',\n", - " 'velocity_field_input': {'u_inf': ws.u_inf * 0,\n", - " 'u_inf_direction': [1., 0., 0.]},\n", - " 'rho': ws.rho,\n", - " 'n_time_steps': ws.n_tstep,\n", - " 'dt': ws.dt,\n", - " 'gamma_dot_filtering': 3}\n", - "\n", - "settings['DynamicCoupled'] = {'print_info': 'on',\n", - " 'structural_solver': 'NonLinearDynamicCoupledStep',\n", - " 'structural_solver_settings': struct_solver_settings,\n", - " 'aero_solver': 'StepUvlm',\n", - " 'aero_solver_settings': step_uvlm_settings,\n", - " 'fsi_substeps': 200,\n", - " 'fsi_tolerance': ws.fsi_tolerance,\n", - " 'relaxation_factor': ws.relaxation_factor,\n", - " 'minimum_steps': 1,\n", - " 'relaxation_steps': 150,\n", - " 'final_relaxation_factor': 0.5,\n", - " 'n_time_steps': 1,\n", - " 'dt': ws.dt,\n", - " 'include_unsteady_force_contribution': 'off',\n", - " }" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Modal Solver Settings" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "settings['Modal'] = {'print_info': True,\n", - " 'use_undamped_modes': True,\n", - " 'NumLambda': 30,\n", - " 'rigid_body_modes': True,\n", - " 'write_modes_vtk': 'on',\n", - " 'print_matrices': 'on',\n", - " 'continuous_eigenvalues': 'off',\n", - " 'dt': ws.dt,\n", - " 'plot_eigenvalues': False,\n", - " 'rigid_modes_cg': False}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Linear Assembler Settings\n", - "\n", - "Note that for the assembly of the linear system, we replace the parametrisation of the orientation with Euler angles instead of quaternions." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "settings['LinearAssembler'] = {'linear_system': 'LinearAeroelastic',\n", - " 'linear_system_settings': {\n", - " 'beam_settings': {'modal_projection': 'off',\n", - " 'inout_coords': 'modes',\n", - " 'discrete_time': True,\n", - " 'newmark_damp': 0.5e-2,\n", - " 'discr_method': 'newmark',\n", - " 'dt': ws.dt,\n", - " 'proj_modes': 'undamped',\n", - " 'num_modes': 9,\n", - " 'print_info': 'on',\n", - " 'gravity': 'on',\n", - " 'remove_dofs': []},\n", - " 'aero_settings': {'dt': ws.dt,\n", - " 'integr_order': 2,\n", - " 'density': ws.rho,\n", - " 'remove_predictor': 'off',\n", - " 'use_sparse': 'off',\n", - " 'remove_inputs': ['u_gust']},\n", - " 'track_body': 'on',\n", - " 'use_euler': 'on',\n", - " }}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Asymptotic Stability Post-processor" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "settings['AsymptoticStability'] = {\n", - " 'print_info': 'on',\n", - " 'frequency_cutoff': 0,\n", - " 'export_eigenvalues': 'on',\n", - " 'num_evals': 100,\n", - " }" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Write solver file" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "config = configobj.ConfigObj()\n", - "np.set_printoptions(precision=16)\n", - "file_name = ws.case_route + '/' + ws.case_name + '.sharpy'\n", - "config.filename = file_name\n", - "for k, v in settings.items():\n", - " config[k] = v\n", - "config.write()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Run Simulation" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--------------------------------------------------------------------------------\u001b[0m\n", - " ###### ## ## ### ######## ######## ## ##\u001b[0m\n", - " ## ## ## ## ## ## ## ## ## ## ## ##\u001b[0m\n", - " ## ## ## ## ## ## ## ## ## ####\u001b[0m\n", - " ###### ######### ## ## ######## ######## ##\u001b[0m\n", - " ## ## ## ######### ## ## ## ##\u001b[0m\n", - " ## ## ## ## ## ## ## ## ## ##\u001b[0m\n", - " ###### ## ## ## ## ## ## ## ##\u001b[0m\n", - "--------------------------------------------------------------------------------\u001b[0m\n", - "Aeroelastics Lab, Aeronautics Department.\u001b[0m\n", - " Copyright (c), Imperial College London.\u001b[0m\n", - " All rights reserved.\u001b[0m\n", - " License available at https://github.com/imperialcollegelondon/sharpy\u001b[0m\n", - "\u001b[36mRunning SHARPy from /home/ng213/2TB/pazy_code/pazy-sharpy/lib/sharpy/docs/source/content/example_notebooks\u001b[0m\n", - "\u001b[36mSHARPy being run is in /home/ng213/2TB/pazy_code/pazy-sharpy/lib/sharpy\u001b[0m\n", - "\u001b[36mThe branch being run is dev_setting_error\u001b[0m\n", - "\u001b[36mThe version and commit hash are: v1.2.1-344-g0239644-0239644\u001b[0m\n", - "SHARPy output folder set\u001b[0m\n", - "\u001b[34m\t./output//horten_u_inf2800_M4N11Msf5/\u001b[0m\n", - "\u001b[36mGenerating an instance of BeamLoader\u001b[0m\n", - "Variable for_pos has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: [0.0, 0, 0]\u001b[0m\n", - "\u001b[36mGenerating an instance of AerogridLoader\u001b[0m\n", - "Variable control_surface_deflection_generator_settings has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: {}\u001b[0m\n", - "Variable dx1 has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: -1.0\u001b[0m\n", - "Variable ndx1 has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1\u001b[0m\n", - "Variable r has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1.0\u001b[0m\n", - "Variable dxmax has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: -1.0\u001b[0m\n", - "\u001b[34mThe aerodynamic grid contains 4 surfaces\u001b[0m\n", - "\u001b[34m Surface 0, M=4, N=2\u001b[0m\n", - " Wake 0, M=20, N=2\u001b[0m\n", - "\u001b[34m Surface 1, M=4, N=22\u001b[0m\n", - " Wake 1, M=20, N=22\u001b[0m\n", - "\u001b[34m Surface 2, M=4, N=2\u001b[0m\n", - " Wake 2, M=20, N=2\u001b[0m\n", - "\u001b[34m Surface 3, M=4, N=22\u001b[0m\n", - " Wake 3, M=20, N=22\u001b[0m\n", - " In total: 192 bound panels\u001b[0m\n", - " In total: 960 wake panels\u001b[0m\n", - " Total number of panels = 1152\u001b[0m\n", - "\u001b[36mGenerating an instance of StaticTrim\u001b[0m\n", - "Variable print_info has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: True\u001b[0m\n", - "Variable tail_cs_index has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0\u001b[0m\n", - "Variable initial_angle_eps has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0.05\u001b[0m\n", - "Variable initial_thrust_eps has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 2.0\u001b[0m\n", - "Variable relaxation_factor has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0.2\u001b[0m\n", - "Variable save_info has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "\u001b[36mGenerating an instance of StaticCoupled\u001b[0m\n", - "Variable correct_forces_method has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: \u001b[0m\n", - "Variable runtime_generators has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: {}\u001b[0m\n", - "\u001b[36mGenerating an instance of NonLinearStatic\u001b[0m\n", - "Variable newmark_damp has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0.0001\u001b[0m\n", - "Variable gravity_dir has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: [0.0, 0.0, 1.0]\u001b[0m\n", - "Variable relaxation_factor has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0.3\u001b[0m\n", - "Variable dt has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0.01\u001b[0m\n", - "Variable num_steps has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 500\u001b[0m\n", - "Variable initial_position has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: [0. 0. 0.]\u001b[0m\n", - "\u001b[36mGenerating an instance of StaticUvlm\u001b[0m\n", - "Variable iterative_solver has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable iterative_tol has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0.0001\u001b[0m\n", - "Variable iterative_precond has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable cfl1 has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: True\u001b[0m\n", - "Variable vortex_radius has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1e-06\u001b[0m\n", - "Variable vortex_radius_wake_ind has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1e-06\u001b[0m\n", - "Variable rbm_vel_g has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]\u001b[0m\n", - "Variable centre_rot_g has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: [0.0, 0.0, 0.0]\u001b[0m\n", - "\u001b[0m\n", - "\u001b[0m\n", - "\u001b[0m\n", - "|=====|=====|============|==========|==========|==========|==========|==========|==========|\u001b[0m\n", - "|iter |step | log10(res) | Fx | Fy | Fz | Mx | My | Mz |\u001b[0m\n", - "|=====|=====|============|==========|==========|==========|==========|==========|==========|\u001b[0m\n", - "\u001b[0m\n", - "\u001b[0m\n", - "\u001b[0m\n", - "|==========|==========|==========|==========|==========|==========|==========|==========|==========|==========|\u001b[0m\n", - "| iter |alpha[deg]|elev[deg] | thrust | Fx | Fy | Fz | Mx | My | Mz |\u001b[0m\n", - "|==========|==========|==========|==========|==========|==========|==========|==========|==========|==========|\u001b[0m\n", - "| 0 | 0 | 0.00000 | -0.1051 | -0.0000 | 0.0598 | -0.0000 | 1.0837 | -0.0000 |\u001b[0m\n", - "| 1 | 0 | -7.62384 | -0.1284 | -0.0000 | 0.1276 | -0.0000 | 0.0045 | -0.0000 |\u001b[0m\n", - "| 2 | 0 | -8.33392 | -0.1190 | -0.0000 | 0.0397 | -0.0000 | -0.0774 | -0.0000 |\u001b[0m\n", - "| 3 | 0 | -9.30379 | -0.1133 | 0.0000 | 0.0011 | -0.0000 | -0.0070 | 0.0000 |\u001b[0m\n", - "| 4 | 0 | -10.71602 | -0.1136 | -0.0000 | 0.0032 | 0.0000 | -0.0100 | -0.0000 |\u001b[0m\n", - "| 5 | 0 | -10.88827 | -0.1138 | -0.0000 | 0.0043 | 0.0000 | -0.0119 | -0.0000 |\u001b[0m\n", - "| 6 | 0 | -11.66331 | -0.1138 | -0.0000 | 0.0042 | 0.0000 | -0.0116 | -0.0000 |\u001b[0m\n", - "| 7 | 0 | -12.88496 | -0.1138 | -0.0000 | 0.0041 | 0.0000 | -0.0116 | 0.0000 |\u001b[0m\n", - "| 0 | 4.5135 | 0.1814 | 5.5129 | -0.1138 | -0.0000 | 0.0041 | 0.0000 | -0.0116 | 0.0000 |\u001b[0m\n", - "| 0 | 0 | 0.00000 |-116.9870 | -0.0000 | 994.8063 | -0.0000 |-882.4104 | -0.0000 |\u001b[0m\n", - "| 1 | 0 | -5.79178 | -72.3841 | 0.0000 | 944.6140 | 0.0000 |-802.5912 | 0.0000 |\u001b[0m\n", - "| 2 | 0 | -6.63730 | -62.4378 | 0.0000 | 937.6662 | 0.0000 |-792.1259 | -0.0000 |\u001b[0m\n", - "| 3 | 0 | -7.22937 | -62.8923 | -0.0000 | 939.7866 | -0.0000 |-795.7093 | -0.0000 |\u001b[0m\n", - "| 4 | 0 | -8.65323 | -62.8757 | -0.0000 | 939.7100 | -0.0000 |-795.5764 | -0.0000 |\u001b[0m\n", - "| 5 | 0 | -8.81438 | -62.8640 | -0.0000 | 939.6554 | -0.0000 |-795.4837 | -0.0000 |\u001b[0m\n", - "| 6 | 0 | -9.59386 | -62.8660 | 0.0000 | 939.6644 | -0.0000 |-795.4991 | 0.0000 |\u001b[0m\n", - "| 7 | 0 | -10.80422 | -62.8661 | 0.0000 | 939.6650 | -0.0000 |-795.5000 | 0.0000 |\u001b[0m\n", - "| 8 | 0 | -11.01365 | -62.8660 | -0.0000 | 939.6647 | -0.0000 |-795.4994 | -0.0000 |\u001b[0m\n", - "| 9 | 0 | -12.15197 | -62.8660 | 0.0000 | 939.6647 | -0.0000 |-795.4995 | 0.0000 |\u001b[0m\n", - "| 0 | 7.3783 | -2.6834 | 5.5129 | -62.8660 | 0.0000 | 939.6647 | -0.0000 |-795.4995 | 0.0000 |\u001b[0m\n", - "| 0 | 0 | 0.00000 | -32.9132 | -0.0000 | 371.4715 | -0.0000 |-902.7953 | -0.0000 |\u001b[0m\n", - "| 1 | 0 | -5.48409 | -8.7241 | -0.0000 | 298.8484 | 0.0000 |-777.2938 | 0.0000 |\u001b[0m\n", - "| 2 | 0 | -6.39387 | -4.0957 | -0.0000 | 289.8092 | -0.0000 |-761.4855 | -0.0000 |\u001b[0m\n", - "| 3 | 0 | -6.85613 | -4.6263 | -0.0000 | 293.2407 | -0.0000 |-767.3376 | 0.0000 |\u001b[0m\n", - "| 4 | 0 | -8.25962 | -4.6052 | -0.0000 | 293.1048 | -0.0000 |-767.1066 | 0.0000 |\u001b[0m\n", - "| 5 | 0 | -8.44065 | -4.5914 | -0.0000 | 293.0156 | 0.0000 |-766.9545 | 0.0000 |\u001b[0m\n", - "| 6 | 0 | -9.20968 | -4.5937 | -0.0000 | 293.0308 | -0.0000 |-766.9804 | -0.0000 |\u001b[0m\n", - "| 7 | 0 | -10.44736 | -4.5939 | -0.0000 | 293.0316 | -0.0000 |-766.9819 | -0.0000 |\u001b[0m\n", - "| 8 | 0 | -10.63000 | -4.5938 | -0.0000 | 293.0311 | 0.0000 |-766.9809 | -0.0000 |\u001b[0m\n", - "| 9 | 0 | -11.74670 | -4.5938 | 0.0000 | 293.0311 | 0.0000 |-766.9810 | 0.0000 |\u001b[0m\n", - "| 10 | 0 | -12.29943 | -4.5938 | -0.0000 | 293.0311 | 0.0000 |-766.9810 | 0.0000 |\u001b[0m\n", - "| 0 | 4.5135 | 3.0462 | 5.5129 | -4.5938 | -0.0000 | 293.0311 | 0.0000 |-766.9810 | 0.0000 |\u001b[0m\n", - "| 0 | 0 | 0.00000 | -4.1051 | -0.0000 | 0.0598 | -0.0000 | 1.0834 | -0.0000 |\u001b[0m\n", - "| 1 | 0 | -7.62384 | -4.1284 | -0.0000 | 0.1276 | -0.0000 | 0.0042 | -0.0000 |\u001b[0m\n", - "| 2 | 0 | -8.33392 | -4.1190 | -0.0000 | 0.0397 | 0.0000 | -0.0778 | -0.0000 |\u001b[0m\n", - "| 3 | 0 | -9.30379 | -4.1133 | -0.0000 | 0.0011 | 0.0000 | -0.0074 | -0.0000 |\u001b[0m\n", - "| 4 | 0 | -10.71602 | -4.1136 | 0.0000 | 0.0032 | -0.0000 | -0.0104 | 0.0000 |\u001b[0m\n", - "| 5 | 0 | -10.88827 | -4.1138 | 0.0000 | 0.0043 | 0.0000 | -0.0123 | 0.0000 |\u001b[0m\n", - "| 6 | 0 | -11.66331 | -4.1138 | -0.0000 | 0.0042 | 0.0000 | -0.0119 | -0.0000 |\u001b[0m\n", - "| 7 | 0 | -12.88496 | -4.1138 | -0.0000 | 0.0041 | 0.0000 | -0.0119 | -0.0000 |\u001b[0m\n", - "| 0 | 4.5135 | 0.1814 | 7.5129 | -4.1138 | -0.0000 | 0.0041 | 0.0000 | -0.0119 | -0.0000 |\u001b[0m\n", - "| 0 | 0 | 0.00000 | 0.0095 | -0.0000 | 0.0498 | 0.0000 | 1.1013 | -0.0000 |\u001b[0m\n", - "| 1 | 0 | -7.62357 | -0.0140 | 0.0000 | 0.1189 | 0.0000 | 0.0198 | 0.0000 |\u001b[0m\n", - "| 2 | 0 | -8.33375 | -0.0046 | -0.0000 | 0.0312 | -0.0000 | -0.0624 | 0.0000 |\u001b[0m\n", - "| 3 | 0 | -9.30318 | 0.0010 | -0.0000 | -0.0075 | 0.0000 | 0.0081 | -0.0000 |\u001b[0m\n", - "| 4 | 0 | -10.71542 | 0.0007 | -0.0000 | -0.0054 | 0.0000 | 0.0051 | 0.0000 |\u001b[0m\n", - "| 5 | 0 | -10.88766 | 0.0006 | -0.0000 | -0.0043 | 0.0000 | 0.0032 | -0.0000 |\u001b[0m\n", - "| 6 | 0 | -11.66271 | 0.0006 | 0.0000 | -0.0044 | -0.0000 | 0.0035 | 0.0000 |\u001b[0m\n", - "| 7 | 0 | -12.88441 | 0.0006 | -0.0000 | -0.0045 | 0.0000 | 0.0035 | -0.0000 |\u001b[0m\n", - "| 1 | 4.5135 | 0.1814 | 5.4560 | 0.0006 | -0.0000 | -0.0045 | 0.0000 | 0.0035 | -0.0000 |\u001b[0m\n", - "\u001b[36mGenerating an instance of BeamPlot\u001b[0m\n", - "Variable include_applied_moments has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: True\u001b[0m\n", - "Variable name_prefix has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: \u001b[0m\n", - "Variable output_rbm has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: True\u001b[0m\n", - "\u001b[34m...Finished\u001b[0m\n", - "\u001b[36mGenerating an instance of AerogridPlot\u001b[0m\n", - "Variable include_forward_motion has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable include_unsteady_applied_forces has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable name_prefix has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: \u001b[0m\n", - "Variable dt has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0.0\u001b[0m\n", - "Variable include_velocities has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable include_incidence_angle has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable num_cores has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1\u001b[0m\n", - "Variable vortex_radius has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1e-06\u001b[0m\n", - "\u001b[34m...Finished\u001b[0m\n", - "\u001b[36mGenerating an instance of AeroForcesCalculator\u001b[0m\n", - "--------------------------------------------------------------------------------\u001b[0m\n", - "\u001b[34mtstep | fx_g | fy_g | fz_g | Cfx_g | Cfy_g | Cfz_g \u001b[0m\n", - "\u001b[34m 0 | 1.088e+01 | -4.476e-13 | 1.835e+03 | 2.124e-03 | -8.740e-17 | 3.583e-01\u001b[0m\n", - "\u001b[34m...Finished\u001b[0m\n", - "\u001b[36mGenerating an instance of DynamicCoupled\u001b[0m\n", - "Variable structural_substeps has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0\u001b[0m\n", - "Variable dynamic_relaxation has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable postprocessors has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: []\u001b[0m\n", - "Variable postprocessors_settings has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: {}\u001b[0m\n", - "Variable controller_id has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: {}\u001b[0m\n", - "Variable controller_settings has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: {}\u001b[0m\n", - "Variable cleanup_previous_solution has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable steps_without_unsteady_force has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0\u001b[0m\n", - "Variable pseudosteps_ramp_unsteady_force has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0\u001b[0m\n", - "Variable correct_forces_method has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: \u001b[0m\n", - "Variable network_settings has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: {}\u001b[0m\n", - "Variable runtime_generators has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: {}\u001b[0m\n", - "\u001b[36mGenerating an instance of NonLinearDynamicCoupledStep\u001b[0m\n", - "Variable num_load_steps has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1\u001b[0m\n", - "Variable gravity_dir has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: [0.0, 0.0, 1.0]\u001b[0m\n", - "Variable relaxation_factor has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0.3\u001b[0m\n", - "Variable balancing has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "\u001b[36mGenerating an instance of StepUvlm\u001b[0m\n", - "Variable iterative_solver has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable iterative_tol has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0.0001\u001b[0m\n", - "Variable iterative_precond has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable cfl1 has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: True\u001b[0m\n", - "Variable vortex_radius has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1e-06\u001b[0m\n", - "Variable vortex_radius_wake_ind has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1e-06\u001b[0m\n", - "Variable interp_coords has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0\u001b[0m\n", - "Variable filter_method has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0\u001b[0m\n", - "Variable interp_method has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0\u001b[0m\n", - "Variable yaw_slerp has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0.0\u001b[0m\n", - "Variable centre_rot has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: [0.0, 0.0, 0.0]\u001b[0m\n", - "Variable quasi_steady has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "\u001b[0m\n", - "\u001b[0m\n", - "\u001b[0m\n", - "|=======|========|======|==============|==============|==============|==============|==============|\u001b[0m\n", - "| ts | t | iter | struc ratio | iter time | residual vel | FoR_vel(x) | FoR_vel(z) |\u001b[0m\n", - "|=======|========|======|==============|==============|==============|==============|==============|\u001b[0m\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/ng213/2TB/pazy_code/pazy-sharpy/lib/sharpy/sharpy/aero/utils/uvlmlib.py:264: RuntimeWarning: invalid value encountered in true_divide\n", - " flightconditions.uinf_direction = np.ctypeslib.as_ctypes(ts_info.u_ext[0][:, 0, 0]/flightconditions.uinf)\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "| 1 | 0.0089 | 3 | 0.877319 | 0.593232 | -10.598250 |-2.791317e+01 |-2.203426e+00 |\u001b[0m\n", - "\u001b[34m...Finished\u001b[0m\n", - "\u001b[36mGenerating an instance of Modal\u001b[0m\n", - "Variable keep_linear_matrices has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: True\u001b[0m\n", - "Variable write_dat has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: True\u001b[0m\n", - "Variable delta_curved has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0.01\u001b[0m\n", - "Variable max_rotation_deg has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 15.0\u001b[0m\n", - "Variable max_displacement has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0.15\u001b[0m\n", - "Variable use_custom_timestep has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: -1\u001b[0m\n", - "Structural eigenvalues\u001b[0m\n", - "\u001b[0m\n", - "\u001b[0m\n", - "\u001b[0m\n", - "|==============|==============|==============|==============|==============|==============|==============|\u001b[0m\n", - "| mode | eval_real | eval_imag | freq_n (Hz) | freq_d (Hz) | damping | period (s) |\u001b[0m\n", - "|==============|==============|==============|==============|==============|==============|==============|\u001b[0m\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/ng213/2TB/pazy_code/pazy-sharpy/lib/sharpy/sharpy/solvers/modal.py:265: UserWarning: Projecting a system with damping on undamped modal shapes\n", - " warnings.warn('Projecting a system with damping on undamped modal shapes')\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "| 0 | -0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 1 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 2 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 3 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 4 | -0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 5 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 6 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 7 | -0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 8 | -0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 9 | -0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 10 | 0.000000 | 28.293939 | 4.503120 | 4.503120 | -0.000000 | 0.222068 |\u001b[0m\n", - "| 11 | 0.000000 | 29.271318 | 4.658675 | 4.658675 | -0.000000 | 0.214653 |\u001b[0m\n", - "| 12 | 0.000000 | 54.780234 | 8.718545 | 8.718545 | -0.000000 | 0.114698 |\u001b[0m\n", - "| 13 | 0.000000 | 58.999779 | 9.390106 | 9.390106 | -0.000000 | 0.106495 |\u001b[0m\n", - "| 14 | 0.000000 | 70.520741 | 11.223724 | 11.223724 | -0.000000 | 0.089097 |\u001b[0m\n", - "| 15 | 0.000000 | 76.917111 | 12.241738 | 12.241738 | -0.000000 | 0.081688 |\u001b[0m\n", - "| 16 | 0.000000 | 87.324076 | 13.898058 | 13.898058 | -0.000000 | 0.071952 |\u001b[0m\n", - "| 17 | 0.000000 | 108.035577 | 17.194396 | 17.194396 | -0.000000 | 0.058158 |\u001b[0m\n", - "| 18 | 0.000000 | 119.692139 | 19.049596 | 19.049596 | -0.000000 | 0.052495 |\u001b[0m\n", - "| 19 | 0.000000 | 133.495187 | 21.246419 | 21.246419 | -0.000000 | 0.047067 |\u001b[0m\n", - "| 20 | 0.000000 | 134.444788 | 21.397553 | 21.397553 | -0.000000 | 0.046734 |\u001b[0m\n", - "| 21 | 0.000000 | 151.060442 | 24.042016 | 24.042016 | -0.000000 | 0.041594 |\u001b[0m\n", - "| 22 | 0.000000 | 159.369020 | 25.364367 | 25.364367 | -0.000000 | 0.039425 |\u001b[0m\n", - "| 23 | 0.000000 | 171.256102 | 27.256255 | 27.256255 | -0.000000 | 0.036689 |\u001b[0m\n", - "| 24 | 0.000000 | 173.895881 | 27.676389 | 27.676389 | -0.000000 | 0.036132 |\u001b[0m\n", - "| 25 | 0.000000 | 199.016557 | 31.674469 | 31.674469 | -0.000000 | 0.031571 |\u001b[0m\n", - "| 26 | 0.000000 | 205.412581 | 32.692428 | 32.692428 | -0.000000 | 0.030588 |\u001b[0m\n", - "| 27 | 0.000000 | 205.419531 | 32.693534 | 32.693534 | -0.000000 | 0.030587 |\u001b[0m\n", - "| 28 | 0.000000 | 223.563796 | 35.581283 | 35.581283 | -0.000000 | 0.028105 |\u001b[0m\n", - "| 29 | 0.000000 | 227.924750 | 36.275351 | 36.275351 | -0.000000 | 0.027567 |\u001b[0m\n", - "\u001b[36mGenerating an instance of LinearAssembler\u001b[0m\n", - "Variable linearisation_tstep has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: -1\u001b[0m\n", - "Variable modal_tstep has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: -1\u001b[0m\n", - "Variable inout_coordinates has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: \u001b[0m\n", - "Variable retain_inputs has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: []\u001b[0m\n", - "Variable retain_outputs has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: []\u001b[0m\n", - "\u001b[36mGenerating an instance of LinearAeroelastic\u001b[0m\n", - "Variable uvlm_filename has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: \u001b[0m\n", - "\u001b[36mGenerating an instance of LinearUVLM\u001b[0m\n", - "Variable ScalingDict has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: {}\u001b[0m\n", - "Variable gust_assembler has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: \u001b[0m\n", - "Variable rom_method has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: []\u001b[0m\n", - "Variable rom_method_settings has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: {}\u001b[0m\n", - "Variable vortex_radius has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1e-06\u001b[0m\n", - "Variable cfl1 has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: True\u001b[0m\n", - "Variable length has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1.0\u001b[0m\n", - "Variable speed has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1.0\u001b[0m\n", - "Variable density has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1.0\u001b[0m\n", - "Variable velocity_field_generator has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: SteadyVelocityField\u001b[0m\n", - "Variable velocity_field_input has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: {}\u001b[0m\n", - "Variable physical_model has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: True\u001b[0m\n", - "Variable track_body has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable track_body_number has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: -1\u001b[0m\n", - "Initialising Static linear UVLM solver class...\u001b[0m\n", - "\t\t\t...done in 0.39 sec\u001b[0m\n", - "State-space realisation of UVLM equations started...\u001b[0m\n", - "\u001b[34mComputing wake propagation matrix with CFL1=True\u001b[0m\n", - "\u001b[34m\tstate-space model produced in form:\u001b[0m\n", - "\u001b[34m\tx_{n+1} = A x_{n} + Bp u_{n+1}\u001b[0m\n", - "\t\t\t...done in 2.43 sec\u001b[0m\n", - "\u001b[36mGenerating an instance of LinearBeam\u001b[0m\n", - "Variable remove_sym_modes has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Warning, projecting system with damping onto undamped modes\u001b[0m\n", - "\u001b[0m\n", - "Linearising gravity terms...\u001b[0m\n", - "\u001b[34m\tM = 187.12 kg\u001b[0m\n", - "\u001b[34m\tX_CG A -> 1.19 0.00 0.01\u001b[0m\n", - "\u001b[36mNode 1 \t-> B 0.000 -0.089 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.089 0.206 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.089 0.206 -0.007\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 2.6141\u001b[0m\n", - "\u001b[36mNode 2 \t-> B -0.010 -0.019 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.019 0.403 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.019 0.403 -0.001\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 7.3672\u001b[0m\n", - "\u001b[36mNode 3 \t-> B -0.019 -0.087 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.234 0.800 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.234 0.800 -0.018\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 5.8780\u001b[0m\n", - "\u001b[36mNode 4 \t-> B -0.019 -0.084 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.390 1.238 0.001\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.389 1.238 -0.030\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 2.8288\u001b[0m\n", - "\u001b[36mNode 5 \t-> B -0.018 -0.081 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.546 1.676 0.001\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.544 1.676 -0.041\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 5.4372\u001b[0m\n", - "\u001b[36mNode 6 \t-> B -0.017 -0.078 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.702 2.113 0.002\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.700 2.113 -0.053\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 2.6084\u001b[0m\n", - "\u001b[36mNode 7 \t-> B -0.016 -0.074 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.857 2.551 0.003\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.855 2.551 -0.064\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 4.9963\u001b[0m\n", - "\u001b[36mNode 8 \t-> B -0.016 -0.071 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 1.013 2.988 0.004\u001b[0m\n", - "\u001b[36m\t\t\t-> G 1.010 2.988 -0.076\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 2.3879\u001b[0m\n", - "\u001b[36mNode 9 \t-> B -0.015 -0.068 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 1.169 3.426 0.005\u001b[0m\n", - "\u001b[36m\t\t\t-> G 1.166 3.426 -0.087\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 4.5555\u001b[0m\n", - "\u001b[36mNode 10 \t-> B -0.014 -0.065 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 1.325 3.863 0.006\u001b[0m\n", - "\u001b[36m\t\t\t-> G 1.321 3.863 -0.098\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 2.1675\u001b[0m\n", - "\u001b[36mNode 11 \t-> B -0.013 -0.061 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 1.480 4.301 0.007\u001b[0m\n", - "\u001b[36m\t\t\t-> G 1.476 4.301 -0.109\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 4.1146\u001b[0m\n", - "\u001b[36mNode 12 \t-> B -0.013 -0.058 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 1.636 4.739 0.009\u001b[0m\n", - "\u001b[36m\t\t\t-> G 1.632 4.739 -0.120\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 1.9471\u001b[0m\n", - "\u001b[36mNode 13 \t-> B -0.012 -0.055 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 1.792 5.176 0.010\u001b[0m\n", - "\u001b[36m\t\t\t-> G 1.787 5.176 -0.131\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 3.6738\u001b[0m\n", - "\u001b[36mNode 14 \t-> B -0.011 -0.052 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 1.948 5.614 0.011\u001b[0m\n", - "\u001b[36m\t\t\t-> G 1.943 5.614 -0.142\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 1.7267\u001b[0m\n", - "\u001b[36mNode 15 \t-> B -0.011 -0.048 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 2.104 6.052 0.012\u001b[0m\n", - "\u001b[36m\t\t\t-> G 2.098 6.052 -0.153\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 3.2329\u001b[0m\n", - "\u001b[36mNode 16 \t-> B -0.010 -0.045 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 2.260 6.489 0.014\u001b[0m\n", - "\u001b[36m\t\t\t-> G 2.254 6.489 -0.164\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 1.5062\u001b[0m\n", - "\u001b[36mNode 17 \t-> B -0.009 -0.042 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 2.415 6.927 0.015\u001b[0m\n", - "\u001b[36m\t\t\t-> G 2.409 6.927 -0.175\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 2.7921\u001b[0m\n", - "\u001b[36mNode 18 \t-> B -0.008 -0.039 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 2.571 7.364 0.016\u001b[0m\n", - "\u001b[36m\t\t\t-> G 2.564 7.364 -0.186\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 1.2858\u001b[0m\n", - "\u001b[36mNode 19 \t-> B -0.008 -0.035 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 2.727 7.802 0.017\u001b[0m\n", - "\u001b[36m\t\t\t-> G 2.720 7.802 -0.197\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 2.3512\u001b[0m\n", - "\u001b[36mNode 20 \t-> B -0.007 -0.032 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 2.883 8.239 0.019\u001b[0m\n", - "\u001b[36m\t\t\t-> G 2.875 8.239 -0.208\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 1.0654\u001b[0m\n", - "\u001b[36mNode 21 \t-> B -0.006 -0.028 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 3.038 8.677 0.020\u001b[0m\n", - "\u001b[36m\t\t\t-> G 3.030 8.677 -0.219\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 1.9104\u001b[0m\n", - "\u001b[36mNode 22 \t-> B -0.006 -0.026 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 3.194 9.114 0.021\u001b[0m\n", - "\u001b[36m\t\t\t-> G 3.186 9.114 -0.230\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 0.8450\u001b[0m\n", - "\u001b[36mNode 23 \t-> B -0.005 -0.022 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 3.350 9.552 0.023\u001b[0m\n", - "\u001b[36m\t\t\t-> G 3.341 9.552 -0.241\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 1.4695\u001b[0m\n", - "\u001b[36mNode 24 \t-> B -0.005 -0.022 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 3.508 9.988 0.024\u001b[0m\n", - "\u001b[36m\t\t\t-> G 3.499 9.988 -0.252\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 0.3674\u001b[0m\n", - "\u001b[36mNode 25 \t-> B 0.000 0.089 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.089 -0.206 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.089 -0.206 -0.007\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 2.6141\u001b[0m\n", - "\u001b[36mNode 26 \t-> B -0.010 0.019 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.019 -0.403 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.019 -0.403 -0.001\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 7.3672\u001b[0m\n", - "\u001b[36mNode 27 \t-> B -0.019 0.087 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.234 -0.800 0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.234 -0.800 -0.018\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 5.8780\u001b[0m\n", - "\u001b[36mNode 28 \t-> B -0.019 0.084 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.390 -1.238 0.001\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.389 -1.238 -0.030\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 2.8288\u001b[0m\n", - "\u001b[36mNode 29 \t-> B -0.018 0.081 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.546 -1.676 0.001\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.544 -1.676 -0.041\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 5.4372\u001b[0m\n", - "\u001b[36mNode 30 \t-> B -0.017 0.078 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.702 -2.113 0.002\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.700 -2.113 -0.053\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 2.6084\u001b[0m\n", - "\u001b[36mNode 31 \t-> B -0.016 0.074 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 0.857 -2.551 0.003\u001b[0m\n", - "\u001b[36m\t\t\t-> G 0.855 -2.551 -0.064\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 4.9963\u001b[0m\n", - "\u001b[36mNode 32 \t-> B -0.016 0.071 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 1.013 -2.988 0.004\u001b[0m\n", - "\u001b[36m\t\t\t-> G 1.010 -2.988 -0.076\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 2.3879\u001b[0m\n", - "\u001b[36mNode 33 \t-> B -0.015 0.068 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 1.169 -3.426 0.005\u001b[0m\n", - "\u001b[36m\t\t\t-> G 1.166 -3.426 -0.087\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 4.5555\u001b[0m\n", - "\u001b[36mNode 34 \t-> B -0.014 0.065 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 1.325 -3.863 0.006\u001b[0m\n", - "\u001b[36m\t\t\t-> G 1.321 -3.863 -0.098\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 2.1675\u001b[0m\n", - "\u001b[36mNode 35 \t-> B -0.013 0.061 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 1.480 -4.301 0.007\u001b[0m\n", - "\u001b[36m\t\t\t-> G 1.476 -4.301 -0.109\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 4.1146\u001b[0m\n", - "\u001b[36mNode 36 \t-> B -0.013 0.058 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 1.636 -4.739 0.009\u001b[0m\n", - "\u001b[36m\t\t\t-> G 1.632 -4.739 -0.120\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 1.9471\u001b[0m\n", - "\u001b[36mNode 37 \t-> B -0.012 0.055 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 1.792 -5.176 0.010\u001b[0m\n", - "\u001b[36m\t\t\t-> G 1.787 -5.176 -0.131\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 3.6738\u001b[0m\n", - "\u001b[36mNode 38 \t-> B -0.011 0.052 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 1.948 -5.614 0.011\u001b[0m\n", - "\u001b[36m\t\t\t-> G 1.943 -5.614 -0.142\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 1.7267\u001b[0m\n", - "\u001b[36mNode 39 \t-> B -0.011 0.048 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 2.104 -6.052 0.012\u001b[0m\n", - "\u001b[36m\t\t\t-> G 2.098 -6.052 -0.153\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 3.2329\u001b[0m\n", - "\u001b[36mNode 40 \t-> B -0.010 0.045 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 2.260 -6.489 0.014\u001b[0m\n", - "\u001b[36m\t\t\t-> G 2.254 -6.489 -0.164\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 1.5062\u001b[0m\n", - "\u001b[36mNode 41 \t-> B -0.009 0.042 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 2.415 -6.927 0.015\u001b[0m\n", - "\u001b[36m\t\t\t-> G 2.409 -6.927 -0.175\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 2.7921\u001b[0m\n", - "\u001b[36mNode 42 \t-> B -0.008 0.039 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 2.571 -7.364 0.016\u001b[0m\n", - "\u001b[36m\t\t\t-> G 2.564 -7.364 -0.186\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 1.2858\u001b[0m\n", - "\u001b[36mNode 43 \t-> B -0.008 0.035 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 2.727 -7.802 0.017\u001b[0m\n", - "\u001b[36m\t\t\t-> G 2.720 -7.802 -0.197\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 2.3512\u001b[0m\n", - "\u001b[36mNode 44 \t-> B -0.007 0.032 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 2.883 -8.239 0.019\u001b[0m\n", - "\u001b[36m\t\t\t-> G 2.875 -8.239 -0.208\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 1.0654\u001b[0m\n", - "\u001b[36mNode 45 \t-> B -0.006 0.028 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 3.038 -8.677 0.020\u001b[0m\n", - "\u001b[36m\t\t\t-> G 3.030 -8.677 -0.219\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 1.9104\u001b[0m\n", - "\u001b[36mNode 46 \t-> B -0.006 0.026 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 3.194 -9.114 0.021\u001b[0m\n", - "\u001b[36m\t\t\t-> G 3.186 -9.114 -0.230\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 0.8450\u001b[0m\n", - "\u001b[36mNode 47 \t-> B -0.005 0.022 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 3.350 -9.552 0.023\u001b[0m\n", - "\u001b[36m\t\t\t-> G 3.341 -9.552 -0.241\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 1.4695\u001b[0m\n", - "\u001b[36mNode 48 \t-> B -0.005 0.022 -0.000\u001b[0m\n", - "\u001b[36m\t\t\t-> A 3.508 -9.988 0.024\u001b[0m\n", - "\u001b[36m\t\t\t-> G 3.499 -9.988 -0.252\u001b[0m\n", - "\u001b[36m\tNode mass:\u001b[0m\n", - "\u001b[36m\t\tMatrix: 0.3674\u001b[0m\n", - " Updated the beam C, modal C and K matrices with the terms from the\n", - "gravity linearisation\u001b[0m\n", - "\u001b[0m\n", - "Aeroelastic system assembled:\u001b[0m\n", - "\u001b[34m\tAerodynamic states: 1536\u001b[0m\n", - "\u001b[34m\tStructural states: 594\u001b[0m\n", - "\u001b[34m\tTotal states: 2130\u001b[0m\n", - "\u001b[34m\tInputs: 893\u001b[0m\n", - "\u001b[34m\tOutputs: 891\u001b[0m\n", - "\u001b[34mFinal system is:\u001b[0m\n", - "\u001b[34mState-space system\u001b[0m\n", - "\u001b[34mStates: 2130\u001b[0m\n", - "\u001b[34mInputs: 893\u001b[0m\n", - "\u001b[34mOutputs: 891\u001b[0m\n", - "\u001b[34m\u001b[0m\n", - "\u001b[36mGenerating an instance of AsymptoticStability\u001b[0m\n", - "Variable reference_velocity has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1.0\u001b[0m\n", - "Variable display_root_locus has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable velocity_analysis has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: []\u001b[0m\n", - "Variable iterative_eigvals has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: False\u001b[0m\n", - "Variable modes_to_plot has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: []\u001b[0m\n", - "Variable postprocessors has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: []\u001b[0m\n", - "Variable postprocessors_settings has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: {}\u001b[0m\n", - "Dynamical System Eigenvalues\u001b[0m\n", - "Calculating eigenvalues using direct method\u001b[0m\n", - "\u001b[0m\n", - "\u001b[0m\n", - "\u001b[0m\n", - "|==============|==============|==============|==============|==============|==============|==============|\u001b[0m\n", - "| mode | eval_real | eval_imag | freq_n (Hz) | freq_d (Hz) | damping | period (s) |\u001b[0m\n", - "|==============|==============|==============|==============|==============|==============|==============|\u001b[0m\n", - "| 0 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 1 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 2 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 3 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 4 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 5 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 6 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 7 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 8 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 9 | -0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 10 | -0.000884 | -0.321712 | 0.051202 | 0.051202 | 0.002747 | 19.530439 |\u001b[0m\n", - "| 11 | -0.000884 | 0.321712 | 0.051202 | 0.051202 | 0.002747 | 19.530439 |\u001b[0m\n", - "| 12 | -0.008470 | -0.391287 | 0.062290 | 0.062275 | 0.021642 | 16.057731 |\u001b[0m\n", - "| 13 | -0.008470 | 0.391287 | 0.062290 | 0.062275 | 0.021642 | 16.057731 |\u001b[0m\n", - "| 14 | -0.022506 | 0.000000 | 0.003582 | 0.000000 | 1.000000 | inf |\u001b[0m\n", - "| 15 | -0.064808 | -53.732514 | 8.551801 | 8.551795 | 0.001206 | 0.116935 |\u001b[0m\n", - "| 16 | -0.064808 | 53.732514 | 8.551801 | 8.551795 | 0.001206 | 0.116935 |\u001b[0m\n", - "| 17 | -0.101946 | 68.319141 | 10.873341 | 10.873329 | 0.001492 | 0.091968 |\u001b[0m\n", - "| 18 | -0.101946 | -68.319141 | 10.873341 | 10.873329 | 0.001492 | 0.091968 |\u001b[0m\n", - "| 19 | -0.147587 | 83.265282 | 13.252102 | 13.252081 | 0.001772 | 0.075460 |\u001b[0m\n", - "| 20 | -0.147587 | -83.265282 | 13.252102 | 13.252081 | 0.001772 | 0.075460 |\u001b[0m\n", - "| 21 | -0.248703 | -109.925782 | 17.495276 | 17.495232 | 0.002262 | 0.057158 |\u001b[0m\n", - "| 22 | -0.248703 | 109.925782 | 17.495276 | 17.495232 | 0.002262 | 0.057158 |\u001b[0m\n", - "| 23 | -0.293472 | 120.387631 | 19.160344 | 19.160287 | 0.002438 | 0.052191 |\u001b[0m\n", - "| 24 | -0.293472 | -120.387631 | 19.160344 | 19.160287 | 0.002438 | 0.052191 |\u001b[0m\n", - "| 25 | -0.350319 | 132.903280 | 21.152288 | 21.152214 | 0.002636 | 0.047276 |\u001b[0m\n", - "| 26 | -0.350319 | -132.903280 | 21.152288 | 21.152214 | 0.002636 | 0.047276 |\u001b[0m\n", - "| 27 | -0.376401 | 138.516954 | 22.045739 | 22.045658 | 0.002717 | 0.045360 |\u001b[0m\n", - "| 28 | -0.376401 | -138.516954 | 22.045739 | 22.045658 | 0.002717 | 0.045360 |\u001b[0m\n", - "| 29 | -0.494445 | 162.714232 | 25.896894 | 25.896774 | 0.003039 | 0.038615 |\u001b[0m\n", - "| 30 | -0.494445 | -162.714232 | 25.896894 | 25.896774 | 0.003039 | 0.038615 |\u001b[0m\n", - "| 31 | -0.511651 | -166.238306 | 26.457773 | 26.457648 | 0.003078 | 0.037796 |\u001b[0m\n", - "| 32 | -0.511651 | 166.238306 | 26.457773 | 26.457648 | 0.003078 | 0.037796 |\u001b[0m\n", - "| 33 | -0.559180 | 175.709816 | 27.965227 | 27.965086 | 0.003182 | 0.035759 |\u001b[0m\n", - "| 34 | -0.559180 | -175.709816 | 27.965227 | 27.965086 | 0.003182 | 0.035759 |\u001b[0m\n", - "| 35 | -0.569755 | 177.873274 | 28.309556 | 28.309411 | 0.003203 | 0.035324 |\u001b[0m\n", - "| 36 | -0.569755 | -177.873274 | 28.309556 | 28.309411 | 0.003203 | 0.035324 |\u001b[0m\n", - "| 37 | -0.669914 | -197.999020 | 31.512703 | 31.512523 | 0.003383 | 0.031733 |\u001b[0m\n", - "| 38 | -0.669914 | 197.999020 | 31.512703 | 31.512523 | 0.003383 | 0.031733 |\u001b[0m\n", - "| 39 | -0.678424 | 199.782714 | 31.796590 | 31.796406 | 0.003396 | 0.031450 |\u001b[0m\n", - "| 40 | -0.678424 | -199.782714 | 31.796590 | 31.796406 | 0.003396 | 0.031450 |\u001b[0m\n", - "| 41 | -0.715684 | 207.440562 | 33.015387 | 33.015191 | 0.003450 | 0.030289 |\u001b[0m\n", - "| 42 | -0.715684 | -207.440562 | 33.015387 | 33.015191 | 0.003450 | 0.030289 |\u001b[0m\n", - "| 43 | -0.721193 | -208.623018 | 33.203583 | 33.203385 | 0.003457 | 0.030117 |\u001b[0m\n", - "| 44 | -0.721193 | 208.623018 | 33.203583 | 33.203385 | 0.003457 | 0.030117 |\u001b[0m\n", - "| 45 | -0.796838 | -224.809928 | 35.779836 | 35.779611 | 0.003544 | 0.027949 |\u001b[0m\n", - "| 46 | -0.796838 | 224.809928 | 35.779836 | 35.779611 | 0.003544 | 0.027949 |\u001b[0m\n", - "| 47 | -0.801462 | -225.851223 | 35.945565 | 35.945339 | 0.003549 | 0.027820 |\u001b[0m\n", - "| 48 | -0.801462 | 225.851223 | 35.945565 | 35.945339 | 0.003549 | 0.027820 |\u001b[0m\n", - "| 49 | -0.823218 | 257.880058 | 41.043095 | 41.042886 | 0.003192 | 0.024365 |\u001b[0m\n", - "| 50 | -0.823218 | -257.880058 | 41.043095 | 41.042886 | 0.003192 | 0.024365 |\u001b[0m\n", - "| 51 | -0.829849 | 232.223377 | 36.959734 | 36.959498 | 0.003573 | 0.027057 |\u001b[0m\n", - "| 52 | -0.829849 | -232.223377 | 36.959734 | 36.959498 | 0.003573 | 0.027057 |\u001b[0m\n", - "| 53 | -0.833132 | 232.986723 | 37.081226 | 37.080989 | 0.003576 | 0.026968 |\u001b[0m\n", - "| 54 | -0.833132 | -232.986723 | 37.081226 | 37.080989 | 0.003576 | 0.026968 |\u001b[0m\n", - "| 55 | -0.837692 | 252.830750 | 40.239484 | 40.239264 | 0.003313 | 0.024851 |\u001b[0m\n", - "| 56 | -0.837692 | -252.830750 | 40.239484 | 40.239264 | 0.003313 | 0.024851 |\u001b[0m\n", - "| 57 | -0.843057 | -274.636584 | 43.709976 | 43.709770 | 0.003070 | 0.022878 |\u001b[0m\n", - "| 58 | -0.843057 | 274.636584 | 43.709976 | 43.709770 | 0.003070 | 0.022878 |\u001b[0m\n", - "| 59 | -0.855990 | -264.468496 | 42.091689 | 42.091468 | 0.003237 | 0.023758 |\u001b[0m\n", - "| 60 | -0.855990 | 264.468496 | 42.091689 | 42.091468 | 0.003237 | 0.023758 |\u001b[0m\n", - "| 61 | -0.864725 | 271.184095 | 43.160509 | 43.160289 | 0.003189 | 0.023169 |\u001b[0m\n", - "| 62 | -0.864725 | -271.184095 | 43.160509 | 43.160289 | 0.003189 | 0.023169 |\u001b[0m\n", - "| 63 | -0.871325 | -283.421756 | 45.108187 | 45.107973 | 0.003074 | 0.022169 |\u001b[0m\n", - "| 64 | -0.871325 | 283.421756 | 45.108187 | 45.107973 | 0.003074 | 0.022169 |\u001b[0m\n", - "| 65 | -0.878445 | 267.336890 | 42.548217 | 42.547987 | 0.003286 | 0.023503 |\u001b[0m\n", - "| 66 | -0.878445 | -267.336890 | 42.548217 | 42.547987 | 0.003286 | 0.023503 |\u001b[0m\n", - "| 67 | -0.882869 | -280.833495 | 44.696260 | 44.696039 | 0.003144 | 0.022373 |\u001b[0m\n", - "| 68 | -0.882869 | 280.833495 | 44.696260 | 44.696039 | 0.003144 | 0.022373 |\u001b[0m\n", - "| 69 | -0.884024 | -245.027542 | 38.997598 | 38.997344 | 0.003608 | 0.025643 |\u001b[0m\n", - "| 70 | -0.884024 | 245.027542 | 38.997598 | 38.997344 | 0.003608 | 0.025643 |\u001b[0m\n", - "| 71 | -0.886589 | -245.661879 | 39.098557 | 39.098302 | 0.003609 | 0.025577 |\u001b[0m\n", - "| 72 | -0.886589 | 245.661879 | 39.098557 | 39.098302 | 0.003609 | 0.025577 |\u001b[0m\n", - "| 73 | -0.891210 | -288.915188 | 45.982499 | 45.982280 | 0.003085 | 0.021748 |\u001b[0m\n", - "| 74 | -0.891210 | 288.915188 | 45.982499 | 45.982280 | 0.003085 | 0.021748 |\u001b[0m\n", - "| 75 | -0.908699 | 251.206723 | 39.981053 | 39.980792 | 0.003617 | 0.025012 |\u001b[0m\n", - "| 76 | -0.908699 | -251.206723 | 39.981053 | 39.980792 | 0.003617 | 0.025012 |\u001b[0m\n", - "| 77 | -0.910251 | -251.606127 | 40.044621 | 40.044359 | 0.003618 | 0.024972 |\u001b[0m\n", - "| 78 | -0.910251 | 251.606127 | 40.044621 | 40.044359 | 0.003618 | 0.024972 |\u001b[0m\n", - "| 79 | -0.914184 | -241.156682 | 38.381554 | 38.381278 | 0.003791 | 0.026054 |\u001b[0m\n", - "| 80 | -0.914184 | 241.156682 | 38.381554 | 38.381278 | 0.003791 | 0.026054 |\u001b[0m\n", - "| 81 | -0.915395 | 290.517030 | 46.237451 | 46.237221 | 0.003151 | 0.021628 |\u001b[0m\n", - "| 82 | -0.915395 | -290.517030 | 46.237451 | 46.237221 | 0.003151 | 0.021628 |\u001b[0m\n", - "| 83 | -0.933974 | -278.955360 | 44.397373 | 44.397124 | 0.003348 | 0.022524 |\u001b[0m\n", - "| 84 | -0.933974 | 278.955360 | 44.397373 | 44.397124 | 0.003348 | 0.022524 |\u001b[0m\n", - "| 85 | -0.943144 | -260.320871 | 41.431625 | 41.431353 | 0.003623 | 0.024136 |\u001b[0m\n", - "| 86 | -0.943144 | 260.320871 | 41.431625 | 41.431353 | 0.003623 | 0.024136 |\u001b[0m\n", - "| 87 | -0.944542 | -260.700481 | 41.492043 | 41.491770 | 0.003623 | 0.024101 |\u001b[0m\n", - "| 88 | -0.944542 | 260.700481 | 41.492043 | 41.491770 | 0.003623 | 0.024101 |\u001b[0m\n", - "| 89 | -0.953003 | -294.814043 | 46.921357 | 46.921112 | 0.003233 | 0.021312 |\u001b[0m\n", - "| 90 | -0.953003 | 294.814043 | 46.921357 | 46.921112 | 0.003233 | 0.021312 |\u001b[0m\n", - "| 91 | -0.960626 | -295.652742 | 47.054844 | 47.054595 | 0.003249 | 0.021252 |\u001b[0m\n", - "| 92 | -0.960626 | 295.652742 | 47.054844 | 47.054595 | 0.003249 | 0.021252 |\u001b[0m\n", - "| 93 | -0.960976 | -265.315963 | 42.226624 | 42.226347 | 0.003622 | 0.023682 |\u001b[0m\n", - "| 94 | -0.960976 | 265.315963 | 42.226624 | 42.226347 | 0.003622 | 0.023682 |\u001b[0m\n", - "| 95 | -0.961739 | 300.017779 | 47.749558 | 47.749313 | 0.003206 | 0.020943 |\u001b[0m\n", - "| 96 | -0.961739 | -300.017779 | 47.749558 | 47.749313 | 0.003206 | 0.020943 |\u001b[0m\n", - "| 97 | -0.961940 | -265.596058 | 42.271203 | 42.270925 | 0.003622 | 0.023657 |\u001b[0m\n", - "| 98 | -0.961940 | 265.596058 | 42.271203 | 42.270925 | 0.003622 | 0.023657 |\u001b[0m\n", - "| 99 | -0.965385 | -266.582863 | 42.428259 | 42.427980 | 0.003621 | 0.023569 |\u001b[0m\n", - "\u001b[36mFINISHED - Elapsed time = 16.3462206 seconds\u001b[0m\n", - "\u001b[36mFINISHED - CPU process time = 68.0131146 seconds\u001b[0m\n" - ] - } - ], - "source": [ - "data = sharpy.sharpy_main.main(['', ws.case_route + '/' + ws.case_name + '.sharpy'])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Post-processing" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Nonlinear Equilibrium\n", - "\n", - "The files can be opened with Paraview to see the deformation and aerodynamic loading on the flying wing in trim conditions." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Asymptotic Stability" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "eigenvalues_trim = np.loadtxt('./output/horten_u_inf2800_M4N11Msf5/stability/eigenvalues.dat')\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Flight Dynamics modes\n", - "\n", - "The flight dynamics modes can be found close to the origin of the Argand diagram. In particular, the phugoid is the mode that is closest to the imaginary axis. An exercise is left to the user to compare this phugoid predicition with the nonlinear response!" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "fig = plt.figure()\n", - "plt.scatter(eigenvalues_trim[:, 0], eigenvalues_trim[:, 1],\n", - " marker='x',\n", - " color='k')\n", - "plt.xlim(-0.5, 0.5)\n", - "plt.ylim(-0.5, 0.5)\n", - "plt.grid()\n", - "plt.xlabel('Real Part, $Re(\\lambda)$ [rad/s]')\n", - "plt.ylabel('Imaginary Part, $Im(\\lambda)$ [rad/s]');" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Structural Modes\n", - "\n", - "Looking further out on the plot, the structural modes appear. There is a curve given the Newmark-$\\beta$ integration scheme and on top of it several modes are damped by the presence of the aerodynamics.\n", - "\n", - "Try changing `newmark_damp` in the `LinearAssembler` settings to see how this plot changes!" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "fig = plt.figure()\n", - "plt.scatter(eigenvalues_trim[:, 0], eigenvalues_trim[:, 1],\n", - " marker='x',\n", - " color='k')\n", - "plt.xlim(-5, 0.5)\n", - "plt.ylim(-200, 200)\n", - "plt.grid()\n", - "plt.xlabel('Real Part, $Re(\\lambda)$ [rad/s]')\n", - "plt.ylabel('Imaginary Part, $Im(\\lambda)$ [rad/s]');" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.5" - } - }, - "nbformat": 4, - "nbformat_minor": 4 + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Asymptotic Stability of a Flying Wing in Cruise Trimmed Conditions\n", + "\n", + "A Horten flying wing is analysed. The nonlinear trim condition is found and the system is linearised. The eigenvalues of the linearised system are then used to evaluate the stability at the cruise trimmed flight conditions." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# required packages\n", + "import sharpy.utils.algebra as algebra\n", + "import sharpy.sharpy_main\n", + "from sharpy.cases.hangar.richards_wing import Baseline\n", + "import numpy as np\n", + "import configobj\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Flight Conditions\n", + "\n", + "Initial flight conditions. The values for angle of attack ``alpha``, control surface deflection ``cs_deflection`` and ``thrust`` are only initial values. The values required for trim will be calculated by the ``StaticTrim`` routine" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "u_inf = 28\n", + "alpha_deg = 4.5135\n", + "cs_deflection = 0.1814\n", + "thrust = 5.5129" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Discretisation" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "M = 4 # chordwise panels\n", + "N = 11 # spanwise panels\n", + "Msf = 5 # wake length in chord numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create Horten Wing" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "Section Mass: 11.88 \n", + "Linear Mass: 11.88\n", + "Section Ixx: 1.8777\n", + "Section Iyy: 1.0137\n", + "Section Izz: 2.5496\n", + "Linear Ixx: 1.88\n", + "1\n", + "Section Mass: 10.99 \n", + "Linear Mass: 10.99\n", + "Section Ixx: 1.4694\n", + "Section Iyy: 0.9345\n", + "Section Izz: 2.1501\n", + "Linear Ixx: 1.74\n", + "2\n", + "Section Mass: 10.10 \n", + "Linear Mass: 10.10\n", + "Section Ixx: 1.1257\n", + "Section Iyy: 0.8561\n", + "Section Izz: 1.7993\n", + "Linear Ixx: 1.60\n", + "3\n", + "Section Mass: 9.21 \n", + "Linear Mass: 9.21\n", + "Section Ixx: 0.8410\n", + "Section Iyy: 0.7783\n", + "Section Izz: 1.4933\n", + "Linear Ixx: 1.46\n", + "4\n", + "Section Mass: 8.32 \n", + "Linear Mass: 8.32\n", + "Section Ixx: 0.6096\n", + "Section Iyy: 0.7011\n", + "Section Izz: 1.2280\n", + "Linear Ixx: 1.31\n", + "5\n", + "Section Mass: 7.43 \n", + "Linear Mass: 7.43\n", + "Section Ixx: 0.4260\n", + "Section Iyy: 0.6246\n", + "Section Izz: 0.9996\n", + "Linear Ixx: 1.17\n", + "6\n", + "Section Mass: 6.54 \n", + "Linear Mass: 6.54\n", + "Section Ixx: 0.2845\n", + "Section Iyy: 0.5485\n", + "Section Izz: 0.8040\n", + "Linear Ixx: 1.03\n", + "7\n", + "Section Mass: 5.64 \n", + "Linear Mass: 5.64\n", + "Section Ixx: 0.1796\n", + "Section Iyy: 0.4728\n", + "Section Izz: 0.6374\n", + "Linear Ixx: 0.89\n", + "8\n", + "Section Mass: 4.75 \n", + "Linear Mass: 4.75\n", + "Section Ixx: 0.1055\n", + "Section Iyy: 0.3975\n", + "Section Izz: 0.4959\n", + "Linear Ixx: 0.75\n", + "9\n", + "Section Mass: 3.86 \n", + "Linear Mass: 3.86\n", + "Section Ixx: 0.0567\n", + "Section Iyy: 0.3226\n", + "Section Izz: 0.3753\n", + "Linear Ixx: 0.61\n", + "10\n", + "Section Mass: 2.97 \n", + "Linear Mass: 2.97\n", + "Section Ixx: 0.0275\n", + "Section Iyy: 0.2479\n", + "Section Izz: 0.2719\n", + "Linear Ixx: 0.47\n" + ] + } + ], + "source": [ + "ws = Baseline(M=M,\n", + " N=N,\n", + " Mstarfactor=Msf,\n", + " u_inf=u_inf,\n", + " rho=1.02,\n", + " alpha_deg=alpha_deg,\n", + " roll_deg=0,\n", + " cs_deflection_deg=cs_deflection,\n", + " thrust=thrust,\n", + " physical_time=20,\n", + " case_name='horten',\n", + " case_name_format=4,\n", + " case_remarks='M%gN%gMsf%g' % (M, N, Msf))\n", + "\n", + "ws.set_properties()\n", + "ws.initialise()\n", + "ws.clean_test_files()\n", + "\n", + "ws.update_mass_stiffness(sigma=1., sigma_mass=2.5)\n", + "ws.update_fem_prop()\n", + "ws.generate_fem_file()\n", + "ws.update_aero_properties()\n", + "ws.generate_aero_file()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Simulation Information\n", + "\n", + "The ``flow`` setting tells SHARPy which solvers to run and in which order. You may be stranged by the presence of the ``DynamicCoupled`` solver but it is necessary to give an initial speed to the structure. This will allow proper linearisation of the structural and rigid body equations." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "flow = ['BeamLoader',\n", + " 'AerogridLoader',\n", + " 'StaticTrim',\n", + " 'BeamPlot',\n", + " 'AerogridPlot',\n", + " 'AeroForcesCalculator',\n", + " 'DynamicCoupled',\n", + " 'Modal',\n", + " 'LinearAssembler',\n", + " 'AsymptoticStability',\n", + " ]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### SHARPy Settings" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "settings = dict()\n", + "settings['SHARPy'] = {'case': ws.case_name,\n", + " 'route': ws.case_route,\n", + " 'flow': flow,\n", + " 'write_screen': 'on',\n", + " 'write_log': 'on',\n", + " 'log_folder': './output/',\n", + " 'log_file': ws.case_name + '.log'}\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Loaders" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "settings['BeamLoader'] = {'unsteady': 'off',\n", + " 'orientation': algebra.euler2quat(np.array([ws.roll,\n", + " ws.alpha,\n", + " ws.beta]))}\n", + "settings['AerogridLoader'] = {'unsteady': 'off',\n", + " 'aligned_grid': 'on',\n", + " 'mstar': int(ws.M * ws.Mstarfactor),\n", + " 'freestream_dir': ['1', '0', '0'],\n", + " 'control_surface_deflection': [''],\n", + " 'wake_shape_generator': 'StraightWake',\n", + " 'wake_shape_generator_input': {'u_inf': ws.u_inf,\n", + " 'u_inf_direction': ['1', '0', '0'],\n", + " 'dt': ws.dt}}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### StaticCoupled Solver" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "settings['StaticCoupled'] = {'print_info': 'on',\n", + " 'structural_solver': 'NonLinearStatic',\n", + " 'structural_solver_settings': {'print_info': 'off',\n", + " 'max_iterations': 200,\n", + " 'num_load_steps': 1,\n", + " 'delta_curved': 1e-5,\n", + " 'min_delta': ws.tolerance,\n", + " 'gravity_on': 'on',\n", + " 'gravity': 9.81},\n", + " 'aero_solver': 'StaticUvlm',\n", + " 'aero_solver_settings': {'print_info': 'on',\n", + " 'horseshoe': ws.horseshoe,\n", + " 'num_cores': 4,\n", + " 'n_rollup': int(0),\n", + " 'rollup_dt': ws.dt,\n", + " 'rollup_aic_refresh': 1,\n", + " 'rollup_tolerance': 1e-4,\n", + " 'velocity_field_generator': 'SteadyVelocityField',\n", + " 'velocity_field_input': {'u_inf': ws.u_inf,\n", + " 'u_inf_direction': [1., 0, 0]},\n", + " 'rho': ws.rho},\n", + " 'max_iter': 200,\n", + " 'n_load_steps': 1,\n", + " 'tolerance': ws.tolerance,\n", + " 'relaxation_factor': 0.2}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Trim solver" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "settings['StaticTrim'] = {'solver': 'StaticCoupled',\n", + " 'solver_settings': settings['StaticCoupled'],\n", + " 'thrust_nodes': ws.thrust_nodes,\n", + " 'initial_alpha': ws.alpha,\n", + " 'initial_deflection': ws.cs_deflection,\n", + " 'initial_thrust': ws.thrust,\n", + " 'max_iter': 200,\n", + " 'fz_tolerance': 1e-2,\n", + " 'fx_tolerance': 1e-2,\n", + " 'm_tolerance': 1e-2}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Nonlinear Equilibrium Post-process" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "settings['AerogridPlot'] = {\n", + " 'include_rbm': 'off',\n", + " 'include_applied_forces': 'on',\n", + " 'minus_m_star': 0,\n", + " 'u_inf': ws.u_inf\n", + " }\n", + "settings['AeroForcesCalculator'] = {\n", + " 'write_text_file': 'off',\n", + " 'text_file_name': ws.case_name + '_aeroforces.csv',\n", + " 'screen_output': 'on',\n", + " 'unsteady': 'off',\n", + " 'coefficients': True,\n", + " 'q_ref': 0.5 * ws.rho * ws.u_inf ** 2,\n", + " 'S_ref': 12.809,\n", + " }\n", + "\n", + "settings['BeamPlot'] = {\n", + " 'include_rbm': 'on',\n", + " 'include_applied_forces': 'on',\n", + " 'include_FoR': 'on'}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### DynamicCoupled Solver\n", + "\n", + "As mentioned before, a single time step of ``DynamicCoupled`` is required to give the structure the velocity required for the linearisation of the rigid body equations to be correct. Hence `n_time_steps = 1`" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "struct_solver_settings = {'print_info': 'off',\n", + " 'initial_velocity_direction': [-1., 0., 0.],\n", + " 'max_iterations': 950,\n", + " 'delta_curved': 1e-6,\n", + " 'min_delta': ws.tolerance,\n", + " 'newmark_damp': 5e-3,\n", + " 'gravity_on': True,\n", + " 'gravity': 9.81,\n", + " 'num_steps': ws.n_tstep,\n", + " 'dt': ws.dt,\n", + " 'initial_velocity': ws.u_inf * 1}\n", + "\n", + "step_uvlm_settings = {'print_info': 'on',\n", + " 'num_cores': 4,\n", + " 'convection_scheme': ws.wake_type,\n", + " 'velocity_field_generator': 'SteadyVelocityField',\n", + " 'velocity_field_input': {'u_inf': ws.u_inf * 0,\n", + " 'u_inf_direction': [1., 0., 0.]},\n", + " 'rho': ws.rho,\n", + " 'n_time_steps': ws.n_tstep,\n", + " 'dt': ws.dt,\n", + " 'gamma_dot_filtering': 3}\n", + "\n", + "settings['DynamicCoupled'] = {'print_info': 'on',\n", + " 'structural_solver': 'NonLinearDynamicCoupledStep',\n", + " 'structural_solver_settings': struct_solver_settings,\n", + " 'aero_solver': 'StepUvlm',\n", + " 'aero_solver_settings': step_uvlm_settings,\n", + " 'fsi_substeps': 200,\n", + " 'fsi_tolerance': ws.fsi_tolerance,\n", + " 'relaxation_factor': ws.relaxation_factor,\n", + " 'minimum_steps': 1,\n", + " 'relaxation_steps': 150,\n", + " 'final_relaxation_factor': 0.5,\n", + " 'n_time_steps': 1,\n", + " 'dt': ws.dt,\n", + " 'include_unsteady_force_contribution': 'off',\n", + " }" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Modal Solver Settings" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "settings['Modal'] = {'print_info': True,\n", + " 'use_undamped_modes': True,\n", + " 'NumLambda': 30,\n", + " 'rigid_body_modes': True,\n", + " 'write_modes_vtk': 'on',\n", + " 'print_matrices': 'on',\n", + " 'continuous_eigenvalues': 'off',\n", + " 'dt': ws.dt,\n", + " 'plot_eigenvalues': False,\n", + " 'rigid_modes_cg': False}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Linear Assembler Settings\n", + "\n", + "Note that for the assembly of the linear system, we replace the parametrisation of the orientation with Euler angles instead of quaternions." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "settings['LinearAssembler'] = {'linear_system': 'LinearAeroelastic',\n", + " 'linear_system_settings': {\n", + " 'beam_settings': {'modal_projection': 'off',\n", + " 'inout_coords': 'modes',\n", + " 'discrete_time': True,\n", + " 'newmark_damp': 0.5e-2,\n", + " 'discr_method': 'newmark',\n", + " 'dt': ws.dt,\n", + " 'proj_modes': 'undamped',\n", + " 'num_modes': 9,\n", + " 'print_info': 'on',\n", + " 'gravity': 'on',\n", + " 'remove_dofs': []},\n", + " 'aero_settings': {'dt': ws.dt,\n", + " 'integr_order': 2,\n", + " 'density': ws.rho,\n", + " 'remove_predictor': 'off',\n", + " 'use_sparse': 'off',\n", + " 'remove_inputs': ['u_gust']},\n", + " 'track_body': 'on',\n", + " 'use_euler': 'on',\n", + " }}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Asymptotic Stability Post-processor" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "settings['AsymptoticStability'] = {\n", + " 'print_info': 'on',\n", + " 'frequency_cutoff': 0,\n", + " 'export_eigenvalues': 'on',\n", + " 'num_evals': 100,\n", + " }" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Write solver file" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "config = configobj.ConfigObj()\n", + "np.set_printoptions(precision=16)\n", + "file_name = ws.case_route + '/' + ws.case_name + '.sharpy'\n", + "config.filename = file_name\n", + "for k, v in settings.items():\n", + " config[k] = v\n", + "config.write()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Run Simulation" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--------------------------------------------------------------------------------\u001b[0m\n", + " ###### ## ## ### ######## ######## ## ##\u001b[0m\n", + " ## ## ## ## ## ## ## ## ## ## ## ##\u001b[0m\n", + " ## ## ## ## ## ## ## ## ## ####\u001b[0m\n", + " ###### ######### ## ## ######## ######## ##\u001b[0m\n", + " ## ## ## ######### ## ## ## ##\u001b[0m\n", + " ## ## ## ## ## ## ## ## ## ##\u001b[0m\n", + " ###### ## ## ## ## ## ## ## ##\u001b[0m\n", + "--------------------------------------------------------------------------------\u001b[0m\n", + "Aeroelastics Lab, Aeronautics Department.\u001b[0m\n", + " Copyright (c), Imperial College London.\u001b[0m\n", + " All rights reserved.\u001b[0m\n", + " License available at https://github.com/imperialcollegelondon/sharpy\u001b[0m\n", + "\u001b[36mRunning SHARPy from /home/ng213/2TB/pazy_code/pazy-sharpy/lib/sharpy/docs/source/content/example_notebooks\u001b[0m\n", + "\u001b[36mSHARPy being run is in /home/ng213/2TB/pazy_code/pazy-sharpy/lib/sharpy\u001b[0m\n", + "\u001b[36mThe branch being run is dev_setting_error\u001b[0m\n", + "\u001b[36mThe version and commit hash are: v1.2.1-344-g0239644-0239644\u001b[0m\n", + "SHARPy output folder set\u001b[0m\n", + "\u001b[34m\t./output//horten_u_inf2800_M4N11Msf5/\u001b[0m\n", + "\u001b[36mGenerating an instance of BeamLoader\u001b[0m\n", + "Variable for_pos has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: [0.0, 0, 0]\u001b[0m\n", + "\u001b[36mGenerating an instance of AerogridLoader\u001b[0m\n", + "Variable control_surface_deflection_generator_settings has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: {}\u001b[0m\n", + "Variable dx1 has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: -1.0\u001b[0m\n", + "Variable ndx1 has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1\u001b[0m\n", + "Variable r has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1.0\u001b[0m\n", + "Variable dxmax has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: -1.0\u001b[0m\n", + "\u001b[34mThe aerodynamic grid contains 4 surfaces\u001b[0m\n", + "\u001b[34m Surface 0, M=4, N=2\u001b[0m\n", + " Wake 0, M=20, N=2\u001b[0m\n", + "\u001b[34m Surface 1, M=4, N=22\u001b[0m\n", + " Wake 1, M=20, N=22\u001b[0m\n", + "\u001b[34m Surface 2, M=4, N=2\u001b[0m\n", + " Wake 2, M=20, N=2\u001b[0m\n", + "\u001b[34m Surface 3, M=4, N=22\u001b[0m\n", + " Wake 3, M=20, N=22\u001b[0m\n", + " In total: 192 bound panels\u001b[0m\n", + " In total: 960 wake panels\u001b[0m\n", + " Total number of panels = 1152\u001b[0m\n", + "\u001b[36mGenerating an instance of StaticTrim\u001b[0m\n", + "Variable print_info has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: True\u001b[0m\n", + "Variable tail_cs_index has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0\u001b[0m\n", + "Variable initial_angle_eps has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0.05\u001b[0m\n", + "Variable initial_thrust_eps has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 2.0\u001b[0m\n", + "Variable relaxation_factor has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0.2\u001b[0m\n", + "Variable save_info has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "\u001b[36mGenerating an instance of StaticCoupled\u001b[0m\n", + "Variable correct_forces_method has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: \u001b[0m\n", + "Variable runtime_generators has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: {}\u001b[0m\n", + "\u001b[36mGenerating an instance of NonLinearStatic\u001b[0m\n", + "Variable newmark_damp has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0.0001\u001b[0m\n", + "Variable gravity_dir has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: [0.0, 0.0, 1.0]\u001b[0m\n", + "Variable relaxation_factor has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0.3\u001b[0m\n", + "Variable dt has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0.01\u001b[0m\n", + "Variable num_steps has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 500\u001b[0m\n", + "Variable initial_position has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: [0. 0. 0.]\u001b[0m\n", + "\u001b[36mGenerating an instance of StaticUvlm\u001b[0m\n", + "Variable iterative_solver has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable iterative_tol has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0.0001\u001b[0m\n", + "Variable iterative_precond has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable cfl1 has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: True\u001b[0m\n", + "Variable vortex_radius has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1e-06\u001b[0m\n", + "Variable vortex_radius_wake_ind has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1e-06\u001b[0m\n", + "Variable rbm_vel_g has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]\u001b[0m\n", + "Variable centre_rot_g has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: [0.0, 0.0, 0.0]\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\n", + "|=====|=====|============|==========|==========|==========|==========|==========|==========|\u001b[0m\n", + "|iter |step | log10(res) | Fx | Fy | Fz | Mx | My | Mz |\u001b[0m\n", + "|=====|=====|============|==========|==========|==========|==========|==========|==========|\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\n", + "|==========|==========|==========|==========|==========|==========|==========|==========|==========|==========|\u001b[0m\n", + "| iter |alpha[deg]|elev[deg] | thrust | Fx | Fy | Fz | Mx | My | Mz |\u001b[0m\n", + "|==========|==========|==========|==========|==========|==========|==========|==========|==========|==========|\u001b[0m\n", + "| 0 | 0 | 0.00000 | -0.1051 | -0.0000 | 0.0598 | -0.0000 | 1.0837 | -0.0000 |\u001b[0m\n", + "| 1 | 0 | -7.62384 | -0.1284 | -0.0000 | 0.1276 | -0.0000 | 0.0045 | -0.0000 |\u001b[0m\n", + "| 2 | 0 | -8.33392 | -0.1190 | -0.0000 | 0.0397 | -0.0000 | -0.0774 | -0.0000 |\u001b[0m\n", + "| 3 | 0 | -9.30379 | -0.1133 | 0.0000 | 0.0011 | -0.0000 | -0.0070 | 0.0000 |\u001b[0m\n", + "| 4 | 0 | -10.71602 | -0.1136 | -0.0000 | 0.0032 | 0.0000 | -0.0100 | -0.0000 |\u001b[0m\n", + "| 5 | 0 | -10.88827 | -0.1138 | -0.0000 | 0.0043 | 0.0000 | -0.0119 | -0.0000 |\u001b[0m\n", + "| 6 | 0 | -11.66331 | -0.1138 | -0.0000 | 0.0042 | 0.0000 | -0.0116 | -0.0000 |\u001b[0m\n", + "| 7 | 0 | -12.88496 | -0.1138 | -0.0000 | 0.0041 | 0.0000 | -0.0116 | 0.0000 |\u001b[0m\n", + "| 0 | 4.5135 | 0.1814 | 5.5129 | -0.1138 | -0.0000 | 0.0041 | 0.0000 | -0.0116 | 0.0000 |\u001b[0m\n", + "| 0 | 0 | 0.00000 |-116.9870 | -0.0000 | 994.8063 | -0.0000 |-882.4104 | -0.0000 |\u001b[0m\n", + "| 1 | 0 | -5.79178 | -72.3841 | 0.0000 | 944.6140 | 0.0000 |-802.5912 | 0.0000 |\u001b[0m\n", + "| 2 | 0 | -6.63730 | -62.4378 | 0.0000 | 937.6662 | 0.0000 |-792.1259 | -0.0000 |\u001b[0m\n", + "| 3 | 0 | -7.22937 | -62.8923 | -0.0000 | 939.7866 | -0.0000 |-795.7093 | -0.0000 |\u001b[0m\n", + "| 4 | 0 | -8.65323 | -62.8757 | -0.0000 | 939.7100 | -0.0000 |-795.5764 | -0.0000 |\u001b[0m\n", + "| 5 | 0 | -8.81438 | -62.8640 | -0.0000 | 939.6554 | -0.0000 |-795.4837 | -0.0000 |\u001b[0m\n", + "| 6 | 0 | -9.59386 | -62.8660 | 0.0000 | 939.6644 | -0.0000 |-795.4991 | 0.0000 |\u001b[0m\n", + "| 7 | 0 | -10.80422 | -62.8661 | 0.0000 | 939.6650 | -0.0000 |-795.5000 | 0.0000 |\u001b[0m\n", + "| 8 | 0 | -11.01365 | -62.8660 | -0.0000 | 939.6647 | -0.0000 |-795.4994 | -0.0000 |\u001b[0m\n", + "| 9 | 0 | -12.15197 | -62.8660 | 0.0000 | 939.6647 | -0.0000 |-795.4995 | 0.0000 |\u001b[0m\n", + "| 0 | 7.3783 | -2.6834 | 5.5129 | -62.8660 | 0.0000 | 939.6647 | -0.0000 |-795.4995 | 0.0000 |\u001b[0m\n", + "| 0 | 0 | 0.00000 | -32.9132 | -0.0000 | 371.4715 | -0.0000 |-902.7953 | -0.0000 |\u001b[0m\n", + "| 1 | 0 | -5.48409 | -8.7241 | -0.0000 | 298.8484 | 0.0000 |-777.2938 | 0.0000 |\u001b[0m\n", + "| 2 | 0 | -6.39387 | -4.0957 | -0.0000 | 289.8092 | -0.0000 |-761.4855 | -0.0000 |\u001b[0m\n", + "| 3 | 0 | -6.85613 | -4.6263 | -0.0000 | 293.2407 | -0.0000 |-767.3376 | 0.0000 |\u001b[0m\n", + "| 4 | 0 | -8.25962 | -4.6052 | -0.0000 | 293.1048 | -0.0000 |-767.1066 | 0.0000 |\u001b[0m\n", + "| 5 | 0 | -8.44065 | -4.5914 | -0.0000 | 293.0156 | 0.0000 |-766.9545 | 0.0000 |\u001b[0m\n", + "| 6 | 0 | -9.20968 | -4.5937 | -0.0000 | 293.0308 | -0.0000 |-766.9804 | -0.0000 |\u001b[0m\n", + "| 7 | 0 | -10.44736 | -4.5939 | -0.0000 | 293.0316 | -0.0000 |-766.9819 | -0.0000 |\u001b[0m\n", + "| 8 | 0 | -10.63000 | -4.5938 | -0.0000 | 293.0311 | 0.0000 |-766.9809 | -0.0000 |\u001b[0m\n", + "| 9 | 0 | -11.74670 | -4.5938 | 0.0000 | 293.0311 | 0.0000 |-766.9810 | 0.0000 |\u001b[0m\n", + "| 10 | 0 | -12.29943 | -4.5938 | -0.0000 | 293.0311 | 0.0000 |-766.9810 | 0.0000 |\u001b[0m\n", + "| 0 | 4.5135 | 3.0462 | 5.5129 | -4.5938 | -0.0000 | 293.0311 | 0.0000 |-766.9810 | 0.0000 |\u001b[0m\n", + "| 0 | 0 | 0.00000 | -4.1051 | -0.0000 | 0.0598 | -0.0000 | 1.0834 | -0.0000 |\u001b[0m\n", + "| 1 | 0 | -7.62384 | -4.1284 | -0.0000 | 0.1276 | -0.0000 | 0.0042 | -0.0000 |\u001b[0m\n", + "| 2 | 0 | -8.33392 | -4.1190 | -0.0000 | 0.0397 | 0.0000 | -0.0778 | -0.0000 |\u001b[0m\n", + "| 3 | 0 | -9.30379 | -4.1133 | -0.0000 | 0.0011 | 0.0000 | -0.0074 | -0.0000 |\u001b[0m\n", + "| 4 | 0 | -10.71602 | -4.1136 | 0.0000 | 0.0032 | -0.0000 | -0.0104 | 0.0000 |\u001b[0m\n", + "| 5 | 0 | -10.88827 | -4.1138 | 0.0000 | 0.0043 | 0.0000 | -0.0123 | 0.0000 |\u001b[0m\n", + "| 6 | 0 | -11.66331 | -4.1138 | -0.0000 | 0.0042 | 0.0000 | -0.0119 | -0.0000 |\u001b[0m\n", + "| 7 | 0 | -12.88496 | -4.1138 | -0.0000 | 0.0041 | 0.0000 | -0.0119 | -0.0000 |\u001b[0m\n", + "| 0 | 4.5135 | 0.1814 | 7.5129 | -4.1138 | -0.0000 | 0.0041 | 0.0000 | -0.0119 | -0.0000 |\u001b[0m\n", + "| 0 | 0 | 0.00000 | 0.0095 | -0.0000 | 0.0498 | 0.0000 | 1.1013 | -0.0000 |\u001b[0m\n", + "| 1 | 0 | -7.62357 | -0.0140 | 0.0000 | 0.1189 | 0.0000 | 0.0198 | 0.0000 |\u001b[0m\n", + "| 2 | 0 | -8.33375 | -0.0046 | -0.0000 | 0.0312 | -0.0000 | -0.0624 | 0.0000 |\u001b[0m\n", + "| 3 | 0 | -9.30318 | 0.0010 | -0.0000 | -0.0075 | 0.0000 | 0.0081 | -0.0000 |\u001b[0m\n", + "| 4 | 0 | -10.71542 | 0.0007 | -0.0000 | -0.0054 | 0.0000 | 0.0051 | 0.0000 |\u001b[0m\n", + "| 5 | 0 | -10.88766 | 0.0006 | -0.0000 | -0.0043 | 0.0000 | 0.0032 | -0.0000 |\u001b[0m\n", + "| 6 | 0 | -11.66271 | 0.0006 | 0.0000 | -0.0044 | -0.0000 | 0.0035 | 0.0000 |\u001b[0m\n", + "| 7 | 0 | -12.88441 | 0.0006 | -0.0000 | -0.0045 | 0.0000 | 0.0035 | -0.0000 |\u001b[0m\n", + "| 1 | 4.5135 | 0.1814 | 5.4560 | 0.0006 | -0.0000 | -0.0045 | 0.0000 | 0.0035 | -0.0000 |\u001b[0m\n", + "\u001b[36mGenerating an instance of BeamPlot\u001b[0m\n", + "Variable include_applied_moments has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: True\u001b[0m\n", + "Variable name_prefix has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: \u001b[0m\n", + "Variable output_rbm has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: True\u001b[0m\n", + "\u001b[34m...Finished\u001b[0m\n", + "\u001b[36mGenerating an instance of AerogridPlot\u001b[0m\n", + "Variable include_forward_motion has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable include_unsteady_applied_forces has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable name_prefix has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: \u001b[0m\n", + "Variable dt has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0.0\u001b[0m\n", + "Variable include_velocities has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable include_incidence_angle has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable num_cores has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1\u001b[0m\n", + "Variable vortex_radius has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1e-06\u001b[0m\n", + "\u001b[34m...Finished\u001b[0m\n", + "\u001b[36mGenerating an instance of AeroForcesCalculator\u001b[0m\n", + "--------------------------------------------------------------------------------\u001b[0m\n", + "\u001b[34mtstep | fx_g | fy_g | fz_g | Cfx_g | Cfy_g | Cfz_g \u001b[0m\n", + "\u001b[34m 0 | 1.088e+01 | -4.476e-13 | 1.835e+03 | 2.124e-03 | -8.740e-17 | 3.583e-01\u001b[0m\n", + "\u001b[34m...Finished\u001b[0m\n", + "\u001b[36mGenerating an instance of DynamicCoupled\u001b[0m\n", + "Variable structural_substeps has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0\u001b[0m\n", + "Variable dynamic_relaxation has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable postprocessors has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: []\u001b[0m\n", + "Variable postprocessors_settings has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: {}\u001b[0m\n", + "Variable controller_id has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: {}\u001b[0m\n", + "Variable controller_settings has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: {}\u001b[0m\n", + "Variable cleanup_previous_solution has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable steps_without_unsteady_force has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0\u001b[0m\n", + "Variable pseudosteps_ramp_unsteady_force has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0\u001b[0m\n", + "Variable correct_forces_method has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: \u001b[0m\n", + "Variable network_settings has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: {}\u001b[0m\n", + "Variable runtime_generators has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: {}\u001b[0m\n", + "\u001b[36mGenerating an instance of NonLinearDynamicCoupledStep\u001b[0m\n", + "Variable num_load_steps has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1\u001b[0m\n", + "Variable gravity_dir has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: [0.0, 0.0, 1.0]\u001b[0m\n", + "Variable relaxation_factor has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0.3\u001b[0m\n", + "Variable balancing has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "\u001b[36mGenerating an instance of StepUvlm\u001b[0m\n", + "Variable iterative_solver has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable iterative_tol has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0.0001\u001b[0m\n", + "Variable iterative_precond has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable cfl1 has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: True\u001b[0m\n", + "Variable vortex_radius has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1e-06\u001b[0m\n", + "Variable vortex_radius_wake_ind has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1e-06\u001b[0m\n", + "Variable interp_coords has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0\u001b[0m\n", + "Variable filter_method has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0\u001b[0m\n", + "Variable interp_method has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0\u001b[0m\n", + "Variable yaw_slerp has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0.0\u001b[0m\n", + "Variable centre_rot has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: [0.0, 0.0, 0.0]\u001b[0m\n", + "Variable quasi_steady has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\n", + "|=======|========|======|==============|==============|==============|==============|==============|\u001b[0m\n", + "| ts | t | iter | struc ratio | iter time | residual vel | FoR_vel(x) | FoR_vel(z) |\u001b[0m\n", + "|=======|========|======|==============|==============|==============|==============|==============|\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/ng213/2TB/pazy_code/pazy-sharpy/lib/sharpy/sharpy/aero/utils/uvlmlib.py:264: RuntimeWarning: invalid value encountered in true_divide\n", + " flightconditions.uinf_direction = np.ctypeslib.as_ctypes(ts_info.u_ext[0][:, 0, 0]/flightconditions.uinf)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "| 1 | 0.0089 | 3 | 0.877319 | 0.593232 | -10.598250 |-2.791317e+01 |-2.203426e+00 |\u001b[0m\n", + "\u001b[34m...Finished\u001b[0m\n", + "\u001b[36mGenerating an instance of Modal\u001b[0m\n", + "Variable keep_linear_matrices has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: True\u001b[0m\n", + "Variable write_dat has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: True\u001b[0m\n", + "Variable delta_curved has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0.01\u001b[0m\n", + "Variable max_rotation_deg has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 15.0\u001b[0m\n", + "Variable max_displacement has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0.15\u001b[0m\n", + "Variable use_custom_timestep has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: -1\u001b[0m\n", + "Structural eigenvalues\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\n", + "|==============|==============|==============|==============|==============|==============|==============|\u001b[0m\n", + "| mode | eval_real | eval_imag | freq_n (Hz) | freq_d (Hz) | damping | period (s) |\u001b[0m\n", + "|==============|==============|==============|==============|==============|==============|==============|\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/ng213/2TB/pazy_code/pazy-sharpy/lib/sharpy/sharpy/solvers/modal.py:265: UserWarning: Projecting a system with damping on undamped modal shapes\n", + " warnings.warn('Projecting a system with damping on undamped modal shapes')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "| 0 | -0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 1 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 2 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 3 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 4 | -0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 5 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 6 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 7 | -0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 8 | -0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 9 | -0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 10 | 0.000000 | 28.293939 | 4.503120 | 4.503120 | -0.000000 | 0.222068 |\u001b[0m\n", + "| 11 | 0.000000 | 29.271318 | 4.658675 | 4.658675 | -0.000000 | 0.214653 |\u001b[0m\n", + "| 12 | 0.000000 | 54.780234 | 8.718545 | 8.718545 | -0.000000 | 0.114698 |\u001b[0m\n", + "| 13 | 0.000000 | 58.999779 | 9.390106 | 9.390106 | -0.000000 | 0.106495 |\u001b[0m\n", + "| 14 | 0.000000 | 70.520741 | 11.223724 | 11.223724 | -0.000000 | 0.089097 |\u001b[0m\n", + "| 15 | 0.000000 | 76.917111 | 12.241738 | 12.241738 | -0.000000 | 0.081688 |\u001b[0m\n", + "| 16 | 0.000000 | 87.324076 | 13.898058 | 13.898058 | -0.000000 | 0.071952 |\u001b[0m\n", + "| 17 | 0.000000 | 108.035577 | 17.194396 | 17.194396 | -0.000000 | 0.058158 |\u001b[0m\n", + "| 18 | 0.000000 | 119.692139 | 19.049596 | 19.049596 | -0.000000 | 0.052495 |\u001b[0m\n", + "| 19 | 0.000000 | 133.495187 | 21.246419 | 21.246419 | -0.000000 | 0.047067 |\u001b[0m\n", + "| 20 | 0.000000 | 134.444788 | 21.397553 | 21.397553 | -0.000000 | 0.046734 |\u001b[0m\n", + "| 21 | 0.000000 | 151.060442 | 24.042016 | 24.042016 | -0.000000 | 0.041594 |\u001b[0m\n", + "| 22 | 0.000000 | 159.369020 | 25.364367 | 25.364367 | -0.000000 | 0.039425 |\u001b[0m\n", + "| 23 | 0.000000 | 171.256102 | 27.256255 | 27.256255 | -0.000000 | 0.036689 |\u001b[0m\n", + "| 24 | 0.000000 | 173.895881 | 27.676389 | 27.676389 | -0.000000 | 0.036132 |\u001b[0m\n", + "| 25 | 0.000000 | 199.016557 | 31.674469 | 31.674469 | -0.000000 | 0.031571 |\u001b[0m\n", + "| 26 | 0.000000 | 205.412581 | 32.692428 | 32.692428 | -0.000000 | 0.030588 |\u001b[0m\n", + "| 27 | 0.000000 | 205.419531 | 32.693534 | 32.693534 | -0.000000 | 0.030587 |\u001b[0m\n", + "| 28 | 0.000000 | 223.563796 | 35.581283 | 35.581283 | -0.000000 | 0.028105 |\u001b[0m\n", + "| 29 | 0.000000 | 227.924750 | 36.275351 | 36.275351 | -0.000000 | 0.027567 |\u001b[0m\n", + "\u001b[36mGenerating an instance of LinearAssembler\u001b[0m\n", + "Variable linearisation_tstep has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: -1\u001b[0m\n", + "Variable modal_tstep has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: -1\u001b[0m\n", + "Variable inout_coordinates has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: \u001b[0m\n", + "Variable retain_inputs has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: []\u001b[0m\n", + "Variable retain_outputs has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: []\u001b[0m\n", + "\u001b[36mGenerating an instance of LinearAeroelastic\u001b[0m\n", + "Variable uvlm_filename has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: \u001b[0m\n", + "\u001b[36mGenerating an instance of LinearUVLM\u001b[0m\n", + "Variable ScalingDict has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: {}\u001b[0m\n", + "Variable gust_assembler has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: \u001b[0m\n", + "Variable rom_method has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: []\u001b[0m\n", + "Variable rom_method_settings has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: {}\u001b[0m\n", + "Variable vortex_radius has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1e-06\u001b[0m\n", + "Variable cfl1 has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: True\u001b[0m\n", + "Variable length has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1.0\u001b[0m\n", + "Variable speed has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1.0\u001b[0m\n", + "Variable density has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1.0\u001b[0m\n", + "Variable velocity_field_generator has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: SteadyVelocityField\u001b[0m\n", + "Variable velocity_field_input has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: {}\u001b[0m\n", + "Variable physical_model has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: True\u001b[0m\n", + "Variable track_body has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable track_body_number has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: -1\u001b[0m\n", + "Initialising Static linear UVLM solver class...\u001b[0m\n", + "\t\t\t...done in 0.39 sec\u001b[0m\n", + "State-space realisation of UVLM equations started...\u001b[0m\n", + "\u001b[34mComputing wake propagation matrix with CFL1=True\u001b[0m\n", + "\u001b[34m\tstate-space model produced in form:\u001b[0m\n", + "\u001b[34m\tx_{n+1} = A x_{n} + Bp u_{n+1}\u001b[0m\n", + "\t\t\t...done in 2.43 sec\u001b[0m\n", + "\u001b[36mGenerating an instance of LinearBeam\u001b[0m\n", + "Variable remove_sym_modes has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Warning, projecting system with damping onto undamped modes\u001b[0m\n", + "\u001b[0m\n", + "Linearising gravity terms...\u001b[0m\n", + "\u001b[34m\tM = 187.12 kg\u001b[0m\n", + "\u001b[34m\tX_CG A -> 1.19 0.00 0.01\u001b[0m\n", + "\u001b[36mNode 1 \t-> B 0.000 -0.089 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.089 0.206 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.089 0.206 -0.007\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 2.6141\u001b[0m\n", + "\u001b[36mNode 2 \t-> B -0.010 -0.019 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.019 0.403 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.019 0.403 -0.001\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 7.3672\u001b[0m\n", + "\u001b[36mNode 3 \t-> B -0.019 -0.087 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.234 0.800 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.234 0.800 -0.018\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 5.8780\u001b[0m\n", + "\u001b[36mNode 4 \t-> B -0.019 -0.084 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.390 1.238 0.001\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.389 1.238 -0.030\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 2.8288\u001b[0m\n", + "\u001b[36mNode 5 \t-> B -0.018 -0.081 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.546 1.676 0.001\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.544 1.676 -0.041\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 5.4372\u001b[0m\n", + "\u001b[36mNode 6 \t-> B -0.017 -0.078 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.702 2.113 0.002\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.700 2.113 -0.053\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 2.6084\u001b[0m\n", + "\u001b[36mNode 7 \t-> B -0.016 -0.074 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.857 2.551 0.003\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.855 2.551 -0.064\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 4.9963\u001b[0m\n", + "\u001b[36mNode 8 \t-> B -0.016 -0.071 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 1.013 2.988 0.004\u001b[0m\n", + "\u001b[36m\t\t\t-> G 1.010 2.988 -0.076\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 2.3879\u001b[0m\n", + "\u001b[36mNode 9 \t-> B -0.015 -0.068 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 1.169 3.426 0.005\u001b[0m\n", + "\u001b[36m\t\t\t-> G 1.166 3.426 -0.087\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 4.5555\u001b[0m\n", + "\u001b[36mNode 10 \t-> B -0.014 -0.065 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 1.325 3.863 0.006\u001b[0m\n", + "\u001b[36m\t\t\t-> G 1.321 3.863 -0.098\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 2.1675\u001b[0m\n", + "\u001b[36mNode 11 \t-> B -0.013 -0.061 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 1.480 4.301 0.007\u001b[0m\n", + "\u001b[36m\t\t\t-> G 1.476 4.301 -0.109\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 4.1146\u001b[0m\n", + "\u001b[36mNode 12 \t-> B -0.013 -0.058 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 1.636 4.739 0.009\u001b[0m\n", + "\u001b[36m\t\t\t-> G 1.632 4.739 -0.120\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 1.9471\u001b[0m\n", + "\u001b[36mNode 13 \t-> B -0.012 -0.055 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 1.792 5.176 0.010\u001b[0m\n", + "\u001b[36m\t\t\t-> G 1.787 5.176 -0.131\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 3.6738\u001b[0m\n", + "\u001b[36mNode 14 \t-> B -0.011 -0.052 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 1.948 5.614 0.011\u001b[0m\n", + "\u001b[36m\t\t\t-> G 1.943 5.614 -0.142\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 1.7267\u001b[0m\n", + "\u001b[36mNode 15 \t-> B -0.011 -0.048 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 2.104 6.052 0.012\u001b[0m\n", + "\u001b[36m\t\t\t-> G 2.098 6.052 -0.153\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 3.2329\u001b[0m\n", + "\u001b[36mNode 16 \t-> B -0.010 -0.045 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 2.260 6.489 0.014\u001b[0m\n", + "\u001b[36m\t\t\t-> G 2.254 6.489 -0.164\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 1.5062\u001b[0m\n", + "\u001b[36mNode 17 \t-> B -0.009 -0.042 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 2.415 6.927 0.015\u001b[0m\n", + "\u001b[36m\t\t\t-> G 2.409 6.927 -0.175\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 2.7921\u001b[0m\n", + "\u001b[36mNode 18 \t-> B -0.008 -0.039 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 2.571 7.364 0.016\u001b[0m\n", + "\u001b[36m\t\t\t-> G 2.564 7.364 -0.186\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 1.2858\u001b[0m\n", + "\u001b[36mNode 19 \t-> B -0.008 -0.035 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 2.727 7.802 0.017\u001b[0m\n", + "\u001b[36m\t\t\t-> G 2.720 7.802 -0.197\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 2.3512\u001b[0m\n", + "\u001b[36mNode 20 \t-> B -0.007 -0.032 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 2.883 8.239 0.019\u001b[0m\n", + "\u001b[36m\t\t\t-> G 2.875 8.239 -0.208\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 1.0654\u001b[0m\n", + "\u001b[36mNode 21 \t-> B -0.006 -0.028 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 3.038 8.677 0.020\u001b[0m\n", + "\u001b[36m\t\t\t-> G 3.030 8.677 -0.219\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 1.9104\u001b[0m\n", + "\u001b[36mNode 22 \t-> B -0.006 -0.026 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 3.194 9.114 0.021\u001b[0m\n", + "\u001b[36m\t\t\t-> G 3.186 9.114 -0.230\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 0.8450\u001b[0m\n", + "\u001b[36mNode 23 \t-> B -0.005 -0.022 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 3.350 9.552 0.023\u001b[0m\n", + "\u001b[36m\t\t\t-> G 3.341 9.552 -0.241\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 1.4695\u001b[0m\n", + "\u001b[36mNode 24 \t-> B -0.005 -0.022 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 3.508 9.988 0.024\u001b[0m\n", + "\u001b[36m\t\t\t-> G 3.499 9.988 -0.252\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 0.3674\u001b[0m\n", + "\u001b[36mNode 25 \t-> B 0.000 0.089 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.089 -0.206 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.089 -0.206 -0.007\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 2.6141\u001b[0m\n", + "\u001b[36mNode 26 \t-> B -0.010 0.019 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.019 -0.403 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.019 -0.403 -0.001\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 7.3672\u001b[0m\n", + "\u001b[36mNode 27 \t-> B -0.019 0.087 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.234 -0.800 0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.234 -0.800 -0.018\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 5.8780\u001b[0m\n", + "\u001b[36mNode 28 \t-> B -0.019 0.084 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.390 -1.238 0.001\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.389 -1.238 -0.030\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 2.8288\u001b[0m\n", + "\u001b[36mNode 29 \t-> B -0.018 0.081 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.546 -1.676 0.001\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.544 -1.676 -0.041\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 5.4372\u001b[0m\n", + "\u001b[36mNode 30 \t-> B -0.017 0.078 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.702 -2.113 0.002\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.700 -2.113 -0.053\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 2.6084\u001b[0m\n", + "\u001b[36mNode 31 \t-> B -0.016 0.074 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 0.857 -2.551 0.003\u001b[0m\n", + "\u001b[36m\t\t\t-> G 0.855 -2.551 -0.064\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 4.9963\u001b[0m\n", + "\u001b[36mNode 32 \t-> B -0.016 0.071 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 1.013 -2.988 0.004\u001b[0m\n", + "\u001b[36m\t\t\t-> G 1.010 -2.988 -0.076\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 2.3879\u001b[0m\n", + "\u001b[36mNode 33 \t-> B -0.015 0.068 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 1.169 -3.426 0.005\u001b[0m\n", + "\u001b[36m\t\t\t-> G 1.166 -3.426 -0.087\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 4.5555\u001b[0m\n", + "\u001b[36mNode 34 \t-> B -0.014 0.065 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 1.325 -3.863 0.006\u001b[0m\n", + "\u001b[36m\t\t\t-> G 1.321 -3.863 -0.098\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 2.1675\u001b[0m\n", + "\u001b[36mNode 35 \t-> B -0.013 0.061 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 1.480 -4.301 0.007\u001b[0m\n", + "\u001b[36m\t\t\t-> G 1.476 -4.301 -0.109\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 4.1146\u001b[0m\n", + "\u001b[36mNode 36 \t-> B -0.013 0.058 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 1.636 -4.739 0.009\u001b[0m\n", + "\u001b[36m\t\t\t-> G 1.632 -4.739 -0.120\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 1.9471\u001b[0m\n", + "\u001b[36mNode 37 \t-> B -0.012 0.055 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 1.792 -5.176 0.010\u001b[0m\n", + "\u001b[36m\t\t\t-> G 1.787 -5.176 -0.131\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 3.6738\u001b[0m\n", + "\u001b[36mNode 38 \t-> B -0.011 0.052 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 1.948 -5.614 0.011\u001b[0m\n", + "\u001b[36m\t\t\t-> G 1.943 -5.614 -0.142\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 1.7267\u001b[0m\n", + "\u001b[36mNode 39 \t-> B -0.011 0.048 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 2.104 -6.052 0.012\u001b[0m\n", + "\u001b[36m\t\t\t-> G 2.098 -6.052 -0.153\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 3.2329\u001b[0m\n", + "\u001b[36mNode 40 \t-> B -0.010 0.045 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 2.260 -6.489 0.014\u001b[0m\n", + "\u001b[36m\t\t\t-> G 2.254 -6.489 -0.164\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 1.5062\u001b[0m\n", + "\u001b[36mNode 41 \t-> B -0.009 0.042 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 2.415 -6.927 0.015\u001b[0m\n", + "\u001b[36m\t\t\t-> G 2.409 -6.927 -0.175\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 2.7921\u001b[0m\n", + "\u001b[36mNode 42 \t-> B -0.008 0.039 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 2.571 -7.364 0.016\u001b[0m\n", + "\u001b[36m\t\t\t-> G 2.564 -7.364 -0.186\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 1.2858\u001b[0m\n", + "\u001b[36mNode 43 \t-> B -0.008 0.035 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 2.727 -7.802 0.017\u001b[0m\n", + "\u001b[36m\t\t\t-> G 2.720 -7.802 -0.197\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 2.3512\u001b[0m\n", + "\u001b[36mNode 44 \t-> B -0.007 0.032 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 2.883 -8.239 0.019\u001b[0m\n", + "\u001b[36m\t\t\t-> G 2.875 -8.239 -0.208\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 1.0654\u001b[0m\n", + "\u001b[36mNode 45 \t-> B -0.006 0.028 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 3.038 -8.677 0.020\u001b[0m\n", + "\u001b[36m\t\t\t-> G 3.030 -8.677 -0.219\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 1.9104\u001b[0m\n", + "\u001b[36mNode 46 \t-> B -0.006 0.026 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 3.194 -9.114 0.021\u001b[0m\n", + "\u001b[36m\t\t\t-> G 3.186 -9.114 -0.230\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 0.8450\u001b[0m\n", + "\u001b[36mNode 47 \t-> B -0.005 0.022 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 3.350 -9.552 0.023\u001b[0m\n", + "\u001b[36m\t\t\t-> G 3.341 -9.552 -0.241\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 1.4695\u001b[0m\n", + "\u001b[36mNode 48 \t-> B -0.005 0.022 -0.000\u001b[0m\n", + "\u001b[36m\t\t\t-> A 3.508 -9.988 0.024\u001b[0m\n", + "\u001b[36m\t\t\t-> G 3.499 -9.988 -0.252\u001b[0m\n", + "\u001b[36m\tNode mass:\u001b[0m\n", + "\u001b[36m\t\tMatrix: 0.3674\u001b[0m\n", + " Updated the beam C, modal C and K matrices with the terms from the\n", + "gravity linearisation\u001b[0m\n", + "\u001b[0m\n", + "Aeroelastic system assembled:\u001b[0m\n", + "\u001b[34m\tAerodynamic states: 1536\u001b[0m\n", + "\u001b[34m\tStructural states: 594\u001b[0m\n", + "\u001b[34m\tTotal states: 2130\u001b[0m\n", + "\u001b[34m\tInputs: 893\u001b[0m\n", + "\u001b[34m\tOutputs: 891\u001b[0m\n", + "\u001b[34mFinal system is:\u001b[0m\n", + "\u001b[34mState-space system\u001b[0m\n", + "\u001b[34mStates: 2130\u001b[0m\n", + "\u001b[34mInputs: 893\u001b[0m\n", + "\u001b[34mOutputs: 891\u001b[0m\n", + "\u001b[34m\u001b[0m\n", + "\u001b[36mGenerating an instance of AsymptoticStability\u001b[0m\n", + "Variable reference_velocity has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1.0\u001b[0m\n", + "Variable display_root_locus has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable velocity_analysis has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: []\u001b[0m\n", + "Variable iterative_eigvals has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: False\u001b[0m\n", + "Variable modes_to_plot has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: []\u001b[0m\n", + "Variable postprocessors has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: []\u001b[0m\n", + "Variable postprocessors_settings has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: {}\u001b[0m\n", + "Dynamical System Eigenvalues\u001b[0m\n", + "Calculating eigenvalues using direct method\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\n", + "|==============|==============|==============|==============|==============|==============|==============|\u001b[0m\n", + "| mode | eval_real | eval_imag | freq_n (Hz) | freq_d (Hz) | damping | period (s) |\u001b[0m\n", + "|==============|==============|==============|==============|==============|==============|==============|\u001b[0m\n", + "| 0 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 1 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 2 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 3 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 4 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 5 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 6 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 7 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 8 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 9 | -0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 10 | -0.000884 | -0.321712 | 0.051202 | 0.051202 | 0.002747 | 19.530439 |\u001b[0m\n", + "| 11 | -0.000884 | 0.321712 | 0.051202 | 0.051202 | 0.002747 | 19.530439 |\u001b[0m\n", + "| 12 | -0.008470 | -0.391287 | 0.062290 | 0.062275 | 0.021642 | 16.057731 |\u001b[0m\n", + "| 13 | -0.008470 | 0.391287 | 0.062290 | 0.062275 | 0.021642 | 16.057731 |\u001b[0m\n", + "| 14 | -0.022506 | 0.000000 | 0.003582 | 0.000000 | 1.000000 | inf |\u001b[0m\n", + "| 15 | -0.064808 | -53.732514 | 8.551801 | 8.551795 | 0.001206 | 0.116935 |\u001b[0m\n", + "| 16 | -0.064808 | 53.732514 | 8.551801 | 8.551795 | 0.001206 | 0.116935 |\u001b[0m\n", + "| 17 | -0.101946 | 68.319141 | 10.873341 | 10.873329 | 0.001492 | 0.091968 |\u001b[0m\n", + "| 18 | -0.101946 | -68.319141 | 10.873341 | 10.873329 | 0.001492 | 0.091968 |\u001b[0m\n", + "| 19 | -0.147587 | 83.265282 | 13.252102 | 13.252081 | 0.001772 | 0.075460 |\u001b[0m\n", + "| 20 | -0.147587 | -83.265282 | 13.252102 | 13.252081 | 0.001772 | 0.075460 |\u001b[0m\n", + "| 21 | -0.248703 | -109.925782 | 17.495276 | 17.495232 | 0.002262 | 0.057158 |\u001b[0m\n", + "| 22 | -0.248703 | 109.925782 | 17.495276 | 17.495232 | 0.002262 | 0.057158 |\u001b[0m\n", + "| 23 | -0.293472 | 120.387631 | 19.160344 | 19.160287 | 0.002438 | 0.052191 |\u001b[0m\n", + "| 24 | -0.293472 | -120.387631 | 19.160344 | 19.160287 | 0.002438 | 0.052191 |\u001b[0m\n", + "| 25 | -0.350319 | 132.903280 | 21.152288 | 21.152214 | 0.002636 | 0.047276 |\u001b[0m\n", + "| 26 | -0.350319 | -132.903280 | 21.152288 | 21.152214 | 0.002636 | 0.047276 |\u001b[0m\n", + "| 27 | -0.376401 | 138.516954 | 22.045739 | 22.045658 | 0.002717 | 0.045360 |\u001b[0m\n", + "| 28 | -0.376401 | -138.516954 | 22.045739 | 22.045658 | 0.002717 | 0.045360 |\u001b[0m\n", + "| 29 | -0.494445 | 162.714232 | 25.896894 | 25.896774 | 0.003039 | 0.038615 |\u001b[0m\n", + "| 30 | -0.494445 | -162.714232 | 25.896894 | 25.896774 | 0.003039 | 0.038615 |\u001b[0m\n", + "| 31 | -0.511651 | -166.238306 | 26.457773 | 26.457648 | 0.003078 | 0.037796 |\u001b[0m\n", + "| 32 | -0.511651 | 166.238306 | 26.457773 | 26.457648 | 0.003078 | 0.037796 |\u001b[0m\n", + "| 33 | -0.559180 | 175.709816 | 27.965227 | 27.965086 | 0.003182 | 0.035759 |\u001b[0m\n", + "| 34 | -0.559180 | -175.709816 | 27.965227 | 27.965086 | 0.003182 | 0.035759 |\u001b[0m\n", + "| 35 | -0.569755 | 177.873274 | 28.309556 | 28.309411 | 0.003203 | 0.035324 |\u001b[0m\n", + "| 36 | -0.569755 | -177.873274 | 28.309556 | 28.309411 | 0.003203 | 0.035324 |\u001b[0m\n", + "| 37 | -0.669914 | -197.999020 | 31.512703 | 31.512523 | 0.003383 | 0.031733 |\u001b[0m\n", + "| 38 | -0.669914 | 197.999020 | 31.512703 | 31.512523 | 0.003383 | 0.031733 |\u001b[0m\n", + "| 39 | -0.678424 | 199.782714 | 31.796590 | 31.796406 | 0.003396 | 0.031450 |\u001b[0m\n", + "| 40 | -0.678424 | -199.782714 | 31.796590 | 31.796406 | 0.003396 | 0.031450 |\u001b[0m\n", + "| 41 | -0.715684 | 207.440562 | 33.015387 | 33.015191 | 0.003450 | 0.030289 |\u001b[0m\n", + "| 42 | -0.715684 | -207.440562 | 33.015387 | 33.015191 | 0.003450 | 0.030289 |\u001b[0m\n", + "| 43 | -0.721193 | -208.623018 | 33.203583 | 33.203385 | 0.003457 | 0.030117 |\u001b[0m\n", + "| 44 | -0.721193 | 208.623018 | 33.203583 | 33.203385 | 0.003457 | 0.030117 |\u001b[0m\n", + "| 45 | -0.796838 | -224.809928 | 35.779836 | 35.779611 | 0.003544 | 0.027949 |\u001b[0m\n", + "| 46 | -0.796838 | 224.809928 | 35.779836 | 35.779611 | 0.003544 | 0.027949 |\u001b[0m\n", + "| 47 | -0.801462 | -225.851223 | 35.945565 | 35.945339 | 0.003549 | 0.027820 |\u001b[0m\n", + "| 48 | -0.801462 | 225.851223 | 35.945565 | 35.945339 | 0.003549 | 0.027820 |\u001b[0m\n", + "| 49 | -0.823218 | 257.880058 | 41.043095 | 41.042886 | 0.003192 | 0.024365 |\u001b[0m\n", + "| 50 | -0.823218 | -257.880058 | 41.043095 | 41.042886 | 0.003192 | 0.024365 |\u001b[0m\n", + "| 51 | -0.829849 | 232.223377 | 36.959734 | 36.959498 | 0.003573 | 0.027057 |\u001b[0m\n", + "| 52 | -0.829849 | -232.223377 | 36.959734 | 36.959498 | 0.003573 | 0.027057 |\u001b[0m\n", + "| 53 | -0.833132 | 232.986723 | 37.081226 | 37.080989 | 0.003576 | 0.026968 |\u001b[0m\n", + "| 54 | -0.833132 | -232.986723 | 37.081226 | 37.080989 | 0.003576 | 0.026968 |\u001b[0m\n", + "| 55 | -0.837692 | 252.830750 | 40.239484 | 40.239264 | 0.003313 | 0.024851 |\u001b[0m\n", + "| 56 | -0.837692 | -252.830750 | 40.239484 | 40.239264 | 0.003313 | 0.024851 |\u001b[0m\n", + "| 57 | -0.843057 | -274.636584 | 43.709976 | 43.709770 | 0.003070 | 0.022878 |\u001b[0m\n", + "| 58 | -0.843057 | 274.636584 | 43.709976 | 43.709770 | 0.003070 | 0.022878 |\u001b[0m\n", + "| 59 | -0.855990 | -264.468496 | 42.091689 | 42.091468 | 0.003237 | 0.023758 |\u001b[0m\n", + "| 60 | -0.855990 | 264.468496 | 42.091689 | 42.091468 | 0.003237 | 0.023758 |\u001b[0m\n", + "| 61 | -0.864725 | 271.184095 | 43.160509 | 43.160289 | 0.003189 | 0.023169 |\u001b[0m\n", + "| 62 | -0.864725 | -271.184095 | 43.160509 | 43.160289 | 0.003189 | 0.023169 |\u001b[0m\n", + "| 63 | -0.871325 | -283.421756 | 45.108187 | 45.107973 | 0.003074 | 0.022169 |\u001b[0m\n", + "| 64 | -0.871325 | 283.421756 | 45.108187 | 45.107973 | 0.003074 | 0.022169 |\u001b[0m\n", + "| 65 | -0.878445 | 267.336890 | 42.548217 | 42.547987 | 0.003286 | 0.023503 |\u001b[0m\n", + "| 66 | -0.878445 | -267.336890 | 42.548217 | 42.547987 | 0.003286 | 0.023503 |\u001b[0m\n", + "| 67 | -0.882869 | -280.833495 | 44.696260 | 44.696039 | 0.003144 | 0.022373 |\u001b[0m\n", + "| 68 | -0.882869 | 280.833495 | 44.696260 | 44.696039 | 0.003144 | 0.022373 |\u001b[0m\n", + "| 69 | -0.884024 | -245.027542 | 38.997598 | 38.997344 | 0.003608 | 0.025643 |\u001b[0m\n", + "| 70 | -0.884024 | 245.027542 | 38.997598 | 38.997344 | 0.003608 | 0.025643 |\u001b[0m\n", + "| 71 | -0.886589 | -245.661879 | 39.098557 | 39.098302 | 0.003609 | 0.025577 |\u001b[0m\n", + "| 72 | -0.886589 | 245.661879 | 39.098557 | 39.098302 | 0.003609 | 0.025577 |\u001b[0m\n", + "| 73 | -0.891210 | -288.915188 | 45.982499 | 45.982280 | 0.003085 | 0.021748 |\u001b[0m\n", + "| 74 | -0.891210 | 288.915188 | 45.982499 | 45.982280 | 0.003085 | 0.021748 |\u001b[0m\n", + "| 75 | -0.908699 | 251.206723 | 39.981053 | 39.980792 | 0.003617 | 0.025012 |\u001b[0m\n", + "| 76 | -0.908699 | -251.206723 | 39.981053 | 39.980792 | 0.003617 | 0.025012 |\u001b[0m\n", + "| 77 | -0.910251 | -251.606127 | 40.044621 | 40.044359 | 0.003618 | 0.024972 |\u001b[0m\n", + "| 78 | -0.910251 | 251.606127 | 40.044621 | 40.044359 | 0.003618 | 0.024972 |\u001b[0m\n", + "| 79 | -0.914184 | -241.156682 | 38.381554 | 38.381278 | 0.003791 | 0.026054 |\u001b[0m\n", + "| 80 | -0.914184 | 241.156682 | 38.381554 | 38.381278 | 0.003791 | 0.026054 |\u001b[0m\n", + "| 81 | -0.915395 | 290.517030 | 46.237451 | 46.237221 | 0.003151 | 0.021628 |\u001b[0m\n", + "| 82 | -0.915395 | -290.517030 | 46.237451 | 46.237221 | 0.003151 | 0.021628 |\u001b[0m\n", + "| 83 | -0.933974 | -278.955360 | 44.397373 | 44.397124 | 0.003348 | 0.022524 |\u001b[0m\n", + "| 84 | -0.933974 | 278.955360 | 44.397373 | 44.397124 | 0.003348 | 0.022524 |\u001b[0m\n", + "| 85 | -0.943144 | -260.320871 | 41.431625 | 41.431353 | 0.003623 | 0.024136 |\u001b[0m\n", + "| 86 | -0.943144 | 260.320871 | 41.431625 | 41.431353 | 0.003623 | 0.024136 |\u001b[0m\n", + "| 87 | -0.944542 | -260.700481 | 41.492043 | 41.491770 | 0.003623 | 0.024101 |\u001b[0m\n", + "| 88 | -0.944542 | 260.700481 | 41.492043 | 41.491770 | 0.003623 | 0.024101 |\u001b[0m\n", + "| 89 | -0.953003 | -294.814043 | 46.921357 | 46.921112 | 0.003233 | 0.021312 |\u001b[0m\n", + "| 90 | -0.953003 | 294.814043 | 46.921357 | 46.921112 | 0.003233 | 0.021312 |\u001b[0m\n", + "| 91 | -0.960626 | -295.652742 | 47.054844 | 47.054595 | 0.003249 | 0.021252 |\u001b[0m\n", + "| 92 | -0.960626 | 295.652742 | 47.054844 | 47.054595 | 0.003249 | 0.021252 |\u001b[0m\n", + "| 93 | -0.960976 | -265.315963 | 42.226624 | 42.226347 | 0.003622 | 0.023682 |\u001b[0m\n", + "| 94 | -0.960976 | 265.315963 | 42.226624 | 42.226347 | 0.003622 | 0.023682 |\u001b[0m\n", + "| 95 | -0.961739 | 300.017779 | 47.749558 | 47.749313 | 0.003206 | 0.020943 |\u001b[0m\n", + "| 96 | -0.961739 | -300.017779 | 47.749558 | 47.749313 | 0.003206 | 0.020943 |\u001b[0m\n", + "| 97 | -0.961940 | -265.596058 | 42.271203 | 42.270925 | 0.003622 | 0.023657 |\u001b[0m\n", + "| 98 | -0.961940 | 265.596058 | 42.271203 | 42.270925 | 0.003622 | 0.023657 |\u001b[0m\n", + "| 99 | -0.965385 | -266.582863 | 42.428259 | 42.427980 | 0.003621 | 0.023569 |\u001b[0m\n", + "\u001b[36mFINISHED - Elapsed time = 16.3462206 seconds\u001b[0m\n", + "\u001b[36mFINISHED - CPU process time = 68.0131146 seconds\u001b[0m\n" + ] + } + ], + "source": [ + "data = sharpy.sharpy_main.main(['', ws.case_route + '/' + ws.case_name + '.sharpy'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Post-processing" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Nonlinear Equilibrium\n", + "\n", + "The files can be opened with Paraview to see the deformation and aerodynamic loading on the flying wing in trim conditions." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Asymptotic Stability" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "eigenvalues_trim = np.loadtxt('./output/horten_u_inf2800_M4N11Msf5/stability/eigenvalues.dat')\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Flight Dynamics modes\n", + "\n", + "The flight dynamics modes can be found close to the origin of the Argand diagram. In particular, the phugoid is the mode that is closest to the imaginary axis. An exercise is left to the user to compare this phugoid predicition with the nonlinear response!" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig = plt.figure()\n", + "plt.scatter(eigenvalues_trim[:, 0], eigenvalues_trim[:, 1],\n", + " marker='x',\n", + " color='k')\n", + "plt.xlim(-0.5, 0.5)\n", + "plt.ylim(-0.5, 0.5)\n", + "plt.grid()\n", + "plt.xlabel('Real Part, $Re(\\lambda)$ [rad/s]')\n", + "plt.ylabel('Imaginary Part, $Im(\\lambda)$ [rad/s]');" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Structural Modes\n", + "\n", + "Looking further out on the plot, the structural modes appear. There is a curve given the Newmark-$\\beta$ integration scheme and on top of it several modes are damped by the presence of the aerodynamics.\n", + "\n", + "Try changing `newmark_damp` in the `LinearAssembler` settings to see how this plot changes!" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig = plt.figure()\n", + "plt.scatter(eigenvalues_trim[:, 0], eigenvalues_trim[:, 1],\n", + " marker='x',\n", + " color='k')\n", + "plt.xlim(-5, 0.5)\n", + "plt.ylim(-200, 200)\n", + "plt.grid()\n", + "plt.xlabel('Real Part, $Re(\\lambda)$ [rad/s]')\n", + "plt.ylabel('Imaginary Part, $Im(\\lambda)$ [rad/s]');" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 } diff --git a/docs/source/content/example_notebooks/nonlinear_t-tail_HALE.ipynb b/docs/source/content/example_notebooks/nonlinear_t-tail_HALE.ipynb index f4ff2973b..e56020789 100644 --- a/docs/source/content/example_notebooks/nonlinear_t-tail_HALE.ipynb +++ b/docs/source/content/example_notebooks/nonlinear_t-tail_HALE.ipynb @@ -118,8 +118,8 @@ "metadata": {}, "outputs": [], "source": [ - "route_to_case = '../../../../cases/coupled/simple_HALE/'\n", - "%run '../../../../cases/coupled/simple_HALE/generate_hale.py'" + "route_to_case = '../../../../sharpy/cases/coupled/simple_HALE/'\n", + "%run '../../../../sharpy/cases/coupled/simple_HALE/generate_hale.py'" ] }, { @@ -138,13 +138,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "generate_hale.py simple_HALE.aero.h5 simple_HALE.sharpy\n", - "output\t\t simple_HALE.fem.h5\n" + "__init__.py \u001b[34moutput\u001b[m\u001b[m simple_HALE.fem.h5\n", + "generate_hale.py simple_HALE.aero.h5 simple_HALE.sharpy\n" ] } ], "source": [ - "!ls ../../../../cases/coupled/simple_HALE/" + "!ls ../../../../sharpy/cases/coupled/simple_HALE/" ] }, { diff --git a/docs/source/content/example_notebooks/wind_turbine.ipynb b/docs/source/content/example_notebooks/wind_turbine.ipynb index ed3509f04..3d09cba3b 100644 --- a/docs/source/content/example_notebooks/wind_turbine.ipynb +++ b/docs/source/content/example_notebooks/wind_turbine.ipynb @@ -1,2558 +1,2558 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Simulation NREL 5MW wind turbine" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "%config InlineBackend.figure_format = 'svg'\n", - "from IPython.display import Image\n", - "url = 'https://raw.githubusercontent.com/ImperialCollegeLondon/sharpy/dev_doc/docs/source/content/example_notebooks/images/turbulence_no_legend.png'\n", - "Image(url=url, width=800)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this notebook the blade loads on the NREL-5MW reference wind turbine computed with SHARPy and OpenFAST will be compared. However, zero-drag airfoils have been used." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "OpenFAST: _https://openfast.readthedocs.io_" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "NREL-5MW: Jonkman, J.; Butterfield, S.; Musial, W. and Scott, G.. _Definition of a 5-MW Reference Wind Turbine for Offshore System Development_, Technical Report, NREL 2009" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Load the required packages:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "# Required packages\n", - "%matplotlib inline\n", - "import numpy as np\n", - "import os\n", - "import matplotlib.pyplot as plt\n", - "\n", - "# Required SHARPy modules\n", - "import sharpy.sharpy_main\n", - "import sharpy.utils.algebra as algebra\n", - "import sharpy.utils.generate_cases as gc\n", - "import cases.templates.template_wt as template_wt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "These are the results from the OpenFAST simulation for comparison: out-of-plane `of_cNdrR` and in-plane `of_cTdrR` coefficients along the blade and thrust `of_ct` and power `of_cp` rotor coefficients" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "of_rR = np.array([0.20158356, 0.3127131, 0.40794048, 0.5984148, 0.6936519, 0.85238045, 0.899999, 0.95555407, 0.98729974, 1.0])\n", - "of_cNdrR = np.array([0.08621394, 0.14687876, 0.19345148, 0.2942731, 0.36003628, 0.43748564, 0.44762507, 0.38839236, 0.29782477, 0.0])\n", - "of_cTdrR = np.array([0.048268348, 0.051957503, 0.05304592, 0.052862607, 0.056001827, 0.0536646, 0.050112925, 0.038993906, 0.023664437, 0.0])\n", - "\n", - "of_ct = 0.69787693\n", - "of_cp = 0.48813498" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Create SHARPy case" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Definition of parameters" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "# Mathematical constants\n", - "deg2rad = np.pi/180.\n", - "\n", - "# Case\n", - "case = 'rotor'\n", - "route = './'\n", - "\n", - "# Geometry discretization\n", - "chord_panels = np.array([8], dtype=int)\n", - "revs_in_wake = 5\n", - "\n", - "# Operation\n", - "rotation_velocity = 12.1*2*np.pi/60\n", - "pitch_deg = 0. #degrees\n", - "\n", - "# Wind\n", - "WSP = 12.\n", - "air_density = 1.225\n", - "\n", - "# Simulation\n", - "dphi = 4.*deg2rad\n", - "revs_to_simulate = 5" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Computation of associated parameters" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "dt = dphi/rotation_velocity\n", - "time_steps = int(revs_to_simulate*2.*np.pi/dphi)\n", - "mstar = int(revs_in_wake*2.*np.pi/dphi)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Generation of the rotor geometry based on the information in the excel file" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[33mWARNING: The poisson cofficient is assumed equal to 0.3\u001b[0m\n", - "\u001b[33mWARNING: Cross-section area is used as shear area\u001b[0m\n", - "\u001b[33mWARNING: Using perpendicular axis theorem to compute the inertia around xB\u001b[0m\n", - "\u001b[33mWARNING: Replacing node 29 by node 0\u001b[0m\n", - "\u001b[33mWARNING: Replacing node 58 by node 0\u001b[0m\n" - ] - } - ], - "source": [ - "op_params = {'rotation_velocity': rotation_velocity,\n", - " 'pitch_deg': pitch_deg,\n", - " 'wsp': WSP,\n", - " 'dt': dt}\n", - "\n", - "geom_params = {'chord_panels':chord_panels,\n", - " 'tol_remove_points': 1e-8,\n", - " 'n_points_camber': 100,\n", - " 'm_distribution': 'uniform'}\n", - "\n", - "excel_description = {'excel_file_name': route + '/source/type02_db_NREL5MW_v02.xlsx',\n", - " 'excel_sheet_parameters': 'parameters',\n", - " 'excel_sheet_structural_blade': 'structural_blade',\n", - " 'excel_sheet_discretization_blade': 'discretization_blade',\n", - " 'excel_sheet_aero_blade': 'aero_blade',\n", - " 'excel_sheet_airfoil_info': 'airfoil_info',\n", - " 'excel_sheet_airfoil_chord': 'airfoil_coord'}\n", - "\n", - "options = {'camber_effect_on_twist': False,\n", - " 'user_defined_m_distribution_type': None,\n", - " 'include_polars': False}\n", - "\n", - "rotor = template_wt.rotor_from_excel_type03(op_params,\n", - " geom_params,\n", - " excel_description,\n", - " options)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Define simulation details. The steady simulation is faster than the dynamic simulation. However, the dynamic simulation includes wake self-induction and provides more accurate results." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "steady_simulation = False" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "SimInfo = gc.SimulationInformation()\n", - "SimInfo.set_default_values()\n", - "\n", - "if steady_simulation:\n", - " SimInfo.solvers['SHARPy']['flow'] = ['BeamLoader',\n", - " 'AerogridLoader',\n", - " 'StaticCoupledRBM',\n", - " 'BeamPlot',\n", - " 'AerogridPlot', \n", - " 'SaveData'] \n", - "else:\n", - " SimInfo.solvers['SHARPy']['flow'] = ['BeamLoader',\n", - " 'AerogridLoader',\n", - " 'StaticCoupledRBM',\n", - " 'DynamicCoupled']\n", - " \n", - "SimInfo.solvers['SHARPy']['case'] = case\n", - "SimInfo.solvers['SHARPy']['route'] = route\n", - "SimInfo.solvers['SHARPy']['write_log'] = True\n", - "SimInfo.set_variable_all_dicts('dt', dt)\n", - "SimInfo.set_variable_all_dicts('rho', air_density)\n", - "\n", - "SimInfo.solvers['SteadyVelocityField']['u_inf'] = WSP\n", - "SimInfo.solvers['SteadyVelocityField']['u_inf_direction'] = np.array([0., 0., 1.])\n", - "\n", - "SimInfo.solvers['BeamLoader']['unsteady'] = 'on'\n", - "\n", - "SimInfo.solvers['AerogridLoader']['unsteady'] = 'on'\n", - "SimInfo.solvers['AerogridLoader']['mstar'] = mstar\n", - "SimInfo.solvers['AerogridLoader']['freestream_dir'] = np.array([0.,0.,0.])\n", - "SimInfo.solvers['AerogridLoader']['wake_shape_generator'] = 'HelicoidalWake'\n", - "SimInfo.solvers['AerogridLoader']['wake_shape_generator_input'] = {'u_inf': WSP,\n", - " 'u_inf_direction': SimInfo.solvers['SteadyVelocityField']['u_inf_direction'],\n", - " 'rotation_velocity': rotation_velocity*np.array([0., 0., 1.]),\n", - " 'dt': dt,\n", - " 'dphi1': dphi,\n", - " 'ndphi1': mstar,\n", - " 'r': 1.,\n", - " 'dphimax': 10*deg2rad}\n", - " \n", - "SimInfo.solvers['StaticCoupledRBM']['structural_solver'] = 'RigidDynamicPrescribedStep'\n", - "SimInfo.solvers['StaticCoupledRBM']['structural_solver_settings'] = SimInfo.solvers['RigidDynamicPrescribedStep']\n", - "SimInfo.solvers['StaticCoupledRBM']['aero_solver'] = 'StaticUvlm'\n", - "SimInfo.solvers['StaticCoupledRBM']['aero_solver_settings'] = SimInfo.solvers['StaticUvlm']\n", - "\n", - "SimInfo.solvers['StaticCoupledRBM']['tolerance'] = 1e-8\n", - "SimInfo.solvers['StaticCoupledRBM']['n_load_steps'] = 0\n", - "SimInfo.solvers['StaticCoupledRBM']['relaxation_factor'] = 0.\n", - "\n", - "SimInfo.solvers['StaticUvlm']['num_cores'] = 8\n", - "SimInfo.solvers['StaticUvlm']['velocity_field_generator'] = 'SteadyVelocityField'\n", - "SimInfo.solvers['StaticUvlm']['velocity_field_input'] = SimInfo.solvers['SteadyVelocityField']\n", - "\n", - "SimInfo.solvers['SaveData']['compress_float'] = True\n", - " \n", - "# Only used for steady_simulation = False\n", - "SimInfo.solvers['StepUvlm']['convection_scheme'] = 3\n", - "SimInfo.solvers['StepUvlm']['num_cores'] = 8\n", - "SimInfo.solvers['StepUvlm']['velocity_field_generator'] = 'SteadyVelocityField'\n", - "SimInfo.solvers['StepUvlm']['velocity_field_input'] = SimInfo.solvers['SteadyVelocityField']\n", - "\n", - "SimInfo.solvers['DynamicCoupled']['structural_solver'] = 'RigidDynamicPrescribedStep'\n", - "SimInfo.solvers['DynamicCoupled']['structural_solver_settings'] = SimInfo.solvers['RigidDynamicPrescribedStep']\n", - "SimInfo.solvers['DynamicCoupled']['aero_solver'] = 'StepUvlm'\n", - "SimInfo.solvers['DynamicCoupled']['aero_solver_settings'] = SimInfo.solvers['StepUvlm']\n", - "SimInfo.solvers['DynamicCoupled']['postprocessors'] = ['BeamPlot', 'AerogridPlot', 'Cleanup', 'SaveData']\n", - "SimInfo.solvers['DynamicCoupled']['postprocessors_settings'] = {'BeamPlot': SimInfo.solvers['BeamPlot'],\n", - " 'AerogridPlot': SimInfo.solvers['AerogridPlot'],\n", - " 'Cleanup': SimInfo.solvers['Cleanup'],\n", - " 'SaveData': SimInfo.solvers['SaveData']}\n", - "SimInfo.solvers['DynamicCoupled']['minimum_steps'] = 0\n", - "SimInfo.solvers['DynamicCoupled']['include_unsteady_force_contribution'] = True\n", - "SimInfo.solvers['DynamicCoupled']['relaxation_factor'] = 0.\n", - "SimInfo.solvers['DynamicCoupled']['final_relaxation_factor'] = 0.\n", - "SimInfo.solvers['DynamicCoupled']['dynamic_relaxation'] = False\n", - "SimInfo.solvers['DynamicCoupled']['relaxation_steps'] = 0\n", - "\n", - "# Define dynamic simulation (used regardless the value of \"steady_simulation\" variable)\n", - "SimInfo.define_num_steps(time_steps)\n", - "SimInfo.with_forced_vel = True\n", - "SimInfo.for_vel = np.zeros((time_steps,6), dtype=float)\n", - "SimInfo.for_vel[:,5] = rotation_velocity\n", - "SimInfo.for_acc = np.zeros((time_steps,6), dtype=float)\n", - "SimInfo.with_dynamic_forces = True\n", - "SimInfo.dynamic_forces = np.zeros((time_steps,rotor.StructuralInformation.num_node,6), dtype=float)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Generate simulation files" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "gc.clean_test_files(SimInfo.solvers['SHARPy']['route'], SimInfo.solvers['SHARPy']['case'])\n", - "rotor.generate_h5_files(SimInfo.solvers['SHARPy']['route'], SimInfo.solvers['SHARPy']['case'])\n", - "SimInfo.generate_solver_file()\n", - "SimInfo.generate_dyn_file(time_steps)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Run SHARPy case" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "--------------------------------------------------------------------------------\u001b[0m\n", - " ###### ## ## ### ######## ######## ## ##\u001b[0m\n", - " ## ## ## ## ## ## ## ## ## ## ## ##\u001b[0m\n", - " ## ## ## ## ## ## ## ## ## ####\u001b[0m\n", - " ###### ######### ## ## ######## ######## ##\u001b[0m\n", - " ## ## ## ######### ## ## ## ##\u001b[0m\n", - " ## ## ## ## ## ## ## ## ## ##\u001b[0m\n", - " ###### ## ## ## ## ## ## ## ##\u001b[0m\n", - "--------------------------------------------------------------------------------\u001b[0m\n", - "Aeroelastics Lab, Aeronautics Department.\u001b[0m\n", - " Copyright (c), Imperial College London.\u001b[0m\n", - " All rights reserved.\u001b[0m\n", - " License available at https://github.com/imperialcollegelondon/sharpy\u001b[0m\n", - "\u001b[36mRunning SHARPy from /home/am3717/code/sharpy/docs/source/content/example_notebooks\u001b[0m\n", - "\u001b[36mSHARPy being run is in /home/am3717/code/sharpy\u001b[0m\n", - "\u001b[36mThe branch being run is dev_setting_error\u001b[0m\n", - "\u001b[36mThe version and commit hash are: v1.2.1-357-gbb7c4b4-bb7c4b4\u001b[0m\n", - "SHARPy output folder set\u001b[0m\n", - "\u001b[34m\t./output//rotor/\u001b[0m\n", - "\u001b[36mGenerating an instance of BeamLoader\u001b[0m\n", - "\u001b[36mGenerating an instance of AerogridLoader\u001b[0m\n", - "Variable shear_direction has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: [1. 0. 0.]\u001b[0m\n", - "Variable shear_exp has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 0.0\u001b[0m\n", - "Variable h_ref has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1.0\u001b[0m\n", - "Variable h_corr has no assigned value in the settings file.\u001b[0m\n", - "\u001b[34m will default to the value: 1.0\u001b[0m\n", - "\u001b[34mThe aerodynamic grid contains 3 surfaces\u001b[0m\n", - "\u001b[34m Surface 0, M=8, N=26\u001b[0m\n", - " Wake 0, M=450, N=26\u001b[0m\n", - "\u001b[34m Surface 1, M=8, N=26\u001b[0m\n", - " Wake 1, M=450, N=26\u001b[0m\n", - "\u001b[34m Surface 2, M=8, N=26\u001b[0m\n", - " Wake 2, M=450, N=26\u001b[0m\n", - " In total: 624 bound panels\u001b[0m\n", - " In total: 35100 wake panels\u001b[0m\n", - " Total number of panels = 35724\u001b[0m\n", - "\u001b[36mGenerating an instance of StaticCoupledRBM\u001b[0m\n", - "\u001b[36mGenerating an instance of RigidDynamicPrescribedStep\u001b[0m\n", - "\u001b[36mGenerating an instance of StaticUvlm\u001b[0m\n", - "i_step: 0, i_iter: 0\u001b[0m\n", - "Resultant forces and moments: (array([2.54814836e-08, 5.82946313e-09, 2.09459948e+04]), array([1.53411975e-07, 3.24440634e-08, 5.96558361e+06]))\u001b[0m\n", - "\u001b[36mGenerating an instance of DynamicCoupled\u001b[0m\n", - "\u001b[36mGenerating an instance of RigidDynamicPrescribedStep\u001b[0m\n", - "\u001b[36mGenerating an instance of StepUvlm\u001b[0m\n", - "\u001b[36mGenerating an instance of BeamPlot\u001b[0m\n", - "\u001b[36mGenerating an instance of AerogridPlot\u001b[0m\n", - "\u001b[36mGenerating an instance of Cleanup\u001b[0m\n", - "\u001b[36mGenerating an instance of SaveData\u001b[0m\n", - "\u001b[0m\n", - "\u001b[0m\n", - "\u001b[0m\n", - "|=======|========|======|==============|==============|==============|==============|==============|\u001b[0m\n", - "| ts | t | iter | struc ratio | iter time | residual vel | FoR_vel(x) | FoR_vel(z) |\u001b[0m\n", - "|=======|========|======|==============|==============|==============|==============|==============|\u001b[0m\n", - "| 1 | 0.0551 | 1 | 0.000251 | 56.804234 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 2 | 0.1102 | 1 | 0.000177 | 60.232129 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 3 | 0.1653 | 1 | 0.000234 | 59.442611 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 4 | 0.2204 | 1 | 0.000290 | 64.383652 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 5 | 0.2755 | 1 | 0.000275 | 52.774436 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 6 | 0.3306 | 1 | 0.000293 | 48.828457 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 7 | 0.3857 | 1 | 0.000316 | 49.762675 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 8 | 0.4408 | 1 | 0.000227 | 49.627842 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 9 | 0.4959 | 1 | 0.000193 | 58.254770 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 10 | 0.5510 | 1 | 0.000233 | 49.347327 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 11 | 0.6061 | 1 | 0.000222 | 51.950339 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 12 | 0.6612 | 1 | 0.000195 | 58.489715 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 13 | 0.7163 | 1 | 0.000190 | 55.497720 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 14 | 0.7713 | 1 | 0.000287 | 49.864559 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 15 | 0.8264 | 1 | 0.000198 | 56.915311 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 16 | 0.8815 | 1 | 0.000273 | 52.475129 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 17 | 0.9366 | 1 | 0.000212 | 53.472742 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 18 | 0.9917 | 1 | 0.000220 | 51.679432 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 19 | 1.0468 | 1 | 0.000230 | 49.468328 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 20 | 1.1019 | 1 | 0.000233 | 48.806374 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 21 | 1.1570 | 1 | 0.000214 | 51.507987 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 22 | 1.2121 | 1 | 0.000152 | 75.114590 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 23 | 1.2672 | 1 | 0.000178 | 63.764692 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 24 | 1.3223 | 1 | 0.000232 | 48.927093 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 25 | 1.3774 | 1 | 0.000205 | 55.065928 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 26 | 1.4325 | 1 | 0.000209 | 53.747631 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 27 | 1.4876 | 1 | 0.000215 | 65.487096 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 28 | 1.5427 | 1 | 0.000366 | 52.159906 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 29 | 1.5978 | 1 | 0.000286 | 50.405248 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 30 | 1.6529 | 1 | 0.000243 | 53.903698 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 31 | 1.7080 | 1 | 0.000189 | 55.762230 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 32 | 1.7631 | 1 | 0.000230 | 49.679477 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 33 | 1.8182 | 1 | 0.000267 | 49.140923 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 34 | 1.8733 | 1 | 0.000217 | 51.686471 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 35 | 1.9284 | 1 | 0.000227 | 49.924635 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 36 | 1.9835 | 1 | 0.000290 | 49.295199 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 37 | 2.0386 | 1 | 0.000231 | 49.096186 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 38 | 2.0937 | 1 | 0.000267 | 53.824351 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 39 | 2.1488 | 1 | 0.000274 | 50.208116 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 40 | 2.2039 | 1 | 0.000164 | 68.475332 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 41 | 2.2590 | 1 | 0.000219 | 51.476537 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 42 | 2.3140 | 1 | 0.000258 | 50.780835 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 43 | 2.3691 | 1 | 0.000205 | 54.508414 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 44 | 2.4242 | 1 | 0.000229 | 49.643120 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 45 | 2.4793 | 1 | 0.000423 | 49.510440 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 46 | 2.5344 | 1 | 0.000229 | 49.386177 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 47 | 2.5895 | 1 | 0.000224 | 51.096846 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 48 | 2.6446 | 1 | 0.000429 | 67.054611 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 49 | 2.6997 | 1 | 0.000674 | 49.034094 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 50 | 2.7548 | 1 | 0.000473 | 67.770922 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 51 | 2.8099 | 1 | 0.000623 | 52.057162 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 52 | 2.8650 | 1 | 0.000197 | 57.338530 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 53 | 2.9201 | 1 | 0.000211 | 53.323214 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 54 | 2.9752 | 1 | 0.000228 | 49.446313 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 55 | 3.0303 | 1 | 0.000226 | 50.531894 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 56 | 3.0854 | 1 | 0.000216 | 49.277498 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 57 | 3.1405 | 1 | 0.000229 | 49.378688 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 58 | 3.1956 | 1 | 0.000456 | 49.096131 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 59 | 3.2507 | 1 | 0.000638 | 49.407950 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 60 | 3.3058 | 1 | 0.000644 | 49.355487 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 61 | 3.3609 | 1 | 0.000429 | 52.381709 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 62 | 3.4160 | 1 | 0.000227 | 50.244867 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 63 | 3.4711 | 1 | 0.000226 | 50.090238 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 64 | 3.5262 | 1 | 0.000221 | 50.890983 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 65 | 3.5813 | 1 | 0.000292 | 52.582257 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 66 | 3.6364 | 1 | 0.000229 | 48.970770 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 67 | 3.6915 | 1 | 0.000211 | 53.921150 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 68 | 3.7466 | 1 | 0.000276 | 49.332785 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 69 | 3.8017 | 1 | 0.000405 | 49.861912 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 70 | 3.8567 | 1 | 0.000678 | 50.109032 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 71 | 3.9118 | 1 | 0.000627 | 50.052867 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 72 | 3.9669 | 1 | 0.000231 | 49.125818 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 73 | 4.0220 | 1 | 0.000215 | 52.475354 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 74 | 4.0771 | 1 | 0.000302 | 51.044905 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 75 | 4.1322 | 1 | 0.000232 | 49.154350 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 76 | 4.1873 | 1 | 0.000263 | 49.587372 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 77 | 4.2424 | 1 | 0.000382 | 48.959923 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 78 | 4.2975 | 1 | 0.000207 | 54.498279 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 79 | 4.3526 | 1 | 0.000209 | 55.866552 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 80 | 4.4077 | 1 | 0.000222 | 50.659905 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 81 | 4.4628 | 1 | 0.000232 | 49.229321 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 82 | 4.5179 | 1 | 0.000230 | 49.197802 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 83 | 4.5730 | 1 | 0.000244 | 58.532026 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 84 | 4.6281 | 1 | 0.000229 | 49.010943 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 85 | 4.6832 | 1 | 0.000236 | 49.181471 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 86 | 4.7383 | 1 | 0.000234 | 50.404876 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 87 | 4.7934 | 1 | 0.000223 | 50.863604 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 88 | 4.8485 | 1 | 0.000232 | 49.386036 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 89 | 4.9036 | 1 | 0.000292 | 48.966426 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 90 | 4.9587 | 1 | 0.000204 | 55.176201 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 91 | 5.0138 | 1 | 0.000381 | 49.363449 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 92 | 5.0689 | 1 | 0.000254 | 51.436284 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 93 | 5.1240 | 1 | 0.000427 | 54.203413 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 94 | 5.1791 | 1 | 0.000278 | 51.088371 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 95 | 5.2342 | 1 | 0.000221 | 51.267494 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 96 | 5.2893 | 1 | 0.000220 | 51.246126 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 97 | 5.3444 | 1 | 0.000261 | 50.044367 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 98 | 5.3994 | 1 | 0.000237 | 48.928531 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 99 | 5.4545 | 1 | 0.000230 | 48.869230 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 100 | 5.5096 | 1 | 0.000229 | 49.124024 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 101 | 5.5647 | 1 | 0.000230 | 49.170615 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 102 | 5.6198 | 1 | 0.000227 | 51.072559 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 103 | 5.6749 | 1 | 0.000230 | 49.106752 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 104 | 5.7300 | 1 | 0.000230 | 49.157374 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 105 | 5.7851 | 1 | 0.000230 | 48.729726 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 106 | 5.8402 | 1 | 0.000230 | 49.307981 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 107 | 5.8953 | 1 | 0.000232 | 49.303297 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 108 | 5.9504 | 1 | 0.000228 | 49.388923 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 109 | 6.0055 | 1 | 0.000251 | 49.505395 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 110 | 6.0606 | 1 | 0.000294 | 49.671154 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 111 | 6.1157 | 1 | 0.000637 | 49.106910 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 112 | 6.1708 | 1 | 0.000668 | 49.600747 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 113 | 6.2259 | 1 | 0.000694 | 48.788273 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 114 | 6.2810 | 1 | 0.000605 | 49.259812 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 115 | 6.3361 | 1 | 0.000235 | 49.096078 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 116 | 6.3912 | 1 | 0.000235 | 48.627974 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 117 | 6.4463 | 1 | 0.000231 | 48.687230 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 118 | 6.5014 | 1 | 0.000261 | 50.182136 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 119 | 6.5565 | 1 | 0.000220 | 51.480792 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 120 | 6.6116 | 1 | 0.000212 | 52.759361 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 121 | 6.6667 | 1 | 0.000234 | 61.395446 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 122 | 6.7218 | 1 | 0.000196 | 57.312678 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 123 | 6.7769 | 1 | 0.000196 | 57.288595 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 124 | 6.8320 | 1 | 0.000219 | 52.597533 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 125 | 6.8871 | 1 | 0.000279 | 51.215139 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 126 | 6.9421 | 1 | 0.000232 | 48.835388 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 127 | 6.9972 | 1 | 0.000228 | 49.442145 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 128 | 7.0523 | 1 | 0.000232 | 49.943434 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 129 | 7.1074 | 1 | 0.000265 | 48.943739 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 130 | 7.1625 | 1 | 0.000233 | 48.552538 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 131 | 7.2176 | 1 | 0.000235 | 48.576140 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 132 | 7.2727 | 1 | 0.000227 | 50.729522 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 133 | 7.3278 | 1 | 0.000199 | 57.042671 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 134 | 7.3829 | 1 | 0.000229 | 49.647710 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 135 | 7.4380 | 1 | 0.000377 | 53.191903 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 136 | 7.4931 | 1 | 0.000666 | 50.832518 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 137 | 7.5482 | 1 | 0.000560 | 52.129724 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 138 | 7.6033 | 1 | 0.000593 | 48.825715 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 139 | 7.6584 | 1 | 0.000231 | 49.220850 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 140 | 7.7135 | 1 | 0.000347 | 64.642874 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 141 | 7.7686 | 1 | 0.000615 | 54.791703 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 142 | 7.8237 | 1 | 0.000557 | 55.332506 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 143 | 7.8788 | 1 | 0.000649 | 51.958248 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 144 | 7.9339 | 1 | 0.000431 | 52.224418 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 145 | 7.9890 | 1 | 0.000532 | 60.207598 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 146 | 8.0441 | 1 | 0.000267 | 48.557877 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 147 | 8.0992 | 1 | 0.000283 | 50.720948 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 148 | 8.1543 | 1 | 0.000264 | 54.107664 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 149 | 8.2094 | 1 | 0.000225 | 49.998135 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 150 | 8.2645 | 1 | 0.000297 | 48.723537 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 151 | 8.3196 | 1 | 0.000229 | 48.991915 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 152 | 8.3747 | 1 | 0.000223 | 50.525580 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 153 | 8.4298 | 1 | 0.000372 | 48.880427 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 154 | 8.4848 | 1 | 0.000232 | 48.851891 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 155 | 8.5399 | 1 | 0.000231 | 48.731096 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 156 | 8.5950 | 1 | 0.000209 | 53.669843 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 157 | 8.6501 | 1 | 0.000203 | 55.525421 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 158 | 8.7052 | 1 | 0.000216 | 48.727216 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 159 | 8.7603 | 1 | 0.000240 | 52.981239 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 160 | 8.8154 | 1 | 0.000294 | 50.516618 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 161 | 8.8705 | 1 | 0.000198 | 56.957355 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 162 | 8.9256 | 1 | 0.000268 | 48.780843 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 163 | 8.9807 | 1 | 0.000228 | 49.216988 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 164 | 9.0358 | 1 | 0.000228 | 49.020854 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 165 | 9.0909 | 1 | 0.000236 | 48.516192 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 166 | 9.1460 | 1 | 0.000275 | 48.936249 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 167 | 9.2011 | 1 | 0.000229 | 48.984558 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 168 | 9.2562 | 1 | 0.000228 | 50.011495 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 169 | 9.3113 | 1 | 0.000230 | 49.079509 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 170 | 9.3664 | 1 | 0.000232 | 48.940832 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 171 | 9.4215 | 1 | 0.000266 | 48.539668 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 172 | 9.4766 | 1 | 0.000329 | 48.707876 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 173 | 9.5317 | 1 | 0.000344 | 49.214713 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 174 | 9.5868 | 1 | 0.000221 | 50.762513 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 175 | 9.6419 | 1 | 0.000336 | 55.302099 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 176 | 9.6970 | 1 | 0.000217 | 48.736059 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 177 | 9.7521 | 1 | 0.000231 | 48.750098 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 178 | 9.8072 | 1 | 0.000229 | 48.794680 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 179 | 9.8623 | 1 | 0.000225 | 50.519301 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 180 | 9.9174 | 1 | 0.000225 | 49.930789 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 181 | 9.9725 | 1 | 0.000265 | 50.077232 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 182 |10.0275 | 1 | 0.000285 | 50.327260 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 183 |10.0826 | 1 | 0.000319 | 50.215326 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 184 |10.1377 | 1 | 0.000288 | 48.350528 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 185 |10.1928 | 1 | 0.000233 | 48.372314 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 186 |10.2479 | 1 | 0.000232 | 49.104808 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 187 |10.3030 | 1 | 0.000292 | 48.972235 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 188 |10.3581 | 1 | 0.000655 | 48.972110 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 189 |10.4132 | 1 | 0.000233 | 48.457135 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 190 |10.4683 | 1 | 0.000229 | 49.120724 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 191 |10.5234 | 1 | 0.000222 | 51.106254 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 192 |10.5785 | 1 | 0.000232 | 48.794933 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 193 |10.6336 | 1 | 0.000292 | 49.537670 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 194 |10.6887 | 1 | 0.000230 | 49.236260 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 195 |10.7438 | 1 | 0.000603 | 52.529460 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 196 |10.7989 | 1 | 0.000697 | 48.383116 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 197 |10.8540 | 1 | 0.000347 | 48.288813 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 198 |10.9091 | 1 | 0.000227 | 49.550065 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 199 |10.9642 | 1 | 0.000216 | 48.675859 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 200 |11.0193 | 1 | 0.000231 | 49.152487 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 201 |11.0744 | 1 | 0.000235 | 48.778197 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 202 |11.1295 | 1 | 0.000373 | 57.791454 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 203 |11.1846 | 1 | 0.000582 | 57.753718 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 204 |11.2397 | 1 | 0.000451 | 48.675944 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 205 |11.2948 | 1 | 0.000235 | 48.274112 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 206 |11.3499 | 1 | 0.000232 | 48.917871 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 207 |11.4050 | 1 | 0.000293 | 49.482251 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 208 |11.4601 | 1 | 0.000297 | 48.304309 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 209 |11.5152 | 1 | 0.000263 | 49.162448 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 210 |11.5702 | 1 | 0.000227 | 50.845835 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 211 |11.6253 | 1 | 0.000232 | 48.809305 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 212 |11.6804 | 1 | 0.000293 | 48.950330 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 213 |11.7355 | 1 | 0.000298 | 48.481146 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 214 |11.7906 | 1 | 0.000234 | 48.222179 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 215 |11.8457 | 1 | 0.000225 | 48.228569 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 216 |11.9008 | 1 | 0.000218 | 48.173668 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 217 |11.9559 | 1 | 0.000230 | 48.496389 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 218 |12.0110 | 1 | 0.000212 | 53.240908 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 219 |12.0661 | 1 | 0.000224 | 51.372105 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 220 |12.1212 | 1 | 0.000419 | 49.490550 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 221 |12.1763 | 1 | 0.000459 | 49.025501 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 222 |12.2314 | 1 | 0.000210 | 53.157816 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 223 |12.2865 | 1 | 0.000233 | 48.099241 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 224 |12.3416 | 1 | 0.000241 | 59.450060 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 225 |12.3967 | 1 | 0.000224 | 50.153783 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 226 |12.4518 | 1 | 0.000169 | 67.550282 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 227 |12.5069 | 1 | 0.000635 | 51.062920 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 228 |12.5620 | 1 | 0.000233 | 48.498621 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 229 |12.6171 | 1 | 0.000647 | 48.837410 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 230 |12.6722 | 1 | 0.000685 | 48.524438 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 231 |12.7273 | 1 | 0.000645 | 47.972726 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 232 |12.7824 | 1 | 0.000698 | 48.332650 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 233 |12.8375 | 1 | 0.000291 | 49.344210 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 234 |12.8926 | 1 | 0.000231 | 48.621207 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 235 |12.9477 | 1 | 0.000211 | 53.099352 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 236 |13.0028 | 1 | 0.000231 | 48.779174 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 237 |13.0579 | 1 | 0.000221 | 52.508748 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 238 |13.1129 | 1 | 0.000197 | 57.071891 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 239 |13.1680 | 1 | 0.000232 | 48.635785 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 240 |13.2231 | 1 | 0.000228 | 49.653808 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 241 |13.2782 | 1 | 0.000232 | 49.880283 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 242 |13.3333 | 1 | 0.000219 | 50.989864 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 243 |13.3884 | 1 | 0.000218 | 51.562733 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 244 |13.4435 | 1 | 0.000175 | 60.525012 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 245 |13.4986 | 1 | 0.000208 | 53.714735 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 246 |13.5537 | 1 | 0.000188 | 60.430187 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 247 |13.6088 | 1 | 0.000580 | 55.369422 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 248 |13.6639 | 1 | 0.000376 | 52.945929 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 249 |13.7190 | 1 | 0.000221 | 50.621281 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 250 |13.7741 | 1 | 0.000222 | 51.930545 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 251 |13.8292 | 1 | 0.000203 | 55.750479 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 252 |13.8843 | 1 | 0.000228 | 49.736165 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 253 |13.9394 | 1 | 0.000239 | 48.733683 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 254 |13.9945 | 1 | 0.000228 | 48.860423 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 255 |14.0496 | 1 | 0.000234 | 48.158656 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 256 |14.1047 | 1 | 0.000222 | 48.995898 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 257 |14.1598 | 1 | 0.000181 | 61.699569 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 258 |14.2149 | 1 | 0.000217 | 51.853006 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 259 |14.2700 | 1 | 0.000231 | 48.370357 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 260 |14.3251 | 1 | 0.000231 | 48.536412 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 261 |14.3802 | 1 | 0.000231 | 48.706123 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 262 |14.4353 | 1 | 0.000321 | 48.889459 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 263 |14.4904 | 1 | 0.000232 | 48.413389 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 264 |14.5455 | 1 | 0.000183 | 61.637297 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 265 |14.6006 | 1 | 0.000266 | 51.996729 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 266 |14.6556 | 1 | 0.000440 | 51.124606 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 267 |14.7107 | 1 | 0.000206 | 54.482224 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 268 |14.7658 | 1 | 0.000227 | 49.396567 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 269 |14.8209 | 1 | 0.000216 | 48.399076 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 270 |14.8760 | 1 | 0.000225 | 49.932379 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 271 |14.9311 | 1 | 0.000229 | 48.837313 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 272 |14.9862 | 1 | 0.000234 | 48.303012 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 273 |15.0413 | 1 | 0.000297 | 48.557886 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 274 |15.0964 | 1 | 0.000232 | 48.462573 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 275 |15.1515 | 1 | 0.000235 | 48.074680 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 276 |15.2066 | 1 | 0.000237 | 47.978437 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 277 |15.2617 | 1 | 0.000218 | 48.799704 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 278 |15.3168 | 1 | 0.000180 | 61.907709 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 279 |15.3719 | 1 | 0.000223 | 50.256748 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 280 |15.4270 | 1 | 0.000229 | 49.243923 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 281 |15.4821 | 1 | 0.000453 | 49.924487 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 282 |15.5372 | 1 | 0.000350 | 49.501563 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 283 |15.5923 | 1 | 0.000234 | 48.333380 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 284 |15.6474 | 1 | 0.000223 | 50.163273 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 285 |15.7025 | 1 | 0.000225 | 50.068699 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 286 |15.7576 | 1 | 0.000229 | 48.458516 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 287 |15.8127 | 1 | 0.000231 | 49.273332 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 288 |15.8678 | 1 | 0.000232 | 48.548550 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 289 |15.9229 | 1 | 0.000224 | 51.276163 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 290 |15.9780 | 1 | 0.000233 | 48.570122 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 291 |16.0331 | 1 | 0.000192 | 50.266936 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 292 |16.0882 | 1 | 0.000295 | 48.692444 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 293 |16.1433 | 1 | 0.000202 | 48.893389 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 294 |16.1983 | 1 | 0.000443 | 50.813372 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 295 |16.2534 | 1 | 0.000529 | 60.601432 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 296 |16.3085 | 1 | 0.000571 | 50.993108 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 297 |16.3636 | 1 | 0.000228 | 49.065097 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 298 |16.4187 | 1 | 0.000234 | 48.226984 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 299 |16.4738 | 1 | 0.000233 | 48.534926 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 300 |16.5289 | 1 | 0.000627 | 48.788802 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 301 |16.5840 | 1 | 0.000555 | 60.530895 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 302 |16.6391 | 1 | 0.000628 | 48.944436 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 303 |16.6942 | 1 | 0.000427 | 49.049230 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 304 |16.7493 | 1 | 0.000236 | 47.892993 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 305 |16.8044 | 1 | 0.000325 | 48.441506 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 306 |16.8595 | 1 | 0.000236 | 48.047404 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 307 |16.9146 | 1 | 0.000232 | 48.215284 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 308 |16.9697 | 1 | 0.000226 | 50.124858 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 309 |17.0248 | 1 | 0.000213 | 52.395805 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 310 |17.0799 | 1 | 0.000222 | 51.302776 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 311 |17.1350 | 1 | 0.000439 | 51.171773 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 312 |17.1901 | 1 | 0.000232 | 49.032143 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 313 |17.2452 | 1 | 0.000233 | 48.628400 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 314 |17.3003 | 1 | 0.000237 | 47.940311 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 315 |17.3554 | 1 | 0.000216 | 51.865635 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 316 |17.4105 | 1 | 0.000373 | 54.905208 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 317 |17.4656 | 1 | 0.000310 | 48.191338 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 318 |17.5207 | 1 | 0.000409 | 48.607705 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 319 |17.5758 | 1 | 0.000350 | 53.126807 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 320 |17.6309 | 1 | 0.000236 | 47.827493 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 321 |17.6860 | 1 | 0.000233 | 48.142512 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 322 |17.7410 | 1 | 0.000233 | 48.315800 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 323 |17.7961 | 1 | 0.000203 | 55.821999 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 324 |17.8512 | 1 | 0.000227 | 49.786573 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 325 |17.9063 | 1 | 0.000424 | 53.387859 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 326 |17.9614 | 1 | 0.000447 | 50.685506 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 327 |18.0165 | 1 | 0.000235 | 48.078798 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 328 |18.0716 | 1 | 0.000232 | 48.322922 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 329 |18.1267 | 1 | 0.000223 | 48.098878 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 330 |18.1818 | 1 | 0.000201 | 48.656743 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 331 |18.2369 | 1 | 0.000273 | 48.314217 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 332 |18.2920 | 1 | 0.000224 | 48.475068 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 333 |18.3471 | 1 | 0.000230 | 48.642246 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 334 |18.4022 | 1 | 0.000194 | 57.654265 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 335 |18.4573 | 1 | 0.000225 | 50.092508 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 336 |18.5124 | 1 | 0.000291 | 49.505815 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 337 |18.5675 | 1 | 0.000228 | 49.799143 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 338 |18.6226 | 1 | 0.000272 | 48.344175 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 339 |18.6777 | 1 | 0.000219 | 48.669889 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 340 |18.7328 | 1 | 0.000221 | 48.424671 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 341 |18.7879 | 1 | 0.000204 | 55.293948 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 342 |18.8430 | 1 | 0.000361 | 48.517776 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 343 |18.8981 | 1 | 0.000218 | 52.256841 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 344 |18.9532 | 1 | 0.000232 | 48.441100 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 345 |19.0083 | 1 | 0.000234 | 48.389977 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 346 |19.0634 | 1 | 0.000230 | 48.982534 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 347 |19.1185 | 1 | 0.000231 | 48.746604 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 348 |19.1736 | 1 | 0.000229 | 56.961390 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 349 |19.2287 | 1 | 0.000188 | 59.830517 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 350 |19.2837 | 1 | 0.000296 | 51.906979 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 351 |19.3388 | 1 | 0.000217 | 51.754677 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 352 |19.3939 | 1 | 0.000244 | 48.090983 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 353 |19.4490 | 1 | 0.000234 | 48.219884 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 354 |19.5041 | 1 | 0.000211 | 53.274204 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 355 |19.5592 | 1 | 0.000236 | 47.988238 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 356 |19.6143 | 1 | 0.000236 | 48.149312 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 357 |19.6694 | 1 | 0.000231 | 48.684465 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 358 |19.7245 | 1 | 0.000245 | 48.573752 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 359 |19.7796 | 1 | 0.000255 | 48.387298 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 360 |19.8347 | 1 | 0.000233 | 48.496933 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 361 |19.8898 | 1 | 0.000298 | 48.227055 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 362 |19.9449 | 1 | 0.000277 | 50.033512 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 363 |20.0000 | 1 | 0.000230 | 48.865905 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 364 |20.0551 | 1 | 0.000233 | 48.282476 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 365 |20.1102 | 1 | 0.000231 | 48.869913 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 366 |20.1653 | 1 | 0.000240 | 48.197653 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 367 |20.2204 | 1 | 0.000236 | 47.995288 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 368 |20.2755 | 1 | 0.000218 | 48.416211 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 369 |20.3306 | 1 | 0.000229 | 49.650859 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 370 |20.3857 | 1 | 0.000281 | 50.925766 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 371 |20.4408 | 1 | 0.000293 | 49.072721 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 372 |20.4959 | 1 | 0.000237 | 47.848434 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 373 |20.5510 | 1 | 0.000461 | 48.577324 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 374 |20.6061 | 1 | 0.000288 | 48.149040 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 375 |20.6612 | 1 | 0.000232 | 48.140545 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 376 |20.7163 | 1 | 0.000627 | 48.528365 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 377 |20.7713 | 1 | 0.000649 | 48.815492 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 378 |20.8264 | 1 | 0.000220 | 48.691541 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 379 |20.8815 | 1 | 0.000234 | 48.163874 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 380 |20.9366 | 1 | 0.000397 | 47.970319 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 381 |20.9917 | 1 | 0.000266 | 49.183593 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 382 |21.0468 | 1 | 0.000228 | 49.448159 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 383 |21.1019 | 1 | 0.000230 | 48.558385 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 384 |21.1570 | 1 | 0.000279 | 48.382636 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 385 |21.2121 | 1 | 0.000237 | 47.482272 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 386 |21.2672 | 1 | 0.000233 | 48.183362 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 387 |21.3223 | 1 | 0.000232 | 48.062299 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 388 |21.3774 | 1 | 0.000235 | 48.362948 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 389 |21.4325 | 1 | 0.000233 | 48.422455 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 390 |21.4876 | 1 | 0.000236 | 48.077169 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 391 |21.5427 | 1 | 0.000218 | 48.317787 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 392 |21.5978 | 1 | 0.000231 | 48.382455 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 393 |21.6529 | 1 | 0.000233 | 48.310206 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 394 |21.7080 | 1 | 0.000219 | 49.225511 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 395 |21.7631 | 1 | 0.000238 | 47.725182 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 396 |21.8182 | 1 | 0.000403 | 48.100903 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 397 |21.8733 | 1 | 0.000227 | 49.886770 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 398 |21.9284 | 1 | 0.000226 | 49.658201 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 399 |21.9835 | 1 | 0.000236 | 48.305113 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 400 |22.0386 | 1 | 0.000213 | 53.014337 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 401 |22.0937 | 1 | 0.000323 | 48.752367 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 402 |22.1488 | 1 | 0.000231 | 48.655110 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 403 |22.2039 | 1 | 0.000302 | 49.894845 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 404 |22.2590 | 1 | 0.000192 | 54.227928 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 405 |22.3140 | 1 | 0.000213 | 53.815790 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 406 |22.3691 | 1 | 0.000231 | 49.235416 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 407 |22.4242 | 1 | 0.000232 | 48.387503 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 408 |22.4793 | 1 | 0.000220 | 52.815450 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 409 |22.5344 | 1 | 0.000232 | 48.253041 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 410 |22.5895 | 1 | 0.000218 | 52.584628 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 411 |22.6446 | 1 | 0.000222 | 50.874607 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 412 |22.6997 | 1 | 0.000234 | 48.125687 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 413 |22.7548 | 1 | 0.000314 | 65.912785 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 414 |22.8099 | 1 | 0.000294 | 48.712801 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 415 |22.8650 | 1 | 0.000234 | 48.264049 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 416 |22.9201 | 1 | 0.000219 | 52.101943 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 417 |22.9752 | 1 | 0.000233 | 48.181907 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 418 |23.0303 | 1 | 0.000239 | 47.737343 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 419 |23.0854 | 1 | 0.000189 | 55.077763 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 420 |23.1405 | 1 | 0.000252 | 48.412007 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 421 |23.1956 | 1 | 0.000207 | 52.025445 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 422 |23.2507 | 1 | 0.000202 | 55.306192 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 423 |23.3058 | 1 | 0.000235 | 48.069254 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 424 |23.3609 | 1 | 0.000268 | 48.786326 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 425 |23.4160 | 1 | 0.000234 | 48.662434 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 426 |23.4711 | 1 | 0.000430 | 48.229208 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 427 |23.5262 | 1 | 0.000660 | 48.355178 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 428 |23.5813 | 1 | 0.000475 | 47.940589 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 429 |23.6364 | 1 | 0.000464 | 48.627356 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 430 |23.6915 | 1 | 0.000231 | 48.519762 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 431 |23.7466 | 1 | 0.000233 | 48.359547 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 432 |23.8017 | 1 | 0.000273 | 48.313746 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 433 |23.8567 | 1 | 0.000237 | 47.977213 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 434 |23.9118 | 1 | 0.000226 | 49.786822 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 435 |23.9669 | 1 | 0.000231 | 48.641675 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 436 |24.0220 | 1 | 0.000263 | 50.056180 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 437 |24.0771 | 1 | 0.000239 | 48.169318 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 438 |24.1322 | 1 | 0.000237 | 47.903935 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 439 |24.1873 | 1 | 0.000235 | 48.071315 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 440 |24.2424 | 1 | 0.000298 | 47.997039 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 441 |24.2975 | 1 | 0.000204 | 51.404477 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 442 |24.3526 | 1 | 0.000234 | 48.367934 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 443 |24.4077 | 1 | 0.000297 | 48.626223 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 444 |24.4628 | 1 | 0.000254 | 48.214660 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 445 |24.5179 | 1 | 0.000234 | 47.731147 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 446 |24.5730 | 1 | 0.000268 | 48.701918 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 447 |24.6281 | 1 | 0.000233 | 48.425494 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 448 |24.6832 | 1 | 0.000233 | 48.324203 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 449 |24.7383 | 1 | 0.000228 | 49.378323 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "| 450 |24.7934 | 1 | 0.000229 | 48.777402 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", - "\u001b[34m...Finished\u001b[0m\n", - "\u001b[36mFINISHED - Elapsed time = 23245.3692466 seconds\u001b[0m\n", - "\u001b[36mFINISHED - CPU process time = 92349.9106726 seconds\u001b[0m\n" - ] - } - ], - "source": [ - "sharpy_output = sharpy.sharpy_main.main(['', SimInfo.solvers['SHARPy']['route'] + SimInfo.solvers['SHARPy']['case'] + '.sharpy'])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Postprocessing" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Read the structural and aerodynamic information of the last time step" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "tstep = sharpy_output.structure.timestep_info[-1]\n", - "astep = sharpy_output.aero.timestep_info[-1]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Separate the structure into blades" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "# Define beams\n", - "ielem = 0\n", - "nblades = np.max(sharpy_output.structure.beam_number) + 1\n", - "nodes_blade = []\n", - "first_node = 0\n", - "for iblade in range(nblades):\n", - " nodes_blade.append(np.zeros((sharpy_output.structure.num_node,), dtype=bool))\n", - " while sharpy_output.structure.beam_number[ielem] <= iblade:\n", - " ielem += 1\n", - " if ielem == sharpy_output.structure.num_elem:\n", - " break\n", - " nodes_blade[iblade][first_node:sharpy_output.structure.connectivities[ielem-1,1]+1] = True\n", - " first_node = sharpy_output.structure.connectivities[ielem-1,1]+1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Compute the radial position of the nodes and initialise the rest of the variables" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "r = []\n", - "c = []\n", - "dr = []\n", - "forces = []\n", - "CN_drR = []\n", - "CTan_drR = []\n", - "CP_drR = []\n", - "nodes_num = []\n", - "for iblade in range(nblades):\n", - " forces.append(tstep.steady_applied_forces[nodes_blade[iblade]].copy())\n", - "\n", - " nodes_num.append(np.arange(0, sharpy_output.structure.num_node, 1)[nodes_blade[iblade]])\n", - "\n", - " r.append(np.linalg.norm(tstep.pos[nodes_blade[iblade], :], axis=1))\n", - " dr.append(np.zeros(np.sum(nodes_blade[iblade])))\n", - " dr[iblade][0] = 0.5*(r[iblade][1]-r[iblade][0])\n", - " dr[iblade][-1] = 0.5 * (r[iblade][-1] - r[iblade][-2])\n", - " for inode in range(1,len(r[iblade]) - 1):\n", - " dr[iblade][inode] = 0.5*(r[iblade][inode+1] - r[iblade][inode-1])\n", - "\n", - " CN_drR.append(np.zeros(len(r[iblade])))\n", - " c.append(np.zeros(len(r[iblade])))\n", - " CTan_drR.append(np.zeros(len(r[iblade])))\n", - " CP_drR.append(np.zeros(len(r[iblade])))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Transform the loads computed by SHARPy into out-of-plane and in-plane components" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "rho = sharpy_output.settings['StaticCoupledRBM']['aero_solver_settings']['rho']\n", - "uinf = sharpy_output.settings['StaticCoupledRBM']['aero_solver_settings']['velocity_field_input']['u_inf']\n", - "R = np.max(r[0])\n", - "Cp = 0\n", - "Ct = 0\n", - "\n", - "global_force_factor = 0.5 * rho * uinf** 2 * np.pi * R**2\n", - "global_power_factor = global_force_factor*uinf\n", - "for iblade in range(nblades):\n", - " for inode in range(len(r[iblade])):\n", - " forces[iblade][inode, 0] *= 0. # Discard the spanwise component\n", - "\n", - " node_global_index = nodes_num[iblade][inode]\n", - " ielem = sharpy_output.structure.node_master_elem[node_global_index, 0]\n", - " inode_in_elem = sharpy_output.structure.node_master_elem[node_global_index, 1]\n", - " CAB = algebra.crv2rotation(tstep.psi[ielem, inode_in_elem, :])\n", - "\n", - " c[iblade][inode] = sharpy_output.aero.aero_dict['chord'][ielem,inode_in_elem]\n", - "\n", - " forces_AFoR = np.dot(CAB, forces[iblade][inode, 0:3])\n", - "\n", - " CN_drR[iblade][inode] = forces_AFoR[2]/dr[iblade][inode]*R / global_force_factor\n", - " CTan_drR[iblade][inode] = np.linalg.norm(forces_AFoR[0:2])/dr[iblade][inode]*R / global_force_factor\n", - " CP_drR[iblade][inode] = np.linalg.norm(forces_AFoR[0:2])/dr[iblade][inode]*R * r[iblade][inode]*rotation_velocity / global_power_factor\n", - "\n", - " Cp += np.sum(CP_drR[iblade]*dr[iblade]/R)\n", - " Ct += np.sum(CN_drR[iblade]*dr[iblade]/R)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Results" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Plot of the loads along the blade" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "image/svg+xml": [ - "\n", - "\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "\n" - ], - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "fig, list_plots = plt.subplots(1, 2, figsize=(12, 3))\n", - "\n", - "list_plots[0].grid()\n", - "list_plots[0].set_xlabel(\"r/R [-]\")\n", - "list_plots[0].set_ylabel(\"CN/d(r/R) [-]\")\n", - "list_plots[0].plot(r[0]/R, CN_drR[0], '-', label='SHARPy')\n", - "list_plots[0].plot(of_rR, of_cNdrR, '-', label='OpenFAST')\n", - "list_plots[0].legend()\n", - "\n", - "list_plots[1].grid()\n", - "list_plots[1].set_xlabel(\"r/R [-]\")\n", - "list_plots[1].set_ylabel(\"CT/d(r/R) [-]\")\n", - "list_plots[1].plot(r[0]/R, CTan_drR[0], '-', label='SHARPy')\n", - "list_plots[1].plot(of_rR, of_cTdrR, '-', label='OpenFAST')\n", - "list_plots[1].legend()\n", - "\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Print the rotor thrust and power coefficients" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " OpenFAST SHARPy\n", - "Cp[-] 0.49 0.52\n", - "Ct[-] 0.70 0.70\n" - ] - } - ], - "source": [ - "print(\" OpenFAST SHARPy\")\n", - "print(\"Cp[-] %.2f %.2f\" % (of_cp, Cp))\n", - "print(\"Ct[-] %.2f %.2f\" % (of_ct, Ct))" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.5" - } - }, - "nbformat": 4, - "nbformat_minor": 4 + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Simulation NREL 5MW wind turbine" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%config InlineBackend.figure_format = 'svg'\n", + "from IPython.display import Image\n", + "url = 'https://raw.githubusercontent.com/ImperialCollegeLondon/sharpy/dev_doc/docs/source/content/example_notebooks/images/turbulence_no_legend.png'\n", + "Image(url=url, width=800)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this notebook the blade loads on the NREL-5MW reference wind turbine computed with SHARPy and OpenFAST will be compared. However, zero-drag airfoils have been used." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "OpenFAST: _https://openfast.readthedocs.io_" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "NREL-5MW: Jonkman, J.; Butterfield, S.; Musial, W. and Scott, G.. _Definition of a 5-MW Reference Wind Turbine for Offshore System Development_, Technical Report, NREL 2009" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Load the required packages:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Required packages\n", + "%matplotlib inline\n", + "import numpy as np\n", + "import os\n", + "import matplotlib.pyplot as plt\n", + "\n", + "# Required SHARPy modules\n", + "import sharpy.sharpy_main\n", + "import sharpy.utils.algebra as algebra\n", + "import sharpy.utils.generate_cases as gc\n", + "import sharpy.cases.templates.template_wt as template_wt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "These are the results from the OpenFAST simulation for comparison: out-of-plane `of_cNdrR` and in-plane `of_cTdrR` coefficients along the blade and thrust `of_ct` and power `of_cp` rotor coefficients" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "of_rR = np.array([0.20158356, 0.3127131, 0.40794048, 0.5984148, 0.6936519, 0.85238045, 0.899999, 0.95555407, 0.98729974, 1.0])\n", + "of_cNdrR = np.array([0.08621394, 0.14687876, 0.19345148, 0.2942731, 0.36003628, 0.43748564, 0.44762507, 0.38839236, 0.29782477, 0.0])\n", + "of_cTdrR = np.array([0.048268348, 0.051957503, 0.05304592, 0.052862607, 0.056001827, 0.0536646, 0.050112925, 0.038993906, 0.023664437, 0.0])\n", + "\n", + "of_ct = 0.69787693\n", + "of_cp = 0.48813498" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create SHARPy case" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Definition of parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# Mathematical constants\n", + "deg2rad = np.pi/180.\n", + "\n", + "# Case\n", + "case = 'rotor'\n", + "route = './'\n", + "\n", + "# Geometry discretization\n", + "chord_panels = np.array([8], dtype=int)\n", + "revs_in_wake = 5\n", + "\n", + "# Operation\n", + "rotation_velocity = 12.1*2*np.pi/60\n", + "pitch_deg = 0. #degrees\n", + "\n", + "# Wind\n", + "WSP = 12.\n", + "air_density = 1.225\n", + "\n", + "# Simulation\n", + "dphi = 4.*deg2rad\n", + "revs_to_simulate = 5" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Computation of associated parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "dt = dphi/rotation_velocity\n", + "time_steps = int(revs_to_simulate*2.*np.pi/dphi)\n", + "mstar = int(revs_in_wake*2.*np.pi/dphi)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Generation of the rotor geometry based on the information in the excel file" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[33mWARNING: The poisson cofficient is assumed equal to 0.3\u001b[0m\n", + "\u001b[33mWARNING: Cross-section area is used as shear area\u001b[0m\n", + "\u001b[33mWARNING: Using perpendicular axis theorem to compute the inertia around xB\u001b[0m\n", + "\u001b[33mWARNING: Replacing node 29 by node 0\u001b[0m\n", + "\u001b[33mWARNING: Replacing node 58 by node 0\u001b[0m\n" + ] + } + ], + "source": [ + "op_params = {'rotation_velocity': rotation_velocity,\n", + " 'pitch_deg': pitch_deg,\n", + " 'wsp': WSP,\n", + " 'dt': dt}\n", + "\n", + "geom_params = {'chord_panels':chord_panels,\n", + " 'tol_remove_points': 1e-8,\n", + " 'n_points_camber': 100,\n", + " 'm_distribution': 'uniform'}\n", + "\n", + "excel_description = {'excel_file_name': route + '/source/type02_db_NREL5MW_v02.xlsx',\n", + " 'excel_sheet_parameters': 'parameters',\n", + " 'excel_sheet_structural_blade': 'structural_blade',\n", + " 'excel_sheet_discretization_blade': 'discretization_blade',\n", + " 'excel_sheet_aero_blade': 'aero_blade',\n", + " 'excel_sheet_airfoil_info': 'airfoil_info',\n", + " 'excel_sheet_airfoil_chord': 'airfoil_coord'}\n", + "\n", + "options = {'camber_effect_on_twist': False,\n", + " 'user_defined_m_distribution_type': None,\n", + " 'include_polars': False}\n", + "\n", + "rotor = template_wt.rotor_from_excel_type03(op_params,\n", + " geom_params,\n", + " excel_description,\n", + " options)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Define simulation details. The steady simulation is faster than the dynamic simulation. However, the dynamic simulation includes wake self-induction and provides more accurate results." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "steady_simulation = False" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "SimInfo = gc.SimulationInformation()\n", + "SimInfo.set_default_values()\n", + "\n", + "if steady_simulation:\n", + " SimInfo.solvers['SHARPy']['flow'] = ['BeamLoader',\n", + " 'AerogridLoader',\n", + " 'StaticCoupledRBM',\n", + " 'BeamPlot',\n", + " 'AerogridPlot', \n", + " 'SaveData'] \n", + "else:\n", + " SimInfo.solvers['SHARPy']['flow'] = ['BeamLoader',\n", + " 'AerogridLoader',\n", + " 'StaticCoupledRBM',\n", + " 'DynamicCoupled']\n", + " \n", + "SimInfo.solvers['SHARPy']['case'] = case\n", + "SimInfo.solvers['SHARPy']['route'] = route\n", + "SimInfo.solvers['SHARPy']['write_log'] = True\n", + "SimInfo.set_variable_all_dicts('dt', dt)\n", + "SimInfo.set_variable_all_dicts('rho', air_density)\n", + "\n", + "SimInfo.solvers['SteadyVelocityField']['u_inf'] = WSP\n", + "SimInfo.solvers['SteadyVelocityField']['u_inf_direction'] = np.array([0., 0., 1.])\n", + "\n", + "SimInfo.solvers['BeamLoader']['unsteady'] = 'on'\n", + "\n", + "SimInfo.solvers['AerogridLoader']['unsteady'] = 'on'\n", + "SimInfo.solvers['AerogridLoader']['mstar'] = mstar\n", + "SimInfo.solvers['AerogridLoader']['freestream_dir'] = np.array([0.,0.,0.])\n", + "SimInfo.solvers['AerogridLoader']['wake_shape_generator'] = 'HelicoidalWake'\n", + "SimInfo.solvers['AerogridLoader']['wake_shape_generator_input'] = {'u_inf': WSP,\n", + " 'u_inf_direction': SimInfo.solvers['SteadyVelocityField']['u_inf_direction'],\n", + " 'rotation_velocity': rotation_velocity*np.array([0., 0., 1.]),\n", + " 'dt': dt,\n", + " 'dphi1': dphi,\n", + " 'ndphi1': mstar,\n", + " 'r': 1.,\n", + " 'dphimax': 10*deg2rad}\n", + " \n", + "SimInfo.solvers['StaticCoupledRBM']['structural_solver'] = 'RigidDynamicPrescribedStep'\n", + "SimInfo.solvers['StaticCoupledRBM']['structural_solver_settings'] = SimInfo.solvers['RigidDynamicPrescribedStep']\n", + "SimInfo.solvers['StaticCoupledRBM']['aero_solver'] = 'StaticUvlm'\n", + "SimInfo.solvers['StaticCoupledRBM']['aero_solver_settings'] = SimInfo.solvers['StaticUvlm']\n", + "\n", + "SimInfo.solvers['StaticCoupledRBM']['tolerance'] = 1e-8\n", + "SimInfo.solvers['StaticCoupledRBM']['n_load_steps'] = 0\n", + "SimInfo.solvers['StaticCoupledRBM']['relaxation_factor'] = 0.\n", + "\n", + "SimInfo.solvers['StaticUvlm']['num_cores'] = 8\n", + "SimInfo.solvers['StaticUvlm']['velocity_field_generator'] = 'SteadyVelocityField'\n", + "SimInfo.solvers['StaticUvlm']['velocity_field_input'] = SimInfo.solvers['SteadyVelocityField']\n", + "\n", + "SimInfo.solvers['SaveData']['compress_float'] = True\n", + " \n", + "# Only used for steady_simulation = False\n", + "SimInfo.solvers['StepUvlm']['convection_scheme'] = 3\n", + "SimInfo.solvers['StepUvlm']['num_cores'] = 8\n", + "SimInfo.solvers['StepUvlm']['velocity_field_generator'] = 'SteadyVelocityField'\n", + "SimInfo.solvers['StepUvlm']['velocity_field_input'] = SimInfo.solvers['SteadyVelocityField']\n", + "\n", + "SimInfo.solvers['DynamicCoupled']['structural_solver'] = 'RigidDynamicPrescribedStep'\n", + "SimInfo.solvers['DynamicCoupled']['structural_solver_settings'] = SimInfo.solvers['RigidDynamicPrescribedStep']\n", + "SimInfo.solvers['DynamicCoupled']['aero_solver'] = 'StepUvlm'\n", + "SimInfo.solvers['DynamicCoupled']['aero_solver_settings'] = SimInfo.solvers['StepUvlm']\n", + "SimInfo.solvers['DynamicCoupled']['postprocessors'] = ['BeamPlot', 'AerogridPlot', 'Cleanup', 'SaveData']\n", + "SimInfo.solvers['DynamicCoupled']['postprocessors_settings'] = {'BeamPlot': SimInfo.solvers['BeamPlot'],\n", + " 'AerogridPlot': SimInfo.solvers['AerogridPlot'],\n", + " 'Cleanup': SimInfo.solvers['Cleanup'],\n", + " 'SaveData': SimInfo.solvers['SaveData']}\n", + "SimInfo.solvers['DynamicCoupled']['minimum_steps'] = 0\n", + "SimInfo.solvers['DynamicCoupled']['include_unsteady_force_contribution'] = True\n", + "SimInfo.solvers['DynamicCoupled']['relaxation_factor'] = 0.\n", + "SimInfo.solvers['DynamicCoupled']['final_relaxation_factor'] = 0.\n", + "SimInfo.solvers['DynamicCoupled']['dynamic_relaxation'] = False\n", + "SimInfo.solvers['DynamicCoupled']['relaxation_steps'] = 0\n", + "\n", + "# Define dynamic simulation (used regardless the value of \"steady_simulation\" variable)\n", + "SimInfo.define_num_steps(time_steps)\n", + "SimInfo.with_forced_vel = True\n", + "SimInfo.for_vel = np.zeros((time_steps,6), dtype=float)\n", + "SimInfo.for_vel[:,5] = rotation_velocity\n", + "SimInfo.for_acc = np.zeros((time_steps,6), dtype=float)\n", + "SimInfo.with_dynamic_forces = True\n", + "SimInfo.dynamic_forces = np.zeros((time_steps,rotor.StructuralInformation.num_node,6), dtype=float)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Generate simulation files" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "gc.clean_test_files(SimInfo.solvers['SHARPy']['route'], SimInfo.solvers['SHARPy']['case'])\n", + "rotor.generate_h5_files(SimInfo.solvers['SHARPy']['route'], SimInfo.solvers['SHARPy']['case'])\n", + "SimInfo.generate_solver_file()\n", + "SimInfo.generate_dyn_file(time_steps)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Run SHARPy case" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--------------------------------------------------------------------------------\u001b[0m\n", + " ###### ## ## ### ######## ######## ## ##\u001b[0m\n", + " ## ## ## ## ## ## ## ## ## ## ## ##\u001b[0m\n", + " ## ## ## ## ## ## ## ## ## ####\u001b[0m\n", + " ###### ######### ## ## ######## ######## ##\u001b[0m\n", + " ## ## ## ######### ## ## ## ##\u001b[0m\n", + " ## ## ## ## ## ## ## ## ## ##\u001b[0m\n", + " ###### ## ## ## ## ## ## ## ##\u001b[0m\n", + "--------------------------------------------------------------------------------\u001b[0m\n", + "Aeroelastics Lab, Aeronautics Department.\u001b[0m\n", + " Copyright (c), Imperial College London.\u001b[0m\n", + " All rights reserved.\u001b[0m\n", + " License available at https://github.com/imperialcollegelondon/sharpy\u001b[0m\n", + "\u001b[36mRunning SHARPy from /home/am3717/code/sharpy/docs/source/content/example_notebooks\u001b[0m\n", + "\u001b[36mSHARPy being run is in /home/am3717/code/sharpy\u001b[0m\n", + "\u001b[36mThe branch being run is dev_setting_error\u001b[0m\n", + "\u001b[36mThe version and commit hash are: v1.2.1-357-gbb7c4b4-bb7c4b4\u001b[0m\n", + "SHARPy output folder set\u001b[0m\n", + "\u001b[34m\t./output//rotor/\u001b[0m\n", + "\u001b[36mGenerating an instance of BeamLoader\u001b[0m\n", + "\u001b[36mGenerating an instance of AerogridLoader\u001b[0m\n", + "Variable shear_direction has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: [1. 0. 0.]\u001b[0m\n", + "Variable shear_exp has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 0.0\u001b[0m\n", + "Variable h_ref has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1.0\u001b[0m\n", + "Variable h_corr has no assigned value in the settings file.\u001b[0m\n", + "\u001b[34m will default to the value: 1.0\u001b[0m\n", + "\u001b[34mThe aerodynamic grid contains 3 surfaces\u001b[0m\n", + "\u001b[34m Surface 0, M=8, N=26\u001b[0m\n", + " Wake 0, M=450, N=26\u001b[0m\n", + "\u001b[34m Surface 1, M=8, N=26\u001b[0m\n", + " Wake 1, M=450, N=26\u001b[0m\n", + "\u001b[34m Surface 2, M=8, N=26\u001b[0m\n", + " Wake 2, M=450, N=26\u001b[0m\n", + " In total: 624 bound panels\u001b[0m\n", + " In total: 35100 wake panels\u001b[0m\n", + " Total number of panels = 35724\u001b[0m\n", + "\u001b[36mGenerating an instance of StaticCoupledRBM\u001b[0m\n", + "\u001b[36mGenerating an instance of RigidDynamicPrescribedStep\u001b[0m\n", + "\u001b[36mGenerating an instance of StaticUvlm\u001b[0m\n", + "i_step: 0, i_iter: 0\u001b[0m\n", + "Resultant forces and moments: (array([2.54814836e-08, 5.82946313e-09, 2.09459948e+04]), array([1.53411975e-07, 3.24440634e-08, 5.96558361e+06]))\u001b[0m\n", + "\u001b[36mGenerating an instance of DynamicCoupled\u001b[0m\n", + "\u001b[36mGenerating an instance of RigidDynamicPrescribedStep\u001b[0m\n", + "\u001b[36mGenerating an instance of StepUvlm\u001b[0m\n", + "\u001b[36mGenerating an instance of BeamPlot\u001b[0m\n", + "\u001b[36mGenerating an instance of AerogridPlot\u001b[0m\n", + "\u001b[36mGenerating an instance of Cleanup\u001b[0m\n", + "\u001b[36mGenerating an instance of SaveData\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\n", + "\u001b[0m\n", + "|=======|========|======|==============|==============|==============|==============|==============|\u001b[0m\n", + "| ts | t | iter | struc ratio | iter time | residual vel | FoR_vel(x) | FoR_vel(z) |\u001b[0m\n", + "|=======|========|======|==============|==============|==============|==============|==============|\u001b[0m\n", + "| 1 | 0.0551 | 1 | 0.000251 | 56.804234 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 2 | 0.1102 | 1 | 0.000177 | 60.232129 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 3 | 0.1653 | 1 | 0.000234 | 59.442611 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 4 | 0.2204 | 1 | 0.000290 | 64.383652 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 5 | 0.2755 | 1 | 0.000275 | 52.774436 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 6 | 0.3306 | 1 | 0.000293 | 48.828457 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 7 | 0.3857 | 1 | 0.000316 | 49.762675 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 8 | 0.4408 | 1 | 0.000227 | 49.627842 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 9 | 0.4959 | 1 | 0.000193 | 58.254770 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 10 | 0.5510 | 1 | 0.000233 | 49.347327 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 11 | 0.6061 | 1 | 0.000222 | 51.950339 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 12 | 0.6612 | 1 | 0.000195 | 58.489715 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 13 | 0.7163 | 1 | 0.000190 | 55.497720 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 14 | 0.7713 | 1 | 0.000287 | 49.864559 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 15 | 0.8264 | 1 | 0.000198 | 56.915311 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 16 | 0.8815 | 1 | 0.000273 | 52.475129 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 17 | 0.9366 | 1 | 0.000212 | 53.472742 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 18 | 0.9917 | 1 | 0.000220 | 51.679432 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 19 | 1.0468 | 1 | 0.000230 | 49.468328 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 20 | 1.1019 | 1 | 0.000233 | 48.806374 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 21 | 1.1570 | 1 | 0.000214 | 51.507987 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 22 | 1.2121 | 1 | 0.000152 | 75.114590 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 23 | 1.2672 | 1 | 0.000178 | 63.764692 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 24 | 1.3223 | 1 | 0.000232 | 48.927093 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 25 | 1.3774 | 1 | 0.000205 | 55.065928 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 26 | 1.4325 | 1 | 0.000209 | 53.747631 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 27 | 1.4876 | 1 | 0.000215 | 65.487096 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 28 | 1.5427 | 1 | 0.000366 | 52.159906 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 29 | 1.5978 | 1 | 0.000286 | 50.405248 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 30 | 1.6529 | 1 | 0.000243 | 53.903698 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 31 | 1.7080 | 1 | 0.000189 | 55.762230 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 32 | 1.7631 | 1 | 0.000230 | 49.679477 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 33 | 1.8182 | 1 | 0.000267 | 49.140923 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 34 | 1.8733 | 1 | 0.000217 | 51.686471 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 35 | 1.9284 | 1 | 0.000227 | 49.924635 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 36 | 1.9835 | 1 | 0.000290 | 49.295199 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 37 | 2.0386 | 1 | 0.000231 | 49.096186 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 38 | 2.0937 | 1 | 0.000267 | 53.824351 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 39 | 2.1488 | 1 | 0.000274 | 50.208116 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 40 | 2.2039 | 1 | 0.000164 | 68.475332 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 41 | 2.2590 | 1 | 0.000219 | 51.476537 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 42 | 2.3140 | 1 | 0.000258 | 50.780835 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 43 | 2.3691 | 1 | 0.000205 | 54.508414 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 44 | 2.4242 | 1 | 0.000229 | 49.643120 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 45 | 2.4793 | 1 | 0.000423 | 49.510440 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 46 | 2.5344 | 1 | 0.000229 | 49.386177 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 47 | 2.5895 | 1 | 0.000224 | 51.096846 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 48 | 2.6446 | 1 | 0.000429 | 67.054611 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 49 | 2.6997 | 1 | 0.000674 | 49.034094 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 50 | 2.7548 | 1 | 0.000473 | 67.770922 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 51 | 2.8099 | 1 | 0.000623 | 52.057162 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 52 | 2.8650 | 1 | 0.000197 | 57.338530 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 53 | 2.9201 | 1 | 0.000211 | 53.323214 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 54 | 2.9752 | 1 | 0.000228 | 49.446313 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 55 | 3.0303 | 1 | 0.000226 | 50.531894 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 56 | 3.0854 | 1 | 0.000216 | 49.277498 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 57 | 3.1405 | 1 | 0.000229 | 49.378688 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 58 | 3.1956 | 1 | 0.000456 | 49.096131 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 59 | 3.2507 | 1 | 0.000638 | 49.407950 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 60 | 3.3058 | 1 | 0.000644 | 49.355487 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 61 | 3.3609 | 1 | 0.000429 | 52.381709 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 62 | 3.4160 | 1 | 0.000227 | 50.244867 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 63 | 3.4711 | 1 | 0.000226 | 50.090238 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 64 | 3.5262 | 1 | 0.000221 | 50.890983 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 65 | 3.5813 | 1 | 0.000292 | 52.582257 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 66 | 3.6364 | 1 | 0.000229 | 48.970770 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 67 | 3.6915 | 1 | 0.000211 | 53.921150 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 68 | 3.7466 | 1 | 0.000276 | 49.332785 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 69 | 3.8017 | 1 | 0.000405 | 49.861912 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 70 | 3.8567 | 1 | 0.000678 | 50.109032 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 71 | 3.9118 | 1 | 0.000627 | 50.052867 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 72 | 3.9669 | 1 | 0.000231 | 49.125818 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 73 | 4.0220 | 1 | 0.000215 | 52.475354 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 74 | 4.0771 | 1 | 0.000302 | 51.044905 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 75 | 4.1322 | 1 | 0.000232 | 49.154350 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 76 | 4.1873 | 1 | 0.000263 | 49.587372 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 77 | 4.2424 | 1 | 0.000382 | 48.959923 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 78 | 4.2975 | 1 | 0.000207 | 54.498279 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 79 | 4.3526 | 1 | 0.000209 | 55.866552 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 80 | 4.4077 | 1 | 0.000222 | 50.659905 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 81 | 4.4628 | 1 | 0.000232 | 49.229321 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 82 | 4.5179 | 1 | 0.000230 | 49.197802 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 83 | 4.5730 | 1 | 0.000244 | 58.532026 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 84 | 4.6281 | 1 | 0.000229 | 49.010943 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 85 | 4.6832 | 1 | 0.000236 | 49.181471 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 86 | 4.7383 | 1 | 0.000234 | 50.404876 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 87 | 4.7934 | 1 | 0.000223 | 50.863604 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 88 | 4.8485 | 1 | 0.000232 | 49.386036 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 89 | 4.9036 | 1 | 0.000292 | 48.966426 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 90 | 4.9587 | 1 | 0.000204 | 55.176201 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 91 | 5.0138 | 1 | 0.000381 | 49.363449 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 92 | 5.0689 | 1 | 0.000254 | 51.436284 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 93 | 5.1240 | 1 | 0.000427 | 54.203413 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 94 | 5.1791 | 1 | 0.000278 | 51.088371 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 95 | 5.2342 | 1 | 0.000221 | 51.267494 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 96 | 5.2893 | 1 | 0.000220 | 51.246126 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 97 | 5.3444 | 1 | 0.000261 | 50.044367 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 98 | 5.3994 | 1 | 0.000237 | 48.928531 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 99 | 5.4545 | 1 | 0.000230 | 48.869230 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 100 | 5.5096 | 1 | 0.000229 | 49.124024 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 101 | 5.5647 | 1 | 0.000230 | 49.170615 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 102 | 5.6198 | 1 | 0.000227 | 51.072559 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 103 | 5.6749 | 1 | 0.000230 | 49.106752 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 104 | 5.7300 | 1 | 0.000230 | 49.157374 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 105 | 5.7851 | 1 | 0.000230 | 48.729726 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 106 | 5.8402 | 1 | 0.000230 | 49.307981 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 107 | 5.8953 | 1 | 0.000232 | 49.303297 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 108 | 5.9504 | 1 | 0.000228 | 49.388923 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 109 | 6.0055 | 1 | 0.000251 | 49.505395 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 110 | 6.0606 | 1 | 0.000294 | 49.671154 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 111 | 6.1157 | 1 | 0.000637 | 49.106910 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 112 | 6.1708 | 1 | 0.000668 | 49.600747 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 113 | 6.2259 | 1 | 0.000694 | 48.788273 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 114 | 6.2810 | 1 | 0.000605 | 49.259812 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 115 | 6.3361 | 1 | 0.000235 | 49.096078 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 116 | 6.3912 | 1 | 0.000235 | 48.627974 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 117 | 6.4463 | 1 | 0.000231 | 48.687230 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 118 | 6.5014 | 1 | 0.000261 | 50.182136 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 119 | 6.5565 | 1 | 0.000220 | 51.480792 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 120 | 6.6116 | 1 | 0.000212 | 52.759361 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 121 | 6.6667 | 1 | 0.000234 | 61.395446 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 122 | 6.7218 | 1 | 0.000196 | 57.312678 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 123 | 6.7769 | 1 | 0.000196 | 57.288595 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 124 | 6.8320 | 1 | 0.000219 | 52.597533 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 125 | 6.8871 | 1 | 0.000279 | 51.215139 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 126 | 6.9421 | 1 | 0.000232 | 48.835388 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 127 | 6.9972 | 1 | 0.000228 | 49.442145 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 128 | 7.0523 | 1 | 0.000232 | 49.943434 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 129 | 7.1074 | 1 | 0.000265 | 48.943739 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 130 | 7.1625 | 1 | 0.000233 | 48.552538 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 131 | 7.2176 | 1 | 0.000235 | 48.576140 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 132 | 7.2727 | 1 | 0.000227 | 50.729522 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 133 | 7.3278 | 1 | 0.000199 | 57.042671 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 134 | 7.3829 | 1 | 0.000229 | 49.647710 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 135 | 7.4380 | 1 | 0.000377 | 53.191903 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 136 | 7.4931 | 1 | 0.000666 | 50.832518 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 137 | 7.5482 | 1 | 0.000560 | 52.129724 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 138 | 7.6033 | 1 | 0.000593 | 48.825715 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 139 | 7.6584 | 1 | 0.000231 | 49.220850 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 140 | 7.7135 | 1 | 0.000347 | 64.642874 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 141 | 7.7686 | 1 | 0.000615 | 54.791703 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 142 | 7.8237 | 1 | 0.000557 | 55.332506 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 143 | 7.8788 | 1 | 0.000649 | 51.958248 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 144 | 7.9339 | 1 | 0.000431 | 52.224418 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 145 | 7.9890 | 1 | 0.000532 | 60.207598 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 146 | 8.0441 | 1 | 0.000267 | 48.557877 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 147 | 8.0992 | 1 | 0.000283 | 50.720948 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 148 | 8.1543 | 1 | 0.000264 | 54.107664 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 149 | 8.2094 | 1 | 0.000225 | 49.998135 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 150 | 8.2645 | 1 | 0.000297 | 48.723537 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 151 | 8.3196 | 1 | 0.000229 | 48.991915 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 152 | 8.3747 | 1 | 0.000223 | 50.525580 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 153 | 8.4298 | 1 | 0.000372 | 48.880427 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 154 | 8.4848 | 1 | 0.000232 | 48.851891 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 155 | 8.5399 | 1 | 0.000231 | 48.731096 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 156 | 8.5950 | 1 | 0.000209 | 53.669843 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 157 | 8.6501 | 1 | 0.000203 | 55.525421 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 158 | 8.7052 | 1 | 0.000216 | 48.727216 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 159 | 8.7603 | 1 | 0.000240 | 52.981239 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 160 | 8.8154 | 1 | 0.000294 | 50.516618 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 161 | 8.8705 | 1 | 0.000198 | 56.957355 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 162 | 8.9256 | 1 | 0.000268 | 48.780843 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 163 | 8.9807 | 1 | 0.000228 | 49.216988 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 164 | 9.0358 | 1 | 0.000228 | 49.020854 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 165 | 9.0909 | 1 | 0.000236 | 48.516192 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 166 | 9.1460 | 1 | 0.000275 | 48.936249 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 167 | 9.2011 | 1 | 0.000229 | 48.984558 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 168 | 9.2562 | 1 | 0.000228 | 50.011495 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 169 | 9.3113 | 1 | 0.000230 | 49.079509 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 170 | 9.3664 | 1 | 0.000232 | 48.940832 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 171 | 9.4215 | 1 | 0.000266 | 48.539668 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 172 | 9.4766 | 1 | 0.000329 | 48.707876 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 173 | 9.5317 | 1 | 0.000344 | 49.214713 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 174 | 9.5868 | 1 | 0.000221 | 50.762513 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 175 | 9.6419 | 1 | 0.000336 | 55.302099 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 176 | 9.6970 | 1 | 0.000217 | 48.736059 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 177 | 9.7521 | 1 | 0.000231 | 48.750098 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 178 | 9.8072 | 1 | 0.000229 | 48.794680 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 179 | 9.8623 | 1 | 0.000225 | 50.519301 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 180 | 9.9174 | 1 | 0.000225 | 49.930789 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 181 | 9.9725 | 1 | 0.000265 | 50.077232 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 182 |10.0275 | 1 | 0.000285 | 50.327260 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 183 |10.0826 | 1 | 0.000319 | 50.215326 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 184 |10.1377 | 1 | 0.000288 | 48.350528 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 185 |10.1928 | 1 | 0.000233 | 48.372314 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 186 |10.2479 | 1 | 0.000232 | 49.104808 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 187 |10.3030 | 1 | 0.000292 | 48.972235 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 188 |10.3581 | 1 | 0.000655 | 48.972110 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 189 |10.4132 | 1 | 0.000233 | 48.457135 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 190 |10.4683 | 1 | 0.000229 | 49.120724 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 191 |10.5234 | 1 | 0.000222 | 51.106254 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 192 |10.5785 | 1 | 0.000232 | 48.794933 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 193 |10.6336 | 1 | 0.000292 | 49.537670 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 194 |10.6887 | 1 | 0.000230 | 49.236260 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 195 |10.7438 | 1 | 0.000603 | 52.529460 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 196 |10.7989 | 1 | 0.000697 | 48.383116 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 197 |10.8540 | 1 | 0.000347 | 48.288813 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 198 |10.9091 | 1 | 0.000227 | 49.550065 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 199 |10.9642 | 1 | 0.000216 | 48.675859 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 200 |11.0193 | 1 | 0.000231 | 49.152487 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 201 |11.0744 | 1 | 0.000235 | 48.778197 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 202 |11.1295 | 1 | 0.000373 | 57.791454 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 203 |11.1846 | 1 | 0.000582 | 57.753718 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 204 |11.2397 | 1 | 0.000451 | 48.675944 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 205 |11.2948 | 1 | 0.000235 | 48.274112 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 206 |11.3499 | 1 | 0.000232 | 48.917871 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 207 |11.4050 | 1 | 0.000293 | 49.482251 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 208 |11.4601 | 1 | 0.000297 | 48.304309 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 209 |11.5152 | 1 | 0.000263 | 49.162448 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 210 |11.5702 | 1 | 0.000227 | 50.845835 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 211 |11.6253 | 1 | 0.000232 | 48.809305 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 212 |11.6804 | 1 | 0.000293 | 48.950330 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 213 |11.7355 | 1 | 0.000298 | 48.481146 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 214 |11.7906 | 1 | 0.000234 | 48.222179 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 215 |11.8457 | 1 | 0.000225 | 48.228569 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 216 |11.9008 | 1 | 0.000218 | 48.173668 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 217 |11.9559 | 1 | 0.000230 | 48.496389 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 218 |12.0110 | 1 | 0.000212 | 53.240908 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 219 |12.0661 | 1 | 0.000224 | 51.372105 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 220 |12.1212 | 1 | 0.000419 | 49.490550 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 221 |12.1763 | 1 | 0.000459 | 49.025501 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 222 |12.2314 | 1 | 0.000210 | 53.157816 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 223 |12.2865 | 1 | 0.000233 | 48.099241 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 224 |12.3416 | 1 | 0.000241 | 59.450060 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 225 |12.3967 | 1 | 0.000224 | 50.153783 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 226 |12.4518 | 1 | 0.000169 | 67.550282 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 227 |12.5069 | 1 | 0.000635 | 51.062920 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 228 |12.5620 | 1 | 0.000233 | 48.498621 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 229 |12.6171 | 1 | 0.000647 | 48.837410 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 230 |12.6722 | 1 | 0.000685 | 48.524438 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 231 |12.7273 | 1 | 0.000645 | 47.972726 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 232 |12.7824 | 1 | 0.000698 | 48.332650 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 233 |12.8375 | 1 | 0.000291 | 49.344210 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 234 |12.8926 | 1 | 0.000231 | 48.621207 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 235 |12.9477 | 1 | 0.000211 | 53.099352 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 236 |13.0028 | 1 | 0.000231 | 48.779174 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 237 |13.0579 | 1 | 0.000221 | 52.508748 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 238 |13.1129 | 1 | 0.000197 | 57.071891 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 239 |13.1680 | 1 | 0.000232 | 48.635785 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 240 |13.2231 | 1 | 0.000228 | 49.653808 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 241 |13.2782 | 1 | 0.000232 | 49.880283 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 242 |13.3333 | 1 | 0.000219 | 50.989864 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 243 |13.3884 | 1 | 0.000218 | 51.562733 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 244 |13.4435 | 1 | 0.000175 | 60.525012 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 245 |13.4986 | 1 | 0.000208 | 53.714735 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 246 |13.5537 | 1 | 0.000188 | 60.430187 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 247 |13.6088 | 1 | 0.000580 | 55.369422 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 248 |13.6639 | 1 | 0.000376 | 52.945929 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 249 |13.7190 | 1 | 0.000221 | 50.621281 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 250 |13.7741 | 1 | 0.000222 | 51.930545 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 251 |13.8292 | 1 | 0.000203 | 55.750479 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 252 |13.8843 | 1 | 0.000228 | 49.736165 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 253 |13.9394 | 1 | 0.000239 | 48.733683 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 254 |13.9945 | 1 | 0.000228 | 48.860423 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 255 |14.0496 | 1 | 0.000234 | 48.158656 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 256 |14.1047 | 1 | 0.000222 | 48.995898 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 257 |14.1598 | 1 | 0.000181 | 61.699569 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 258 |14.2149 | 1 | 0.000217 | 51.853006 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 259 |14.2700 | 1 | 0.000231 | 48.370357 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 260 |14.3251 | 1 | 0.000231 | 48.536412 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 261 |14.3802 | 1 | 0.000231 | 48.706123 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 262 |14.4353 | 1 | 0.000321 | 48.889459 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 263 |14.4904 | 1 | 0.000232 | 48.413389 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 264 |14.5455 | 1 | 0.000183 | 61.637297 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 265 |14.6006 | 1 | 0.000266 | 51.996729 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 266 |14.6556 | 1 | 0.000440 | 51.124606 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 267 |14.7107 | 1 | 0.000206 | 54.482224 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 268 |14.7658 | 1 | 0.000227 | 49.396567 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 269 |14.8209 | 1 | 0.000216 | 48.399076 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 270 |14.8760 | 1 | 0.000225 | 49.932379 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 271 |14.9311 | 1 | 0.000229 | 48.837313 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 272 |14.9862 | 1 | 0.000234 | 48.303012 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 273 |15.0413 | 1 | 0.000297 | 48.557886 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 274 |15.0964 | 1 | 0.000232 | 48.462573 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 275 |15.1515 | 1 | 0.000235 | 48.074680 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 276 |15.2066 | 1 | 0.000237 | 47.978437 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 277 |15.2617 | 1 | 0.000218 | 48.799704 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 278 |15.3168 | 1 | 0.000180 | 61.907709 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 279 |15.3719 | 1 | 0.000223 | 50.256748 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 280 |15.4270 | 1 | 0.000229 | 49.243923 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 281 |15.4821 | 1 | 0.000453 | 49.924487 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 282 |15.5372 | 1 | 0.000350 | 49.501563 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 283 |15.5923 | 1 | 0.000234 | 48.333380 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 284 |15.6474 | 1 | 0.000223 | 50.163273 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 285 |15.7025 | 1 | 0.000225 | 50.068699 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 286 |15.7576 | 1 | 0.000229 | 48.458516 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 287 |15.8127 | 1 | 0.000231 | 49.273332 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 288 |15.8678 | 1 | 0.000232 | 48.548550 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 289 |15.9229 | 1 | 0.000224 | 51.276163 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 290 |15.9780 | 1 | 0.000233 | 48.570122 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 291 |16.0331 | 1 | 0.000192 | 50.266936 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 292 |16.0882 | 1 | 0.000295 | 48.692444 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 293 |16.1433 | 1 | 0.000202 | 48.893389 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 294 |16.1983 | 1 | 0.000443 | 50.813372 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 295 |16.2534 | 1 | 0.000529 | 60.601432 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 296 |16.3085 | 1 | 0.000571 | 50.993108 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 297 |16.3636 | 1 | 0.000228 | 49.065097 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 298 |16.4187 | 1 | 0.000234 | 48.226984 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 299 |16.4738 | 1 | 0.000233 | 48.534926 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 300 |16.5289 | 1 | 0.000627 | 48.788802 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 301 |16.5840 | 1 | 0.000555 | 60.530895 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 302 |16.6391 | 1 | 0.000628 | 48.944436 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 303 |16.6942 | 1 | 0.000427 | 49.049230 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 304 |16.7493 | 1 | 0.000236 | 47.892993 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 305 |16.8044 | 1 | 0.000325 | 48.441506 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 306 |16.8595 | 1 | 0.000236 | 48.047404 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 307 |16.9146 | 1 | 0.000232 | 48.215284 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 308 |16.9697 | 1 | 0.000226 | 50.124858 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 309 |17.0248 | 1 | 0.000213 | 52.395805 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 310 |17.0799 | 1 | 0.000222 | 51.302776 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 311 |17.1350 | 1 | 0.000439 | 51.171773 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 312 |17.1901 | 1 | 0.000232 | 49.032143 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 313 |17.2452 | 1 | 0.000233 | 48.628400 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 314 |17.3003 | 1 | 0.000237 | 47.940311 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 315 |17.3554 | 1 | 0.000216 | 51.865635 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 316 |17.4105 | 1 | 0.000373 | 54.905208 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 317 |17.4656 | 1 | 0.000310 | 48.191338 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 318 |17.5207 | 1 | 0.000409 | 48.607705 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 319 |17.5758 | 1 | 0.000350 | 53.126807 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 320 |17.6309 | 1 | 0.000236 | 47.827493 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 321 |17.6860 | 1 | 0.000233 | 48.142512 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 322 |17.7410 | 1 | 0.000233 | 48.315800 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 323 |17.7961 | 1 | 0.000203 | 55.821999 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 324 |17.8512 | 1 | 0.000227 | 49.786573 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 325 |17.9063 | 1 | 0.000424 | 53.387859 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 326 |17.9614 | 1 | 0.000447 | 50.685506 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 327 |18.0165 | 1 | 0.000235 | 48.078798 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 328 |18.0716 | 1 | 0.000232 | 48.322922 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 329 |18.1267 | 1 | 0.000223 | 48.098878 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 330 |18.1818 | 1 | 0.000201 | 48.656743 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 331 |18.2369 | 1 | 0.000273 | 48.314217 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 332 |18.2920 | 1 | 0.000224 | 48.475068 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 333 |18.3471 | 1 | 0.000230 | 48.642246 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 334 |18.4022 | 1 | 0.000194 | 57.654265 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 335 |18.4573 | 1 | 0.000225 | 50.092508 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 336 |18.5124 | 1 | 0.000291 | 49.505815 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 337 |18.5675 | 1 | 0.000228 | 49.799143 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 338 |18.6226 | 1 | 0.000272 | 48.344175 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 339 |18.6777 | 1 | 0.000219 | 48.669889 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 340 |18.7328 | 1 | 0.000221 | 48.424671 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 341 |18.7879 | 1 | 0.000204 | 55.293948 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 342 |18.8430 | 1 | 0.000361 | 48.517776 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 343 |18.8981 | 1 | 0.000218 | 52.256841 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 344 |18.9532 | 1 | 0.000232 | 48.441100 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 345 |19.0083 | 1 | 0.000234 | 48.389977 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 346 |19.0634 | 1 | 0.000230 | 48.982534 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 347 |19.1185 | 1 | 0.000231 | 48.746604 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 348 |19.1736 | 1 | 0.000229 | 56.961390 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 349 |19.2287 | 1 | 0.000188 | 59.830517 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 350 |19.2837 | 1 | 0.000296 | 51.906979 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 351 |19.3388 | 1 | 0.000217 | 51.754677 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 352 |19.3939 | 1 | 0.000244 | 48.090983 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 353 |19.4490 | 1 | 0.000234 | 48.219884 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 354 |19.5041 | 1 | 0.000211 | 53.274204 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 355 |19.5592 | 1 | 0.000236 | 47.988238 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 356 |19.6143 | 1 | 0.000236 | 48.149312 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 357 |19.6694 | 1 | 0.000231 | 48.684465 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 358 |19.7245 | 1 | 0.000245 | 48.573752 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 359 |19.7796 | 1 | 0.000255 | 48.387298 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 360 |19.8347 | 1 | 0.000233 | 48.496933 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 361 |19.8898 | 1 | 0.000298 | 48.227055 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 362 |19.9449 | 1 | 0.000277 | 50.033512 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 363 |20.0000 | 1 | 0.000230 | 48.865905 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 364 |20.0551 | 1 | 0.000233 | 48.282476 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 365 |20.1102 | 1 | 0.000231 | 48.869913 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 366 |20.1653 | 1 | 0.000240 | 48.197653 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 367 |20.2204 | 1 | 0.000236 | 47.995288 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 368 |20.2755 | 1 | 0.000218 | 48.416211 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 369 |20.3306 | 1 | 0.000229 | 49.650859 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 370 |20.3857 | 1 | 0.000281 | 50.925766 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 371 |20.4408 | 1 | 0.000293 | 49.072721 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 372 |20.4959 | 1 | 0.000237 | 47.848434 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 373 |20.5510 | 1 | 0.000461 | 48.577324 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 374 |20.6061 | 1 | 0.000288 | 48.149040 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 375 |20.6612 | 1 | 0.000232 | 48.140545 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 376 |20.7163 | 1 | 0.000627 | 48.528365 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 377 |20.7713 | 1 | 0.000649 | 48.815492 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 378 |20.8264 | 1 | 0.000220 | 48.691541 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 379 |20.8815 | 1 | 0.000234 | 48.163874 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 380 |20.9366 | 1 | 0.000397 | 47.970319 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 381 |20.9917 | 1 | 0.000266 | 49.183593 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 382 |21.0468 | 1 | 0.000228 | 49.448159 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 383 |21.1019 | 1 | 0.000230 | 48.558385 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 384 |21.1570 | 1 | 0.000279 | 48.382636 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 385 |21.2121 | 1 | 0.000237 | 47.482272 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 386 |21.2672 | 1 | 0.000233 | 48.183362 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 387 |21.3223 | 1 | 0.000232 | 48.062299 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 388 |21.3774 | 1 | 0.000235 | 48.362948 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 389 |21.4325 | 1 | 0.000233 | 48.422455 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 390 |21.4876 | 1 | 0.000236 | 48.077169 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 391 |21.5427 | 1 | 0.000218 | 48.317787 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 392 |21.5978 | 1 | 0.000231 | 48.382455 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 393 |21.6529 | 1 | 0.000233 | 48.310206 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 394 |21.7080 | 1 | 0.000219 | 49.225511 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 395 |21.7631 | 1 | 0.000238 | 47.725182 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 396 |21.8182 | 1 | 0.000403 | 48.100903 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 397 |21.8733 | 1 | 0.000227 | 49.886770 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 398 |21.9284 | 1 | 0.000226 | 49.658201 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 399 |21.9835 | 1 | 0.000236 | 48.305113 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 400 |22.0386 | 1 | 0.000213 | 53.014337 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 401 |22.0937 | 1 | 0.000323 | 48.752367 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 402 |22.1488 | 1 | 0.000231 | 48.655110 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 403 |22.2039 | 1 | 0.000302 | 49.894845 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 404 |22.2590 | 1 | 0.000192 | 54.227928 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 405 |22.3140 | 1 | 0.000213 | 53.815790 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 406 |22.3691 | 1 | 0.000231 | 49.235416 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 407 |22.4242 | 1 | 0.000232 | 48.387503 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 408 |22.4793 | 1 | 0.000220 | 52.815450 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 409 |22.5344 | 1 | 0.000232 | 48.253041 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 410 |22.5895 | 1 | 0.000218 | 52.584628 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 411 |22.6446 | 1 | 0.000222 | 50.874607 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 412 |22.6997 | 1 | 0.000234 | 48.125687 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 413 |22.7548 | 1 | 0.000314 | 65.912785 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 414 |22.8099 | 1 | 0.000294 | 48.712801 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 415 |22.8650 | 1 | 0.000234 | 48.264049 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 416 |22.9201 | 1 | 0.000219 | 52.101943 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 417 |22.9752 | 1 | 0.000233 | 48.181907 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 418 |23.0303 | 1 | 0.000239 | 47.737343 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 419 |23.0854 | 1 | 0.000189 | 55.077763 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 420 |23.1405 | 1 | 0.000252 | 48.412007 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 421 |23.1956 | 1 | 0.000207 | 52.025445 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 422 |23.2507 | 1 | 0.000202 | 55.306192 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 423 |23.3058 | 1 | 0.000235 | 48.069254 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 424 |23.3609 | 1 | 0.000268 | 48.786326 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 425 |23.4160 | 1 | 0.000234 | 48.662434 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 426 |23.4711 | 1 | 0.000430 | 48.229208 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 427 |23.5262 | 1 | 0.000660 | 48.355178 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 428 |23.5813 | 1 | 0.000475 | 47.940589 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 429 |23.6364 | 1 | 0.000464 | 48.627356 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 430 |23.6915 | 1 | 0.000231 | 48.519762 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 431 |23.7466 | 1 | 0.000233 | 48.359547 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 432 |23.8017 | 1 | 0.000273 | 48.313746 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 433 |23.8567 | 1 | 0.000237 | 47.977213 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 434 |23.9118 | 1 | 0.000226 | 49.786822 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 435 |23.9669 | 1 | 0.000231 | 48.641675 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 436 |24.0220 | 1 | 0.000263 | 50.056180 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 437 |24.0771 | 1 | 0.000239 | 48.169318 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 438 |24.1322 | 1 | 0.000237 | 47.903935 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 439 |24.1873 | 1 | 0.000235 | 48.071315 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 440 |24.2424 | 1 | 0.000298 | 47.997039 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 441 |24.2975 | 1 | 0.000204 | 51.404477 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 442 |24.3526 | 1 | 0.000234 | 48.367934 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 443 |24.4077 | 1 | 0.000297 | 48.626223 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 444 |24.4628 | 1 | 0.000254 | 48.214660 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 445 |24.5179 | 1 | 0.000234 | 47.731147 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 446 |24.5730 | 1 | 0.000268 | 48.701918 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 447 |24.6281 | 1 | 0.000233 | 48.425494 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 448 |24.6832 | 1 | 0.000233 | 48.324203 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 449 |24.7383 | 1 | 0.000228 | 49.378323 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "| 450 |24.7934 | 1 | 0.000229 | 48.777402 | 0.000000 | 0.000000e+00 | 0.000000e+00 |\u001b[0m\n", + "\u001b[34m...Finished\u001b[0m\n", + "\u001b[36mFINISHED - Elapsed time = 23245.3692466 seconds\u001b[0m\n", + "\u001b[36mFINISHED - CPU process time = 92349.9106726 seconds\u001b[0m\n" + ] + } + ], + "source": [ + "sharpy_output = sharpy.sharpy_main.main(['', SimInfo.solvers['SHARPy']['route'] + SimInfo.solvers['SHARPy']['case'] + '.sharpy'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Postprocessing" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read the structural and aerodynamic information of the last time step" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "tstep = sharpy_output.structure.timestep_info[-1]\n", + "astep = sharpy_output.aero.timestep_info[-1]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Separate the structure into blades" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "# Define beams\n", + "ielem = 0\n", + "nblades = np.max(sharpy_output.structure.beam_number) + 1\n", + "nodes_blade = []\n", + "first_node = 0\n", + "for iblade in range(nblades):\n", + " nodes_blade.append(np.zeros((sharpy_output.structure.num_node,), dtype=bool))\n", + " while sharpy_output.structure.beam_number[ielem] <= iblade:\n", + " ielem += 1\n", + " if ielem == sharpy_output.structure.num_elem:\n", + " break\n", + " nodes_blade[iblade][first_node:sharpy_output.structure.connectivities[ielem-1,1]+1] = True\n", + " first_node = sharpy_output.structure.connectivities[ielem-1,1]+1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Compute the radial position of the nodes and initialise the rest of the variables" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "r = []\n", + "c = []\n", + "dr = []\n", + "forces = []\n", + "CN_drR = []\n", + "CTan_drR = []\n", + "CP_drR = []\n", + "nodes_num = []\n", + "for iblade in range(nblades):\n", + " forces.append(tstep.steady_applied_forces[nodes_blade[iblade]].copy())\n", + "\n", + " nodes_num.append(np.arange(0, sharpy_output.structure.num_node, 1)[nodes_blade[iblade]])\n", + "\n", + " r.append(np.linalg.norm(tstep.pos[nodes_blade[iblade], :], axis=1))\n", + " dr.append(np.zeros(np.sum(nodes_blade[iblade])))\n", + " dr[iblade][0] = 0.5*(r[iblade][1]-r[iblade][0])\n", + " dr[iblade][-1] = 0.5 * (r[iblade][-1] - r[iblade][-2])\n", + " for inode in range(1,len(r[iblade]) - 1):\n", + " dr[iblade][inode] = 0.5*(r[iblade][inode+1] - r[iblade][inode-1])\n", + "\n", + " CN_drR.append(np.zeros(len(r[iblade])))\n", + " c.append(np.zeros(len(r[iblade])))\n", + " CTan_drR.append(np.zeros(len(r[iblade])))\n", + " CP_drR.append(np.zeros(len(r[iblade])))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Transform the loads computed by SHARPy into out-of-plane and in-plane components" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "rho = sharpy_output.settings['StaticCoupledRBM']['aero_solver_settings']['rho']\n", + "uinf = sharpy_output.settings['StaticCoupledRBM']['aero_solver_settings']['velocity_field_input']['u_inf']\n", + "R = np.max(r[0])\n", + "Cp = 0\n", + "Ct = 0\n", + "\n", + "global_force_factor = 0.5 * rho * uinf** 2 * np.pi * R**2\n", + "global_power_factor = global_force_factor*uinf\n", + "for iblade in range(nblades):\n", + " for inode in range(len(r[iblade])):\n", + " forces[iblade][inode, 0] *= 0. # Discard the spanwise component\n", + "\n", + " node_global_index = nodes_num[iblade][inode]\n", + " ielem = sharpy_output.structure.node_master_elem[node_global_index, 0]\n", + " inode_in_elem = sharpy_output.structure.node_master_elem[node_global_index, 1]\n", + " CAB = algebra.crv2rotation(tstep.psi[ielem, inode_in_elem, :])\n", + "\n", + " c[iblade][inode] = sharpy_output.aero.aero_dict['chord'][ielem,inode_in_elem]\n", + "\n", + " forces_AFoR = np.dot(CAB, forces[iblade][inode, 0:3])\n", + "\n", + " CN_drR[iblade][inode] = forces_AFoR[2]/dr[iblade][inode]*R / global_force_factor\n", + " CTan_drR[iblade][inode] = np.linalg.norm(forces_AFoR[0:2])/dr[iblade][inode]*R / global_force_factor\n", + " CP_drR[iblade][inode] = np.linalg.norm(forces_AFoR[0:2])/dr[iblade][inode]*R * r[iblade][inode]*rotation_velocity / global_power_factor\n", + "\n", + " Cp += np.sum(CP_drR[iblade]*dr[iblade]/R)\n", + " Ct += np.sum(CN_drR[iblade]*dr[iblade]/R)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Results" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot of the loads along the blade" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig, list_plots = plt.subplots(1, 2, figsize=(12, 3))\n", + "\n", + "list_plots[0].grid()\n", + "list_plots[0].set_xlabel(\"r/R [-]\")\n", + "list_plots[0].set_ylabel(\"CN/d(r/R) [-]\")\n", + "list_plots[0].plot(r[0]/R, CN_drR[0], '-', label='SHARPy')\n", + "list_plots[0].plot(of_rR, of_cNdrR, '-', label='OpenFAST')\n", + "list_plots[0].legend()\n", + "\n", + "list_plots[1].grid()\n", + "list_plots[1].set_xlabel(\"r/R [-]\")\n", + "list_plots[1].set_ylabel(\"CT/d(r/R) [-]\")\n", + "list_plots[1].plot(r[0]/R, CTan_drR[0], '-', label='SHARPy')\n", + "list_plots[1].plot(of_rR, of_cTdrR, '-', label='OpenFAST')\n", + "list_plots[1].legend()\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Print the rotor thrust and power coefficients" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " OpenFAST SHARPy\n", + "Cp[-] 0.49 0.52\n", + "Ct[-] 0.70 0.70\n" + ] + } + ], + "source": [ + "print(\" OpenFAST SHARPy\")\n", + "print(\"Cp[-] %.2f %.2f\" % (of_cp, Cp))\n", + "print(\"Ct[-] %.2f %.2f\" % (of_ct, Ct))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 } diff --git a/docs/source/content/installation.md b/docs/source/content/installation.md index 9d4d1cd58..f14eb99e5 100644 --- a/docs/source/content/installation.md +++ b/docs/source/content/installation.md @@ -127,10 +127,10 @@ change compilers see the Custom Installation. ``` where the number after the `-j` flag will specify how many cores to use during installation. -4. Finally, load the SHARPy variables +4. Finally, leave the build directory and install SHARPy ```bash - cd ../ - source bin/sharpy_vars.sh + cd .. + pip install . ``` __You are ready to run SHARPy__. Continue reading the [Running SHARPy](#running-sharpy) section. @@ -187,10 +187,17 @@ file if you are installing SHARPy on Mac OS X make install -j 4 ``` -7. Finally, load the SHARPy variables +7. Finally, leave the build directory and install SHARPy ```bash - source bin/sharpy_vars.sh + cd .. + pip install . ``` + If you want to install it in development mode (the source files will stay + where the are so you can modify them), you can make an editable install: + ``` + pip install -e . + ``` + You can obtain further information on editable installs [here](https://pip.pypa.io/en/stable/cli/pip_install/#editable-installs) 8. This concludes the installation! Continue reading the [Running SHARPy](#running-sharpy) section. @@ -259,19 +266,13 @@ above replacing the `stable` tag for `experimental`. ## Running SHARPy -In order to run SHARPy, you need to load the conda environment and load the SHARPy variables (so your computer knows -where SHARPy is). Therefore, __before you run any SHARPy case__: +In order to run SHARPy, you need to load the conda environment. Therefore, __before you run any SHARPy case__: 1. Activate the SHARPy conda environment ```bash conda activate sharpy_env ``` -2. Load the SHARPy variables - ```bash - source sharpy/bin/sharpy_vars.sh - ``` - You are now ready to run SHARPy cases from the terminal. ### Automated tests @@ -311,7 +312,7 @@ This script creates the output files that will then be used by SHARPy, namely: * The linear input files `.lininput.h5` (when required). * The ROM settings file `.rom.h5` (when required). - See the [chapter](./casefiles.html) on the case files for a detailed description on the contents of each one. + See the [chapter](./casefiles.html) on the case files for a detailed description on the contents of each one. Data is exchanged in binary format by means of `.h5` files that make the transmission efficient between the different languages of the required libraries. To view these `.h5` files, a viewer like [HDF5](https://portal.hdfgroup.org/display/support) is recommended. diff --git a/docs/source/content/publications.md b/docs/source/content/publications.md index 74391fff5..92d2d5f3b 100644 --- a/docs/source/content/publications.md +++ b/docs/source/content/publications.md @@ -3,10 +3,24 @@ SHARPy has been used in many technical papers that have been both published in Journals and presented at conferences. Here we present a list of past papers which have used SHARPy for research purposes: +## 2022 + +* Goizueta, N., Wynn, A., Palacios, R., Drachinsky, A., Raveh, D. E. (2022). Flutter Predictions for Very Flexible Wing Wind Tunnel Test. Journal of Aircraft. Article in Advance. [https://doi.org/10.2514/1.C036710](https://doi.org/10.2514/1.C036710) + +* Muñoz-Simón A, Wynn A, Palacios R (2022). Some modelling improvements for prediction of wind turbine rotor loads in turbulent wind. Wind Energy: 25(2), pp. 333-353. [https://doi.org/10.1002/we.2675](https://doi.org/10.1002/we.2675) + +* Goizueta, N., Wynn, A., Palacios, R. (2022). Fast flutter evaluation of very flexible wing using interpolation on an optimal training dataset. AIAA SciTech Forum. [https://doi.org/10.2514/6.2022-1345](https://doi.org/10.2514/6.2022-1345) + +* Düssler, S., Goizueta, N., Muñoz-Simón, A., & Palacios, R. (2022). Modelling and Numerical Enhancements on a UVLM for Nonlinear Aeroelastic Simulation. AIAA SciTech Forum. [https://doi.org/10.2514/6.2022-2455](https://doi.org/10.2514/6.2022-2455) + +* Cea, A., Palacios, R. (2022). Parametric Reduced Order Models for Aeroelastic Design of Very Flexible Aircraft. AIAA SciTech Forum. [https://doi.org/10.2514/6.2022-0727](https://doi.org/10.2514/6.2022-0727) + +* Wynn, A., Artola, M., Palacios, R. (2022). Nonlinear optimal control for gust load alleviation with a physics-constrained data-driven internal model. AIAA SciTech Forum. [https://doi.org/10.2514/6.2022-0442](https://doi.org/10.2514/6.2022-0442) + ## 2021 * Artola, M., Goizueta, N., Wynn, A., & Palacios, R. (2021). Aeroelastic Control and Estimation with a Minimal -Nonlinear Modal Description. AIAA Journal, 1–17. [https://doi.org/10.2514/1.j060018](https://doi.org/10.2514/1.j060018) +Nonlinear Modal Description. AIAA Journal: 59(7), pp. 2697–2713. [https://doi.org/10.2514/1.j060018](https://doi.org/10.2514/1.j060018) * Artola, M., Goizueta, N., Wynn, A., & Palacios, R. (2021). Proof of Concept for a Hardware-in-the-Loop Nonlinear. In AIAA SciTech Forum (pp. 1–26). [https://doi.org/10.2514/6.2021-1392](https://doi.org/10.2514/6.2021-1392) @@ -53,14 +67,14 @@ rotor loads. In AIAA SciTech Forum. [https://doi.org/10.2514/6.2020-0991](https: ## 2019 -* Carre, A., Muñoz-Simón, A., Goizueta, N., & Palacios, R. (2019). SHARPy : A dynamic aeroelastic simulation toolbox +* del Carre, A., Muñoz-Simón, A., Goizueta, N., & Palacios, R. (2019). SHARPy : A dynamic aeroelastic simulation toolbox for very flexible aircraft and wind turbines. Journal of Open Source Software, 4(44), 1885. [https://doi.org/10.21105/joss.01885](https://doi.org/10.21105/joss.01885) -* Del Carre, A., Teixeira, P. C., Palacios, R., & Cesnik, C. E. S. (2019). Nonlinear Response of a Very Flexible +* del Carre, A., Teixeira, P. C., Palacios, R., & Cesnik, C. E. S. (2019). Nonlinear Response of a Very Flexible Aircraft Under Lateral Gust. In International Forum on Aeroelasticity and Structural Dynamics. -* Del Carre, A., & Palacios, R. (2019). Efficient Time-Domain Simulations in Nonlinear Aeroelasticity. In AIAA Scitech +* del Carre, A., & Palacios, R. (2019). Efficient Time-Domain Simulations in Nonlinear Aeroelasticity. In AIAA Scitech Forum (pp. 1–20). [https://doi.org/10.2514/6.2019-2038](https://doi.org/10.2514/6.2019-2038) * Maraniello, S., & Palacios, R. (2019). State-Space Realizations and Internal Balancing in Potential-Flow Aerodynamics diff --git a/docs/source/includes/aero/utils/mapping/index.rst b/docs/source/includes/aero/utils/mapping/index.rst index 4e4d78b11..d6f16df50 100644 --- a/docs/source/includes/aero/utils/mapping/index.rst +++ b/docs/source/includes/aero/utils/mapping/index.rst @@ -7,3 +7,4 @@ Force Mapping Utilities :glob: ./aero2struct_force_mapping + ./total_forces_moments diff --git a/docs/source/includes/aero/utils/mapping/total_forces_moments.rst b/docs/source/includes/aero/utils/mapping/total_forces_moments.rst new file mode 100644 index 000000000..0bc2119af --- /dev/null +++ b/docs/source/includes/aero/utils/mapping/total_forces_moments.rst @@ -0,0 +1,4 @@ +total_forces_moments +-------------------- + +.. automodule:: sharpy.aero.utils.mapping.total_forces_moments \ No newline at end of file diff --git a/docs/source/includes/cases/coupled/X-HALE/generate_xhale/generate_naca_camber.rst b/docs/source/includes/cases/coupled/X-HALE/generate_xhale/generate_naca_camber.rst new file mode 100644 index 000000000..ee0a7170a --- /dev/null +++ b/docs/source/includes/cases/coupled/X-HALE/generate_xhale/generate_naca_camber.rst @@ -0,0 +1,4 @@ +generate_naca_camber +-------------------- + +.. automodule:: sharpy.cases.coupled.X-HALE.generate_xhale.generate_naca_camber \ No newline at end of file diff --git a/docs/source/includes/cases/coupled/X-HALE/generate_xhale/generate_solver_file.rst b/docs/source/includes/cases/coupled/X-HALE/generate_xhale/generate_solver_file.rst new file mode 100644 index 000000000..05e285de0 --- /dev/null +++ b/docs/source/includes/cases/coupled/X-HALE/generate_xhale/generate_solver_file.rst @@ -0,0 +1,4 @@ +generate_solver_file +-------------------- + +.. automodule:: sharpy.cases.coupled.X-HALE.generate_xhale.generate_solver_file \ No newline at end of file diff --git a/docs/source/includes/cases/coupled/X-HALE/generate_xhale/index.rst b/docs/source/includes/cases/coupled/X-HALE/generate_xhale/index.rst new file mode 100644 index 000000000..6e72779c1 --- /dev/null +++ b/docs/source/includes/cases/coupled/X-HALE/generate_xhale/index.rst @@ -0,0 +1,6 @@ +.. toctree:: + :glob: + + ./generate_naca_camber + ./generate_solver_file + ./read_beam_data diff --git a/docs/source/includes/cases/coupled/X-HALE/generate_xhale/read_beam_data.rst b/docs/source/includes/cases/coupled/X-HALE/generate_xhale/read_beam_data.rst new file mode 100644 index 000000000..a49971929 --- /dev/null +++ b/docs/source/includes/cases/coupled/X-HALE/generate_xhale/read_beam_data.rst @@ -0,0 +1,4 @@ +read_beam_data +-------------- + +.. automodule:: sharpy.cases.coupled.X-HALE.generate_xhale.read_beam_data \ No newline at end of file diff --git a/docs/source/includes/cases/coupled/X-HALE/index.rst b/docs/source/includes/cases/coupled/X-HALE/index.rst new file mode 100644 index 000000000..539611c61 --- /dev/null +++ b/docs/source/includes/cases/coupled/X-HALE/index.rst @@ -0,0 +1,7 @@ +X-hale +------ + +.. toctree:: + :maxdepth: 1 + + ./generate_xhale/index diff --git a/docs/source/includes/cases/coupled/index.rst b/docs/source/includes/cases/coupled/index.rst new file mode 100644 index 000000000..0834386c7 --- /dev/null +++ b/docs/source/includes/cases/coupled/index.rst @@ -0,0 +1,7 @@ +Coupled +------- + +.. toctree:: + :maxdepth: 1 + + ./X-HALE/index diff --git a/docs/source/includes/cases/hangar/horten_wing/HortenWing.rst b/docs/source/includes/cases/hangar/horten_wing/HortenWing.rst new file mode 100644 index 000000000..7175acf4b --- /dev/null +++ b/docs/source/includes/cases/hangar/horten_wing/HortenWing.rst @@ -0,0 +1,5 @@ +HortenWing +---------- + +.. autoclass:: sharpy.cases.hangar.horten_wing.HortenWing + :members: \ No newline at end of file diff --git a/docs/source/includes/cases/hangar/horten_wing/index.rst b/docs/source/includes/cases/hangar/horten_wing/index.rst new file mode 100644 index 000000000..12301e7a6 --- /dev/null +++ b/docs/source/includes/cases/hangar/horten_wing/index.rst @@ -0,0 +1,11 @@ +Horten Wing Class Generator ++++++++++++++++++++++++++++ + +Horten Wing Class Generator +N Goizueta Nov 18 + + +.. toctree:: + :glob: + + ./HortenWing diff --git a/docs/source/includes/cases/hangar/index.rst b/docs/source/includes/cases/hangar/index.rst new file mode 100644 index 000000000..8929cfce8 --- /dev/null +++ b/docs/source/includes/cases/hangar/index.rst @@ -0,0 +1,9 @@ +Hangar +------ + +.. toctree:: + :maxdepth: 1 + + ./horten_wing/index + ./richards_wing/index + ./swept_flying_wing/index diff --git a/docs/source/includes/cases/hangar/richards_wing/HortenWing.rst b/docs/source/includes/cases/hangar/richards_wing/HortenWing.rst new file mode 100644 index 000000000..2dd4e048e --- /dev/null +++ b/docs/source/includes/cases/hangar/richards_wing/HortenWing.rst @@ -0,0 +1,5 @@ +HortenWing +---------- + +.. autoclass:: sharpy.cases.hangar.richards_wing.HortenWing + :members: \ No newline at end of file diff --git a/docs/source/includes/cases/hangar/richards_wing/index.rst b/docs/source/includes/cases/hangar/richards_wing/index.rst new file mode 100644 index 000000000..44e333506 --- /dev/null +++ b/docs/source/includes/cases/hangar/richards_wing/index.rst @@ -0,0 +1,13 @@ +Simple Horten Wing as used by Richards. Baseline and simplified models +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Simple Horten Wing as used by Richards. Baseline and simplified models + +Richards, P. W., Yao, Y., Herd, R. A., Hodges, D. H., & Mardanpour, P. (2016). Effect of Inertial and Constitutive +Properties on Body-Freedom Flutter for Flying Wings. Journal of Aircraft. https://doi.org/10.2514/1.C033435 + + +.. toctree:: + :glob: + + ./HortenWing diff --git a/docs/source/includes/cases/hangar/swept_flying_wing/SweptWing.rst b/docs/source/includes/cases/hangar/swept_flying_wing/SweptWing.rst new file mode 100644 index 000000000..a3f41c933 --- /dev/null +++ b/docs/source/includes/cases/hangar/swept_flying_wing/SweptWing.rst @@ -0,0 +1,5 @@ +SweptWing +--------- + +.. autoclass:: sharpy.cases.hangar.swept_flying_wing.SweptWing + :members: \ No newline at end of file diff --git a/docs/source/includes/cases/hangar/swept_flying_wing/index.rst b/docs/source/includes/cases/hangar/swept_flying_wing/index.rst new file mode 100644 index 000000000..820587b06 --- /dev/null +++ b/docs/source/includes/cases/hangar/swept_flying_wing/index.rst @@ -0,0 +1,11 @@ +Generic Swept Flying Wing Class Generator ++++++++++++++++++++++++++++++++++++++++++ + +Generic Swept Flying Wing Class Generator +N Goizueta Nov 18 + + +.. toctree:: + :glob: + + ./SweptWing diff --git a/docs/source/includes/cases/index.rst b/docs/source/includes/cases/index.rst new file mode 100644 index 000000000..aa70c9467 --- /dev/null +++ b/docs/source/includes/cases/index.rst @@ -0,0 +1,9 @@ +Cases +----- + +.. toctree:: + :maxdepth: 1 + + ./coupled/index + ./hangar/index + ./templates/index diff --git a/docs/source/includes/cases/templates/Ttail/Ttail_3beams.rst b/docs/source/includes/cases/templates/Ttail/Ttail_3beams.rst new file mode 100644 index 000000000..203c887c4 --- /dev/null +++ b/docs/source/includes/cases/templates/Ttail/Ttail_3beams.rst @@ -0,0 +1,5 @@ +Ttail_3beams +------------ + +.. autoclass:: sharpy.cases.templates.Ttail.Ttail_3beams + :members: \ No newline at end of file diff --git a/docs/source/includes/cases/templates/Ttail/Ttail_canonical.rst b/docs/source/includes/cases/templates/Ttail/Ttail_canonical.rst new file mode 100644 index 000000000..91e3e1170 --- /dev/null +++ b/docs/source/includes/cases/templates/Ttail/Ttail_canonical.rst @@ -0,0 +1,5 @@ +Ttail_canonical +--------------- + +.. autoclass:: sharpy.cases.templates.Ttail.Ttail_canonical + :members: \ No newline at end of file diff --git a/docs/source/includes/cases/templates/Ttail/index.rst b/docs/source/includes/cases/templates/Ttail/index.rst new file mode 100644 index 000000000..a81aa5279 --- /dev/null +++ b/docs/source/includes/cases/templates/Ttail/index.rst @@ -0,0 +1,17 @@ +Templates to build T-tail models +++++++++++++++++++++++++++++++++ + +Templates to build T-tail models +S. Maraniello, Oct 2018 + +classes: +- Ttail_3beam allows to generate general T-tail models with 3-beam. +- Ttail_canonical: builds the canonical test case as per + Murua et al., Prog. Aerosp. Sci., 71 (2014) 54-84 + + +.. toctree:: + :glob: + + ./Ttail_3beams + ./Ttail_canonical diff --git a/docs/source/includes/cases/templates/flying_wings/FlyingWing.rst b/docs/source/includes/cases/templates/flying_wings/FlyingWing.rst new file mode 100644 index 000000000..0535c0f9e --- /dev/null +++ b/docs/source/includes/cases/templates/flying_wings/FlyingWing.rst @@ -0,0 +1,5 @@ +FlyingWing +---------- + +.. autoclass:: sharpy.cases.templates.flying_wings.FlyingWing + :members: \ No newline at end of file diff --git a/docs/source/includes/cases/templates/flying_wings/Goland.rst b/docs/source/includes/cases/templates/flying_wings/Goland.rst new file mode 100644 index 000000000..d2c3ffa81 --- /dev/null +++ b/docs/source/includes/cases/templates/flying_wings/Goland.rst @@ -0,0 +1,5 @@ +Goland +------ + +.. autoclass:: sharpy.cases.templates.flying_wings.Goland + :members: \ No newline at end of file diff --git a/docs/source/includes/cases/templates/flying_wings/Pazy.rst b/docs/source/includes/cases/templates/flying_wings/Pazy.rst new file mode 100644 index 000000000..25ac024a5 --- /dev/null +++ b/docs/source/includes/cases/templates/flying_wings/Pazy.rst @@ -0,0 +1,5 @@ +Pazy +---- + +.. autoclass:: sharpy.cases.templates.flying_wings.Pazy + :members: \ No newline at end of file diff --git a/docs/source/includes/cases/templates/flying_wings/QuasiInfinite.rst b/docs/source/includes/cases/templates/flying_wings/QuasiInfinite.rst new file mode 100644 index 000000000..eea48cd60 --- /dev/null +++ b/docs/source/includes/cases/templates/flying_wings/QuasiInfinite.rst @@ -0,0 +1,5 @@ +QuasiInfinite +------------- + +.. autoclass:: sharpy.cases.templates.flying_wings.QuasiInfinite + :members: \ No newline at end of file diff --git a/docs/source/includes/cases/templates/flying_wings/Smith.rst b/docs/source/includes/cases/templates/flying_wings/Smith.rst new file mode 100644 index 000000000..0fd3f894f --- /dev/null +++ b/docs/source/includes/cases/templates/flying_wings/Smith.rst @@ -0,0 +1,5 @@ +Smith +----- + +.. autoclass:: sharpy.cases.templates.flying_wings.Smith + :members: \ No newline at end of file diff --git a/docs/source/includes/cases/templates/flying_wings/index.rst b/docs/source/includes/cases/templates/flying_wings/index.rst new file mode 100644 index 000000000..4cd1bb28d --- /dev/null +++ b/docs/source/includes/cases/templates/flying_wings/index.rst @@ -0,0 +1,22 @@ +Templates to build flying wing models ++++++++++++++++++++++++++++++++++++++ + +Templates to build flying wing models +S. Maraniello, Jul 2018 + +classes: +- FlyingWing: generate a flying wing model from a reduced set of input. The +built in method 'update_mass_stiff' can be re-defined by the user to enter more +complex inertial/stiffness properties +- Smith(FlyingWing): generate HALE wing model +- Goland(FlyingWing): generate Goland wing model + + +.. toctree:: + :glob: + + ./FlyingWing + ./Goland + ./Pazy + ./QuasiInfinite + ./Smith diff --git a/docs/source/includes/cases/templates/index.rst b/docs/source/includes/cases/templates/index.rst new file mode 100644 index 000000000..c8567d181 --- /dev/null +++ b/docs/source/includes/cases/templates/index.rst @@ -0,0 +1,9 @@ +Templates +--------- + +.. toctree:: + :maxdepth: 1 + + ./Ttail/index + ./flying_wings/index + ./template_wt/index diff --git a/docs/source/includes/cases/templates/template_wt/create_blade_coordinates.rst b/docs/source/includes/cases/templates/template_wt/create_blade_coordinates.rst new file mode 100644 index 000000000..093d96566 --- /dev/null +++ b/docs/source/includes/cases/templates/template_wt/create_blade_coordinates.rst @@ -0,0 +1,4 @@ +create_blade_coordinates +------------------------ + +.. automodule:: sharpy.cases.templates.template_wt.create_blade_coordinates \ No newline at end of file diff --git a/docs/source/includes/cases/templates/template_wt/create_node_radial_pos_from_elem_centres.rst b/docs/source/includes/cases/templates/template_wt/create_node_radial_pos_from_elem_centres.rst new file mode 100644 index 000000000..8b63cee08 --- /dev/null +++ b/docs/source/includes/cases/templates/template_wt/create_node_radial_pos_from_elem_centres.rst @@ -0,0 +1,4 @@ +create_node_radial_pos_from_elem_centres +---------------------------------------- + +.. automodule:: sharpy.cases.templates.template_wt.create_node_radial_pos_from_elem_centres \ No newline at end of file diff --git a/docs/source/includes/cases/templates/template_wt/generate_from_excel_type03.rst b/docs/source/includes/cases/templates/template_wt/generate_from_excel_type03.rst new file mode 100644 index 000000000..ef479fae1 --- /dev/null +++ b/docs/source/includes/cases/templates/template_wt/generate_from_excel_type03.rst @@ -0,0 +1,4 @@ +generate_from_excel_type03 +-------------------------- + +.. automodule:: sharpy.cases.templates.template_wt.generate_from_excel_type03 \ No newline at end of file diff --git a/docs/source/includes/cases/templates/template_wt/index.rst b/docs/source/includes/cases/templates/template_wt/index.rst new file mode 100644 index 000000000..92531684e --- /dev/null +++ b/docs/source/includes/cases/templates/template_wt/index.rst @@ -0,0 +1,19 @@ +template_wt ++++++++++++ + +template_wt + +Functions needed to generate a wind turbines + +Notes: + To load this library: import cases.templates.template_wt as template_wt + + +.. toctree:: + :glob: + + ./create_blade_coordinates + ./create_node_radial_pos_from_elem_centres + ./generate_from_excel_type03 + ./rotor_from_excel_type03 + ./spar_from_excel_type04 diff --git a/docs/source/includes/cases/templates/template_wt/rotor_from_excel_type03.rst b/docs/source/includes/cases/templates/template_wt/rotor_from_excel_type03.rst new file mode 100644 index 000000000..c8f57a022 --- /dev/null +++ b/docs/source/includes/cases/templates/template_wt/rotor_from_excel_type03.rst @@ -0,0 +1,4 @@ +rotor_from_excel_type03 +----------------------- + +.. automodule:: sharpy.cases.templates.template_wt.rotor_from_excel_type03 \ No newline at end of file diff --git a/docs/source/includes/cases/templates/template_wt/spar_from_excel_type04.rst b/docs/source/includes/cases/templates/template_wt/spar_from_excel_type04.rst new file mode 100644 index 000000000..cebd450e4 --- /dev/null +++ b/docs/source/includes/cases/templates/template_wt/spar_from_excel_type04.rst @@ -0,0 +1,4 @@ +spar_from_excel_type04 +---------------------- + +.. automodule:: sharpy.cases.templates.template_wt.spar_from_excel_type04 \ No newline at end of file diff --git a/docs/source/includes/index.rst b/docs/source/includes/index.rst index 15afa4891..f518b714a 100644 --- a/docs/source/includes/index.rst +++ b/docs/source/includes/index.rst @@ -15,6 +15,7 @@ If you feel that a function/class is not well documented and, hence, you cannot :maxdepth: 1 ./aero/index + ./cases/index ./controllers/index ./generators/index ./io/index diff --git a/docs/source/includes/linear/assembler/lineargustassembler/LeadingEdge.rst b/docs/source/includes/linear/assembler/lineargustassembler/LeadingEdge.rst new file mode 100644 index 000000000..3a880ccf5 --- /dev/null +++ b/docs/source/includes/linear/assembler/lineargustassembler/LeadingEdge.rst @@ -0,0 +1,5 @@ +LeadingEdge +----------- + +.. autoclass:: sharpy.linear.assembler.lineargustassembler.LeadingEdge + :members: \ No newline at end of file diff --git a/docs/source/includes/linear/assembler/lineargustassembler/LinearGustGenerator.rst b/docs/source/includes/linear/assembler/lineargustassembler/LinearGustGenerator.rst deleted file mode 100644 index 83cdd4ca0..000000000 --- a/docs/source/includes/linear/assembler/lineargustassembler/LinearGustGenerator.rst +++ /dev/null @@ -1,5 +0,0 @@ -LinearGustGenerator -------------------- - -.. autoclass:: sharpy.linear.assembler.lineargustassembler.LinearGustGenerator - :members: \ No newline at end of file diff --git a/docs/source/includes/linear/assembler/lineargustassembler/MultiLeadingEdge.rst b/docs/source/includes/linear/assembler/lineargustassembler/MultiLeadingEdge.rst new file mode 100644 index 000000000..a45d964fa --- /dev/null +++ b/docs/source/includes/linear/assembler/lineargustassembler/MultiLeadingEdge.rst @@ -0,0 +1,5 @@ +MultiLeadingEdge +---------------- + +.. autoclass:: sharpy.linear.assembler.lineargustassembler.MultiLeadingEdge + :members: \ No newline at end of file diff --git a/docs/source/includes/linear/assembler/lineargustassembler/campbell.rst b/docs/source/includes/linear/assembler/lineargustassembler/campbell.rst new file mode 100644 index 000000000..a102d794e --- /dev/null +++ b/docs/source/includes/linear/assembler/lineargustassembler/campbell.rst @@ -0,0 +1,4 @@ +campbell +-------- + +.. automodule:: sharpy.linear.assembler.lineargustassembler.campbell \ No newline at end of file diff --git a/docs/source/includes/linear/assembler/lineargustassembler/gust_from_string.rst b/docs/source/includes/linear/assembler/lineargustassembler/gust_from_string.rst new file mode 100644 index 000000000..0366dce9e --- /dev/null +++ b/docs/source/includes/linear/assembler/lineargustassembler/gust_from_string.rst @@ -0,0 +1,4 @@ +gust_from_string +---------------- + +.. automodule:: sharpy.linear.assembler.lineargustassembler.gust_from_string \ No newline at end of file diff --git a/docs/source/includes/linear/assembler/lineargustassembler/index.rst b/docs/source/includes/linear/assembler/lineargustassembler/index.rst index c6a0113b3..bd3718f47 100644 --- a/docs/source/includes/linear/assembler/lineargustassembler/index.rst +++ b/docs/source/includes/linear/assembler/lineargustassembler/index.rst @@ -1,4 +1,8 @@ .. toctree:: :glob: - ./LinearGustGenerator + ./LeadingEdge + ./MultiLeadingEdge + ./campbell + ./gust_from_string + ./spanwise_interpolation diff --git a/docs/source/includes/linear/assembler/lineargustassembler/spanwise_interpolation.rst b/docs/source/includes/linear/assembler/lineargustassembler/spanwise_interpolation.rst new file mode 100644 index 000000000..cc206310f --- /dev/null +++ b/docs/source/includes/linear/assembler/lineargustassembler/spanwise_interpolation.rst @@ -0,0 +1,4 @@ +spanwise_interpolation +---------------------- + +.. automodule:: sharpy.linear.assembler.lineargustassembler.spanwise_interpolation \ No newline at end of file diff --git a/docs/source/includes/linear/src/libss/StateSpace.rst b/docs/source/includes/linear/src/libss/StateSpace.rst new file mode 100644 index 000000000..dc2571338 --- /dev/null +++ b/docs/source/includes/linear/src/libss/StateSpace.rst @@ -0,0 +1,5 @@ +StateSpace +---------- + +.. autoclass:: sharpy.linear.src.libss.StateSpace + :members: \ No newline at end of file diff --git a/docs/source/includes/linear/src/libss/index.rst b/docs/source/includes/linear/src/libss/index.rst index cfd72d46b..5f05d2cb1 100644 --- a/docs/source/includes/linear/src/libss/index.rst +++ b/docs/source/includes/linear/src/libss/index.rst @@ -11,7 +11,7 @@ the sparse arrays types defined in libsparse. The module includes: Classes: -- ss: provides a class to build DLTI/LTI systems with full and/or sparse +- StateSpace: provides a class to build DLTI/LTI systems with full and/or sparse matrices and wraps many of the methods in these library. Methods include: - freqresp: wraps the freqresp function - addGain: adds gains in input/output. This is not a wrapper of addGain, as @@ -61,6 +61,7 @@ to do: ./SSconv ./SSderivative ./SSintegr + ./StateSpace ./addGain ./adjust_phase ./build_SS_poly @@ -76,10 +77,10 @@ to do: ./parallel ./project ./random_ss + ./retain_inout_channels ./scale_SS ./series ./simulate - ./ss ./ss_block ./ss_to_scipy ./sum_ss diff --git a/docs/source/includes/linear/src/libss/retain_inout_channels.rst b/docs/source/includes/linear/src/libss/retain_inout_channels.rst new file mode 100644 index 000000000..67cd41d84 --- /dev/null +++ b/docs/source/includes/linear/src/libss/retain_inout_channels.rst @@ -0,0 +1,4 @@ +retain_inout_channels +--------------------- + +.. automodule:: sharpy.linear.src.libss.retain_inout_channels \ No newline at end of file diff --git a/docs/source/includes/linear/src/libss/ss.rst b/docs/source/includes/linear/src/libss/ss.rst deleted file mode 100644 index 6fbe89b77..000000000 --- a/docs/source/includes/linear/src/libss/ss.rst +++ /dev/null @@ -1,5 +0,0 @@ -ss --- - -.. autoclass:: sharpy.linear.src.libss.ss - :members: \ No newline at end of file diff --git a/docs/source/includes/postprocs/StabilityDerivatives.rst b/docs/source/includes/postprocs/StabilityDerivatives.rst new file mode 100644 index 000000000..a8bf30a08 --- /dev/null +++ b/docs/source/includes/postprocs/StabilityDerivatives.rst @@ -0,0 +1,7 @@ +StabilityDerivatives +-------------------- + + + +.. autoclass:: sharpy.postproc.stabilityderivatives.StabilityDerivatives + :members: \ No newline at end of file diff --git a/docs/source/includes/structure/utils/modalutils/index.rst b/docs/source/includes/structure/utils/modalutils/index.rst index 2d63a80e9..611a05a70 100644 --- a/docs/source/includes/structure/utils/modalutils/index.rst +++ b/docs/source/includes/structure/utils/modalutils/index.rst @@ -6,6 +6,8 @@ ./free_modes_principal_axes ./get_mode_zeta ./mode_sign_convention + ./modes_to_cg_ref + ./principal_axes_inertia ./scale_mass_normalised_modes ./scale_mode ./write_modes_vtk diff --git a/docs/source/includes/structure/utils/modalutils/modes_to_cg_ref.rst b/docs/source/includes/structure/utils/modalutils/modes_to_cg_ref.rst new file mode 100644 index 000000000..6694ae30b --- /dev/null +++ b/docs/source/includes/structure/utils/modalutils/modes_to_cg_ref.rst @@ -0,0 +1,4 @@ +modes_to_cg_ref +--------------- + +.. automodule:: sharpy.structure.utils.modalutils.modes_to_cg_ref \ No newline at end of file diff --git a/docs/source/includes/structure/utils/modalutils/principal_axes_inertia.rst b/docs/source/includes/structure/utils/modalutils/principal_axes_inertia.rst new file mode 100644 index 000000000..e2828c8f0 --- /dev/null +++ b/docs/source/includes/structure/utils/modalutils/principal_axes_inertia.rst @@ -0,0 +1,4 @@ +principal_axes_inertia +---------------------- + +.. automodule:: sharpy.structure.utils.modalutils.principal_axes_inertia \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 000000000..76901175a --- /dev/null +++ b/setup.py @@ -0,0 +1,43 @@ +from setuptools import setup, find_packages +import re +import os + +__version__ = re.findall( + r"""__version__ = ["']+([0-9\.]*)["']+""", + open("sharpy/__init__.py").read(), +)[0] + +this_directory = os.path.abspath(os.path.dirname(__file__)) +with open(os.path.join(this_directory, "README.md"), encoding="utf-8") as f: + long_description = f.read() + +setup( + name="sharpy", + version=__version__, + description="""SHARPy is a nonlinear aeroelastic analysis package developed + at the Department of Aeronautics, Imperial College London. It can be used + for the structural, aerodynamic and aeroelastic analysis of flexible + aircraft, flying wings and wind turbines.""", + long_description=long_description, + long_description_content_type="text/markdown", + keywords="nonlinear aeroelastic structural aerodynamic analysis", + author="", + author_email="", + url="https://github.com/ImperialCollegeLondon/sharpy", + license="BSD 3-Clause License", + packages=find_packages( + where='./', + include=['sharpy*'], + exclude=['tests'] + ), + install_requires=[ + ], + classifiers=[ + "Operating System :: Linux, Mac OS", + "Programming Language :: Python, C++", + ], + + entry_points={ + 'console_scripts': ['sharpy=sharpy.sharpy_main:sharpy_run'], + } +) diff --git a/sharpy/__init__.py b/sharpy/__init__.py index e69de29bb..f2dc0e400 100644 --- a/sharpy/__init__.py +++ b/sharpy/__init__.py @@ -0,0 +1 @@ +__version__ = "2.0" diff --git a/sharpy/aero/utils/utils.py b/sharpy/aero/utils/utils.py index 204b6300c..c1f842884 100644 --- a/sharpy/aero/utils/utils.py +++ b/sharpy/aero/utils/utils.py @@ -121,3 +121,55 @@ def span_chord(i_node_surf, zeta): dir_chord = algebra.unit_vector(dir_chord) return dir_span, span, dir_chord, chord + + +def find_aerodynamic_solver(settings): + """ + Retrieves the name and settings of the first aerodynamic solver used in the solution ``flow``. + + Args: + settings (dict): SHARPy settings (usually found in ``data.settings`` ) + + Returns: + tuple: Aerodynamic solver name and solver settings + """ + flow = settings['SHARPy']['flow'] + # Look for the aerodynamic solver + if 'StaticUvlm' in flow: + aero_solver_name = 'StaticUvlm' + aero_solver_settings = settings['StaticUvlm'] + elif 'StaticCoupled' in flow: + aero_solver_name = settings['StaticCoupled']['aero_solver'] + aero_solver_settings = settings['StaticCoupled']['aero_solver_settings'] + elif 'StaticCoupledRBM' in flow: + aero_solver_name = settings['StaticCoupledRBM']['aero_solver'] + aero_solver_settings = settings['StaticCoupledRBM']['aero_solver_settings'] + elif 'DynamicCoupled' in flow: + aero_solver_name = settings['DynamicCoupled']['aero_solver'] + aero_solver_settings = settings['DynamicCoupled']['aero_solver_settings'] + elif 'StepUvlm' in flow: + aero_solver_name = 'StepUvlm' + aero_solver_settings = settings['StepUvlm'] + else: + raise KeyError("ERROR: aerodynamic solver not found") + + return aero_solver_name, aero_solver_settings + + +def find_velocity_generator(settings): + """ + Retrieves the name and settings of the fluid velocity generator in the first aerodynamic solver used in the + solution ``flow``. + + Args: + settings (dict): SHARPy settings (usually found in ``data.settings`` ) + + Returns: + tuple: velocity generator name and velocity generator settings + """ + aero_solver_name, aero_solver_settings = find_aerodynamic_solver(settings) + + vel_gen_name = aero_solver_settings['velocity_field_generator'] + vel_gen_settings = aero_solver_settings['velocity_field_input'] + + return vel_gen_name, vel_gen_settings diff --git a/sharpy/aero/utils/uvlmlib.py b/sharpy/aero/utils/uvlmlib.py index 3369f4aae..3e0466300 100644 --- a/sharpy/aero/utils/uvlmlib.py +++ b/sharpy/aero/utils/uvlmlib.py @@ -7,7 +7,12 @@ import os from sharpy.utils.constants import NDIM, vortex_radius_def -UvlmLib = ct_utils.import_ctypes_lib(SharpyDir + '/lib/UVLM/lib/', 'libuvlm') +try: + UvlmLib = ct_utils.import_ctypes_lib(SharpyDir + '/UVLM', 'libuvlm') +except OSError: + UvlmLib = ct_utils.import_ctypes_lib(SharpyDir + '/lib/UVLM/lib', 'libuvlm') + + class VMopts(ct.Structure): diff --git a/sharpy/cases/__init__.py b/sharpy/cases/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sharpy/cases/coupled/WindTurbine/__init__.py b/sharpy/cases/coupled/WindTurbine/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cases/coupled/WindTurbine/generate_rotor.py b/sharpy/cases/coupled/WindTurbine/generate_rotor.py similarity index 96% rename from cases/coupled/WindTurbine/generate_rotor.py rename to sharpy/cases/coupled/WindTurbine/generate_rotor.py index 0685f3dc6..8b13d7374 100644 --- a/cases/coupled/WindTurbine/generate_rotor.py +++ b/sharpy/cases/coupled/WindTurbine/generate_rotor.py @@ -2,8 +2,9 @@ import os import sharpy.utils.generate_cases as gc -import cases.templates.template_wt as template_wt +import sharpy.cases.templates.template_wt as template_wt from sharpy.utils.constants import deg2rad +import sharpy.utils.sharpydir as sharpydir ###################################################################### ########################### PARAMETERS ############################# @@ -46,8 +47,7 @@ 'tol_remove_points': 1e-8, 'n_points_camber': 100, 'm_distribution': 'uniform'} - -excel_description = {'excel_file_name': route + '../../../docs/source/content/example_notebooks/source/type02_db_NREL5MW_v02.xlsx', +excel_description = {'excel_file_name': os.path.abspath(sharpydir.SharpyDir + '/docs/source/content/example_notebooks/source/type02_db_NREL5MW_v02.xlsx'), 'excel_sheet_parameters': 'parameters', 'excel_sheet_structural_blade': 'structural_blade', 'excel_sheet_discretization_blade': 'discretization_blade', diff --git a/cases/coupled/X-HALE/generate_xhale.py b/sharpy/cases/coupled/X-HALE/generate_xhale.py similarity index 99% rename from cases/coupled/X-HALE/generate_xhale.py rename to sharpy/cases/coupled/X-HALE/generate_xhale.py index 23de00d98..654762182 100644 --- a/cases/coupled/X-HALE/generate_xhale.py +++ b/sharpy/cases/coupled/X-HALE/generate_xhale.py @@ -341,7 +341,7 @@ def clean_test_files(): if os.path.isfile(flightcon_file_name): os.remove(flightcon_file_name) - def read_beam_data(filename='inputs/beam_properties.xlsx'): + def read_beam_data(filename=route + '/inputs/beam_properties.xlsx'): """ Function reading the xlsx file with beam properties, including stiffness and distributed mass. @@ -395,7 +395,7 @@ def read_beam_data(filename='inputs/beam_properties.xlsx'): return mass_data, stiff_data - def read_lumped_mass_data(filename='inputs/lumped_mass.xlsx'): + def read_lumped_mass_data(filename=route + '/inputs/lumped_mass.xlsx'): import pandas as pd xl = pd.ExcelFile(filename) sheets = {sheet_name: xl.parse(sheet_name, header=0, skiprows=(0, 2)) for sheet_name in xl.sheet_names} @@ -1133,7 +1133,7 @@ def generate_fem(): lumped_mass_position_handle = h5file.create_dataset( 'lumped_mass_position', data=lumped_mass_position) - def read_aero_data(filename='inputs/aero_properties.xlsx'): + def read_aero_data(filename=route + '/inputs/aero_properties.xlsx'): import pandas as pd xl = pd.ExcelFile(filename) @@ -1579,7 +1579,7 @@ def generate_aero_file(): with h5.File(route + '/' + case_name + '.aero.h5', 'a') as h5file: airfoils_group = h5file.create_group('airfoils') # add one airfoil - emx07_main = airfoils_group.create_dataset('0', data=load_airfoil('inputs/EMX-07_camber.txt')) + emx07_main = airfoils_group.create_dataset('0', data=load_airfoil(route + '/inputs/EMX-07_camber.txt')) flat_tail = airfoils_group.create_dataset('1', data=np.column_stack( generate_naca_camber(P=0, M=0))) diff --git a/cases/coupled/X-HALE/inputs/EMX-07_camber.txt b/sharpy/cases/coupled/X-HALE/inputs/EMX-07_camber.txt similarity index 100% rename from cases/coupled/X-HALE/inputs/EMX-07_camber.txt rename to sharpy/cases/coupled/X-HALE/inputs/EMX-07_camber.txt diff --git a/cases/coupled/X-HALE/inputs/aero_properties.xlsx b/sharpy/cases/coupled/X-HALE/inputs/aero_properties.xlsx similarity index 100% rename from cases/coupled/X-HALE/inputs/aero_properties.xlsx rename to sharpy/cases/coupled/X-HALE/inputs/aero_properties.xlsx diff --git a/cases/coupled/X-HALE/inputs/beam_properties.xlsx b/sharpy/cases/coupled/X-HALE/inputs/beam_properties.xlsx similarity index 100% rename from cases/coupled/X-HALE/inputs/beam_properties.xlsx rename to sharpy/cases/coupled/X-HALE/inputs/beam_properties.xlsx diff --git a/cases/coupled/X-HALE/inputs/lumped_mass.xlsx b/sharpy/cases/coupled/X-HALE/inputs/lumped_mass.xlsx similarity index 100% rename from cases/coupled/X-HALE/inputs/lumped_mass.xlsx rename to sharpy/cases/coupled/X-HALE/inputs/lumped_mass.xlsx diff --git a/sharpy/cases/coupled/__init__.py b/sharpy/cases/coupled/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sharpy/cases/coupled/hinged_controlled_wing/__init__.py b/sharpy/cases/coupled/hinged_controlled_wing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cases/coupled/hinged_controlled_wing/generate_hinged_controlled_wing.py b/sharpy/cases/coupled/hinged_controlled_wing/generate_hinged_controlled_wing.py similarity index 99% rename from cases/coupled/hinged_controlled_wing/generate_hinged_controlled_wing.py rename to sharpy/cases/coupled/hinged_controlled_wing/generate_hinged_controlled_wing.py index 9b79c010a..9462aefd1 100644 --- a/cases/coupled/hinged_controlled_wing/generate_hinged_controlled_wing.py +++ b/sharpy/cases/coupled/hinged_controlled_wing/generate_hinged_controlled_wing.py @@ -29,7 +29,7 @@ alpha_rad = alpha*np.pi/180 -pitch_file = 'pitch.csv' +pitch_file = route + 'pitch.csv' gains = -np.array([0.9, 6.0, 0.75]) diff --git a/cases/coupled/hinged_controlled_wing/generate_hinged_roll_controlled_wing.py b/sharpy/cases/coupled/hinged_controlled_wing/generate_hinged_roll_controlled_wing.py similarity index 99% rename from cases/coupled/hinged_controlled_wing/generate_hinged_roll_controlled_wing.py rename to sharpy/cases/coupled/hinged_controlled_wing/generate_hinged_roll_controlled_wing.py index 9a06a09d5..48ef7ebe1 100644 --- a/cases/coupled/hinged_controlled_wing/generate_hinged_roll_controlled_wing.py +++ b/sharpy/cases/coupled/hinged_controlled_wing/generate_hinged_roll_controlled_wing.py @@ -30,7 +30,7 @@ alpha_rad = alpha*np.pi/180 -roll_file = 'roll.csv' +roll_file = route + 'roll.csv' gains = -np.array([45, 50.0, 1.5]) diff --git a/sharpy/cases/coupled/linear_horten/__init__.py b/sharpy/cases/coupled/linear_horten/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sharpy/cases/coupled/multibody_takeoff/__init__.py b/sharpy/cases/coupled/multibody_takeoff/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cases/coupled/multibody_takeoff/generate_hale.py b/sharpy/cases/coupled/multibody_takeoff/generate_hale.py similarity index 100% rename from cases/coupled/multibody_takeoff/generate_hale.py rename to sharpy/cases/coupled/multibody_takeoff/generate_hale.py diff --git a/sharpy/cases/coupled/simple_HALE/__init__.py b/sharpy/cases/coupled/simple_HALE/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cases/coupled/simple_HALE/generate_hale.py b/sharpy/cases/coupled/simple_HALE/generate_hale.py similarity index 100% rename from cases/coupled/simple_HALE/generate_hale.py rename to sharpy/cases/coupled/simple_HALE/generate_hale.py diff --git a/sharpy/cases/hangar/__init__.py b/sharpy/cases/hangar/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cases/hangar/horten_wing.py b/sharpy/cases/hangar/horten_wing.py similarity index 99% rename from cases/hangar/horten_wing.py rename to sharpy/cases/hangar/horten_wing.py index 9f3611078..3a8fbe7e0 100644 --- a/cases/hangar/horten_wing.py +++ b/sharpy/cases/hangar/horten_wing.py @@ -270,7 +270,6 @@ def dynamic_control_surface(self, *delta): np.savetxt(self.case_route + '/' + self.case_name + '.input.txt', cs) i += 1 - def planform_area(self): S_fus = 0.5 * (self.c_fuselage + self.c_root) * self.fuselage_width S_wing = 0.5 * (self.c_root + self.c_root*self.taper_ratio) * self.span / 2 @@ -1299,7 +1298,7 @@ def set_default_config_dict(self, route=None, case_name=None): 'rigid_body_modes': True, 'write_modes_vtk': 'on', 'print_matrices': 'on', - 'write_data': 'on', + 'save_data': 'on', 'continuous_eigenvalues': 'off', 'dt': dt, 'plot_eigenvalues': False} diff --git a/cases/hangar/richards_wing.py b/sharpy/cases/hangar/richards_wing.py similarity index 95% rename from cases/hangar/richards_wing.py rename to sharpy/cases/hangar/richards_wing.py index 3c80cb0a3..41eada3d2 100644 --- a/cases/hangar/richards_wing.py +++ b/sharpy/cases/hangar/richards_wing.py @@ -5,7 +5,7 @@ Properties on Body-Freedom Flutter for Flying Wings. Journal of Aircraft. https://doi.org/10.2514/1.C033435 """ import numpy as np -from cases.hangar.horten_wing import HortenWing +from sharpy.cases.hangar.horten_wing import HortenWing import sharpy.utils.algebra as algebra @@ -135,13 +135,13 @@ def update_mass_stiffness(self, sigma=1., sigma_mass=1., payload=0): # Create full cross section c_bar = self.c_root * ((1-alpha[i_elem]) + self.taper_ratio * alpha[i_elem]) x_section = WingCrossSection(c_bar) - print(i_elem) - print('Section Mass: %.2f ' %x_section.mass) - print('Linear Mass: %.2f' % (mu_0 * (1-alpha[i_elem]) + mu_0 * self.taper_ratio * alpha[i_elem])) - print('Section Ixx: %.4f' % x_section.ixx) - print('Section Iyy: %.4f' % x_section.iyy) - print('Section Izz: %.4f' % x_section.izz) - print('Linear Ixx: %.2f' % (j_xx * (1-alpha[i_elem]) + j_xx * self.taper_ratio * alpha[i_elem])) + # print(i_elem) + # print('Section Mass: %.2f ' %x_section.mass) + # print('Linear Mass: %.2f' % (mu_0 * (1-alpha[i_elem]) + mu_0 * self.taper_ratio * alpha[i_elem])) + # print('Section Ixx: %.4f' % x_section.ixx) + # print('Section Iyy: %.4f' % x_section.iyy) + # print('Section Izz: %.4f' % x_section.izz) + # print('Linear Ixx: %.2f' % (j_xx * (1-alpha[i_elem]) + j_xx * self.taper_ratio * alpha[i_elem])) # base_mass[i_elem, :, :] = mass_root_right*(1-alpha[i_elem]) + mass_tip_right*alpha[i_elem] # base_mass[i_elem + self.n_elem_wing + self.n_elem_fuselage - 1] = mass_root_left*(1-alpha[i_elem]) + mass_tip_left*alpha[i_elem] @@ -383,4 +383,4 @@ def izz(self): # ws.generate_fem_file() ws.update_aero_properties() # ws.generate_aero_file() - # ws.set_default_config_dict() \ No newline at end of file + # ws.set_default_config_dict() diff --git a/cases/hangar/swept_flying_wing.py b/sharpy/cases/hangar/swept_flying_wing.py similarity index 99% rename from cases/hangar/swept_flying_wing.py rename to sharpy/cases/hangar/swept_flying_wing.py index 2e2b57a19..c95c880b4 100644 --- a/cases/hangar/swept_flying_wing.py +++ b/sharpy/cases/hangar/swept_flying_wing.py @@ -1095,7 +1095,7 @@ def set_default_config_dict(self, route=None, case_name=None): 'rigid_body_modes': True, 'write_modes_vtk': 'on', 'print_matrices': 'on', - 'write_data': 'on', + 'save_data': 'on', 'continuous_eigenvalues': 'off', 'dt': dt, 'plot_eigenvalues': False} diff --git a/cases/templates/Ttail.py b/sharpy/cases/templates/Ttail.py similarity index 99% rename from cases/templates/Ttail.py rename to sharpy/cases/templates/Ttail.py index d2b5be010..7a5643cb1 100644 --- a/cases/templates/Ttail.py +++ b/sharpy/cases/templates/Ttail.py @@ -559,7 +559,6 @@ def set_default_config_dict(self): config['Modal'] = {'NumLambda': 60, 'print_matrices': 'off', - 'keep_linear_matrices': 'on', 'write_modes_vtk': True, 'use_undamped_modes': True} diff --git a/sharpy/cases/templates/__init__.py b/sharpy/cases/templates/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cases/templates/flying_wings.py b/sharpy/cases/templates/flying_wings.py similarity index 99% rename from cases/templates/flying_wings.py rename to sharpy/cases/templates/flying_wings.py index 01b006119..3b3cd03b7 100644 --- a/cases/templates/flying_wings.py +++ b/sharpy/cases/templates/flying_wings.py @@ -600,8 +600,7 @@ def set_default_config_dict(self): config['Modal'] = {'NumLambda': 20, 'rigid_body_modes': 'off', 'print_matrices': 'off', - 'keep_linear_matrices': 'on', - 'write_dat': 'off', + 'save_data': 'off', 'continuous_eigenvalues': 'off', 'dt': 0, 'plot_eigenvalues': False, diff --git a/cases/templates/template_wt.py b/sharpy/cases/templates/template_wt.py similarity index 99% rename from cases/templates/template_wt.py rename to sharpy/cases/templates/template_wt.py index 25beef137..e36687d17 100644 --- a/cases/templates/template_wt.py +++ b/sharpy/cases/templates/template_wt.py @@ -4,7 +4,7 @@ Functions needed to generate a wind turbines Notes: - To load this library: import cases.templates.template_wt as template_wt + To load this library: import sharpy.cases.templates.template_wt as template_wt """ diff --git a/sharpy/io/inout_variables.py b/sharpy/io/inout_variables.py index 1d115dffc..fec6d8082 100644 --- a/sharpy/io/inout_variables.py +++ b/sharpy/io/inout_variables.py @@ -58,8 +58,31 @@ def get_variable_value(self, data, timestep_index=-1): """ if self.node is not None: # structural variables for now - variable = getattr(data.structure.timestep_info[timestep_index], self.name) - if len(variable.shape) == 2: + try: + #Look for the variables in time_step_info + variable = getattr(data.structure.timestep_info[timestep_index], self.name) + except AttributeError: + try: + #First get the dict postproc_cell and the try to find the variable in it. + get_postproc_cell = getattr(data.structure.timestep_info[timestep_index], 'postproc_cell') + variable = get_postproc_cell[self.name] + except (KeyError, AttributeError): + msg = ('Node {} is neither in timestep_info nor in postproc_cell.'.format(self.node)) + logger.error(msg) + raise IndexError(msg) + + #Needed for for_pos and for_vel since they are arrays. + if len(variable.shape) == 1: + try: + value = variable[self.node, self.index] + except IndexError: + msg = 'Node {} and/or Index {} are out of index of variable {}, ' \ + 'which is of size ({})'.format(self.node, self.index, self.dref_name, + variable.shape) + logger.error(msg) + raise IndexError(msg) + + elif len(variable.shape) == 2: try: value = variable[self.node, self.index] except IndexError: @@ -135,24 +158,48 @@ def set_in_timestep(self, data): data (sharpy.presharpy.PreSharpy): Simulation data object """ - if self.node is not None: # structural variable then - variable = getattr(data.structure.timestep_info[-1], self.name) - try: - variable[self.node, self.index] = self.value - except IndexError: - logger.warning('Unable to set node {}, index {} of variable {}'.format( - self.node, self.index, self.dref_name - )) + if self.node is not None: + # Check if the node is an app_forces (f.e. Thrust) + if self.name == 'app_forces': + logger.debug('Setting thrust variable') + variable = data.structure.ini_info.steady_applied_forces + try: + variable[self.node, self.index] = self.value + except IndexError: + logger.warning('Unable to set node {}, index {} of variable {}'.format( + self.node, self.index, self.dref_name + )) + + data.structure.ini_info.steady_applied_forces = variable + logger.debug('Updated timestep') + + # else it is a structural variable else: + variable = getattr(data.structure.timestep_info[-1], self.name) + try: + variable[self.node, self.index] = self.value + except IndexError: + logger.warning('Unable to set node {}, index {} of variable {}'.format( + self.node, self.index, self.dref_name + )) + setattr(data.structure.timestep_info[-1], self.name, variable) logger.debug('Updated timestep') if self.cs_index is not None: variable = getattr(data.aero.timestep_info[-1], self.name) - if len(variable) == 0: - variable = np.hstack((variable, np.array([self.value]))) - else: + + # Creates an array as long as needed. Not required Cs_deflections will be set to zero. If the CS_type in the + # aero.h5 file is 0 this shouldnt have a influence on them. + + while len(variable) <= self.cs_index: + # Adds an element in the array for the new control surface. + variable = np.hstack((variable, np.array(0))) + try: variable[self.cs_index] = self.value + except IndexError: + logger.warning('Unable to set control surface deflection {}. Check the order of ' + 'you control surfaces.'.format(self.cs_index)) setattr(data.aero.timestep_info[-1], self.name, variable) logger.debug('Updated control surface deflection') diff --git a/sharpy/linear/assembler/lincontrolsurfacedeflector.py b/sharpy/linear/assembler/lincontrolsurfacedeflector.py index 169a37c9a..11dbb84fd 100644 --- a/sharpy/linear/assembler/lincontrolsurfacedeflector.py +++ b/sharpy/linear/assembler/lincontrolsurfacedeflector.py @@ -4,12 +4,23 @@ import sharpy.linear.utils.ss_interface as ss_interface import numpy as np import sharpy.utils.algebra as algebra +import sharpy.linear.src.libsparse as libsp +import scipy.sparse as sp +import sharpy.linear.src.libss as libss @ss_interface.linear_system class LinControlSurfaceDeflector(object): """ - Subsystem that deflects control surfaces for use with linear state space systems + Subsystem that deflects control surfaces for use with linear state space systems. + + Note: + The control surface sign convention is different to the convention in the nonlinear solver. See + https://www.github.com/imperialcollegelondon/sharpy/issues/193 for more details. + + In the linear implementation, the control surface deflection sign convention follows the :math:`x_B` vector, + thus, in order to have symmetric control surface deflections, additional inputs may be required compared to the + implementation in the nonlinear solver. The current version supports only deflections. Future work will include standalone state-space systems to model physical actuators. @@ -31,11 +42,11 @@ def __init__(self): self.tsaero0 = None self.tsstruct0 = None + self.gain_cs = None # type: np.ndarray # input gain to the UVLM + + self.print_info = False # used for debugging purposes + def initialise(self, data, linuvlm): - # Tasks: - # 1 - Generic information - # * How many control surfaces (number of actual inputs) - # * How many uvlm surfaces self.n_control_surfaces = data.aero.n_control_surfaces self.linuvlm = linuvlm @@ -93,7 +104,7 @@ def generate(self): if global_node in structure.connectivities[i_elem, :]: i_local_node = np.where(structure.connectivities[i_elem, :] == global_node)[0][0] - for_delta = structure.frame_of_reference_delta[i_elem, :, 0] + for_delta = structure.frame_of_reference_delta[i_elem, i_local_node, :] # CRV to transform from G to B frame psi = tsstruct0.psi[i_elem, i_local_node] @@ -165,27 +176,80 @@ def generate(self): zeta_node = zeta0[i_vertex] # Gframe chord_vec = (zeta_node - zeta_hinge) + if self.print_info: + print(f'i_node = {global_node}') + print(f'i_node_chord = {i_node_chord}') + print(f'zeta_node = {zeta_node}') + print(f'chord_vec = {chord_vec}') + print(f'zeta_hinge = {zeta_hinge}') + print(f'zeta_next_hinge = {zeta_next_hinge}') + print(f'hinge_axis = {hinge_axis}') + + # Flap displacement Kdisp[i_vertex, i_control_surface] = \ - Cgb.dot(der_R_arbitrary_axis_times_v(Cbg.dot(hinge_axis), - 0, - -for_delta * Cbg.dot(chord_vec))) * for_delta * -1 + der_R_arbitrary_axis_times_v(hinge_axis, 0, chord_vec) # Flap velocity - Kvel[i_vertex, i_control_surface] = -algebra.skew(-for_delta * chord_vec).dot( - hinge_axis) * for_delta * -1 + Kvel[i_vertex, i_control_surface] = -algebra.skew(chord_vec).dot( + hinge_axis) + + if self.print_info: + print('Matrix entries:') + print('Kdisp:') + print(Kdisp[i_vertex, i_control_surface]) + print('Kvel:') + print(Kvel[i_vertex, i_control_surface]) else: with_control_surface = False hinge_axis = None # Reset for next control surface - # >>>> Merge control surfaces 0 and 1 - # Kdisp[:, 0] -= Kdisp[:, 1] - # Kvel[:, 0] -= Kvel[:, 1] - self.Kzeta_delta = Kdisp self.Kdzeta_ddelta = Kvel + return Kdisp, Kvel + def apply(self, ss): + """ + Applies the control surface deflection to the UVLM state space + + Args: + ss (libss.StateSpace): UVLM state space + + Returns: + libss.StateSpace: UVLM state-space with control surfaces and control surface deflection rate as inputs + """ + + Kzeta_delta, Kdzeta_ddelta = self.generate() + n_zeta, n_ctrl_sfc = Kzeta_delta.shape + + if type(ss.A) is libsp.csc_matrix: + gain_cs = sp.eye(ss.inputs, ss.inputs + 2 * self.n_control_surfaces, + format='lil') + gain_cs[:n_zeta, ss.inputs: ss.inputs + n_ctrl_sfc] = Kzeta_delta + gain_cs[n_zeta: 2*n_zeta, ss.inputs + n_ctrl_sfc: ss.inputs + 2 * n_ctrl_sfc] = Kdzeta_ddelta + gain_cs = libsp.csc_matrix(gain_cs) + else: + gain_cs = np.eye(ss.inputs, ss.inputs + 2 * self.n_control_surfaces) + gain_cs[:n_zeta, ss.inputs: ss.inputs + n_ctrl_sfc] = Kzeta_delta + gain_cs[n_zeta: 2*n_zeta, ss.inputs + n_ctrl_sfc: ss.inputs + 2 * n_ctrl_sfc] = Kdzeta_ddelta + + control_surface_gain = libss.Gain(gain_cs) + in_vars = ss.input_variables.copy() + in_vars.append('control_surface_deflection', size=n_ctrl_sfc) + in_vars.append('dot_control_surface_deflection', size=n_ctrl_sfc) + control_surface_gain.input_variables = in_vars + control_surface_gain.output_variables = ss_interface.LinearVector.transform(ss.input_variables, + to_type=ss_interface.OutputVariable) + + self.gain_cs = control_surface_gain + + return ss + # def generator(): + # future feature idea: instead of defining the inputs for the time domain simulations as the whole input vector + # etc, we could add a generate() method to these systems that can be called from the LinDynamicSim to apply + # the gust and generate the correct input vector. + def der_Cx_by_v(delta, v): sd = np.sin(delta) diff --git a/sharpy/linear/assembler/linearaeroelastic.py b/sharpy/linear/assembler/linearaeroelastic.py index 370ba2e75..752654cd1 100644 --- a/sharpy/linear/assembler/linearaeroelastic.py +++ b/sharpy/linear/assembler/linearaeroelastic.py @@ -8,6 +8,8 @@ import sharpy.utils.cout_utils as cout import sharpy.utils.algebra as algebra import sharpy.utils.generator_interface as gi +from sharpy.linear.utils.ss_interface import LinearVector, InputVariable, StateVariable, OutputVariable +import sharpy.aero.utils.utils as aero_utils @ss_interface.linear_system @@ -101,30 +103,8 @@ def initialise(self, data): self.uvlm = ss_interface.initialise_system('LinearUVLM') self.uvlm.initialise(data, custom_settings=self.settings['aero_settings']) - # Look for the aerodynamic solver - if 'StaticUvlm' in data.settings: - aero_solver_name = 'StaticUvlm' - aero_solver_settings = data.settings['StaticUvlm'] - elif 'StaticCoupled' in data.settings: - aero_solver_name = data.settings['StaticCoupled']['aero_solver'] - aero_solver_settings = data.settings['StaticCoupled']['aero_solver_settings'] - elif 'StaticCoupledRBM' in data.settings: - aero_solver_name = data.settings['StaticCoupledRBM']['aero_solver'] - aero_solver_settings = data.settings['StaticCoupledRBM']['aero_solver_settings'] - elif 'DynamicCoupled' in data.settings: - aero_solver_name = data.settings['DynamicCoupled']['aero_solver'] - aero_solver_settings = data.settings['DynamicCoupled']['aero_solver_settings'] - elif 'StepUvlm' in data.settings: - aero_solver_name = 'StepUvlm' - aero_solver_settings = data.settings['StepUvlm'] - else: - raise RuntimeError("ERROR: aerodynamic solver not found") - - # Velocity generator - vel_gen_name = aero_solver_settings['velocity_field_generator'] - vel_gen_settings = aero_solver_settings['velocity_field_input'] - # Get the minimum parameters needed to define the wake + vel_gen_name, vel_gen_settings = aero_utils.find_velocity_generator(data.settings) vel_gen_type = gi.generator_from_string(vel_gen_name) vel_gen = vel_gen_type() vel_gen.initialise(vel_gen_settings) @@ -198,6 +178,12 @@ def assemble(self): if not self.load_uvlm_from_file: # Projecting the UVLM inputs and outputs onto the structural degrees of freedom Ksa = self.Kforces[:beam.sys.num_dof, :] # maps aerodynamic grid forces to nodal forces + gain_ksa = libss.Gain(Ksa) + gain_ksa.input_variables = LinearVector.transform(uvlm.ss.output_variables, to_type=InputVariable) + if beam.sys.Kin is not None: + gain_ksa.output_variables = LinearVector.transform(beam.sys.Kin.input_variables, to_type=OutputVariable) + else: + gain_ksa.output_variables = LinearVector.transform(beam.ss.input_variables, to_type=OutputVariable) # Map the nodal displacement and velocities onto the grid displacements and velocities Kas = np.zeros((uvlm.ss.inputs, 2*beam.sys.num_dof + (uvlm.ss.inputs - 2*self.Kdisp.shape[0]))) @@ -208,12 +194,23 @@ def assemble(self): # Retain other inputs Kas[2*self.Kdisp.shape[0]:, 2*beam.sys.num_dof:] = np.eye(uvlm.ss.inputs - 2 * self.Kdisp.shape[0]) + gain_kas = libss.Gain(Kas) + gain_kas.output_variables = LinearVector.transform(uvlm.ss.input_variables, to_type=OutputVariable) + if beam.sys.Kout is not None: + kas_in_vars = LinearVector.transform(beam.sys.Kout.output_variables, to_type=InputVariable) + else: + kas_in_vars = LinearVector.transform(beam.ss.output_variables, to_type=InputVariable) + for variable in uvlm.ss.input_variables: + if variable.name not in ['zeta', 'zeta_dot']: + kas_in_vars.append(variable) + gain_kas.input_variables = kas_in_vars + # Scaling if uvlm.scaled: Kas /= uvlm.sys.ScalingFacts['length'] - uvlm.ss.addGain(Ksa, where='out') - uvlm.ss.addGain(Kas, where='in') + uvlm.connect_output(gain_ksa) + uvlm.connect_input(gain_kas) # Stiffenning and damping terms within the uvlm Dmod = np.zeros_like(uvlm.ss.D) @@ -227,8 +224,8 @@ def assemble(self): Dmod /= uvlm.sys.ScalingFacts['force'] uvlm.ss.D += Dmod - self.couplings['Ksa'] = Ksa - self.couplings['Kas'] = Kas + self.couplings['Ksa'] = gain_ksa + self.couplings['Kas'] = gain_kas if self.settings['beam_settings']['modal_projection'] is True and \ self.settings['beam_settings']['inout_coords'] == 'modes': @@ -237,10 +234,29 @@ def assemble(self): in_mode_matrix = np.zeros((uvlm.ss.inputs, beam.ss.outputs + (uvlm.ss.inputs - 2*beam.sys.num_dof))) in_mode_matrix[:2*beam.sys.num_dof, :2*beam.sys.num_modes] = sclalg.block_diag(phi, phi) in_mode_matrix[2*beam.sys.num_dof:, 2*beam.sys.num_modes:] = np.eye(uvlm.ss.inputs - 2*beam.sys.num_dof) + + in_mode_gain = libss.Gain(in_mode_matrix) + in_mode_inputs = LinearVector.transform(beam.ss.output_variables, to_type=InputVariable) + LinearVector.check_same_vectors(in_mode_inputs, beam.ss.output_variables) + for variable in uvlm.ss.input_variables.copy(): + if variable.name not in ['eta', 'eta_dot', 'beta_bar', 'beta']: + in_mode_inputs.append(variable) + + in_mode_gain.input_variables = in_mode_inputs + + in_mode_gain.output_variables = LinearVector.transform(uvlm.ss.input_variables, to_type=OutputVariable) + out_mode_matrix = phi.T + out_mode_gain = libss.Gain(out_mode_matrix, + input_vars=LinearVector.transform(uvlm.ss.output_variables, + to_type=InputVariable), + output_vars=LinearVector.transform(beam.ss.input_variables, + to_type=OutputVariable)) - uvlm.ss.addGain(in_mode_matrix, where='in') - uvlm.ss.addGain(out_mode_matrix, where='out') + uvlm.connect_input(in_mode_gain) + uvlm.connect_output(out_mode_gain) + self.couplings['in_mode_gain'] = in_mode_gain + self.couplings['out_mode_gain'] = out_mode_gain # Reduce uvlm projected onto structural coordinates if uvlm.rom: @@ -254,18 +270,22 @@ def assemble(self): uvlm.ss = self.load_uvlm(self.settings['uvlm_filename']) # Coupling matrices - Tas = np.eye(uvlm.ss.inputs, beam.ss.outputs) - Tsa = np.eye(beam.ss.inputs, uvlm.ss.outputs) + Tas = libss.Gain(np.eye(uvlm.ss.inputs, beam.ss.outputs), + input_vars=LinearVector.transform(beam.ss.output_variables, to_type=InputVariable), + output_vars=LinearVector.transform(uvlm.ss.input_variables, to_type=OutputVariable)) + Tsa = libss.Gain(np.eye(beam.ss.inputs, uvlm.ss.outputs), + input_vars=LinearVector.transform(uvlm.ss.output_variables, to_type=InputVariable), + output_vars=LinearVector.transform(beam.ss.input_variables, to_type=OutputVariable)) # Scale coupling matrices if uvlm.scaled: - Tsa *= uvlm.sys.ScalingFacts['force'] * uvlm.sys.ScalingFacts['time'] ** 2 + Tsa.value *= uvlm.sys.ScalingFacts['force'] * uvlm.sys.ScalingFacts['time'] ** 2 if rigid_dof > 0: - Tas[:flex_nodes + 6, :flex_nodes + 6] /= uvlm.sys.ScalingFacts['length'] - Tas[total_dof: total_dof + flex_nodes + 6] /= uvlm.sys.ScalingFacts['length'] + Tas.value[:flex_nodes + 6, :flex_nodes + 6] /= uvlm.sys.ScalingFacts['length'] + Tas.value[total_dof: total_dof + flex_nodes + 6] /= uvlm.sys.ScalingFacts['length'] else: if not self.settings['beam_settings']['modal_projection']: - Tas /= uvlm.sys.ScalingFacts['length'] + Tas.value /= uvlm.sys.ScalingFacts['length'] ss = libss.couple(ss01=uvlm.ss, ss02=beam.ss, K12=Tas, K21=Tsa) @@ -303,14 +323,19 @@ def update(self, u_infty): u_infty (float): New reference velocity Returns: - sharpy.linear.src.libss.ss: Updated aeroelastic state-space system + sharpy.linear.src.libss.StateSpace: Updated aeroelastic state-space system """ t_ref = self.uvlm.sys.ScalingFacts['length'] / u_infty self.beam.sys.update_matrices_time_scale(t_ref) self.beam.sys.assemble() - self.beam.ss = self.beam.sys.SSdisc + if self.beam.sys.SSdisc is not None: + self.beam.ss = self.beam.sys.SSdisc + elif self.beam.sys.SScont is not None: + self.beam.ss = self.beam.sys.SScont + else: + raise AttributeError('Could not find either a continuous or discrete system in Beam') self.ss = libss.couple(ss01=self.uvlm.ss, ss02=self.beam.ss, K12=self.couplings['Tas'], K21=self.couplings['Tsa']) @@ -325,7 +350,7 @@ def runrom_rbm(self, uvlm): else: orient_dof = 4 # Input side - if self.settings['beam_settings']['modal_projection'] == True and \ + if self.settings['beam_settings']['modal_projection'] is True and \ self.settings['beam_settings']['inout_coords'] == 'modes': rem_int_modes = np.zeros((ss.inputs, ss.inputs - rig_nodes)) rem_int_modes[rig_nodes:, :] = np.eye(ss.inputs - rig_nodes) @@ -342,7 +367,22 @@ def runrom_rbm(self, uvlm): rem_quat_out[j, i] = 1 j += 1 + in_vars = ss.input_variables.copy() + in_vars.modify('q', size=in_vars.get_variable_from_name('q').size - rig_nodes) + remove_integro_inputs = libss.Gain(rem_int_modes, + input_vars=in_vars, + output_vars=libss.LinearVector.transform(ss.input_variables, + to_type=OutputVariable)) + + out_vars = ss.output_variables.copy() + out_vars.modify('Q', size=out_vars.get_variable_from_name('Q').size-orient_dof) + remove_quaternion_out = libss.Gain(rem_quat_out, + input_vars=libss.LinearVector.transform(ss.output_variables, + to_type=InputVariable), + output_vars=out_vars) + else: + # TODO: THESE NEED DOING rem_int_modes = np.zeros((ss.inputs, ss.inputs - rig_nodes)) rem_int_modes[:self.beam.sys.num_dof_flex, :self.beam.sys.num_dof_flex] = \ np.eye(self.beam.sys.num_dof_flex) @@ -352,13 +392,15 @@ def runrom_rbm(self, uvlm): rem_quat_out = np.zeros((ss.outputs-orient_dof, ss.outputs)) rem_quat_out[:, :-orient_dof] = np.eye(ss.outputs-orient_dof) - ss.addGain(rem_int_modes, where='in') - ss.addGain(rem_quat_out, where='out') + ss.addGain(remove_integro_inputs, where='in') + ss.addGain(remove_quaternion_out, where='out') for k, rom in uvlm.rom.items(): uvlm.ss = rom.run(uvlm.ss) - uvlm.ss.addGain(rem_int_modes.T, where='in') - uvlm.ss.addGain(rem_quat_out.T, where='out') + add_integro_inputs = remove_integro_inputs.transpose() + add_quaternion_outputs = remove_quaternion_out.transpose() + uvlm.ss.addGain(add_integro_inputs, where='in') + uvlm.ss.addGain(add_quaternion_outputs, where='out') def get_gebm2uvlm_gains(self, data): r""" @@ -892,17 +934,55 @@ def get_gebm2uvlm_gains(self, data): self.Crr = Crr def to_nodal_coordinates(self): + """ + Transforms the outputs of the system to nodal coordinates if they were previously expressed in modal space + """ is_modal = self.beam.sys.modal if is_modal: - phi = self.beam.sys.U - - # these would include control surface deflections, thrust etc. - non_structural_inputs = self.uvlm.ss.inputs - self.beam.ss.outputs - - input_gain = sclalg.block_diag(phi.T, phi.T, np.eye(non_structural_inputs), phi.T) - output_gain = sclalg.block_diag(phi, phi, phi) + beam_kin = self.beam.sys.Kin # N to Q + beam_kout = self.beam.sys.Kout # q to eta + + aug_in_gain = sclalg.block_diag(self.couplings['in_mode_gain'].value.T, beam_kin.value) + input_gain = libss.Gain(aug_in_gain, + input_vars=LinearVector.merge( + LinearVector.transform( + self.couplings['in_mode_gain'].output_variables, to_type=InputVariable), + beam_kin.input_variables), + output_vars=LinearVector.merge( + LinearVector.transform( + self.couplings['in_mode_gain'].input_variables, to_type=OutputVariable), + beam_kin.output_variables) + ) + try: + acceleration_gain = self.beam.sys.acceleration_modal_gain + except AttributeError: + aug_out_gain = sclalg.block_diag(self.couplings['out_mode_gain'].value.T, beam_kout.value) + output_gain = libss.Gain(aug_out_gain, + input_vars=LinearVector.merge( + LinearVector.transform( + self.couplings['out_mode_gain'].output_variables, to_type=InputVariable), + beam_kout.input_variables), + output_vars=LinearVector.merge( + LinearVector.transform( + self.couplings['out_mode_gain'].input_variables, to_type=OutputVariable), + beam_kout.output_variables) + ) + else: + aug_out_gain = sclalg.block_diag(self.couplings['out_mode_gain'].value.T, beam_kout.value, + acceleration_gain.value) + output_gain = libss.Gain(aug_out_gain, + input_vars=LinearVector.merge( + LinearVector.transform( + self.couplings['out_mode_gain'].output_variables, to_type=InputVariable), + LinearVector.merge(beam_kout.input_variables, acceleration_gain.input_variables), + ), + output_vars=LinearVector.merge( + LinearVector.transform( + self.couplings['out_mode_gain'].input_variables, to_type=OutputVariable), + LinearVector.merge(beam_kout.output_variables, acceleration_gain.output_variables)) + ) self.ss.addGain(input_gain, where='in') self.ss.addGain(output_gain, where='out') @@ -914,4 +994,5 @@ def load_uvlm(filename): read_data = h5.readh5(filename).ss # uvlm_ss_read = read_data.linear.linear_system.uvlm.ss uvlm_ss_read = read_data - return libss.ss(uvlm_ss_read.A, uvlm_ss_read.B, uvlm_ss_read.C, uvlm_ss_read.D, dt=uvlm_ss_read.dt) + return libss.StateSpace(uvlm_ss_read.A, uvlm_ss_read.B, uvlm_ss_read.C, uvlm_ss_read.D, dt=uvlm_ss_read.dt) + diff --git a/sharpy/linear/assembler/linearbeam.py b/sharpy/linear/assembler/linearbeam.py index 8d88ea412..e98d8efb6 100644 --- a/sharpy/linear/assembler/linearbeam.py +++ b/sharpy/linear/assembler/linearbeam.py @@ -2,6 +2,7 @@ Linear State Beam Element Class """ +import h5py from sharpy.linear.utils.ss_interface import BaseElement, linear_system, LinearVector import sharpy.linear.src.lingebm as lingebm @@ -9,6 +10,8 @@ import sharpy.utils.settings as settings import sharpy.utils.algebra as algebra import sharpy.structure.utils.modalutils as modalutils +from sharpy.linear.utils.ss_interface import VectorVariable, OutputVariable, InputVariable +import sharpy.linear.src.libss as libss @linear_system @@ -21,7 +24,18 @@ class LinearBeam(BaseElement): State-space models can be defined in continuous or discrete time (dt required). Modal projection, either on the damped or undamped modal shapes, - is also avaiable. + is also available. + + The beam state space has information on the states which will depend on whether the system is modal or expressed + in physical coordinates. + + If ``modal`` the state variables will be ``q`` and ``q_dot`` representing the modal displacements and the + time derivatives. + + If ``nodal`` and free-flying, the state variables will be ``eta`` for the flexible degrees of freedom (displacements + and CRVs for each node (dim6)), ``V`` representing the linear velocities at the A frame (dim3), ``W`` representing + the angular velocities at the ``A`` frame (dim3), and ``orient`` representing the orientation variable of the + ``A`` frame with respect to ``G`` Notes on the settings: @@ -101,7 +115,7 @@ class LinearBeam(BaseElement): settings_types['newmark_damp'] = 'float' settings_description['newmark_damp'] = 'Newmark damping value. For systems assembled using ``newmark``' - settings_default['use_euler'] = False + settings_default['use_euler'] = True settings_types['use_euler'] = 'bool' settings_description['use_euler'] = 'Use euler angles for rigid body parametrisation' @@ -115,13 +129,19 @@ class LinearBeam(BaseElement): settings_types['remove_dofs'] = 'list(str)' settings_default['remove_dofs'] = [] - settings_description['remove_dofs'] = 'Remove desired degrees of freedom' + settings_description['remove_dofs'] = 'Remove desired degrees of freedom (flexible DOFs, ' \ + 'linear velocities, rotational velocities, orientation)' settings_options['remove_dofs'] = ['eta', 'V', 'W', 'orient'] settings_types['remove_sym_modes'] = 'bool' settings_default['remove_sym_modes'] = False settings_description['remove_sym_modes'] = 'Remove symmetric modes if wing is clamped' + settings_types['remove_rigid_states'] = 'bool' + settings_default['remove_rigid_states'] = False + settings_description['remove_rigid_states'] = '(For Stability Derivatives) - Remove RIGID STATES from SS leaving' \ + ' input/output channels unchanged' + settings_table = settings.SettingsTable() __doc__ += settings_table.generate(settings_types, settings_default, settings_description, settings_options) @@ -147,30 +167,36 @@ def initialise(self, data, custom_settings=None): settings.to_custom_types(self.settings, self.settings_types, self.settings_default, self.settings_options, no_ctype=True) - self.settings['rigid_modes_cg'] = data.settings['Modal']['rigid_modes_cg'] # use the same value as in Modal solver + self.settings['rigid_modes_ppal_axes'] = data.settings['Modal']['rigid_modes_ppal_axes'] # use the same value as in Modal solver beam = lingebm.FlexDynamic(data.linear.tsstruct0, data.structure, self.settings) self.sys = beam self.tsstruct0 = data.linear.tsstruct0 - # State variables + # State variables - for the purposes of dof removal PRIOR to first order system assembly num_dof_flex = self.sys.structure.num_dof.value num_dof_rig = self.sys.Mstr.shape[0] - num_dof_flex - state_db = {'eta': [0, num_dof_flex], - 'V_bar': [num_dof_flex, num_dof_flex + 3], - 'W_bar': [num_dof_flex + 3, num_dof_flex + 6], - 'orient_bar': [num_dof_flex + 6, num_dof_flex + num_dof_rig], - 'dot_eta': [num_dof_flex + num_dof_rig, 2 * num_dof_flex + num_dof_rig], - 'V': [2 * num_dof_flex + num_dof_rig, 2 * num_dof_flex + num_dof_rig + 3], - 'W': [2 * num_dof_flex + num_dof_rig + 3, 2 * num_dof_flex + num_dof_rig + 6], - 'orient': [2 * num_dof_flex + num_dof_rig + 6, 2 * num_dof_flex + 2 * num_dof_rig]} - self.state_variables = LinearVector(state_db, self.sys_id) + + # Variables described in class docstring. If modified, remember to change docstring + if num_dof_rig == 0: + state_variable_list = [ + VectorVariable('eta', size=num_dof_flex, index=0), + ] + else: + state_variable_list = [ + VectorVariable('eta', size=num_dof_flex, index=0), # flexible dofs + VectorVariable('V', size=3, index=1), # translational velocities + VectorVariable('W', size=3, index=2), # angular velocities + VectorVariable('orient', size=num_dof_rig - 6, index=3), # orientation + ] + + self.state_variables = LinearVector(state_variable_list) if num_dof_rig == 0: self.clamped = True self.linearisation_vectors['eta'] = self.tsstruct0.q self.linearisation_vectors['eta_dot'] = self.tsstruct0.dqdt - self.linearisation_vectors['forces_struct'] = self.tsstruct0.steady_applied_forces.reshape(-1, order='C') + self.linearisation_vectors['forces_struct'] = self.tsstruct0.steady_applied_forces.reshape(-1, order='C') # B frame def assemble(self, t_ref=None): """ @@ -210,63 +236,70 @@ def assemble(self, t_ref=None): elif self.sys.SScont: self.ss = self.sys.SScont + if self.settings['remove_rigid_states']: + # Temporary - should be incorporated into StabilityDerivatives and the coupled system reassembled + self.remove_rigid_states() + return self.ss + def remove_rigid_states(self): + if self.sys.clamped: + return + num_rig_dof = 9 + if self.sys.modal: + + self.ss.A = self.ss.A[num_rig_dof:, num_rig_dof:] + self.ss.B = self.ss.B[num_rig_dof:, :] + self.ss.C = self.ss.C[:, num_rig_dof:] + self.ss.state_variables.modify('q', size=self.ss.state_variables[0].size - num_rig_dof) + self.ss.state_variables.update_locations() + + retain_state = np.zeros((self.ss.states - 9, self.ss.states)) + retain_state[:self.ss.state_variables[0].size, :self.ss.state_variables[0].size] = \ + np.eye(self.ss.state_variables[0].size) + retain_state[self.ss.state_variables[0].size:, self.ss.state_variables[0].size + 9:] = \ + np.eye(self.ss.state_variables[0].size) + self.ss.A = retain_state.dot(self.ss.A.dot(retain_state.T)) + self.ss.B = retain_state.dot(self.ss.B) + self.ss.C = self.ss.C.dot(retain_state.T) + self.ss.state_variables.modify('q_dot', size=self.ss.state_variables[1].size - 9) + self.ss.state_variables.update_locations() + + else: + retain_state = np.zeros((self.ss.states - 2 * num_rig_dof, self.ss.states)) + eta_size = self.ss.state_variables[0].size + retain_state[:eta_size, :eta_size] = np.eye(eta_size) + retain_state[eta_size: 2 * eta_size, eta_size + 9: 2 * eta_size + 9] = np.eye(eta_size) + + self.ss.A = retain_state.dot(self.ss.A.dot(retain_state.T)) + self.ss.B = retain_state.dot(self.ss.B) + self.ss.C = self.ss.C.dot(retain_state.T) + self.ss.state_variables.remove('beta_bar') + self.ss.state_variables.remove('beta') + self.ss.state_variables.update_locations() + def x0(self): x = np.concatenate((self.tsstruct0.q, self.tsstruct0.dqdt)) return x def trim_nodes(self, trim_list=list): + """ + Removes degrees of freedom from the second order system. + + Args: + trim_list (list): List of degrees of freedom to remove ``eta``, ``V``, ``W`` or ``orient`` + + """ num_dof_flex = self.sys.structure.num_dof.value - num_dof_rig = self.sys.Mstr.shape[0] - num_dof_flex - # Dictionary containing DOFs and corresponding equations - dof_db = {'eta': [0, num_dof_flex, 1], - 'V': [num_dof_flex, num_dof_flex + 3, 2], - 'W': [num_dof_flex + 3, num_dof_flex + 6, 3], - 'orient': [num_dof_flex + 6, num_dof_flex + num_dof_rig, 4], - 'yaw': [num_dof_flex + 8, num_dof_flex + num_dof_rig, 1]} - - # ----------------------------------------------------------------------- - # Better to place in a function available to all elements since it will equally apply - # Therefore, the dof_db should be a class attribute - # Take away alongside the vector variable class - - # All variables - vec_db = dict() - for item in dof_db: - vector_var = VectorVariable(item, dof_db[item], 'LinearBeam') - vec_db[item] = vector_var - - used_vars_db = vec_db.copy() - - # Variables to remove - removed_dofs = 0 - removed_db = dict() - for item in trim_list: - removed_db[item] = vec_db[item] - removed_dofs += vec_db[item].size - del used_vars_db[item] - - # Update variables position - for rem_item in removed_db: - for item in used_vars_db: - if used_vars_db[item].rows_loc[0] < removed_db[rem_item].first_pos: - continue - else: - # Update order and position - used_vars_db[item].first_pos -= removed_db[rem_item].size - used_vars_db[item].end_pos -= removed_db[rem_item].size - - self.state_variables = used_vars_db - # TODO: input and output variables - ### ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + n_dofs = self.state_variables.size + self.state_variables.remove(*trim_list) + removed_dofs = n_dofs - self.state_variables.size + trim_matrix = np.zeros((n_dofs, n_dofs - removed_dofs)) - # Map dofs to equations - trim_matrix = np.zeros((num_dof_rig+num_dof_flex, num_dof_flex+num_dof_rig-removed_dofs)) - for item in used_vars_db: - trim_matrix[used_vars_db[item].rows_loc, used_vars_db[item].cols_loc] = 1 + for variable in self.state_variables: + trim_matrix[variable.rows_loc, variable.cols_loc] = 1 # Update matrices self.sys.Mstr = trim_matrix.T.dot(self.sys.Mstr.dot(trim_matrix)) @@ -392,15 +425,17 @@ def unpack_ss_vector(self, x_n, u_n, struct_tstep): # Missing the forces # dqddt = self.sys.Minv.dot(-self.sys.Cstr.dot(dqdt) - self.sys.Kstr.dot(q)) - for i_node in vdof[vdof >= 0]: - pos[i_node + 1, :] = q[6*i_node: 6*i_node + 3] - pos_dot[i_node + 1, :] = dqdt[6*i_node + 0: 6*i_node + 3] - + # for i_node in vdof[vdof >= 0]: + # pos[i_node + 1, :] = q[6*i_node: 6*i_node + 3] + # pos_dot[i_node + 1, :] = dqdt[6*i_node + 0: 6*i_node + 3] + # # TODO: CRV of clamped node and double check that the CRV takes this form - for i_elem in range(struct_tstep.num_elem): - for i_node in range(struct_tstep.num_node_elem): - psi[i_elem, i_node, :] = np.linalg.inv(algebra.crv2tan(struct_tstep.psi[i_elem, i_node]).T).dot(q[i_node + 3: i_node + 6]) - psi_dot[i_elem, i_node, :] = dqdt[i_node + 3: i_node + 6] + # for i_elem in range(struct_tstep.num_elem): + # for i_node in range(struct_tstep.num_node_elem): + # psi[i_elem, i_node, :] = np.linalg.inv(algebra.crv2tan(struct_tstep.psi[i_elem, i_node]).T).dot(q[i_node + 3: i_node + 6]) + # psi_dot[i_elem, i_node, :] = dqdt[i_node + 3: i_node + 6] + + pos, psi, pos_dot, psi_dot = self.unpack_flex_dof(q, dqdt) if not clamped: for_vel = dqdt[-rig_dof: -rig_dof + 6] @@ -413,9 +448,18 @@ def unpack_ss_vector(self, x_n, u_n, struct_tstep): for_acc = dqddt[-rig_dof:-rig_dof + 6] if u_n is not None: + cba_m = algebra.get_transformation_matrix('ba') for i_node in vdof[vdof >= 0]: - steady_applied_forces[i_node+1] = u_n[6*i_node: 6*i_node + 6] - steady_applied_forces[0] = u_n[-10:-4] - np.sum(steady_applied_forces[1:, :], 0) + i_elem = self.sys.structure.node_master_elem[i_node, 0] + i_local_node = self.sys.structure.node_master_elem[i_node, 1] + cba = cba_m(struct_tstep.psi[i_elem, i_local_node]) + steady_applied_forces[i_node+1, :3] = cba.dot(u_n[6*i_node: 6*i_node + 3]) + steady_applied_forces[i_node+1, 3:] = cba.dot(u_n[6*i_node + 3: 6*i_node + 6]) + i_elem = self.sys.structure.node_master_elem[0, 0] + i_local_node = self.sys.structure.node_master_elem[0, 1] + cba = cba_m(struct_tstep.psi[i_elem, i_local_node]) + steady_applied_forces[0, :3] = cba.dot(u_n[-10:-7]) - np.sum(steady_applied_forces[1:, :3], 0) + steady_applied_forces[0, 3:] = cba.dot(u_n[-7:-4]) - np.sum(steady_applied_forces[1:, 3:], 0) # gravity forces - careful - debug C_grav = np.zeros((q.shape[0], q.shape[0])) @@ -455,28 +499,106 @@ def unpack_ss_vector(self, x_n, u_n, struct_tstep): return current_time_step + def unpack_flex_dof(self, eta, eta_dot=None): + """ + Unpacks a vector of structural displacements and velocities into a SHARPy familiar + form of pos, psi and their time derivatives -class VectorVariable(object): + Args: + eta (np.array): Vector of structural displacements + eta_dot (np.array (Optional): Vector of structural velocities - def __init__(self, name, pos_list, var_system): + Returns: + tuple: Containing ``pos``, ``psi``, ``pos_dot``, ``psi_dot`` if ``eta_dot`` is provided, else + only the displacements are returned + """ + vdof = self.sys.structure.vdof + if np.max(np.abs(eta.imag)) > 0: + dtype=complex + else: + dtype=float + pos = np.zeros_like(self.tsstruct0.pos, dtype=dtype) + psi = np.zeros_like(self.tsstruct0.psi, dtype=dtype) + pos_dot = np.zeros_like(self.tsstruct0.pos_dot, dtype=dtype) + psi_dot = np.zeros_like(self.tsstruct0.psi_dot, dtype=dtype) - self.name = name - self.var_system = var_system + return_vels = True + if eta_dot is None: + return_vels = False + eta_dot = np.zeros_like(eta) - self.first_pos = pos_list[0] - self.end_pos = pos_list[1] - self.rows_loc = np.arange(self.first_pos, self.end_pos, dtype=int) # Original location, should not update + for i_node in vdof[vdof >= 0]: + pos[i_node + 1, :] = eta[6*i_node: 6*i_node + 3] + pos_dot[i_node + 1, :] = eta_dot[6*i_node + 0: 6*i_node + 3] + # TODO: CRV of clamped node and double check that the CRV takes this form + for i_elem in range(self.tsstruct0.num_elem): + for i_node in range(self.tsstruct0.num_node_elem): + psi[i_elem, i_node, :] = np.linalg.inv( + algebra.crv2tan(self.tsstruct0.psi[i_elem, i_node]).T).dot(eta[i_node + 3: i_node + 6]) + psi_dot[i_elem, i_node, :] = eta_dot[i_node + 3: i_node + 6] + + if return_vels: + return pos, psi, pos_dot, psi_dot + else: + return pos, psi - # add methods to reorganise into SHARPy method? + def recover_accelerations(self, full_ss): + """ + For a system with displacement and velocity outputs (``full_ss``), recover the accelerations and append them + as new output channels. - @property - def cols_loc(self): - return np.arange(self.first_pos, self.end_pos, dtype=int) + This function produces an output gain that should then be connected in series to the desired system - @property - def size(self): - return self.end_pos - self.first_pos + Args: + full_ss (libss.StateSpace): State space for which to provide output gain to recover accelerations -if __name__=='__main__': - print(LinearBeam.__doc__) + Returns: + libss.Gain: Gain adding the accelerations as new output channels + """ + n_in = full_ss.outputs + n_out = full_ss.outputs + self.ss.states // 2 + + acc_gain = np.zeros((n_out, n_in)) + + input_variables = LinearVector.transform(full_ss.output_variables, to_type=InputVariable) + + output_variables = full_ss.output_variables.copy() + acceleration_variables = [] + for var in self.ss.output_variables[self.ss.output_variables.num_variables//2:]: + new_var = var.copy() + new_var.name += '_dot' + output_variables.append(new_var) + acceleration_variables.append(new_var) + + acc_gain[:n_in, :n_in] = np.eye(n_in) + acc_gain[-self.ss.states//2:, :self.ss.inputs] = self.ss.B[self.ss.states//2:] + acc_gain[-self.ss.states//2:, self.ss.inputs:] = self.ss.A[self.ss.states//2:, :] + + acceleration_recovery = libss.Gain(acc_gain, + input_vars=input_variables, + output_vars=output_variables) + + if self.sys.modal: + output_variables = [] + for var in self.sys.Kout.output_variables[self.sys.Kout.output_variables.num_variables//2:]: + new_var = var.copy() + new_var.name += '_dot' + output_variables.append(new_var) + modal_gain = libss.Gain(self.sys.U, + input_vars=LinearVector.transform(LinearVector(acceleration_variables), + to_type=InputVariable), + output_vars=LinearVector(output_variables)) + self.sys.acceleration_modal_gain = modal_gain + + return acceleration_recovery + + def save_reduced_order_bases(self, file_name): + gain = libss.Gain(self.sys.U) + gain.save(file_name) + + def save_structural_matrices(self, file_name): + with h5py.File(file_name, 'w') as f: + f.create_dataset('m', data=self.sys.Mstr) + f.create_dataset('c', data=self.sys.Cstr) + f.create_dataset('k', data=self.sys.Kstr) diff --git a/sharpy/linear/assembler/lineargustassembler.py b/sharpy/linear/assembler/lineargustassembler.py index cfb0f34f2..3a02b4838 100644 --- a/sharpy/linear/assembler/lineargustassembler.py +++ b/sharpy/linear/assembler/lineargustassembler.py @@ -1,58 +1,554 @@ import sharpy.linear.utils.ss_interface as ss_interface import numpy as np -import sharpy.utils.algebra as algebra -import scipy.sparse as scsp import sharpy.linear.src.libss as libss +import scipy.signal as scsig +import sharpy.utils.settings as settings +import sharpy.utils.cout_utils as cout +from abc import ABCMeta, abstractmethod -@ss_interface.linear_system -class LinearGustGenerator(object): - """Reduces the entire gust field input to a user-defined set of more comprehensive inputs +dict_of_linear_gusts = {} + + +def linear_gust(arg): + global dict_of_linear_gusts + try: + arg.gust_id + except AttributeError: + raise AttributeError('Class defined as gust has no gust_id attribute') + dict_of_linear_gusts[arg.gust_id] = arg + return arg + + +def gust_from_string(gust_name): """ - sys_id = 'LinearGustGenerator' + Returns an instance of the ``gust_name`` class + + Args: + gust_name (str): Name of gust + + Returns: + LinearGust: instance of linear gust + """ + return dict_of_linear_gusts[gust_name]() + + +class LinearGust(metaclass=ABCMeta): + # Base class from which to develop the desired gusts + settings_types = {} + settings_default = {} + settings_description = {} + + print_info = True # for debugging def __init__(self): - self.aero = None - self.ss_gust = None - self.Kout = None + self.aero = None #: aerogrid + self.tsaero0 = None # timestep info at the linearisation point + + self.dt = None # type: float + self.remove_predictor = None # type: bool + self.Kzeta = None # type: int # total number of lattice vertices + self.KKzeta = None # type: list # list of number of lattice vertices in each surface + + self.state_to_uext = None #type: np.array # Gust system C matrix (for unpacking into timestep_info) + self.uin_to_uext = None #type: np.array # Gust system D matrix (for unpacking into timestep_info) + self.gust_ss = None #type: libss.StateSpace # containing the gust state-space system - def initialise(self, aero): + self.settings = None + + self.u_ext_direction = None # np.ndarray Unit external velocity vector in G frame + self.u_inf = None # float Free stream velocity magnitude + + def initialise(self, aero, linuvlm, tsaero0, u_ext, custom_settings=None): + """ + Initialise gust class + + Args: + aero (sharpy.aero.models.Aerogrid): + linuvlm (sharpy.linear.src.linuvlm.Dynamic) : + tsaero0 (sharpy.utils.datstructures.AeroTimestepInfo) : time step at reference state + u_ext (np.ndarray): free-stream velocity + custom_settings (dict): + """ self.aero = aero + self.tsaero0 = tsaero0 + self.dt = linuvlm.dt + self.remove_predictor = linuvlm.remove_predictor + self.KKzeta = linuvlm.MS.KKzeta + self.Kzeta = sum(self.KKzeta) + + if custom_settings is not None: + self.settings = custom_settings + settings.to_custom_types(self.settings, self.settings_types, self.settings_default) + + # find free stream velocity + self.u_inf = np.linalg.norm(u_ext) + self.u_ext_direction = u_ext / self.u_inf + + def get_x_max(self): + """ + Returns the maximum and minimum and minimum coordinates of the UVLM lattice projected onto the free-stream + velocity vector + + Returns: + tuple: minimum and maximum coordinates of lattice projected onto velocity vector + """ + max_chord_surf = [] + min_chord_surf = [] + for zeta in self.tsaero0.zeta: + zeta_proj = np.zeros_like(zeta) + _, m, n = zeta.shape + for i_m in range(m): + for i_n in range(n): + zeta_proj[:, i_m, i_n] = zeta[:, i_m, i_n].dot(self.u_ext_direction) * self.u_ext_direction + max_chord_surf.append(np.max(zeta_proj)) + min_chord_surf.append(np.min(zeta_proj)) + return min(min_chord_surf), max(max_chord_surf) + + @abstractmethod + def assemble(self): + """ + Assemble gust system according to specific gust requirements in inherited classes + + Returns: + libss.StateSpace + """ + return libss.StateSpace(0, 0, 0, 0) # placeholder for state space + + def apply(self, ssuvlm): + r""" + Couples in series the gust system assembled by the LinearGust with the Linear UVLM. + + It generates an augmented gust system which feeds through the other UVLM inputs + (grid displacements and velocities). The resulting augmented gust state space takes the form + of: + + .. math:: + \boldsymbol{B}_{aug} = \begin{pmatrix} \boldsymbol{0}_{K_\zeta} & \boldsymbol{0}_{K_\zeta} + & \boldsymbol{B}_{gust} \end{pmatrix} + + .. math:: + \boldsymbol{C}_{aug} = \begin{pmatrix} \boldsymbol{0}_{K_\zeta} \\ \boldsymbol{0}_{K_\zeta} \\ + \boldsymbol{C}_{gust} \end{pmatrix} + + .. math:: + \boldsymbol{D}_{aug} = \begin{pmatrix} \boldsymbol{I}_{K_\zeta} \\ \ & \boldsymbol{I}_{K_\zeta} \\ + \boldsymbol{0}_{K_\zeta} \end{pmatrix} + + where :math:`K_\zeta` is 3 times the number of vertices in the UVLM lattice and the size of the input + sets displacements and velocities, as well as external velocity inputs. + + Therefore, the inputs to the resulting coupled system become + + .. math:: \boldsymbol{u} = \begin{bmatrix} \boldsymbol{\zeta} & \boldsymbol{\dot{\zeta}} & \boldsymbol{u}_{gust} + + where the size of :math:`\boldsymbol{u}_{gust} will depend on the chosen gust assembly scheme. + + Args: + ssuvlm (libss.StateSpace): State space object of the linear UVLM. + + Returns: + libss.StateSpace: Coupled gust system with Linear UVLM. + """ + ssgust = self.assemble() # libss.StateSpace() + # + # Feed through UVLM inputs + b_aug = np.zeros((ssgust.states, ssuvlm.inputs - ssgust.outputs + ssgust.inputs)) + c_aug = np.zeros((ssuvlm.inputs, ssgust.states)) + d_aug = np.zeros((ssuvlm.inputs, b_aug.shape[1])) + b_aug[:, -ssgust.inputs:] = ssgust.B + c_aug[-ssgust.outputs:, :] = ssgust.C + d_aug[:-ssgust.outputs, :-ssgust.inputs] = np.eye(ssuvlm.inputs - ssgust.outputs) + + self.gust_ss = libss.StateSpace(ssgust.A, b_aug, c_aug, d_aug, dt=ssgust.dt) + input_variables = ssuvlm.input_variables.copy() + input_variables.remove('u_gust') + self.gust_ss.input_variables = \ + ss_interface.LinearVector.merge(input_variables, ssgust.input_variables) + self.gust_ss.state_variables = ssgust.state_variables.copy() + self.gust_ss.output_variables = ss_interface.LinearVector.transform(ssuvlm.input_variables, + to_type=ss_interface.OutputVariable) + ss = libss.series(self.gust_ss, ssuvlm) + + return ss + + def assemble_gust_statespace(self, a_i, b_i, c_i, d_i): + """ + Assembles the individual gust system in discrete time and removes the predictor term if that is specified + in the linear UVLM settings. + + Args: + a_i (np.array): Gust A matrix + b_i (np.array): Gust B matrix + c_i (np.array): Gust C matrix + d_i (np.array): Gust D matrix + + Returns: + libss.StateSpace: StateSpace object of the individual gust system + """ + if self.remove_predictor: + a_mod, b_mod, c_mod, d_mod = libss.SSconv(a_i, None, b_i, c_i, d_i) + gustss = libss.StateSpace(a_mod, b_mod, c_mod, d_mod, dt=self.dt) + self.state_to_uext = c_mod + self.uin_to_uext = d_mod + else: + gustss = libss.StateSpace(a_i, b_i, c_i, d_i, + dt=self.dt) + self.state_to_uext = c_i + self.uin_to_uext = d_i + gustss.input_variables = ss_interface.LinearVector( + [ss_interface.InputVariable('u_gust', size=1, index=0)]) + gustss.state_variables = ss_interface.LinearVector( + [ss_interface.StateVariable('gust', size=gustss.states, index=0)]) + gustss.output_variables = ss_interface.LinearVector( + [ss_interface.OutputVariable('u_gust', size=gustss.outputs, index=0)]) + + return gustss + + def discretise_domain(self): + """ + Generates a "gust-station" domain, aligned with the free-stream velocity equispaced in + :math:`\Delta t U_\infty` increments. + + Returns: + tuple: domain and number of elements in the domain + """ + x_min, x_max = self.get_x_max() # G frame, min and max of panels x coordinates + + N = int(np.ceil((x_max - x_min) / self.dt / self.u_inf)) + x_domain = np.linspace(x_min, x_max, N) + + return x_domain, N + + +@linear_gust +class LeadingEdge(LinearGust): + """ + Reduces the gust input to a single input for the vertical component of the gust at the leading edge. + + This is vertical velocity is then convected downstream with the free stream velocity. The gust is uniform in span. + + """ + gust_id = 'LeadingEdge' + + def assemble(self): + """ + Assembles the gust state space system, creating the (A, B, C and D) matrices that convect the single gust input + at the leading edge downstream and uniformly across the span + """ + Kzeta = self.Kzeta # total number of lattice vertices + + # Convection system: needed for as many inputs (since it carries their time histories) + x_domain, N = self.discretise_domain() + + # State Equation + # for each input... + a_i = np.zeros((N, N)) + a_i[1:, :-1] = np.eye(N-1) + b_i = np.zeros((N, 1)) + b_i[0, 0] = 1 + + c_i = np.zeros((3 * Kzeta, N)) + for i_surf in range(self.aero.n_surf): + + M_surf, N_surf = self.aero.aero_dimensions[i_surf] + Kzeta_start = 3 * sum(self.KKzeta[:i_surf]) # number of coordinates up to current surface + shape_zeta = (3, M_surf + 1, N_surf + 1) + + if self.print_info: + cout.cout_wrap(f'Aero surface {i_surf}') + cout.cout_wrap(f'Gust monitoring station domain:\n{x_domain}', 1) + + for i_node_span in range(N_surf + 1): + for i_node_chord in range(M_surf + 1): + i_vertex = [Kzeta_start + np.ravel_multi_index((i_axis, i_node_chord, i_node_span), + shape_zeta) for i_axis in range(3)] + zeta = self.tsaero0.zeta[i_surf][:, i_node_chord, i_node_span] + x_vertex = zeta.dot(self.u_ext_direction) + interpolation_weights, column_indices = linear_interpolation_weights(x_vertex, x_domain) + c_i[i_vertex, column_indices[0]] = np.array([0, 0, interpolation_weights[0]]) + c_i[i_vertex, column_indices[1]] = np.array([0, 0, interpolation_weights[1]]) + + if self.print_info: + cout.cout_wrap(f'Vertex info: i_n = {i_node_span}\ti_m = {i_node_chord}') + cout.cout_wrap(f'\tCoordinate: {zeta}', 1) + cout.cout_wrap(f'\tProjected coordinate: {x_vertex}', 1) + cout.cout_wrap(f'\tInterpolation weights: {interpolation_weights}', 2) + cout.cout_wrap(f'\tC matrix column indices: {column_indices}', 2) + cout.cout_wrap(f'\ti_vertex: {i_vertex}', 2) + + + + d_i = np.zeros((c_i.shape[0], b_i.shape[1])) - def generate(self, linuvlm, aero): + gustss = self.assemble_gust_statespace(a_i, b_i, c_i, d_i) - if aero is None: - aero = self.aero + return gustss - Kzeta = linuvlm.Kzeta - M = linuvlm.MS.MM[0] - # Create state-space to convect gust downstream - A_gust = np.zeros((M+1, M+1)) - A_gust[1:, :-1] = np.eye(M, M) +@linear_gust +class MultiLeadingEdge(LinearGust): + """ + Gust input channels defined at user-defined spanwise locations. Linearly interpolated in between + the spanwise input positions. + + """ + + gust_id = 'MultiLeadingEdge' + + settings_types = {} + settings_default = {} + settings_description = {} + + settings_types['span_location'] = 'list(float)' + settings_default['span_location'] = [-10., 10.] + settings_description['span_location'] = 'Spanwise location of the input streams of the gust' + + settings_table = settings.SettingsTable() + __doc__ += settings_table.generate(settings_types, settings_default, settings_description) + + def __init__(self): + super().__init__() + + self.span_loc = None + self.n_gust = None + + def initialise(self, aero, linuvlm, tsaero0, u_ext, custom_settings=None): + super().initialise(aero, linuvlm, tsaero0, u_ext, custom_settings) + + self.span_loc = self.settings['span_location'] + self.n_gust = len(self.span_loc) - B_gust = np.zeros((M+1, 6 * Kzeta + 1)) - B_gust[0, 6 * Kzeta] = 1 + assert self.n_gust > 1, 'Use LeadingEdge gust for single inputs' - C_gust = np.zeros((9 * Kzeta, M+1)) + def assemble(self): - D_gust = np.zeros((9*Kzeta, 6*Kzeta + 1)) + n_gust = self.n_gust + Kzeta = self.Kzeta + span_loc = self.span_loc - Kout = np.zeros((3 * Kzeta, M+1)) + # Convection system: needed for as many inputs (since it carries their time histories) + x_domain, N = self.discretise_domain() - for i_surf in range(aero.n_surf): + # State Equation + # for each input... + gust_a = np.zeros((N * n_gust, N * n_gust)) + gust_b = np.zeros((N * n_gust, n_gust)) + for ith_gust in range(n_gust): + gust_a[ith_gust * N + 1: ith_gust * N + N, ith_gust * N: ith_gust * N + N - 1] = np.eye(N-1) + gust_b[ith_gust * N, ith_gust] = 1 - M_surf, N_surf = aero.aero_dimensions[i_surf] - Kzeta_start = 3 * sum(linuvlm.MS.KKzeta[:i_surf]) # number of coordinates up to current surface + gust_c = np.zeros((3 * Kzeta, n_gust * N)) + gust_d = np.zeros((gust_c.shape[0], gust_b.shape[1])) + for i_surf in range(self.aero.n_surf): + + M_surf, N_surf = self.aero.aero_dimensions[i_surf] + Kzeta_start = 3 * sum(self.KKzeta[:i_surf]) # number of coordinates up to current surface shape_zeta = (3, M_surf + 1, N_surf + 1) for i_node_span in range(N_surf + 1): for i_node_chord in range(M_surf + 1): i_vertex = [Kzeta_start + np.ravel_multi_index((i_axis, i_node_chord, i_node_span), shape_zeta) for i_axis in range(3)] - Kout[i_vertex, i_node_chord] = np.array([0, 0, 1]) + x_vertex = self.tsaero0.zeta[i_surf][0, i_node_chord, i_node_span] + y_vertex = self.tsaero0.zeta[i_surf][1, i_node_chord, i_node_span] + interpolation_weights, column_indices = spanwise_interpolation(y_vertex, span_loc, x_vertex, x_domain) + for i in range(len(column_indices)): + gust_c[i_vertex, column_indices[i]] = np.array([0, 0, interpolation_weights[i]]) + + gustss = self.assemble_gust_statespace(gust_a, gust_b, gust_c, gust_d) + return gustss + + +def get_freestream_velocity(data): + try: + u_inf = data.settings['StaticUvlm']['aero_solver_settings']['u_inf'] + u_inf_direction = data.settings['StaticCoupled']['aero_solver_settings']['u_inf_direction'] + except KeyError: + try: + u_inf = data.settings['StaticCoupled']['aero_solver_settings']['velocity_field_input']['u_inf'] + u_inf_direction = data.settings['StaticCoupled']['aero_solver_settings']['velocity_field_input']['u_inf_direction'] + except KeyError: + cout.cout_wrap('Unable to find free stream velocity settings in StaticUvlm or StaticCoupled,' + 'please ensure these settings are provided in the config .sharpy file. If' + 'you are running a restart simulation make sure they are included too, regardless' + 'of these solvers being present in the SHARPy flow', 4) + raise KeyError + + try: + v0 = u_inf * u_inf_direction + except TypeError: + # For restart solutions, where the settings may have not been processed and thus may + # exist but in string format + try: + u_inf_direction = np.array(u_inf_direction, dtype=float) + except ValueError: + if u_inf_direction.find(',') < 0: + u_inf_direction = np.fromstring(u_inf_direction.strip('[]'), sep=' ', dtype=float) + else: + u_inf_direction = np.fromstring(u_inf_direction.strip('[]'), sep=',', dtype=float) + finally: + v0 = np.array(u_inf_direction, dtype=float) * float(u_inf) + + return v0 + + +def linear_interpolation_weights(x_vertex, x_domain): + + column_ind_left = np.argwhere(x_domain >= x_vertex)[0][0] - 1 + if column_ind_left == - 1: + column_ind_left = 0 + column_indices = (column_ind_left, column_ind_left + 1) + interpolation_weights = np.array([x_domain[column_ind_left + 1] - x_vertex, x_vertex - x_domain[column_ind_left]]) + interpolation_weights /= (x_domain[column_ind_left + 1] - x_domain[column_ind_left]) + return interpolation_weights, column_indices + + +def chordwise_interpolation(x_vertex, x_domain): + + column_ind_left = np.argwhere(x_domain >= x_vertex)[0][0] - 1 + if column_ind_left == - 1: + column_ind_left = 0 + column_indices = (column_ind_left, column_ind_left + 1) + interpolation_weights = np.array([x_domain[column_ind_left + 1] - x_vertex, x_vertex - x_domain[column_ind_left]]) + interpolation_weights /= (x_domain[column_ind_left + 1] - x_domain[column_ind_left]) + return interpolation_weights, column_indices + + +def spanwise_interpolation(y_vertex, span_loc, x_vertex, x_domain): + r""" + Returns the interpolation weights and indices of said weights for a span wise interpolation. The indices refer to + column indices of a row vector containing the gust disturbance at each chordwise control point. + + This is used in non-span uniform gusts with multiple inputs, where each set of chordwise + control points in the state vector corresponds to one gust (i.e. its time history). + + The interpolation is performed as follows. + + For an arbitrary point within a grid delimited by ``(0, 0)``, ``(0, 1)``, ``(1, 0)`` and ``(1, 1)``, the velocity + at a given point is: + + .. math:: + + u_z = \alpha_1 u_1 + \alpha_0 u_0 + + where :math:`\alpha_1 = \frac{y - y_0}{y_{1} - y_{0}}` and :math:`\alpha_0 = \frac{y_1-y}{y_1-y_0}`. + + The velocities :math:`u_1` and :math:`u_0` are at the current chordwise point, thus translating into + + .. math:: + + u_1 = \beta_{11} u_{11} + \beta_{10} u_{10} \\ + u_0 = \beta_{01} u_{01} + \beta_{00} u_{00} + + where :math:`\beta_{11} = \frac{x - x_0}{x_1 - x_0}` and the rest follow a similar pattern. + + The resulting interpolation scheme gives + + .. math:: + + u_z = \begin{pmatrix} + \alpha_1 \beta_{11} u_{11} & \alpha_1 \beta{10} & \alpha_0 \beta_{01} & \alpha_0 \beta_{00} + \end{pmatrix} + \begin{bmatrix} u_{11} \\ u_{10} \\ u_{01} \\ u_{00} \end{bmatrix}. + + The indices returned as part of the output correspond to the column indices above to match with the + corresponding interpolation control points. + + Args: + y_vertex (np.float): y (span) coordinate of point + span_loc (np.array): Domain of y-coordinates + x_vertex (np.float): x (chord) coordinate of point + x_domain (np.array): Domain of x coordinates + + Returns: + tuple: 2-tuple containing i) the 4-array of interpolation weights and ii) the 4-array of column + indicies to place such weights. + """ + N = len(x_domain) + span_weights, span_indices = linear_interpolation_weights(y_vertex, span_loc) + interpolation_weights = np.zeros(4) + interpolation_columns = [] + + chord_weights, chord_ind = linear_interpolation_weights(x_vertex, x_domain) + + for i_span, span_location in enumerate(span_indices): + interpolation_weights[2 * i_span - 1] = span_weights[i_span] * chord_weights[0] + interpolation_columns.append(span_location * N + chord_ind[0]) + interpolation_weights[2 * i_span] = span_weights[i_span] * chord_weights[1] + interpolation_columns.append(span_location * N + chord_ind[1]) + + return interpolation_weights, interpolation_columns + + +def campbell(sigma_w, length_scale, velocity, dt=None): + """ + Campbell approximation to the Von Karman turbulence filter. + + Args: + sigma_w (float): Turbulence intensity in feet/s + length_scale (float): Turbulence length scale in feet + velocity (float): Flight velocity in feet/s + dt (float (optional)): Discrete-time time step for discrete time systems + + Returns: + libss.StateSpace: SHARPy State space representation of the Campbell approximation to the Von Karman + filter + + References: + Palacios, R. and Cesnik, C.. Dynamics of Flexible Aircraft: Coupled flight dynamics, aeroelasticity and control. + pg 28. + """ + a = 1.339 + time_scale = a * length_scale / velocity + num_tf = np.array([91/12, 52, 60]) * sigma_w * np.sqrt(time_scale / a / np.pi) + den_tf = np.array([935/216, 561/12, 102, 60]) + if dt is not None: + num_tf, den_tf, dt = scsig.cont2discrete((num_tf, den_tf), dt, method='bilinear') + camp_tf = scsig.ltisys.TransferFunctionDiscrete(num_tf, den_tf, dt=dt) + else: + camp_tf = scsig.ltisys.TransferFunction(num_tf, den_tf) + ss_turb = libss.StateSpace.from_scipy(camp_tf.to_ss()) + + ss_turb.initialise_variables(({'name': 'noise_in', 'size': 1}), var_type='in') + ss_turb.initialise_variables(({'name': 'u_gust', 'size': 1}), var_type='out') + ss_turb.initialise_variables(({'name': 'campbell_state', 'size': ss_turb.states}), var_type='state') + + return ss_turb + + +if __name__ == '__main__': + pass + # sys = campbell(90, 1750, 30, dt=0.1) + + import unittest + + class TestInterpolation(unittest.TestCase): + + def test_interpolation(self): + + x_domain = np.linspace(0, 1, 4) + span_loc = np.linspace(0, 1, 2) + + # mesh coordinates + x_grid = np.linspace(0.25, 0.75, 3) + y_grid = np.linspace(0, 1, 3) + x_mesh, y_mesh = np.meshgrid(x_grid, y_grid) + + print(x_mesh) + print(y_mesh) + print(y_mesh.shape) + for i in range(len(x_grid)): + for j in range(len(y_grid)): + print(i) + print(j) + print('Vertex x({:g}) = {:.2f}, y({:g}) = {:.2f}'.format(j, x_mesh[j, i], + i, y_mesh[j, i])) + weights, columns = spanwise_interpolation(y_mesh[j, i], span_loc, + x_mesh[j, i], x_domain) + + print('Weights', weights) + print('Columns', columns) + print('\n') - C_gust[-3 * Kzeta:] = Kout - D_gust[:6 * Kzeta, :6 * Kzeta] = np.eye(6 * Kzeta) - self.Kout = Kout - return A_gust, B_gust, C_gust, D_gust + unittest.main() \ No newline at end of file diff --git a/sharpy/linear/assembler/linearuvlm.py b/sharpy/linear/assembler/linearuvlm.py index 8b6f32f5f..b9b20e277 100644 --- a/sharpy/linear/assembler/linearuvlm.py +++ b/sharpy/linear/assembler/linearuvlm.py @@ -11,6 +11,7 @@ import sharpy.utils.rom_interface as rom_interface import sharpy.linear.src.libss as libss from sharpy.utils.constants import vortex_radius_def +from sharpy.linear.utils.ss_interface import VectorVariable, LinearVector, StateVariable @ss_interface.linear_system @@ -66,6 +67,12 @@ class LinearUVLM(ss_interface.BaseElement): .. math:: \dot{\mathbf{\Gamma}}^{n+1} = \frac{3\mathbf{\Gamma}^{n+1}-4\mathbf{\Gamma}^n + \mathbf{\Gamma}^{n-1}} {2\Delta t} + Note: + Control surface deflections are implemented using :class:`~sharpy.linear.assembler.lincontrolsurfacedeflector.LinControlSurfaceDeflector` + and the sign convention differs from the nonlinear solver. In the linear solver, the control + surface deflects according to the local :math:`x_B` vector. See the control surface deflection class for more + details. + References: [1] Franklin, GF and Powell, JD. Digital Control of Dynamic Systems, Addison-Wesley Publishing Company, 1980 @@ -113,7 +120,11 @@ class LinearUVLM(ss_interface.BaseElement): settings_types['gust_assembler'] = 'str' settings_default['gust_assembler'] = '' settings_description['gust_assembler'] = 'Selected linear gust assembler.' - settings_options['gust_assembler'] = ['leading_edge'] + settings_options['gust_assembler'] = ['LeadingEdge', 'MultiLeadingEdge'] + + settings_types['gust_assembler_inputs'] = 'dict' + settings_default['gust_assembler_inputs'] = dict() + settings_description['gust_assembler_inputs'] = 'Selected linear gust assembler parameter inputs.' settings_types['rom_method'] = 'list(str)' settings_default['rom_method'] = [] @@ -132,6 +143,11 @@ class LinearUVLM(ss_interface.BaseElement): settings_default['cfl1'] = True settings_description['cfl1'] = 'If it is ``True``, it assumes that the discretisation complies with CFL=1' + settings_types['convert_to_ct'] = 'bool' + settings_default['convert_to_ct'] = False + settings_description['convert_to_ct'] = 'Convert system to Continuous Time. Note: features above the original ' \ + 'Nyquist frequency limit will not be captured.' + settings_table = settings.SettingsTable() __doc__ += settings_table.generate(settings_types, settings_default, settings_description, settings_options) @@ -160,7 +176,7 @@ def __init__(self): self.sys = None self.ss = None self.tsaero0 = None - self.rom = None + self.rom = None # dict: rom_name: rom_class dictionary self.settings = dict() self.state_variables = None @@ -175,13 +191,16 @@ def __init__(self): self.linearisation_vectors = dict() # reference conditions at the linearisation + self.input_gain = None + def initialise(self, data, custom_settings=None): if custom_settings: self.settings = custom_settings else: try: - self.settings = data.settings['LinearAssembler']['linear_system_settings'] # Load settings, the settings should be stored in data.linear.settings + self.settings = data.settings['LinearAssembler'][ + 'linear_system_settings'] # Load settings, the settings should be stored in data.linear.settings except KeyError: pass @@ -211,42 +230,40 @@ def initialise(self, data, custom_settings=None): self.tsaero0 = data.linear.tsaero0 self.sys = uvlm - input_variables_database = {'zeta': [0, 3*self.sys.Kzeta], - 'zeta_dot': [3*self.sys.Kzeta, 6*self.sys.Kzeta], - 'u_gust': [6*self.sys.Kzeta, 9*self.sys.Kzeta]} - state_variables_database = {'gamma': [0, self.sys.K], - 'gamma_w': [self.sys.K, self.sys.K_star], - 'dtgamma_dot': [self.sys.K + self.sys.K_star, 2*self.sys.K + self.sys.K_star], - 'gamma_m1': [2*self.sys.K + self.sys.K_star, 3*self.sys.K + self.sys.K_star]} - + state_variables_list = [ + VectorVariable('gamma', size=self.sys.K, index=0), + VectorVariable('gamma_w', size=self.sys.K_star, index=1), + VectorVariable('dtgamma_dot', size=self.sys.K, index=2), + VectorVariable('gamma_m1', size=self.sys.K, index=3), + ] self.linearisation_vectors['zeta'] = np.concatenate([self.tsaero0.zeta[i_surf].reshape(-1, order='C') for i_surf in range(self.tsaero0.n_surf)]) self.linearisation_vectors['zeta_dot'] = np.concatenate([self.tsaero0.zeta_dot[i_surf].reshape(-1, order='C') for i_surf in range(self.tsaero0.n_surf)]) self.linearisation_vectors['u_ext'] = np.concatenate([self.tsaero0.u_ext[i_surf].reshape(-1, order='C') for i_surf in range(self.tsaero0.n_surf)]) - self.linearisation_vectors['forces_aero'] = np.concatenate([self.tsaero0.forces[i_surf][:3].reshape(-1, order='C') - for i_surf in range(self.tsaero0.n_surf)]) - - self.input_variables = ss_interface.LinearVector(input_variables_database, self.sys_id) - self.state_variables = ss_interface.LinearVector(state_variables_database, self.sys_id) + self.linearisation_vectors['forces_aero'] = np.concatenate( + [self.tsaero0.forces[i_surf][:3].reshape(-1, order='C') + for i_surf in range(self.tsaero0.n_surf)]) if data.aero.n_control_surfaces >= 1: import sharpy.linear.assembler.lincontrolsurfacedeflector as lincontrolsurfacedeflector self.control_surface = lincontrolsurfacedeflector.LinControlSurfaceDeflector() self.control_surface.initialise(data, uvlm) - if self.settings['rom_method'] != '': + if self.settings['rom_method']: # Initialise ROM self.rom = dict() for rom_name in self.settings['rom_method']: self.rom[rom_name] = rom_interface.initialise_rom(rom_name) self.rom[rom_name].initialise(self.settings['rom_method_settings'][rom_name]) - if 'u_gust' not in self.settings['remove_inputs'] and self.settings['gust_assembler'] == 'leading_edge': + if 'u_gust' not in self.settings['remove_inputs'] and self.settings['gust_assembler'] != '': import sharpy.linear.assembler.lineargustassembler as lineargust - self.gust_assembler = lineargust.LinearGustGenerator() - self.gust_assembler.initialise(data.aero) + self.gust_assembler = lineargust.gust_from_string(self.settings['gust_assembler']) + self.gust_assembler.initialise(data.aero, self.sys, self.tsaero0, + u_ext=lineargust.get_freestream_velocity(data), + custom_settings=self.settings['gust_assembler_inputs']) def assemble(self, track_body=False, wake_prop_settings=None): r""" @@ -266,38 +283,32 @@ def assemble(self, track_body=False, wake_prop_settings=None): if self.scaled: self.sys.nondimss() - self.ss = self.sys.SS - self.C_to_vertex_forces = self.ss.C.copy() - nzeta = 3 * self.sys.Kzeta + if self.settings['convert_to_ct']: + self.sys.SS = libss.disc2cont(self.sys.SS) + + self.ss = self.sys.SS if self.settings['remove_inputs']: self.remove_inputs(self.settings['remove_inputs']) if self.gust_assembler is not None: - A, B, C, D = self.gust_assembler.generate(self.sys, aero=None) - ss_gust = libss.ss(A, B, C, D, dt=self.ss.dt) - self.gust_assembler.ss_gust = ss_gust - self.ss = libss.series(ss_gust, self.ss) + self.ss = self.gust_assembler.apply(self.ss) + + self.input_gain = libss.Gain(np.eye(self.ss.inputs), + input_vars=self.ss.input_variables.copy(), + output_vars=LinearVector.transform(self.ss.input_variables, + to_type=ss_interface.OutputVariable)) if self.control_surface is not None: - Kzeta_delta, Kdzeta_ddelta = self.control_surface.generate() - n_zeta, n_ctrl_sfc = Kzeta_delta.shape - - # Modify the state space system with a gain at the input side - # such that the control surface deflections are last - if self.sys.use_sparse: - gain_cs = sp.eye(self.ss.inputs, self.ss.inputs + 2 * self.control_surface.n_control_surfaces, - format='lil') - gain_cs[:n_zeta, self.ss.inputs: self.ss.inputs + n_ctrl_sfc] = Kzeta_delta - gain_cs[n_zeta: 2*n_zeta, self.ss.inputs + n_ctrl_sfc: self.ss.inputs + 2 * n_ctrl_sfc] = Kdzeta_ddelta - gain_cs = libsp.csc_matrix(gain_cs) - else: - gain_cs = np.eye(self.ss.inputs, self.ss.inputs + 2 * self.control_surface.n_control_surfaces) - gain_cs[:n_zeta, self.ss.inputs: self.ss.inputs + n_ctrl_sfc] = Kzeta_delta - gain_cs[n_zeta: 2*n_zeta, self.ss.inputs + n_ctrl_sfc: self.ss.inputs + 2 * n_ctrl_sfc] = Kdzeta_ddelta - self.ss.addGain(gain_cs, where='in') - self.gain_cs = gain_cs + ss2 = self.control_surface.apply(self.ss) + self.gain_cs = self.control_surface.gain_cs + self.connect_input(self.gain_cs) + # np.testing.assert_almost_equal(ss2.B, self.ss.B) + + self.D_to_vertex_forces = self.ss.D.copy() # post-processing issue + self.B_to_vertex_forces = self.ss.B.copy() # post-processing issue + self.C_to_vertex_forces = self.ss.C.copy() # post-processing issue def remove_inputs(self, remove_list=list): """ @@ -309,21 +320,9 @@ def remove_inputs(self, remove_list=list): Args: remove_list (list): Inputs to remove """ + self.sys.SS.remove_inputs(*remove_list) - self.input_variables.remove(remove_list) - - i = 0 - for variable in self.input_variables.vector_vars: - if i == 0: - trim_array = self.input_variables.vector_vars[variable].cols_loc - else: - trim_array = np.hstack((trim_array, self.input_variables.vector_vars[variable].cols_loc)) - i += 1 - - self.sys.SS.B = libsp.csc_matrix(self.sys.SS.B[:, trim_array]) - self.sys.SS.D = libsp.csc_matrix(self.sys.SS.D[:, trim_array]) - - def unpack_ss_vector(self, data, x_n, aero_tstep, track_body=False): + def unpack_ss_vector(self, data, x_n, u_aero, aero_tstep, track_body=False, state_variables=None, gust_in=False): r""" Transform column vectors used in the state space formulation into SHARPy format @@ -384,9 +383,32 @@ def unpack_ss_vector(self, data, x_n, aero_tstep, track_body=False): else: Cg_uvlm = np.eye(3) - y_n = self.C_to_vertex_forces.dot(x_n) - # y_n = np.zeros((3 * self.sys.Kzeta)) + if self.rom is not None: + try: + rom = self.rom['Krylov'] + except KeyError: + pass + # The krylov ROM variable names are applied here + # the remaining are applied in the balanced rom class + else: + x_n = rom.projection_gain.dot(x_n).real + state_variables = LinearVector.transform(rom.projection_gain.output_variables, StateVariable) + + try: + gust_vars_size = state_variables.get_variable_from_name('gust').size + gust_state = x_n[:gust_vars_size] + except ValueError: + gust_vars_size = 0 + gust_state = [] + + y_n = self.C_to_vertex_forces.dot(x_n) + self.D_to_vertex_forces.dot(u_aero) + + if self.sys.remove_predictor: + # x_n += self.B_to_vertex_forces.dot(u_aero) + pass + + x_n = x_n[gust_vars_size:] gamma_vec, gamma_star_vec, gamma_dot_vec = self.sys.unpack_state(x_n) # Reshape output into forces[i_surface] where forces[i_surface] is a (6,M+1,N+1) matrix and circulation terms @@ -413,7 +435,7 @@ def unpack_ss_vector(self, data, x_n, aero_tstep, track_body=False): # Append reshaped forces to each entry in list (one for each surface) f_aero = y_n - forces.append(f_aero[worked_points:worked_points+points_in_surface].reshape(dimensions, order='C')) + forces.append(f_aero[worked_points:worked_points + points_in_surface].reshape(dimensions, order='C')) ### project forces. # - forces are in UVLM linearisation frame. Hence, these are projected @@ -421,50 +443,59 @@ def unpack_ss_vector(self, data, x_n, aero_tstep, track_body=False): if track_body: for mm in range(dimensions[1]): for nn in range(dimensions[2]): - forces[i_surf][:,mm,nn] = np.dot(Cg_uvlm, forces[i_surf][:,mm,nn]) + forces[i_surf][:, mm, nn] = np.dot(Cg_uvlm, forces[i_surf][:, mm, nn]) # Add the null bottom 3 rows to to the forces entry forces[i_surf] = np.concatenate((forces[i_surf], np.zeros(dimensions))) # Reshape bound circulation terms - gamma.append(gamma_vec[worked_panels:worked_panels+panels_in_surface].reshape( + gamma.append(gamma_vec[worked_panels:worked_panels + panels_in_surface].reshape( dimensions_gamma, order='C')) - gamma_dot.append(gamma_dot_vec[worked_panels:worked_panels+panels_in_surface].reshape( + gamma_dot.append(gamma_dot_vec[worked_panels:worked_panels + panels_in_surface].reshape( dimensions_gamma, order='C')) # Reshape wake circulation terms - gamma_star.append(gamma_star_vec[worked_wake_panels:worked_wake_panels+panels_in_wake].reshape( + gamma_star.append(gamma_star_vec[worked_wake_panels:worked_wake_panels + panels_in_wake].reshape( dimensions_wake, order='C')) worked_points += points_in_surface worked_panels += panels_in_surface worked_wake_panels += panels_in_wake - return forces, gamma, gamma_dot, gamma_star + if gust_in: + return forces, gamma, gamma_dot, gamma_star, gust_state + else: + return forces, gamma, gamma_dot, gamma_star - def unpack_input_vector(self, u_n): + def unpack_input_vector(self, u_n, u_ext_gust, input_variables): """ Unpacks the input vector into the corresponding grid coordinates, velocities and external velocities. Args: u_n (np.ndarray): UVLM input vector. May contain control surface deflections and external velocities. + u_ext_gust (np.ndarray): Inputs to the Gust system only. Optional, an empty array may be parsed. + input_variables (LinearVector): Vector of input variables to the aerodynamic system Returns: tuple: Tuple containing ``zeta``, ``zeta_dot`` and ``u_ext``, accounting for the effect of control surfaces. """ - # if self.gust_assembler is not None: - # u_n = self.gust_assembler.ss_gust - if self.control_surface is not None: u_n = self.gain_cs.dot(u_n) - input_vars = self.input_variables.vector_vars tsaero0 = self.tsaero0 input_vectors = dict() - for var in input_vars: - input_vectors[input_vars[var].name] = u_n[input_vars[var].cols_loc] + for var in input_variables: + try: + if var.name == 'u_gust': + # if len(u_ext_gust) != var.size: + # continue # provided input for external velocities does not match size. will be zero + input_vectors['u_gust'] = u_ext_gust + else: + input_vectors[var.name] = u_n[var.cols_loc] + except IndexError: + break zeta = [] zeta_dot = [] @@ -474,15 +505,18 @@ def unpack_input_vector(self, u_n): for i_surf in range(tsaero0.n_surf): vertices_in_surface = tsaero0.zeta[i_surf].size dimensions_zeta = tsaero0.zeta[i_surf].shape - zeta.append(input_vectors['zeta'][worked_vertices:worked_vertices+vertices_in_surface].reshape( + zeta.append(input_vectors['zeta'][worked_vertices:worked_vertices + vertices_in_surface].reshape( dimensions_zeta, order='C')) - zeta_dot.append(input_vectors['zeta_dot'][worked_vertices:worked_vertices+vertices_in_surface].reshape( + zeta_dot.append(input_vectors['zeta_dot'][worked_vertices:worked_vertices + vertices_in_surface].reshape( dimensions_zeta, order='C')) try: u_gust = input_vectors['u_gust'] + # TODO: fix this check because it is not correct + # u_gust is not 3 * vertices *n_surf because different surfaces can have different vertices + # take outside of loop and fix at the top! except KeyError: - u_gust = np.zeros(3*vertices_in_surface*tsaero0.n_surf) - u_ext.append(u_gust[worked_vertices:worked_vertices+vertices_in_surface].reshape( + u_gust = np.zeros(3 * vertices_in_surface * tsaero0.n_surf) + u_ext.append(u_gust[worked_vertices:worked_vertices + vertices_in_surface].reshape( dimensions_zeta, order='C')) zeta[i_surf] += tsaero0.zeta[i_surf] @@ -491,3 +525,37 @@ def unpack_input_vector(self, u_n): worked_vertices += vertices_in_surface return zeta, zeta_dot, u_ext + + def connect_input(self, element): + """ + Connect a gain or a StateSpace to the input of the UVLM + + Args: + element (libss.StateSpace or libss.Gain): element to connect to the input of the UVLM + + """ + if type(element) is libss.StateSpace: + self.ss = libss.series(element, self.ss) + elif type(element) is libss.Gain: + self.ss.addGain(element, where='in') + self.input_gain = self.input_gain.dot(element) + else: + TypeError('Unable to connect system that is not StateSpace or Gain') + + def connect_output(self, element): + """ + Connect a gain or a StateSpace to the output of the UVLM + + Args: + element (libss.StateSpace or libss.Gain): element to connect to the output of the UVLM + + """ + if type(element) is libss.StateSpace: + self.ss = libss.series(self.ss, element) + elif type(element) is libss.Gain: + self.ss.addGain(element, where='out') + else: + TypeError('Unable to connect system that is not StateSpace or Gain') + + def unpack(self, u): + return self.input_gain.value.dot(u) diff --git a/sharpy/linear/dev/__init__.py b/sharpy/linear/dev/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sharpy/linear/src/libfit.py b/sharpy/linear/src/libfit.py index 90d882a5e..833a486a0 100644 --- a/sharpy/linear/src/libfit.py +++ b/sharpy/linear/src/libfit.py @@ -461,7 +461,7 @@ def rfa_mimo(Yfull, kv, ds, tolAbs, Nnum, Nden, Dmatrix=None, NtrialMax=6, Ncpu= # cfnum = np.array([4, 1.25, 1.5]) # cfden = np.array([2, .5, 1]) # A, B, C, D = tf2ss(cfnum, cfden) -# SS = libss.ss(A, B, C, D, dt=ds) +# SS = libss.StateSpace(A, B, C, D, dt=ds) # Cvref = libss.freqresp(SS, kv) # Cvref = Cvref[0, 0, :] # diff --git a/sharpy/linear/src/libss.py b/sharpy/linear/src/libss.py index 85af3babe..4df2428be 100644 --- a/sharpy/linear/src/libss.py +++ b/sharpy/linear/src/libss.py @@ -9,7 +9,7 @@ The module includes: Classes: -- ss: provides a class to build DLTI/LTI systems with full and/or sparse +- StateSpace: provides a class to build DLTI/LTI systems with full and/or sparse matrices and wraps many of the methods in these library. Methods include: - freqresp: wraps the freqresp function - addGain: adds gains in input/output. This is not a wrapper of addGain, as @@ -57,7 +57,10 @@ import numpy as np import scipy.signal as scsig import scipy.linalg as scalg +from sharpy.linear.utils.ss_interface import LinearVector, StateVariable, InputVariable, OutputVariable import scipy.interpolate as scint +import h5py +import sharpy.utils.h5utils as h5utils # dependency import sharpy.linear.src.libsparse as libsp @@ -65,7 +68,7 @@ # ------------------------------------------------------------- Dedicated class -class ss(): +class StateSpace: """ Wrap state-space models allocation into a single class and support both full and sparse matrices. The class emulates @@ -94,15 +97,10 @@ def __init__(self, A, B, C, D, dt=None): self.dt = dt self.check_types() - # determine inputs/outputs/states - if self.B.shape.__len__() == 1: - # Allow for SISO systems - self.inputs = 1 - self.states = self.B.shape[0] - else: - (self.states, self.inputs) = self.B.shape - - self.outputs = self.C.shape[0] + # vector variable tracking + self._input_variables = None # type: LinearVector + self._state_variables = None + self._output_variables = None # verify dimensions assert self.A.shape == (self.states, self.states), 'A and B rows not matching' @@ -117,36 +115,102 @@ def __init__(self, A, B, C, D, dt=None): def inputs(self): """Number of inputs :math:`m` to the system.""" if self.B.shape.__len__() == 1: - self.inputs = 1 + return 1 else: - self.inputs = self.B.shape[1] - - return self._inputs - - @inputs.setter - def inputs(self, value): - # print('Setting Number of inputs') - self._inputs = value + return self.B.shape[1] @property def outputs(self): """Number of outputs :math:`p` of the system.""" - self.outputs = self.C.shape[0] - return self._outputs - - @outputs.setter - def outputs(self, value): - self._outputs = value + return self.C.shape[0] @property def states(self): """Number of states :math:`n` of the system.""" - self.states = self.A.shape[0] - return self._states + return self.A.shape[0] - @states.setter - def states(self, value): - self._states = value + @property + def input_variables(self): + return self._input_variables + + @input_variables.setter + def input_variables(self, variables): + if variables.variable_class is not InputVariable: + raise TypeError('LinearVector does not include InputVariable s') + if variables.size != self.inputs: + raise IndexError('Size of LinearVector of InputVariable s ({:g}) is not the same as the number of ' + 'inputs in the ' + 'system ({:g})'.format(variables.size, self.inputs)) + self._input_variables = variables + + @property + def output_variables(self): + return self._output_variables + + @output_variables.setter + def output_variables(self, variables): + if variables.variable_class is not OutputVariable: + raise TypeError('LinearVector does not include OutputVariable s') + if variables.size != self.outputs: + raise IndexError('Size of LinearVector of OutputVariable s ({:g}) is not the same as the number of ' + 'outputs in the ' + 'system ({:g})'.format(variables.size, self.outputs)) + self._output_variables = variables + + @property + def state_variables(self): + return self._state_variables + + @state_variables.setter + def state_variables(self, variables): + if variables.variable_class is not StateVariable: + raise TypeError('LinearVector does not include StateVariable s') + if variables.size != self.states: + raise IndexError('Size of LinearVector of StateVariable s ({:g}) is not the same as the number ' + 'of states in the ' + 'system ({:g})'.format(variables.size, self.states)) + self._state_variables = variables + + def initialise_variables(self, *variable_tuple, var_type='in'): + if var_type == 'in' or var_type == 'input': + var_class = InputVariable + elif var_type == 'out' or var_type == 'output': + var_class = OutputVariable + elif var_type == 'state': + var_class = StateVariable + else: + raise TypeError('Unknown variable type') + + list_of_variables = [] + for ith, var_dict in enumerate(variable_tuple): + list_of_variables.append(var_class(name=var_dict['name'], + size=var_dict['size'], + index=var_dict.get('index', ith))) + + if var_type == 'in' or var_type == 'input': + self._input_variables = LinearVector(list_of_variables) + elif var_type == 'out' or var_type == 'output': + self._output_variables = LinearVector(list_of_variables) + elif var_type == 'state': + self._state_variables = LinearVector(list_of_variables) + + def __repr__(self): + str_out = '' + str_out += 'State-space object\n' + str_out += 'States: {:g}\n'.format(self.states) + str_out += 'Inputs: {:g}\n'.format(self.inputs) + str_out += 'Outputs: {:g}\n'.format(self.outputs) + if self.dt is not None: + str_out += 'dt: {:g}'.format(self.dt) + + if self.input_variables is not None: + str_out += '\nInput Variables:\n' + str(self.input_variables) + if self.state_variables is not None: + str_out += 'State Variables:\n' + str(self.state_variables) + if self.output_variables is not None: + str_out += 'Output Variables:\n' + str(self.output_variables) + + return str_out def check_types(self): assert type(self.A) in libsp.SupportedTypes, \ @@ -168,7 +232,8 @@ def freqresp(self, wv): Note: this wraps frequency response function. """ dlti = True - if self.dt == None: dlti = False + if self.dt is None: + dlti = False return freqresp(self, wv, dlti=dlti) def addGain(self, K, where): @@ -181,25 +246,35 @@ def addGain(self, K, where): - where='out': outputs are projected such that: u -> SS -> y -> y_new=K*y => u -> SSnew -> ynew - Warning: this is not a wrapper of the addGain method in this module, as - the state-space matrices are directly overwritten. + Args: + K (np.array or Gain): gain matrix or Gain object + where (str): ``in`` or ``out`` + + Warning: + This is not a wrapper of the addGain method in this module, as + the state-space matrices are directly overwritten. """ assert where in ['in', 'out'], \ 'Specify whether gains are added to input or output' + with_vars = False + if isinstance(K, Gain): + gain = K + K = K.value + with_vars = True + if where == 'in': self.B = libsp.dot(self.B, K) self.D = libsp.dot(self.D, K) - # try: # No need to update inputs/outputs as they are now properties accessed on demand NG 26/3/19 - # self._inputs = K.shape[1] - # except IndexError: - # self._inputs = 1 + if with_vars: + self._input_variables = gain.input_variables if where == 'out': self.C = libsp.dot(K, self.C) self.D = libsp.dot(K, self.D) - # self.outputs = K.shape[0] + if with_vars: + self._output_variables = gain.output_variables def scale(self, input_scal=1., output_scal=1., state_scal=1.): """ @@ -222,30 +297,38 @@ def scale(self, input_scal=1., output_scal=1., state_scal=1.): """ scale_SS(self, input_scal, output_scal, state_scal, byref=True) - - def project(self,WT,V): - ''' - Given 2 transformation matrices, (WT,V) of shapes (Nk,self.states) and - (self.states,Nk) respectively, this routine projects the state space + def project(self, wt, v): + """ + Given 2 transformation matrices, ``(WT, V)`` of shapes ``(Nk, self.states)`` and + ``(self.states, Nk)`` respectively, this routine projects the state space model states according to: - Anew = WT A V - Bnew = WT B - Cnew = C V - Dnew = D + + .. math:: + Anew = WT A V \\ + Bnew = WT B \\ + Cnew = C V \\ + Dnew = D \\ The projected model has the same number of inputs/outputs as the original one, but Nk states. - ''' - - self.A = libsp.dot( WT, libsp.dot(self.A, V) ) - self.B = libsp.dot( WT, self.B) - self.C = libsp.dot( self.C, V) - self.states=V.shape[1] + Args: + wt (Gain or np.ndarray): Left projection matrix + v (Gain or np.ndarray): Righty projection matrix + """ + if isinstance(wt, Gain) and isinstance(v, Gain): + self.A = libsp.dot(wt.value, libsp.dot(self.A, v.value)) + self.B = libsp.dot(wt.value, self.B) + self.C = libsp.dot(self.C, v.value) + self.state_variables = LinearVector.transform(v.input_variables, to_type=StateVariable) + else: + self.A = libsp.dot(wt, libsp.dot(self.A, v)) + self.B = libsp.dot(wt, self.B) + self.C = libsp.dot(self.C, v) def truncate(self, N): - ''' Retains only the first N states. ''' + """ Retains only the first N states. """ assert N > 0 and N <= self.states, 'N must be in [1,self.states]' @@ -255,9 +338,9 @@ def truncate(self, N): # self.states = N # No need to update, states is now a property. NG 26/3/19 def max_eig(self): - ''' + """ Returns most unstable eigenvalue - ''' + """ ev = np.linalg.eigvals(self.A) @@ -272,7 +355,7 @@ def eigvals(self): np.ndarray: Eigenvalues of the system """ - if ss.dt: + if self.dt: return eigvals(self.A, dlti=True) else: return eigvals(self.A, dlti=False) @@ -287,8 +370,15 @@ def disc2cont(self): if self.dt: self = disc2cont(self) - def remove_inout_channels(self, retain_channels, where): - remove_inout_channels(self, retain_channels, where) + def retain_inout_channels(self, retain_channels, where): + """ + Retain selected input or output channels only. + + Args: + retain_channels (list): List of channels to retain + where (str): ``in`` or ``out`` for input/output channels + """ + retain_inout_channels(self, retain_channels, where) def summary(self): msg = 'State-space system\nStates: %g\nInputs: %g\nOutputs: %g\n' % (self.states, self.inputs, self.outputs) @@ -310,10 +400,367 @@ def transfer_function_evaluation(self, s): return c.dot(scalg.inv(s * np.eye(n) - a)).dot(b) + d + def save(self, path): + """Save state-space object to h5 file""" + with h5py.File(path, 'w') as f: + f.create_dataset('a', data=self.A) + f.create_dataset('b', data=self.B) + f.create_dataset('c', data=self.C) + f.create_dataset('d', data=self.D) + if self.dt: + f.create_dataset('dt', data=self.dt) + + if self.input_variables is not None: + self.input_variables.add_to_h5_file(f) + self.output_variables.add_to_h5_file(f) + self.state_variables.add_to_h5_file(f) + + @classmethod + def load_from_h5(cls, h5_file_name): + """ + Loads a state-space object from an h5 file, including variable information + + Args: + h5_file_name (str): Path to file + + Returns: + StateSpace: loaded state-space from file + """ + + with h5py.File(h5_file_name, 'r') as f: + data_dict = h5utils.load_h5_in_dict(f) + + new_ss = cls(data_dict['a'], + data_dict['b'], + data_dict['c'], + data_dict['d'], + dt=data_dict.get('dt')) + + input_variables = data_dict.get('InputVariable') + if input_variables is not None: + new_ss.input_variables = LinearVector.load_from_h5_file('InputVariable', + data_dict['InputVariable']) + new_ss.output_variables = LinearVector.load_from_h5_file('OutputVariable', + data_dict['OutputVariable']) + new_ss.state_variables = LinearVector.load_from_h5_file('StateVariable', + data_dict['StateVariable']) + + return new_ss + else: + return new_ss + + def remove_inputs(self, *input_remove_list): + """ + Removes inputs through their variable names. + + Needs that the ``StateSpace`` attribute ``input_variables`` is defined. + + Args: + input_remove_list (list(str)): List of inputs to remove + + """ + if self.input_variables is None: + raise AttributeError('No input variables have been defined for the current state-space object. Define ' + 'some variables prior to using the remove_inputs() method.') + + self.input_variables.remove(*input_remove_list) + + i = 0 + retain_input_array = None + for variable in self.input_variables: + if i == 0: + retain_input_array = variable.cols_loc + else: + retain_input_array = np.hstack((retain_input_array, variable.cols_loc)) + i += 1 + + if retain_input_array is not None: + if type(self.B) is libsp.csc_matrix: + self.B = libsp.csc_matrix(self.B[:, retain_input_array]) + self.D = libsp.csc_matrix(self.D[:, retain_input_array]) + else: + self.B = self.B[:, retain_input_array] + self.D = self.D[:, retain_input_array] + + self.input_variables.update_locations() + + def remove_outputs(self, *output_remove_list): + """ + Removes outputs through their variable names. + + Needs that the ``StateSpace`` attribute ``output_variables`` is defined. + + Args: + output_remove_list (list(str)): List of outputs to remove + + """ + if self.output_variables is None: + raise AttributeError('No output variables have been defined for the current state-space object. Define ' + 'some variables prior to using the remove_outputs() method.') + + new_outputs = 0 + for variable in self.output_variables: + if variable.name not in output_remove_list: + new_outputs += variable.size + + out_gain = np.zeros((new_outputs, self.outputs)) + worked_outputs = 0 + for variable in self.output_variables: + if variable.name not in output_remove_list: + index = variable.rows_loc + out_gain[worked_outputs:worked_outputs + variable.size, index] = np.eye(variable.size) + worked_outputs += variable.size + + if new_outputs != self.outputs: + if type(self.B) is libsp.csc_matrix: + self.C = libsp.csc_matrix(out_gain.dot(self.C)) + self.D = libsp.csc_matrix(out_gain.dot(self.D)) + else: + self.C = out_gain.dot(self.C) + self.D = out_gain.dot(self.D) + + self.output_variables.remove(*output_remove_list) + self.output_variables.update_locations() + + @classmethod + def from_scipy(cls, scipy_ss): + """ + Transforms a ``scipy.signal.lti`` or dlti into a StateSpace class + + Args: + scipy_ss (scipy.signal.ltisys.StateSpaceContinous or scipy.signal.ltisys.StateSpaceDiscrete): Scipy + State Space object. + + Returns: + StateSpace: SHARPy state space object + """ + a = scipy_ss.A + b = scipy_ss.B + c = scipy_ss.C + d = scipy_ss.D + + return cls(a, b, c, d, dt=scipy_ss.dt) + + +class Gain: + + def __init__(self, value, input_vars=None, output_vars=None): + self.value = value + self._input_variables = None + self._output_variables = None + + if input_vars is not None: + self.input_variables = input_vars + if output_vars is not None: + self.output_variables = output_vars + + @property + def input_variables(self): + return self._input_variables + + @input_variables.setter + def input_variables(self, variables): + if variables.variable_class is not InputVariable: + raise TypeError('LinearVector does not include InputVariable s') + if variables.size != self.inputs: + raise IndexError('Size of LinearVector of InputVariable s ({:g}) is not the same as the number of ' + 'inputs in the ' + 'system ({:g})'.format(variables.size, self.inputs)) + self._input_variables = variables + + @property + def output_variables(self): + return self._output_variables + + @output_variables.setter + def output_variables(self, variables): + if variables.variable_class is not OutputVariable: + raise TypeError('LinearVector does not include OutputVariable s') + if variables.size != self.outputs: + raise IndexError('Size of LinearVector of OutputVariable s ({:g}) is not the same as the number of ' + 'outputs in the ' + 'system ({:g})'.format(variables.size, self.outputs)) + self._output_variables = variables + + @property + def inputs(self): + """Number of inputs :math:`m` to the system.""" + if self.value.shape.__len__() == 1: + return 1 + else: + return self.value.shape[1] + + @property + def outputs(self): + """Number of outputs :math:`p` of the gain.""" + return self.value.shape[0] + + def dot(self, elem): + """ + Dot product of two Gains + + Args: + elem (np.array or Gain): + + Returns: + np.array or Gain: new matrix/Gain containing the dot product + """ + if type(elem) is Gain: + LinearVector.check_connection(elem.output_variables, self.input_variables) + new_gain_value = libsp.dot(self.value, elem.value) + return Gain(new_gain_value, + input_vars=elem.input_variables.copy(), + output_vars=self.output_variables.copy()) + else: + return self.value.dot(elem) + + def __repr__(self): + str_out = '' + str_out += 'Gain object\n' + str_out += 'Inputs: {:g}\n'.format(self.inputs) + str_out += 'Outputs: {:g}\n'.format(self.outputs) + + if self.input_variables is not None: + str_out += '\nInput Variables:\n' + str(self.input_variables) + if self.output_variables is not None: + str_out += 'Output Variables:\n' + str(self.output_variables) + + return str_out + + def transpose(self): + """ + Transposes the gain, such that the inputs become the outputs and vice-versa. + """ + + if self.input_variables is not None: + temp_input_var = self.input_variables.copy() + input_variables = LinearVector.transform(self.output_variables, + to_type=InputVariable) + output_variables = LinearVector.transform(temp_input_var, + to_type=OutputVariable) + + return Gain(self.value.T, + input_vars=input_variables, + output_vars=output_variables) + else: + return Gain(self.value.T) + + @property + def T(self): + return self.transpose() + + def copy(self): + if self.input_variables is not None: + return Gain(self.value, input_vars=self.input_variables.copy(), output_vars=self.output_variables.copy()) + else: + return Gain(self.value) + + def save(self, path): + """Save gain object to h5 file""" + with h5py.File(path, 'w') as f: + f.create_dataset('gain', data=self.value) + + if self.input_variables is not None: + self.input_variables.add_to_h5_file(f) + self.output_variables.add_to_h5_file(f) + + def add_as_group_to_h5(self, h5_file_handle, group_name): + """ + Adds gain to an h5 file handle + + Args: + h5_file_handle (h5py.File): writeable h5 file handle + group_name (str): Desired group name to save gain in h5 + + """ + gain_group = h5_file_handle.create_group(group_name) + gain_group.create_dataset(name='gain', data=self.value) + + if self.input_variables is not None: + self.input_variables.add_to_h5_file(gain_group) + self.output_variables.add_to_h5_file(gain_group) + + @classmethod + def load_from_h5(cls, h5_file_name): + """ + Returns a gain object from an .h5 file + + Args: + h5_file_name (str): Path to h5 file + + Returns: + Gain: instance of a Gain + """ + with h5py.File(h5_file_name, 'r') as f: + data_dict = h5utils.load_h5_in_dict(f) + + return cls.load_from_dict(data_dict) + + @classmethod + def load_from_dict(cls, data_dict): + """ + + Returns a Gain from a dictionary of data, useful for loading from a group of gains in a single + .h5 file + + Args: + data_dict (dict): Dictionary with keys: ``gain`` and (if available) ``InputVariable`` + and ``OutputVariable``. + + Returns: + Gain: instance of Gain + """ + input_variables = data_dict.get('InputVariable') + if input_variables is not None: + input_variables = LinearVector.load_from_h5_file('InputVariable', + data_dict['InputVariable']) + output_variables = LinearVector.load_from_h5_file('OutputVariable', + data_dict['OutputVariable']) + + return cls(data_dict['gain'], input_vars=input_variables, + output_vars=output_variables) + else: + return cls(data_dict['gain']) + + @classmethod + def save_multiple_gains(cls, h5_file_name, *gains_names_tuple): + """ + Saves multiple gains to a single h5 file + + Args: + h5_file_name (str): Path to h5 file + *gains_names_tuple (tuple): ``(gain_name (str), gain(Gain))`` tuples to save. The gain name will be the name + given on the h5 file + + """ + with h5py.File(h5_file_name, 'w') as f: + for name, gain in gains_names_tuple: + gain.add_as_group_to_h5(f, name) + + @classmethod + def load_multiple_gains(cls, h5_file_name): + """ + Loads multiple gains from a single h5 file + + Args: + h5_file_name (str): Path to h5 file + + Returns: + dict: Dictionary of loaded gains in a gain_name: Gain dictionary + """ + with h5py.File(h5_file_name, 'r') as f: + data_dict = h5utils.load_h5_in_dict(f) + + out_gains = {} + for gain_name, gain_data in data_dict.items(): + out_gains[gain_name] = cls.load_from_dict(gain_data) + + return out_gains + class ss_block(): - ''' - State-space model in block form. This class has the same purpose as "ss", + """ + State-space model in block form. This class has the same purpose as "StateSpace", but the A, B, C, D are allocated in the form of nested lists. The format is similar to the one used in numpy.block but: 1. Block matrices can contain both dense and sparse matrices @@ -323,10 +770,10 @@ class ss_block(): - remove_block: drop one of the blocks from the s-s model - addGain: project inputs/outputs - project: project state - ''' + """ def __init__(self, A, B, C, D, S_states, S_inputs, S_outputs, dt=None): - ''' + """ Allocate state-space model (A,B,C,D) in block form starting from nested lists of full/sparse matrices (as per numpy.block). @@ -335,7 +782,7 @@ def __init__(self, A, B, C, D, S_states, S_inputs, S_outputs, dt=None): - S_states, S_inputs, S_outputs: lists with dimensions of of each block representing the states, inputs and outputs of the model. - dt: time-step. In None, a continuous-time system is assumed. - ''' + """ self.A = A self.B = B @@ -363,13 +810,13 @@ def check_sizes(self): pass def remove_block(self, where, index): - ''' + """ Remove a block from either inputs or outputs. Inputs: - where = {'in', 'out'}: determined whether to remove inputs or outputs - index: index of block to remove - ''' + """ assert where in ['in', 'out'], "'where' must be equal to {'in', 'out'}" @@ -385,7 +832,7 @@ def remove_block(self, where, index): del self.D[ii] def addGain(self, K, where): - ''' + """ Projects input u or output y the state-space system through the gain block matrix K. The input 'where' determines whether inputs or outputs are projected as: @@ -396,7 +843,7 @@ def addGain(self, K, where): Input: K must be a list of list of matrices. The size of K must be compatible with either B or C for block matrix product. - ''' + """ assert where in ['in', 'out'], \ 'Specify whether gains are added to input or output' @@ -418,9 +865,9 @@ def addGain(self, K, where): self.outputs = sum(rows) def get_sizes(self, M): - ''' + """ Get the size of each block in M. - ''' + """ rM, cM = len(M), len(M[0]) rows = rM * [None] @@ -446,7 +893,7 @@ def get_sizes(self, M): return rows, cols def project(self, WT, V, by_arrays=True, overwrite=False): - ''' + """ Given 2 transformation matrices, (W,V) of shape (Nk,self.states), this routine projects the state space model states according to: @@ -464,7 +911,7 @@ def project(self, WT, V, by_arrays=True, overwrite=False): - by_arrays: if True, W, V are either numpy.array or sparse matrices. If False, they are block matrices. - overwrite: if True, overwrites the A, B, C matrices - ''' + """ if by_arrays: # transform to block structures @@ -489,7 +936,6 @@ def project(self, WT, V, by_arrays=True, overwrite=False): libsp.block_dot(WTblock, self.B), libsp.block_dot(self.C, Vblock)) - def solve_step(self, xn, un): # TODO: add options about predictor ... @@ -500,8 +946,8 @@ def solve_step(self, xn, un): # ---------------------------------------- Methods for state-space manipulation -def project(ss_here,WT,V): - ''' +def project(ss_here, WT, V): + """ Given 2 transformation matrices, (WT,V) of shapes (Nk,self.states) and (self.states,Nk) respectively, this routine returns a projection of the state space ss_here according to: @@ -513,13 +959,14 @@ def project(ss_here,WT,V): The projected model has the same number of inputs/outputs as the original one, but Nk states. - ''' + """ + + Ap = libsp.dot(WT, libsp.dot(ss_here.A, V)) + Bp = libsp.dot(WT, ss_here.B) + Cp = libsp.dot(ss_here.C, V) - Ap = libsp.dot( WT, libsp.dot(ss_here.A, V) ) - Bp = libsp.dot( WT, ss_here.B) - Cp = libsp.dot( ss_here.C, V) + return StateSpace(Ap, Bp, Cp, ss_here.D, ss_here.dt) - return ss(Ap,Bp,Cp,ss_here.D,ss_here.dt) def couple(ss01, ss02, K12, K21, out_sparse=False): """ @@ -527,14 +974,32 @@ def couple(ss01, ss02, K12, K21, out_sparse=False): K12 transforms the output of ss02 into an input of ss01. Other inputs: - - out_sparse: if True, the output system is stored as sparse (not recommended) + - out_sparse: if True, the output system is stored as sparse (not recommended) """ - - assert np.abs(ss01.dt - ss02.dt) < 1e-10 * ss01.dt, 'Time-steps not matching!' - assert K12.shape == (ss01.inputs, ss02.outputs), \ - 'Gain K12 shape not matching with systems number of inputs/outputs' - assert K21.shape == (ss02.inputs, ss01.outputs), \ - 'Gain K21 shape not matching with systems number of inputs/outputs' + if ss01.dt is None and ss02.dt is None: + pass + else: + try: + assert np.abs(ss01.dt - ss02.dt) < 1e-10 * ss01.dt, 'Time-steps not matching!' + except TypeError: + raise TypeError('One of the systems to couple is discrete and the other continuous') + + if ss01.input_variables is not None and ss02.input_variables is not None \ + and isinstance(K12, Gain) and isinstance(K21, Gain): + with_enhanced_vars = True + LinearVector.check_connection(K12.output_variables, ss01.input_variables) + LinearVector.check_connection(ss02.output_variables, K12.input_variables) + + LinearVector.check_connection(K21.output_variables, ss02.input_variables) + LinearVector.check_connection(ss01.output_variables, K21.input_variables) + K21 = K21.value + K12 = K12.value + else: + with_enhanced_vars = False + assert K12.shape == (ss01.inputs, ss02.outputs), \ + 'Gain K12 shape not matching with systems number of inputs/outputs' + assert K21.shape == (ss02.inputs, ss01.outputs), \ + 'Gain K21 shape not matching with systems number of inputs/outputs' A1, B1, C1, D1 = ss01.get_mats() A2, B2, C2, D2 = ss02.get_mats() @@ -594,7 +1059,14 @@ def couple(ss01, ss02, K12, K21, out_sparse=False): [libsp.dense(libsp.dot(libsp.dot(D2, cpl_21), D1)), libsp.dense(D2 + libsp.dot(libsp.dot(D2, cpl_22), D2))]]) - return ss(A, B, C, D, dt=ss01.dt) + coupled_ss = StateSpace(A, B, C, D, dt=ss01.dt) + if with_enhanced_vars: + coupled_ss.state_variables = LinearVector.merge(ss01.state_variables, ss02.state_variables) + coupled_ss.input_variables = LinearVector.merge(ss01.input_variables, ss02.input_variables) + coupled_ss.output_variables = LinearVector.merge(ss01.output_variables, ss02.output_variables) + + return coupled_ss + def disc2cont(sys): r""" @@ -615,10 +1087,10 @@ def disc2cont(sys): MIT OCW 6.245 Args: - sys (libss.ss): SHARPy discrete-time state-space object. + sys (libss.StateSpace): SHARPy discrete-time state-space object. Returns: - libss.ss: Converted continuous-time state-space object. + libss.StateSpace: Converted continuous-time state-space object. """ assert sys.dt is not None, 'System to transform is not a discrete-time system.' @@ -633,17 +1105,37 @@ def disc2cont(sys): c = np.sqrt(2 * omega_0) * sys.C.dot(eye_a_inv) d = sys.D - sys.C.dot(eye_a_inv.dot(sys.B)) - return ss(a, b, c, d) + sys_ct = StateSpace(a, b, c, d) + + if sys.input_variables is not None: + sys_ct.input_variables = sys.input_variables + sys_ct.state_variables = sys.state_variables + sys_ct.output_variables = sys.output_variables + + return sys_ct + +def retain_inout_channels(sys, retain_channels, where): + """ + Retain selected input or output channels only. -def remove_inout_channels(sys, retain_channels, where): + Args: + retain_channels (list): List of channels to retain + where (str): ``in`` or ``out`` for input/output channels + Returns: + StateSpace: Updated state-space object + """ retain_m = len(retain_channels) # new number of in/out if where == 'in': m = sys.inputs # current number of in/out + gain_input_vars = sys.input_variables + gain_output_vars = LinearVector.transform(sys.input_variables, to_type=OutputVariable) elif where == 'out': m = sys.outputs + gain_input_vars = LinearVector.transform(sys.output_variables, to_type=InputVariable) + gain_output_vars = sys.output_variables.copy() else: raise NameError('Argument ``where`` can only be ``in`` or ``out``.') @@ -651,8 +1143,25 @@ def remove_inout_channels(sys, retain_channels, where): for ith, channel in enumerate(retain_channels): gain_matrix[ith, channel] = 1 + # Go through variables... + for var in gain_input_vars: + n_vars = np.sum( + (np.array(retain_channels) < var.end_position) * (np.array(retain_channels) >= var.first_position)) + + if n_vars == 0: + gain_output_vars.remove(var.name) + else: + gain_output_vars.modify(var.name, size=n_vars) + + gain_output_vars.update_indices() + gain_output_vars.update_locations() + + gain_matrix = Gain(gain_matrix, + input_vars=gain_input_vars, + output_vars=gain_output_vars) + if where == 'in': - sys.addGain(gain_matrix.T, where='in') + sys.addGain(gain_matrix.transpose(), where='in') elif where == 'out': sys.addGain(gain_matrix, where='out') else: @@ -661,158 +1170,12 @@ def remove_inout_channels(sys, retain_channels, where): return sys - - -# def couple_wrong02(ss01, ss02, K12, K21): -# """ -# Couples 2 dlti systems ss01 and ss02 through the gains K12 and K21, where -# K12 transforms the output of ss02 into an input of ss01. -# """ - -# assert ss01.dt == ss02.dt, 'Time-steps not matching!' -# assert K12.shape == (ss01.inputs, ss02.outputs), \ -# 'Gain K12 shape not matching with systems number of inputs/outputs' -# assert K21.shape == (ss02.inputs, ss01.outputs), \ -# 'Gain K21 shape not matching with systems number of inputs/outputs' - -# A1, B1, C1, D1 = ss01.A, ss01.B, ss01.C, ss01.D -# A2, B2, C2, D2 = ss02.A, ss02.B, ss02.C, ss02.D - -# # extract size -# Nx1, Nu1 = B1.shape -# Ny1 = C1.shape[0] -# Nx2, Nu2 = B2.shape -# Ny2 = C2.shape[0] - -# # terms to invert -# maxD1 = np.max(np.abs(D1)) -# maxD2 = np.max(np.abs(D2)) -# if maxD1 < 1e-32: -# pass -# if maxD2 < 1e-32: -# pass - -# # terms solving for u21 (input of ss02 due to ss01) -# K11 = np.dot(K12, np.dot(D2, K21)) -# # L1=np.eye(Nu1)-np.dot(K11,D1) -# L1inv = np.linalg.inv(np.eye(Nu1) - np.dot(K11, D1)) - -# # coupling terms for u21 -# cpl_11 = np.dot(L1inv, K11) -# cpl_12 = np.dot(L1inv, K12) - -# # terms solving for u12 (input of ss01 due to ss02) -# T = np.dot(np.dot(K21, D1), L1inv) - -# # coupling terms for u21 -# cpl_21 = K21 + np.dot(T, K11) -# cpl_22 = np.dot(T, K12) - -# # Build coupled system -# A = np.block([ -# [A1 + np.dot(np.dot(B1, cpl_11), C1), np.dot(np.dot(B1, cpl_12), C2)], -# [np.dot(np.dot(B2, cpl_21), C1), A2 + np.dot(np.dot(B2, cpl_22), C2)]]) - -# C = np.block([ -# [C1 + np.dot(np.dot(D1, cpl_11), C1), np.dot(np.dot(D1, cpl_12), C2)], -# [np.dot(np.dot(D2, cpl_21), C1), C2 + np.dot(np.dot(D2, cpl_22), C2)]]) - -# B = np.block([ -# [B1 + np.dot(np.dot(B1, cpl_11), D1), np.dot(np.dot(B1, cpl_12), D2)], -# [np.dot(np.dot(B2, cpl_21), D1), B2 + np.dot(np.dot(B2, cpl_22), D2)]]) - -# D = np.block([ -# [D1 + np.dot(np.dot(D1, cpl_11), D1), np.dot(np.dot(D1, cpl_12), D2)], -# [np.dot(np.dot(D2, cpl_21), D1), D2 + np.dot(np.dot(D2, cpl_22), D2)]]) - -# if ss01.dt is None: -# sstot = scsig.lti(A, B, C, D) -# else: -# sstot = scsig.dlti(A, B, C, D, dt=ss01.dt) -# return sstot - - -# def couple_wrong(ss01, ss02, K12, K21): -# """ -# Couples 2 dlti systems ss01 and ss02 through the gains K12 and K21, where -# K12 transforms the output of ss02 into an input of ss01. -# """ - -# assert ss01.dt == ss02.dt, 'Time-steps not matching!' -# assert K12.shape == (ss01.inputs, ss02.outputs), \ -# 'Gain K12 shape not matching with systems number of inputs/outputs' -# assert K21.shape == (ss02.inputs, ss01.outputs), \ -# 'Gain K21 shape not matching with systems number of inputs/outputs' - -# A1, B1, C1, D1 = ss01.A, ss01.B, ss01.C, ss01.D -# A2, B2, C2, D2 = ss02.A, ss02.B, ss02.C, ss02.D - -# # extract size -# Nx1, Nu1 = B1.shape -# Ny1 = C1.shape[0] -# Nx2, Nu2 = B2.shape -# Ny2 = C2.shape[0] - -# # terms to invert -# maxD1 = np.max(np.abs(D1)) -# maxD2 = np.max(np.abs(D2)) -# if maxD1 < 1e-32: -# pass -# if maxD2 < 1e-32: -# pass - -# # compute self-coupling terms -# S1 = np.dot(K12, np.dot(D2, K21)) -# S2 = np.dot(K21, np.dot(D1, K12)) - -# # left hand side terms -# L1 = np.eye(Nu1) - np.dot(S1, D1) -# L2 = np.eye(Nu2) - np.dot(S2, D2) - -# # invert left hand side terms -# L1inv = np.linalg.inv(L1) -# L2inv = np.linalg.inv(L2) - -# # recurrent terms -# L1invS1 = np.dot(L1inv, S1) -# L2invS2 = np.dot(L2inv, S2) - -# L1invK12 = np.dot(L1inv, K12) -# L2invK21 = np.dot(L2inv, K21) - -# # Build coupled system -# A = np.block([ -# [A1 + np.dot(np.dot(B1, L1invS1), C1), np.dot(np.dot(B1, L1invK12), C2)], -# [np.dot(np.dot(B2, L2invK21), C1), A2 + np.dot(np.dot(B2, L2invS2), C2)]]) - -# C = np.block([ -# [C1 + np.dot(np.dot(D1, L1invS1), C1), np.dot(np.dot(D1, L1invK12), C2)], -# [np.dot(np.dot(D2, L2invK21), C1), C2 + np.dot(np.dot(D2, L2invS2), C2)]]) - -# B = np.block([ -# [B1 + np.dot(np.dot(B1, L1invS1), D1), np.dot(np.dot(B1, L1invK12), D2)], -# [np.dot(np.dot(B2, L2invK21), D1), B2 + np.dot(np.dot(B2, L2invS2), D2)]]) - -# D = np.block([ -# [D1 + np.dot(np.dot(D1, L1invS1), D1), np.dot(np.dot(D1, L1invK12), D2)], -# [np.dot(np.dot(D2, L2invK21), D1), D2 + np.dot(np.dot(D2, L2invS2), D2)]]) - -# if ss01.dt is None: -# sstot = scsig.lti(A, B, C, D) -# else: -# sstot = scsig.dlti(A, B, C, D, dt=ss01.dt) -# return sstot - - - - - def freqresp(SS, wv, dlti=True): """ In-house frequency response function supporting dense/sparse types Inputs: - - SS: instance of ss class, or scipy.signal.StateSpace* + - SS: instance of StateSpace class, or scipy.signal.StateSpace* - wv: frequency range - dlti: True if discrete-time system is considered. @@ -825,8 +1188,8 @@ def freqresp(SS, wv, dlti=True): matrices. """ - assert type(SS) == ss, \ - 'Type %s of state-space model not supported. Use libss.ss instead!' % type(SS) + assert type(SS) == StateSpace, \ + 'Type %s of state-space model not supported. Use libss.StateSpace instead!' % type(SS) SS.check_types() if hasattr(SS, 'dt') and dlti: @@ -867,18 +1230,32 @@ def series(SS01, SS02): u \rightarrow \mathsf{SS01} \rightarrow \mathsf{SS02} \rightarrow y \Longrightarrow u \rightarrow \mathsf{SStot} \rightarrow y + where the state vector :math:`x` is :math:`[x_1, x_2]`. + Args: - SS01 (libss.ss): State Space 1 instance. Can be DLTI/CLTI, dense or sparse. - SS02 (libss.ss): State Space 2 instance. Can be DLTI/CLTI, dense or sparse. + SS01 (libss.StateSpace): State Space 1 instance. Can be DLTI/CLTI, dense or sparse. + SS02 (libss.StateSpace): State Space 2 instance. Can be DLTI/CLTI, dense or sparse. Returns - libss.ss: Combined state space system in series in dense format. + libss.StateSpace: Combined state space system in series in dense format. """ if type(SS01) is not type(SS02): - raise NameError('The two input systems need to have the same size!') + raise TypeError('The two input systems are not of the same type') if SS01.dt != SS02.dt: - raise NameError('DLTI systems do not have the same time-step!') + raise NameError('DLTI systems do not have the same time-step. SS01 dt={:f}, SS02 dt={:f}'.format( + SS01.dt, SS02.dt)) + + # check series connection + if SS01.output_variables is not None and SS02.input_variables is not None: + LinearVector.check_connection(SS01.output_variables, SS02.input_variables) + # for i_var in range(SS01.output_variables.num_variables): + # out1 = SS01.output_variables[i_var] + # in2 = SS02.input_variables[i_var] + # if out1.name != in2.name: + # raise NameError('Series coupling outputs1 and inputs2 have different names') + # if not (out1.rows_loc == in2.cols_loc).all: + # raise IndexError('Series coupling. Output1 channels do not line up with input2 channels.') # determine size of total system Nst01, Nst02 = SS01.states, SS02.states @@ -886,6 +1263,9 @@ def series(SS01, SS02): Nin = SS01.inputs Nout = SS02.outputs + if SS01.outputs != SS02.inputs: + raise ValueError('SS01 outputs not equal to SS02 inputs,\nSS01={:s}\nSS02={:s}'.format(str(SS01), str(SS02))) + # Build A matrix A = np.zeros((Nst, Nst)) @@ -898,7 +1278,14 @@ def series(SS01, SS02): C = np.concatenate((libsp.dense(libsp.dot(SS02.D, SS01.C)), libsp.dense(SS02.C)), axis=1) D = libsp.dense(libsp.dot(SS02.D, SS01.D)) - SStot = ss(A, B, C, D, dt=SS01.dt) + SStot = StateSpace(A, B, C, D, dt=SS01.dt) + + SStot.input_variables = SS01.input_variables + try: + SStot.state_variables = LinearVector.merge(SS01.state_variables, SS02.state_variables) + except AttributeError: + SStot.state_variables = None + SStot.output_variables = SS02.output_variables return SStot @@ -1071,9 +1458,9 @@ def addGain(SShere, Kmat, where): D = np.block([Kmat, SShere.D]) if SShere.dt == None: - SSnew = ss(A, B, C, D) + SSnew = StateSpace(A, B, C, D) else: - SSnew = ss(A, B, C, D, dt=SShere.dt) + SSnew = StateSpace(A, B, C, D, dt=SShere.dt) return SSnew @@ -1113,7 +1500,6 @@ def join2(SS1, SS2): SStot = np.block([[SS1, np.zeros((Nout01, Nin02))], [np.zeros((Nout02, Nin01)), SS2]]) - elif isinstance(SS1, np.ndarray) and isinstance(SS2, type_dlti): Nin01, Nout01 = SS1.shape[1], SS1.shape[0] @@ -1129,7 +1515,6 @@ def join2(SS1, SS2): SStot = scsig.StateSpace(A, B, C, D, dt=SS2.dt) - elif isinstance(SS1, type_dlti) and isinstance(SS2, np.ndarray): Nin01, Nout01 = SS1.inputs, SS1.outputs @@ -1145,7 +1530,6 @@ def join2(SS1, SS2): SStot = scsig.StateSpace(A, B, C, D, dt=SS1.dt) - elif isinstance(SS1, type_dlti) and isinstance(SS2, type_dlti): assert SS1.dt == SS2.dt, 'State-space models must have the same time-step' @@ -1164,50 +1548,51 @@ def join2(SS1, SS2): [np.zeros((Nout02, Nin01)), SS2.D]]) SStot = scsig.StateSpace(A, B, C, D, dt=SS1.dt) - else: raise NameError('Input types not recognised in any implemented option!') return SStot -def join(SS_list,wv=None): - ''' - Given a list of state-space models belonging to the ss class, creates a - joined system whose output is the sum of the state-space outputs. If wv is - not None, this is a list of weights, such that the output is: - y = sum( wv[ii] y_ii ) +def join(SS_list, wv=None): + """ + Given a list of state-space models belonging to the StateSpace class, creates a + joined system whose output is the sum of the state-space outputs. If wv is + not None, this is a list of weights, such that the output is: + + y = sum( wv[ii] y_ii ) - Ref: equation (4.22) of - Benner, P., Gugercin, S. & Willcox, K., 2015. A Survey of Projection-Based - Model Reduction Methods for Parametric Dynamical Systems. SIAM Review, 57(4), - pp.483–531. + Ref: equation (4.22) of + Benner, P., Gugercin, S. & Willcox, K., 2015. A Survey of Projection-Based + Model Reduction Methods for Parametric Dynamical Systems. SIAM Review, 57(4), + pp.483–531. + + Warnings: + - system matrices must be numpy arrays + - the function does not perform any check! + """ - Warning: - - system matrices must be numpy arrays - - the function does not perform any check! - ''' + N = len(SS_list) + if wv is not None: + assert N == len(wv), "'weights input should have'" - N = len(SS_list) - if wv is not None: - assert N==len(wv), "'weights input should have'" + A = scalg.block_diag(*[getattr(ss, 'A') for ss in SS_list]) + B = np.block([[getattr(ss, 'B')] for ss in SS_list]) - A = scalg.block_diag(*[ getattr(ss,'A') for ss in SS_list ]) - B = np.block([ [getattr(ss,'B')] for ss in SS_list ]) + if wv is None: + C = np.block([getattr(ss, 'C') for ss in SS_list]) + else: + C = np.block([ww * getattr(ss, 'C') for ww, ss in zip(wv, SS_list)]) - if wv is None: - C = np.block( [ getattr(ss,'C') for ss in SS_list ] ) - else: - C = np.block( [ ww*getattr(ss,'C') for ww,ss in zip(wv,SS_list) ] ) + D = np.zeros_like(SS_list[0].D) + for ii in range(N): + if wv is None: + D += SS_list[ii].D + else: + D += wv[ii] * SS_list[ii].D - D=np.zeros_like(SS_list[0].D) - for ii in range(N): - if wv is None: - D += SS_list[ii].D - else: - D += wv[ii]*SS_list[ii].D + return StateSpace(A, B, C, D, SS_list[0].dt) - return ss(A,B,C,D,SS_list[0].dt) def sum_ss(SS1, SS2, negative=False): """ @@ -1358,7 +1743,9 @@ def scale_SS(SSin, input_scal=1., output_scal=1., state_scal=1., byref=True): def simulate(SShere, U, x0=None): """ Routine to simulate response to generic input. - @warning: this routine is for testing and may lack of robustness. Use + + Warnings: + This routine is for testing and may lack of robustness. Use scipy.signal instead. """ @@ -1596,37 +1983,53 @@ def eigvals(a, dlti=False): return eigs[order] + # --------------------------------------------------------------------- Testing def random_ss(Nx, Nu, Ny, dt=None, use_sparse=False, stable=True): """ Define random system from number of states (Nx), inputs (Nu) and output (Ny). + + Args: + Nx (int): Number of states + Nu (int): Number of inputs + Ny (int): Number of outputs + dt (float (optional)): Time step for discrete systems + use_sparse (bool): Use sparse matrices + stable (bool): Ensure the system is stable + + Returns: + StateSpace: State space object """ A = np.random.rand(Nx, Nx) if stable: - ev,U=np.linalg.eig(A) - evabs=np.abs(ev) + ev, U = np.linalg.eig(A) + evabs = np.abs(ev) for ee in range(len(ev)): - if evabs[ee]>0.99: - ev[ee]/=1.1*evabs[ee] - A = np.dot(U*ev, np.linalg.inv(U) ).real + if evabs[ee] > 0.99: + ev[ee] /= 1.1 * evabs[ee] + A = np.dot(U * ev, np.linalg.inv(U)).real B = np.random.rand(Nx, Nu) C = np.random.rand(Ny, Nx) D = np.random.rand(Ny, Nu) if use_sparse: - SS = ss(libsp.csc_matrix(A), - libsp.csc_matrix(B), - libsp.csc_matrix(C), - libsp.csc_matrix(D), - dt=dt) + ss = StateSpace(libsp.csc_matrix(A), + libsp.csc_matrix(B), + libsp.csc_matrix(C), + libsp.csc_matrix(D), + dt=dt) else: - SS = ss(A, B, C, D, dt=dt) + ss = StateSpace(A, B, C, D, dt=dt) - return SS + ss.initialise_variables(({'name': 'input_variable', 'size': Nu}), var_type='in') + ss.initialise_variables(({'name': 'output_variable', 'size': Ny}), var_type='out') + ss.initialise_variables(({'name': 'state_variable', 'size': Nx}), var_type='state') + + return ss def compare_ss(SS1, SS2, tol=1e-10, Print=False): @@ -1658,171 +2061,12 @@ def compare_ss(SS1, SS2, tol=1e-10, Print=False): # ----------------------------------------------------------------------------- -if __name__ == '__main__': - import unittest - - - class Test_dlti(unittest.TestCase): - """ Test methods into this module for DLTI systems """ - - def setUp(self): - # allocate some state-space model (dense and sparse) - dt = 0.3 - Ny, Nx, Nu = 4, 3, 2 - A = np.random.rand(Nx, Nx) - B = np.random.rand(Nx, Nu) - C = np.random.rand(Ny, Nx) - D = np.random.rand(Ny, Nu) - self.SS = ss(A, B, C, D, dt=dt) - self.SSsp = ss(libsp.csc_matrix(A), libsp.csc_matrix(B), C, D, dt=dt) - - def test_SSconv(self): - - SS = self.SS - SSsp = self.SSsp - Nu, Nx, Ny = SS.inputs, SS.states, SS.outputs - A, B, C, D = SS.get_mats() - - # remove predictor: try different scenario - B1 = np.random.rand(Nx, Nu) - SSpr0 = ss(*SSconv(A, B, B1, C, D), dt=0.3) - SSpr1 = ss(*SSconv(A, B, libsp.csc_matrix(B1), C, D), dt=0.3) - SSpr2 = ss(*SSconv( - libsp.csc_matrix(A), B, libsp.csc_matrix(B1), C, D), dt=0.3) - SSpr3 = ss(*SSconv( - libsp.csc_matrix(A), libsp.csc_matrix(B), B1, C, D), dt=0.3) - SSpr4 = ss(*SSconv( - libsp.csc_matrix(A), libsp.csc_matrix(B), libsp.csc_matrix(B1), C, D), dt=0.3) - compare_ss(SSpr0, SSpr1) - compare_ss(SSpr0, SSpr2) - compare_ss(SSpr0, SSpr3) - compare_ss(SSpr0, SSpr4) - - def test_scale_SS(self): - - SS = self.SS - SSsp = self.SSsp - Nu, Nx, Ny = SS.inputs, SS.states, SS.outputs - - # scale (hard-copy) - insc = np.random.rand(Nu) - stsc = np.random.rand(Nx) - outsc = np.random.rand(Ny) - SSadim = scale_SS(SS, insc, outsc, stsc, byref=False) - SSadim_sp = scale_SS(SSsp, insc, outsc, stsc, byref=False) - compare_ss(SSadim, SSadim_sp) - - # scale (by reference) - SS.scale(insc, outsc, stsc) - SSsp.scale(insc, outsc, stsc) - compare_ss(SS, SSsp) - - def test_addGain(self): - - SS = self.SS - SSsp = self.SSsp - Nu, Nx, Ny = SS.inputs, SS.states, SS.outputs - - # add gains - Kin = np.random.rand(Nu, 5) - Kout = np.random.rand(4, Ny) - SS.addGain(Kin, 'in') - SS.addGain(Kout, 'out') - SSsp.addGain(Kin, 'in') - SSsp.addGain(Kout, 'out') - compare_ss(SS, SSsp) - - def test_freqresp(self): - # freq response: try different scenario - - SS = self.SS - SSsp = self.SSsp - Nu, Nx, Ny = SS.inputs, SS.states, SS.outputs - - kv = np.linspace(0, 1, 8) - Y = SS.freqresp(kv) - Ysp = SSsp.freqresp(kv) - er = np.max(np.abs(Y - Ysp)) - assert er < 1e-10, 'Test on freqresp failed' - - SS.D = libsp.csc_matrix(SS.D) - Y1 = SS.freqresp(kv) - er = np.max(np.abs(Y - Y1)) - assert er < 1e-10, 'Test on freqresp failed' - - def test_couple(self): - dt = .2 - Nx1, Nu1, Ny1 = 3, 4, 2 - Nx2, Nu2, Ny2 = 4, 3, 2 - K12 = np.random.rand(Nu1, Ny2) - K21 = np.random.rand(Nu2, Ny1) - SS1 = random_ss(Nx1, Nu1, Ny1, dt=.2) - SS2 = random_ss(Nx2, Nu2, Ny2, dt=.2) - - SS1sp = ss(libsp.csc_matrix(SS1.A), - libsp.csc_matrix(SS1.B), - libsp.csc_matrix(SS1.C), - libsp.csc_matrix(SS1.D), dt=dt) - SS2sp = ss(libsp.csc_matrix(SS2.A), - libsp.csc_matrix(SS2.B), - libsp.csc_matrix(SS2.C), - libsp.csc_matrix(SS2.D), dt=dt) - K12sp = libsp.csc_matrix(K12) - K21sp = libsp.csc_matrix(K21) - - # SCref=couple_full(SS1,SS2,K12,K21) - SC0 = couple(SS1, SS2, K12, K21) - # compare_ss(SCref,SC0) - for SSa in [SS1, SS1sp]: - for SSb in [SS2, SS2sp]: - for k12 in [K12, K12sp]: - for k21 in [K21, K21sp]: - SChere = couple(SSa, SSb, k12, k21) - compare_ss(SC0, SChere) - - def test_join(self): - - Nx,Nu,Ny = 4, 3, 2 - SS_list = [random_ss(Nx,Nu,Ny,dt=.2) for ii in range(3)] - - wv = [.3, .5, .2] - SSjoin = join(SS_list,wv) - - kv = np.array([0., 1., 3.]) - Yjoin = SSjoin.freqresp(kv) - - Yref = np.zeros_like(Yjoin) - for ii in range(3): - Yref += wv[ii]*SS_list[ii].freqresp(kv) - - er = np.max(np.abs(Yjoin - Yref)) - assert er<1e-14, 'test_join error %.3e too large' %er - - def test_disc2cont(self): - # not the best test given that eigenvalue comparison is not great with random systems. (error grows near - # nyquist frequency) - - # this test is for execution purposes only. - sys = copy.deepcopy(self.SS) - self.SS.disc2cont() - - ct_sys = disc2cont(sys) - - - - outprint = 'Testing libss' - print('\n' + 70 * '-') - print((70 - len(outprint)) * ' ' + outprint) - print(70 * '-') - unittest.main() - - def ss_to_scipy(ss): """ Converts to a scipy.signal linear time invariant system Args: - ss (libss.ss): SHARPy state space object + ss (libss.StateSpace): SHARPy state space object Returns: scipy.signal.dlti @@ -1834,43 +2078,3 @@ def ss_to_scipy(ss): sys = scsig.dlti(ss.A, ss.B, ss.C, ss.D, dt=ss.dt) return sys - -# 1/0 - -# # check parallel connector -# Nout=2 -# Nin01,Nin02=2,3 -# Nst01,Nst02=4,2 - -# # build random systems -# fac=0.1 -# A01,A02=fac*np.random.rand(Nst01,Nst01),fac*np.random.rand(Nst02,Nst02) -# B01,B02=np.random.rand(Nst01,Nin01),np.random.rand(Nst02,Nin02) -# C01,C02=np.random.rand(Nout,Nst01),np.random.rand(Nout,Nst02) -# D01,D02=np.random.rand(Nout,Nin01),np.random.rand(Nout,Nin02) - -# dt=0.1 -# SS01=scsig.StateSpace( A01,B01,C01,D01,dt=dt ) -# SS02=scsig.StateSpace( A02,B02,C02,D02,dt=dt ) - -# # simulate -# NT=11 -# U01,U02=np.random.rand(NT,Nin01),np.random.rand(NT,Nin02) - -# # reference -# Y01,X01=simulate(SS01,U01) -# Y02,X02=simulate(SS02,U02) -# Yref=Y01+Y02 - -# # parallel -# SStot=parallel(SS01,SS02) -# Utot=np.block([U01,U02]) -# Ytot,Xtot=simulate(SStot,Utot) - -# # join method -# SStot=join(SS01,SS02) -# K=np.array([[1,2,3],[4,5,6]]) -# SStot=join(K,SS02) -# SStot=join(SS02,K) -# K2=np.array([[10,20,30],[40,50,60]]).T -# Ktot=join(K,K2) diff --git a/sharpy/linear/src/lingebm.py b/sharpy/linear/src/lingebm.py index ea7d59022..ea1fc3338 100644 --- a/sharpy/linear/src/lingebm.py +++ b/sharpy/linear/src/lingebm.py @@ -14,6 +14,7 @@ import sharpy.utils.cout_utils as cout import sharpy.structure.utils.modalutils as modalutils import warnings +from sharpy.linear.utils.ss_interface import LinearVector, StateVariable, OutputVariable, InputVariable class FlexDynamic(): @@ -140,7 +141,7 @@ def __init__(self, tsinfo, structure=None, custom_settings=dict()): self.discr_method = self.settings['discr_method'] self.newmark_damp = self.settings['newmark_damp'] self.use_euler = self.settings['use_euler'] - self.use_principal_axes = self.settings.get('rigid_modes_cg', False) # this setting is inherited from the setting in Modal solver + self.use_principal_axes = self.settings.get('rigid_modes_ppal_axes', False) # this setting is inherited from the setting in Modal solver ### set state-space variables self.SScont = None @@ -160,6 +161,7 @@ def __init__(self, tsinfo, structure=None, custom_settings=dict()): if self.Mstr.shape[0] == 6*(self.tsstruct0.num_node - 1): self.clamped = True + self.num_dof_rig = 0 else: self.clamped = False @@ -168,7 +170,8 @@ def __init__(self, tsinfo, structure=None, custom_settings=dict()): else: self.num_dof_rig = 10 - self.update_modal() + if self.modal: + self.update_modal() self.num_dof_flex = np.sum(structure.vdof >= 0)*6 @@ -900,21 +903,11 @@ def assemble(self, Nmodes=None): assert self.inout_coords in ['modes', 'nodes'], \ 'inout_coords=%s not implemented!' % self.inout_coords - # cond_mass_matrix = np.linalg.cond(self.Mstr) - # if np.log10(cond_mass_matrix) >= 10.: - # warnings.warn('Mass matrix is poorly conditioned (Cond = 10^%f). Inverse may not be correct.' - # % np.log10(cond_mass_matrix), 3) - # else: - # cout.cout_wrap('Mass matrix condition = %e' % cond_mass_matrix) - dlti = self.dlti modal = self.modal num_dof = self.num_dof if Nmodes is None or Nmodes >= self.num_modes: Nmodes = self.num_modes - # else: - # # Modal truncation - # self.update_truncated_modes(Nmodes) if dlti: # ---------------------------------- assemble discrete time @@ -938,31 +931,51 @@ def assemble(self, Nmodes=None): else: Ccut = np.dot(Phi.T, np.dot(self.Cstr, Phi)) - # Ass, Bss, Css, Dss = newmark_ss( - # np.eye(Nmodes), - # Ccut, - # np.diag(self.freq_natural[:Nmodes] ** 2), - # self.dt, - # self.newmark_damp) Ass, Bss, Css, Dss = newmark_ss( - # Phi.T.dot(self.Mstr.dot(Phi)), np.linalg.inv(np.dot(self.U[:, :Nmodes].T, np.dot(self.Mstr, self.U[:, :Nmodes]))), - # np.eye(Nmodes), Ccut, np.dot(self.U[:, :Nmodes].T, np.dot(self.Kstr, self.U[:, :Nmodes])), - # Phi.T.dot(self.Kstr.dot(Phi)), - # np.diag(self.freq_natural[:Nmodes]**2), self.dt, self.newmark_damp) - self.Kin = Phi.T - self.Kout = sc.linalg.block_diag(*[Phi, Phi]) + + self.Kin = libss.Gain(Phi.T) + self.Kin.input_variables = LinearVector([InputVariable('forces_n', + size=self.Mstr.shape[0], + index=0)]) + self.Kin.output_variables = LinearVector([OutputVariable('Q', + size=Nmodes, + index=0)]) + + self.Kout = libss.Gain(sc.linalg.block_diag(*[Phi, Phi])) + self.Kout.input_variables = LinearVector([InputVariable('q', size=Nmodes, index=0), + InputVariable('q_dot', size=Nmodes, index=1)]) + output_variables = LinearVector([OutputVariable('eta', size=self.num_dof_flex, index=0), + OutputVariable('eta_dot', size=self.num_dof_flex, index=1)]) + if not self.clamped: + output_variables.add('beta_bar', size=self.num_dof_rig, index=0.5) + output_variables.append('beta', size=self.num_dof_rig) + + self.Kout.output_variables = output_variables + else: raise NameError( 'Newmark-beta discretisation not available ' \ 'for projection on damped eigenvectors') # build state-space model - self.SSdisc = libss.ss(Ass, Bss, Css, Dss, dt=self.dt) + self.SSdisc = libss.StateSpace(Ass, Bss, Css, Dss, dt=self.dt) + input_variables = LinearVector([InputVariable('Q', size=Nmodes, index=0)]) + + output_variables = LinearVector([OutputVariable('q', size=Nmodes, index=0), + OutputVariable('q_dot', size=Nmodes, index=1)]) + + state_variables = output_variables.transform(output_variables, + to_type=StateVariable) + + self.SSdisc.input_variables = input_variables + self.SSdisc.output_variables = output_variables + self.SSdisc.state_variables = state_variables + if self.inout_coords == 'nodes': self.SSdisc = libss.addGain(self.SSdisc, self.Kin, 'in') self.SSdisc = libss.addGain(self.SSdisc, self.Kout, 'out') @@ -977,8 +990,21 @@ def assemble(self, Nmodes=None): self.dt, self.newmark_damp) self.Kin = None self.Kout = None - self.SSdisc = libss.ss(Ass, Bss, Css, Dss, dt=self.dt) + self.SSdisc = libss.StateSpace(Ass, Bss, Css, Dss, dt=self.dt) + + input_variables = LinearVector([InputVariable('forces_n', + size=self.Mstr.shape[0], + index=0)]) + + output_variables = LinearVector([OutputVariable('eta', size=self.num_dof_flex, index=0), + OutputVariable('eta_dot', size=self.num_dof_flex, index=1)]) + if not self.clamped: + output_variables.add('beta_bar', size=self.num_dof_rig, index=0.5) + output_variables.append('beta', size=self.num_dof_rig) + self.SSdisc.output_variables = output_variables + self.SSdisc.input_variables = input_variables + self.SSdisc.state_variables = LinearVector.transform(output_variables, to_type=StateVariable) else: raise NameError( 'Discretisation method %s not available' % self.discr_method) @@ -1002,8 +1028,24 @@ def assemble(self, Nmodes=None): Bss = np.zeros((2 * Nmodes, Nmodes)) Dss = np.zeros((2 * Nmodes, Nmodes)) Bss[Nmodes + iivec, iivec] = 1. - self.Kin = Phi.T - self.Kout = sc.linalg.block_diag(*(Phi, Phi)) + self.Kin = libss.Gain(Phi.T) + self.Kin.input_variables = LinearVector([InputVariable('forces_n', + size=self.Mstr.shape[0], + index=0)]) + self.Kin.output_variables = LinearVector([OutputVariable('Q', + size=Nmodes, + index=0)]) + self.Kout = libss.Gain(sc.linalg.block_diag(*[Phi, Phi])) + self.Kout.input_variables = LinearVector([InputVariable('q', size=Nmodes, index=0), + InputVariable('q_dot', size=Nmodes, index=1)]) + + output_variables = LinearVector([OutputVariable('eta', size=self.num_dof_flex, index=0), + OutputVariable('eta_dot', size=self.num_dof_flex, index=1)]) + if not self.clamped: + output_variables.add('beta_bar', size=self.num_dof_rig, index=0.5) + output_variables.append('beta', size=self.num_dof_rig) + + self.Kout.output_variables = output_variables else: # damped mode shapes # The algorithm assumes that for each couple of complex conj # eigenvalues, only one eigenvalue (and the eigenvectors @@ -1023,7 +1065,18 @@ def assemble(self, Nmodes=None): self.Kout = np.block([2. * U.real, (-2.) * U.imag]) # build state-space model - self.SScont = libss.ss(Ass, Bss, Css, Dss) + self.SScont = libss.StateSpace(Ass, Bss, Css, Dss) + input_variables = LinearVector([InputVariable('Q', size=Nmodes, index=0)]) + + output_variables = LinearVector([OutputVariable('q', size=Nmodes, index=0), + OutputVariable('q_dot', size=Nmodes, index=1)]) + + state_variables = output_variables.transform(output_variables, + to_type=StateVariable) + + self.SScont.input_variables = input_variables + self.SScont.output_variables = output_variables + self.SScont.state_variables = state_variables if self.inout_coords == 'nodes': self.SScont = libss.addGain(self.SScont, self.Kin, 'in') self.SScont = libss.addGain(self.SScont, self.Kout, 'out') @@ -1045,7 +1098,22 @@ def assemble(self, Nmodes=None): Bss[num_dof:, :] = -Minv_neg self.Kin = None self.Kout = None - self.SScont = libss.ss(Ass, Bss, Css, Dss) + self.SScont = libss.StateSpace(Ass, Bss, Css, Dss) + + input_variables = LinearVector([InputVariable('forces_n', + size=self.Mstr.shape[0], + index=0)]) + + output_variables = LinearVector([OutputVariable('eta', size=self.num_dof_flex, index=0), + OutputVariable('eta_dot', size=self.num_dof_flex, index=1)]) + if not self.clamped: + output_variables.add('beta_bar', size=self.num_dof_rig, index=0.5) + output_variables.append('beta', size=self.num_dof_rig) + + self.SScont.output_variables = output_variables + self.SScont.input_variables = input_variables + self.SScont.state_variables = LinearVector.transform(output_variables, to_type=StateVariable) + def freqresp(self, wv=None, bode=True): """ @@ -1237,11 +1305,14 @@ def scale_system_normalised_time(self, time_ref): # if time_ref != 1.0 and time_ref is not None: if self.num_rig_dof != 0: warnings.warn('Time normalisation not yet implemented with rigid body motion.') - self.scaled_reference_matrices['dt'] = self.dt - self.dt /= time_ref + + if self.dlti: + self.scaled_reference_matrices['dt'] = self.dt + self.dt /= time_ref if self.settings['print_info']: cout.cout_wrap('Scaling beam according to reduced time...', 0) - cout.cout_wrap('\tSetting the beam time step to (%.4f)' % self.dt, 1) + if self.dlti: + cout.cout_wrap('\tSetting the beam time step to (%.4f)' % self.dt, 1) self.scaled_reference_matrices['C'] = self.Cstr.copy() self.scaled_reference_matrices['K'] = self.Kstr.copy() @@ -1273,13 +1344,13 @@ def cont2disc(self, dt=None): self.dt = dt else: assert self.dt is not None, \ - 'Provide time-step for convertion to discrete-time' + 'Provide time-step for conversion to discrete-time' SScont = self.SScont tpl = scsig.cont2discrete( (SScont.A, SScont.B, SScont.C, SScont.D), dt=self.dt, method=self.discr_method) - self.SSdisc = libss.ss(*tpl[:-1], dt=tpl[-1]) + self.SSdisc = libss.StateSpace(*tpl[:-1], dt=tpl[-1]) self.dlti = True @@ -1412,7 +1483,7 @@ def newmark_ss(Minv, C, K, dt, num_damp=1e-4): MinvK = np.dot(Minv, K) MinvC = np.dot(Minv, C) - # build ss + # build StateSpace Ass0 = np.block([[Imat - a0 * MinvK, dt * Imat - a0 * MinvC], [-b0 * MinvK, Imat - b0 * MinvC]]) Ass1 = np.block([[Imat + a1 * MinvK, a1 * MinvC], diff --git a/sharpy/linear/src/linuvlm.py b/sharpy/linear/src/linuvlm.py index e9e61aa63..69435ded7 100644 --- a/sharpy/linear/src/linuvlm.py +++ b/sharpy/linear/src/linuvlm.py @@ -35,6 +35,7 @@ import sharpy.utils.cout_utils as cout import sharpy.utils.exceptions as exceptions from sharpy.utils.constants import vortex_radius_def +from sharpy.linear.utils.ss_interface import LinearVector, StateVariable, InputVariable, OutputVariable settings_types_static = dict() settings_default_static = dict() @@ -114,6 +115,15 @@ def __init__(self, tsdata, custom_settings=None, for_vel=np.zeros((6,))): self.zeta_dot = np.zeros((3 * self.Kzeta)) self.u_ext = np.zeros((3 * self.Kzeta)) + self.input_variables_list = [InputVariable('zeta', size=3 * self.Kzeta, index=0), + InputVariable('zeta_dot', size=3 * self.Kzeta, index=1), + InputVariable('u_gust', size=3 * self.Kzeta, index=2)] + + self.state_variables_list = [StateVariable('gamma', size=self.K, index=0), + StateVariable('gamma_w', size=self.K_star, index=1), + StateVariable('gamma_m1', size=self.K, index=2)] + + self.output_variables_list = [OutputVariable('forces_v', size=3 * self.Kzeta, index=0)] # profiling output self.prof_out = './asbly.prof' @@ -619,6 +629,20 @@ def __init__(self, tsdata, dt=None, dynamic_settings=None, integr_order=2, ScalingFacts['force'] = ScalingFacts['dyn_pressure'] * ScalingFacts['length'] ** 2 self.ScalingFacts = ScalingFacts + self.input_variables_list = [InputVariable('zeta', size=3 * self.Kzeta, index=0), + InputVariable('zeta_dot', size=3 * self.Kzeta, index=1), + InputVariable('u_gust', size=3 * self.Kzeta, index=2)] + + self.state_variables_list = [StateVariable('gamma', size=self.K, index=0), + StateVariable('gamma_w', size=self.K_star, index=1), + StateVariable('dtgamma_dot', size=self.K, index=2), + StateVariable('gamma_m1', size=self.K, index=3)] + + self.output_variables_list = [OutputVariable('forces_v', size=3 * self.Kzeta, index=0)] + + if self.integr_order == 1: + self.state_variables_list.pop(2) # remove time derivative state + ### collect statistics self.cpu_summary = {'dim': 0., 'nondim': 0., @@ -935,7 +959,7 @@ def assemble_ss(self, wake_prop_settings=None): if self.remove_predictor: Ass, Bmod, Css, Dmod = \ libss.SSconv(Ass, None, Bss, Css, Dss, Bm1=None) - self.SS = libss.ss(Ass, Bmod, Css, Dmod, dt=self.dt) + self.SS = libss.StateSpace(Ass, Bmod, Css, Dmod, dt=self.dt) # Store original B matrix for state unpacking self.B_predictor = Bss @@ -945,10 +969,15 @@ def assemble_ss(self, wake_prop_settings=None): '\t\th_{n+1} = A h_{n} + B u_{n}\n\t' \ '\t\twith:\n\tx_n = h_n + Bp u_n', 1) else: - self.SS = libss.ss(Ass, Bss, Css, Dss, dt=self.dt) + self.SS = libss.StateSpace(Ass, Bss, Css, Dss, dt=self.dt) cout.cout_wrap('\tstate-space model produced in form:\n\t' \ 'x_{n+1} = A x_{n} + Bp u_{n+1}', 1) + # add variable tracker + self.SS.input_variables = LinearVector(self.input_variables_list) + self.SS.state_variables = LinearVector(self.state_variables_list) + self.SS.output_variables = LinearVector(self.output_variables_list) + self.cpu_summary['assemble'] = time.time() - t0 cout.cout_wrap('\t\t\t...done in %.2f sec' % self.cpu_summary['assemble']) @@ -1356,7 +1385,7 @@ def balfreq(self, DictBalFreq, wake_prop_settings=None): Ab = libsp.dot(Ti, libsp.dot(self.SS.A, T)) Bb = libsp.dot(Ti, self.SS.B) Cb = libsp.dot(self.SS.C, T) - SSb = libss.ss(Ab, Bb, Cb, self.SS.D, dt=self.SS.dt) + SSb = libss.StateSpace(Ab, Bb, Cb, self.SS.D, dt=self.SS.dt) ### Eliminate unstable modes - if any: if DictBalFreq['check_stability']: @@ -2042,6 +2071,11 @@ def assemble_ss(self, wake_prop_settings=None): cout.cout_wrap('\tstate-space model produced in form:\n\t' \ 'x_{n+1} = A x_{n} + Bp u_{n+1}', 1) + # add variable tracker + self.SS.input_variables = LinearVector(self.input_variables_list) + self.SS.state_variables = LinearVector(self.state_variables_list) + self.SS.output_variables = LinearVector(self.output_variables_list) + self.cpu_summary['assemble'] = time.time() - t0 cout.cout_wrap('\t\t\t...done in %.2f sec' % self.cpu_summary['assemble'], 1) @@ -2389,10 +2423,10 @@ def balfreq(self, DictBalFreq, wake_prop_settings=None): Ab, Bb, Cb, Db = \ libss.SSconv(np.block(Ab), None, np.block(Bb), np.block(Cb), np.block(self.SS.D), Bm1=None) - SSb = libss.ss(Ab, Bb, Cb, Db, dt=self.dt) + SSb = libss.StateSpace(Ab, Bb, Cb, Db, dt=self.dt) else: - SSb = libss.ss(np.block(Ab), np.block(Bb), - np.block(Cb), np.block(self.SS.D), dt=self.SS.dt) + SSb = libss.StateSpace(np.block(Ab), np.block(Bb), + np.block(Cb), np.block(self.SS.D), dt=self.SS.dt) ### Eliminate unstable modes - if any: if DictBalFreq['check_stability']: diff --git a/sharpy/linear/utils/__init__.py b/sharpy/linear/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sharpy/linear/utils/derivatives.py b/sharpy/linear/utils/derivatives.py new file mode 100644 index 000000000..f4eed49d2 --- /dev/null +++ b/sharpy/linear/utils/derivatives.py @@ -0,0 +1,390 @@ +import h5py +import numpy as np + +from sharpy.utils import algebra as algebra, cout_utils as cout + + +class Derivatives: + """ + Class containing the derivatives set for a given state-space system (i.e. aeroelastic or aerodynamic) + """ + def __init__(self, reference_dimensions, static_state, target_system=None): + + self.target_system = target_system # type: str # name of target system (aerodynamic/aeroelastic) + self.transfer_function = None # type: np.array # matrix of steady-state TF for target system + + self.static_state = static_state # type: tuple # [fx, fy, fz] at ref state + self.reference_dimensions = reference_dimensions # type: dict # name: ref_dimension_value dictionary + + self.separator = '\n' + 80 * '#' + '\n' + + self.dict_of_derivatives = {} # type: dict # {name:DerivativeSet} Each of the derivative sets DerivativeSet + + s_ref = self.reference_dimensions['S_ref'] + b_ref = self.reference_dimensions['b_ref'] + c_ref = self.reference_dimensions['c_ref'] + u_inf = self.reference_dimensions['u_inf'] + rho = self.reference_dimensions['rho'] + self.dynamic_pressure = 0.5 * rho * u_inf ** 2 + + self.coefficients = {'force': self.dynamic_pressure * s_ref, + 'moment_lon': self.dynamic_pressure * s_ref * c_ref, + 'moment_lat': self.dynamic_pressure * s_ref * b_ref, + 'force_angular_vel': self.dynamic_pressure * s_ref * c_ref / u_inf, + 'moment_lon_angular_vel': self.dynamic_pressure * s_ref * c_ref * c_ref / u_inf} # missing rates + + self.steady_coefficients = np.array(self.static_state) / self.coefficients['force'] + + self.filename = 'stability_derivatives.txt' + if target_system is not None: + self.filename = target_system + '_' + self.filename + + self.cg = None + + def initialise_derivatives(self, state_space, steady_forces, quat, v0, phi=None, cg=None, tpa=None): + """ + Initialises the required class attributes for all derivative calculations/ + + Args: + state_space (sharpy.linear.src.libss.StateSpace): State-space object for the target system + steady_forces (np.array): Array of steady forces (at the linearisation) expressed in the beam degrees of + freedom and with size equal to the number of structural degrees of freedom + quat (np.array): Quaternion at the linearisation reference state + v0 (np.array): Free stream velocity vector at the linearisation condition + phi (np.array (optional)): Mode shape matrix for modal systems + tpa (np.array (optional)): Transformation matrix onto principal axes + + """ + cls = DerivativeSet # explain what is the DerivativeSet class + if cls.quat is None: + cls.quat = quat + cls.cga = algebra.quat2rotation(cls.quat) + cls.v0 = v0 + cls.coefficients = self.coefficients + + if phi is not None: + cls.modal = True + cls.phi = phi[-9:-3, :6] + cls.inv_phi_forces = np.linalg.inv(phi[-9:-3, :6].T) + cls.inv_phi_vel = np.linalg.inv(phi[-9:-3, :6]) + else: + cls.modal = False + cls.steady_forces = steady_forces + + H0 = state_space.freqresp(np.array([1e-5]))[:, :, 0].real + if cls.modal: + vel_inputs_variables = state_space.input_variables.get_variable_from_name('q_dot') + output_indices = state_space.output_variables.get_variable_from_name('Q').rows_loc[:6] + cls.steady_forces = cls.inv_phi_forces.dot(cls.steady_forces[output_indices]) + else: + vel_inputs_variables = state_space.input_variables.get_variable_from_name('beta') + output_indices = state_space.output_variables.get_variable_from_name('forces_n').rows_loc[-9:-3] + cls.steady_forces = cls.steady_forces[output_indices] + rbm_indices = vel_inputs_variables.cols_loc[:9] + + # look for control surfaces + try: + cs_input_variables = state_space.input_variables.get_variable_from_name('control_surface_deflection') + dot_cs_input_variables = state_space.input_variables.get_variable_from_name('dot_control_surface_deflection') + except ValueError: + cs_indices = np.array([], dtype=int) + dot_cs_indices = np.array([], dtype=int) + cls.n_control_surfaces = 0 + else: + cs_indices = cs_input_variables.cols_loc + dot_cs_indices = dot_cs_input_variables.cols_loc + cls.n_control_surfaces = cs_input_variables.size + finally: + input_indices = np.concatenate((rbm_indices, cs_indices, dot_cs_indices)) + + self.transfer_function = H0[np.ix_(output_indices, input_indices)].real + + self.cg = cg + self.tpa = tpa + + def save(self, output_route): + with h5py.File(output_route + '/' + self.filename.replace('.txt', '.h5'), 'w') as f: + for k, v in self.dict_of_derivatives.items(): + if v.matrix is None: + continue + f.create_dataset(name=k, data=v.matrix) + + def savetxt(self, folder): + + filename = self.filename + + u_inf = self.reference_dimensions['u_inf'] + s_ref = self.reference_dimensions['S_ref'] + b_ref = self.reference_dimensions['b_ref'] + c_ref = self.reference_dimensions['c_ref'] + rho = self.reference_dimensions['rho'] + quat = self.reference_dimensions['quat'] + euler_orient = algebra.quat2euler(quat) * 180/np.pi + + labels_out = ['CD', 'CY', 'CL', 'Cl', 'Cm', 'Cn'] + + separator = '\n' + 80*'#' + '\n' + + with open(folder + '/' + filename, mode='w') as outfile: + outfile.write('SHARPy Stability Derivatives Analysis\n') + + outfile.write('State:\n') + outfile.write('\t{:4f}\t\t\t # Free stream velocity\n'.format(u_inf)) + outfile.write('\t{:4f}\t\t\t # Free stream density\n'.format(rho)) + outfile.write('\t{:4f}\t\t\t # Alpha [deg]\n'.format(euler_orient[1])) + outfile.write('\t{:4f}\t\t\t # Beta [deg]\n'.format(euler_orient[2])) + + if self.cg is not None: + outfile.write(separator) + outfile.write('Centre of Gravity:\n') + lab = ('x', 'y', 'z') + for i in range(3): + outfile.write('\t{:s}_A = {:.4f}\t\t\t # [m]\n'.format(lab[i], self.cg[i])) + + if self.tpa is not None: + outfile.write('Principal Axes Directions (expressed in the A frame):\n') + for i in range(3): + outfile.write('\t{:s}_ppal in A = [{:.4f}, {:.4f}, {:.4f}]\t\t\t\n'.format( + lab[i], *self.tpa.dot(np.eye(3)[:, i]))) + + outfile.write(separator) + outfile.write('\nReference Dimensions:\n') + outfile.write('\t{:4f}\t\t\t # Reference planform area\n'.format(s_ref)) + outfile.write('\t{:4f}\t\t\t # Reference chord\n'.format(c_ref)) + outfile.write('\t{:4f}\t\t\t # Reference span\n'.format(b_ref)) + + outfile.write(separator) + outfile.write('\nCoefficients:\n') + for ith, coeff in enumerate(self.steady_coefficients): + outfile.write('\t{:4e}\t\t\t # {:s}\n'.format(coeff, labels_out[ith])) + + outfile.write(separator) + + # this needs to be out of with open as it is done in each of the Derivatives objects + for derivative_set in self.dict_of_derivatives.values(): + if derivative_set.matrix is None: + continue + derivative_set.print(derivative_filename=folder + '/' + filename) + + def new_derivative(self, frame_of_reference, derivative_calculation=None, name=None): + """ + Returns a DerivativeSet() instance with the appropriate transfer function included + for the relevant target system. + + Args: + frame_of_reference (str): Output frame of reference. Body or Stability. (Not Yet Implemented) + derivative_calculation (str): Name of function used to create derivative set + name (str (optional)): Optional custom name to use as title in output. + + Returns: + DerivativeSet: Instance of class with the relevant transfer function for the current + target system. + """ + new_derivative = DerivativeSet(frame_of_reference, derivative_calculation, + name, + transfer_function=self.transfer_function) + + return new_derivative + + +class DerivativeSet: + """ + Class containing the stability derivative set for each of the input/output combinations. A derivative set may be + force/angle or force/control_surface, for example. + + The class attributes contain the parameters common across all derivative sets. The instance attributes those + pertaining to the specific set. + """ + steady_forces = None + coefficients = None + quat = None + + cga = None + n_control_surfaces = None + + v0 = None + + # Modal cases + modal = None + phi = None + inv_phi_forces = None + inv_phi_vel = None + + def __init__(self, frame_of_reference, derivative_calculation=None, name=None, + transfer_function=None): + """ + + Args: + frame_of_reference (str): Name of the frame of reference (stability or body axes) + derivative_calculation (str): Name of the method to compute derivatives + name (str): Name of the derivative set + transfer_function (np.array): steady state transfer function for the desired input output channels + """ + + self.transfer_function = transfer_function # type: np.array # steady-state TF for the specific out/in + self.matrix = None # type: np.array # matrix of stability derivatives + self.labels_in = [] # type: list(str) # strings describing the input channels + self.labels_out = [] # type list(str) # strings describing the output channels + self.frame_of_reference = frame_of_reference # type: str # name of the FoR (stability or body axes) + + self.table = None # type: cout.TablePrinter + self.name = name # type: str # name of the set + + # TODO: remove in clean up and make derivative_calculation a position argument + if derivative_calculation is not None: + self.__getattribute__(derivative_calculation)() + + def print(self, derivative_filename=None): + if self.name is not None: + cout.cout_wrap(self.name) + with open(derivative_filename, 'a') as f: + f.write('Derivative set: {:s}\n'.format(self.name)) + f.write('Axes {:s}\n'.format(self.frame_of_reference)) + self.table = cout.TablePrinter(n_fields=len(self.labels_in)+1, + field_types=['s']+len(self.labels_in) * ['e'], filename=derivative_filename) + self.table.print_header(field_names=list(['der'] + self.labels_in)) + for i in range(len(self.labels_out)): + out_list = [self.labels_out[i]] + list(self.matrix[i, :]) + self.table.print_line(out_list) + self.table.print_divider_line() + self.table.character_return(n_lines=2) + + def save(self, derivative_name, output_name): + with h5py.File(output_name + '.stability.h5', 'w') as f: + f.create_dataset(derivative_name, data=self.matrix) + + def angle_derivatives(self): + r""" + Stability derivatives against aerodynamic angles (angle of attack and sideslip) expressed in stability axes, i.e + forces are lift, drag... + + Linearised forces in stability axes are expressed as + + .. math:: + F^S = F_0^S + \frac{\partial}{\partial \alpha}\left(C^{GA}(\alpha)F_0^A\right)\delta\alpha + C_0^{GA}\delta F^A + + Therefore, the stability derivative becomes + + .. math:: \frac{\partial\F^S}{\partial\alpha} =\frac{\partial}{\partial \alpha}\left(C^{GA}(\alpha)F_0^A\right) + + C_0^{GA}\frac{\partial F^A}{\partial\alpha} + + where + + .. math:: \frac{\partial F^A}{\partial\alpha} = \frac{\partial F^A}{\partial v^A}\frac{\partial v^A}{\partial\alpha} + + and + + .. math:: \frac{\partial v^A}{\partial\alpha} = C^{AG}\frac{\partial}{\partial\alpha}\left(C(0)V_0^G\right). + + The term :math:`\frac{\partial F^A}{\partial v^A}` is obtained directly from the steady state transfer + function of the linear UVLM expressed in the beam degrees of freedoms. + + """ + self.labels_in = ['phi', 'alpha', 'beta'] + self.labels_out = ['CD', 'CY', 'CL', 'Cl', 'Cm', 'Cn'] + self.matrix = np.zeros((6, 3)) + + # Get free stream velocity direction + v0 = self.v0 + + f0a = self.steady_forces[:3] + m0a = self.steady_forces[-3:] + + euler0 = algebra.quat2euler(self.quat) + cga = self.cga + + # first term in the stability derivative expression + stab_der_trans = algebra.der_Ceuler_by_v(euler0, f0a) + stab_der_mom = algebra.der_Ceuler_by_v(euler0, m0a) + + # second term in the stability derivative expression + if self.modal: + delta_nodal_vel = np.linalg.inv(self.phi[:3, :3]).dot(cga.T.dot(algebra.der_Peuler_by_v(euler0 * 0, v0))) + delta_nodal_forces = self.inv_phi_forces.dot(self.transfer_function[:6, :3].real.dot(delta_nodal_vel)) + else: + delta_nodal_vel = cga.T.dot(algebra.der_Peuler_by_v(euler0 * 0, v0)) + delta_nodal_forces = self.transfer_function[:6, :3].real.dot(delta_nodal_vel) + + stab_der_trans2 = cga.dot(delta_nodal_forces[:3, :]) + stab_der_mom2 = cga.dot(delta_nodal_forces[3:, :]) + + self.matrix[:3, :] = stab_der_trans + stab_der_trans2 + self.matrix[3:6, :] = stab_der_mom + stab_der_mom2 + + self.apply_coefficients() + + def angle_derivatives_tb(self): + self.name = 'Force/Angle derivatives via angle input' + self.labels_in = ['phi', 'alpha', 'beta'] + self.labels_out = ['CD', 'CY', 'CL', 'Cl', 'Cm', 'Cn'] + self.matrix = np.zeros((6, 3)) + + # Get free stream velocity direction + v0 = self.v0 + + f0a = self.steady_forces[:3] + m0a = self.steady_forces[-3:] + + euler0 = algebra.quat2euler(self.quat) + cga = self.cga + + # first term in the stability derivative expression + stab_der_trans = algebra.der_Ceuler_by_v(euler0, f0a) + stab_der_mom = algebra.der_Ceuler_by_v(euler0, m0a) + + # second term in the stability derivative expression + if self.modal: + delta_nodal_forces = self.inv_phi_forces.dot(self.transfer_function[:6, 6:9].real) + else: + delta_nodal_forces = self.transfer_function[:6, 6:9].real + + stab_der_trans2 = cga.dot(delta_nodal_forces[:3, :]) + stab_der_mom2 = cga.dot(delta_nodal_forces[3:, :]) + + self.matrix[:3, :] = stab_der_trans + stab_der_trans2 + self.matrix[3:6, :] = stab_der_mom + stab_der_mom2 + + self.apply_coefficients() + + def body_derivatives(self): + self.name = 'Force derivatives to rigid body velocities - Body derivatives' + self.labels_in = ['uA', 'vA', 'wA', 'pA', 'qA', 'rA'] + self.labels_out = ['C_XA', 'C_YA', 'C_ZA', 'C_LA', 'C_MA', 'C_NA'] + self.matrix = np.zeros((6, 6)) + + body_derivatives = self.transfer_function[:6, :6] + + if self.modal: + body_derivatives = self.inv_phi_forces.dot(body_derivatives).dot(self.inv_phi_vel) + + self.matrix = body_derivatives + self.apply_coefficients() + + def control_surface_derivatives(self): + n_control_surfaces = self.n_control_surfaces + if n_control_surfaces == 0: + return None + + self.name = 'Force derivatives wrt control surface inputs - Body axes' + self.labels_out = ['C_XA', 'C_YA', 'C_ZA', 'C_LA', 'C_MA', 'C_NA'] + labels_in_deflection = [] + labels_in_rate = [] + for i in range(n_control_surfaces): + labels_in_deflection.append('delta_{:g}'.format(i)) + labels_in_rate.append('dot(delta)_{:g}'.format(i)) + self.labels_in = labels_in_deflection + labels_in_rate + + body_derivatives = self.transfer_function[:6, 9:] + assert body_derivatives.shape == (6, 2 * self.n_control_surfaces), 'Incorrect TF shape' + + if self.modal: + self.matrix = self.inv_phi_forces.dot(body_derivatives) + else: + self.matrix = body_derivatives + + self.apply_coefficients() + + def apply_coefficients(self): + self.matrix[:3, :] /= self.coefficients['force'] + self.matrix[np.ix_([3, 5]), :] /= self.coefficients['moment_lat'] + self.matrix[4, :] /= self.coefficients['moment_lon'] diff --git a/sharpy/linear/utils/ss_interface.py b/sharpy/linear/utils/ss_interface.py index 1b21ff7e4..2f6252715 100644 --- a/sharpy/linear/utils/ss_interface.py +++ b/sharpy/linear/utils/ss_interface.py @@ -1,8 +1,12 @@ """State-space modules loading utilities""" import os + +import h5py + import sharpy.utils.cout_utils as cout from abc import ABCMeta, abstractmethod import numpy as np +import copy dict_of_systems = dict() systems_dict_import = dict() @@ -85,95 +89,427 @@ def dictionary_of_systems(): return dictionary -class VectorVariable(object): - - def __init__(self, name, pos_list, var_system): +class VectorVariable: + # state variable + def __init__(self, name, size, index, var_system=None): self.name = name self.var_system = var_system + self.size = size + self._index = index - self.first_pos = pos_list[0] - self.end_pos = pos_list[1] - self.rows_loc = np.arange(self.first_pos, self.end_pos, dtype=int) # Original location, should not update + self._first_position = 0 + self._rows_loc = None + self._cols_loc = None + + @property + def cols_loc(self): + return np.arange(self.first_position, self.end_position, dtype=int) + + @property + def index(self): + return self._index + + @index.setter + def index(self, value): + self._index = value + + @property + def first_position(self): + return self._first_position - # add methods to reorganise into SHARPy method? + @first_position.setter + def first_position(self, position): + self._first_position = position + + @property + def end_position(self): + return self.first_position + self.size + + @property + def rows_loc(self): + return np.arange(self.first_position, self.end_position, dtype=int) + + def __repr__(self): + return '({:s}: {:s}, size: {:g}, index: {:g}, starting at: {:g}, finishing at: {:g})'.format( + type(self).__name__, + self.name, + self.size, + self.index, + self.first_position, + self.end_position) + + def copy(self): + return copy.deepcopy(self) + + def save(self): + """ + Return: + tuple: info to save to h5 + """ + return type(self).__name__, self.name, self.size, self.index + + +class OutputVariable(VectorVariable): @property def cols_loc(self): - return np.arange(self.first_pos, self.end_pos, dtype=int) + return self._cols_loc + + @cols_loc.setter + def cols_loc(self, value=None): + if self._cols_loc is None: + self._cols_loc = np.arange(self.first_position, self.end_position, dtype=int) # Original location, should not update + + @property + def rows_loc(self): + # In the output variables the rows can change, the columns should stay fixed + return np.arange(self.first_position, self.end_position, dtype=int) + + +class InputVariable(VectorVariable): + + @property + def rows_loc(self): + return self._rows_loc + + @rows_loc.setter + def rows_loc(self, value=None): + if self._rows_loc is None: + self._rows_loc = np.arange(self.first_position, self.end_position, dtype=int) # Original location, should not update + + @property + def cols_loc(self): + return np.arange(self.first_position, self.end_position, dtype=int) + + +class StateVariable(VectorVariable): + pass + + +class LinearVector: + + def __init__(self, list_of_vector_variables): + self.vector_variables = list_of_vector_variables + + # check they are all the same type + self.check_sametype_vars_full_vector() + + # initialise position of variables + self.update_indices() + self.update_locations() + + self.variable_class = type(self.vector_variables[0]) # class + + @property + def num_variables(self): + return len(self.vector_variables) @property def size(self): - return self.end_pos - self.first_pos + return sum([variable.size for variable in self.vector_variables]) + def remove(self, *variable_name_list): -class LinearVector(): + for variable_name in variable_name_list: + list_of_variable_names = [variable.name for variable in self.vector_variables] + try: + remove_variable_index = list_of_variable_names.index(variable_name) + except ValueError: + raise ValueError('Trying to remove non-existent {:s} variable'.format(variable_name)) + else: + self.__remove_variable(remove_variable_index) + + def __remove_variable(self, index): + self.vector_variables.pop(index) + self.update_indices() + + def modify(self, variable_name, **kwargs): + """ + Modifies the attributes of the desired variable. The new attributes are passed as **kwargs. If an attribute is + not recognised a ``NameError`` is returned. + + Note: + If changing the size of the variable, you should then update the linear vector. + + Args: + variable_name (str): Name of the variable to modify + **kwargs: Key-word arguments containing the attributes of the variable as keys and the new values as values. + + """ + variable = self.get_variable_from_name(variable_name) + for var_attribute, new_var_value in kwargs.items(): + if hasattr(variable, var_attribute): + variable.__setattr__(var_attribute, new_var_value) + else: + raise NameError('Unknown variable attribute {:s}'.format(var_attribute)) + + def add(self, vector_variable, **kwargs): + + if isinstance(vector_variable, self.variable_class): + self.__add_vector_variable(vector_variable) + elif type(vector_variable) is str: + new_variable = self.variable_class(name=vector_variable, **kwargs) + self.__add_vector_variable(new_variable) + else: + raise TypeError('Only can add a variable from either a {:s} object or with name and kwargs'.format( + self.variable_class.__name__ + )) + + def append(self, vector_variable, **kwargs): + self.update_indices() + appending_index = self.num_variables + if isinstance(vector_variable, VectorVariable): + vector_variable.index = appending_index + elif type(vector_variable) is str: + kwargs['index'] = appending_index + else: + raise TypeError('Only can add a variable from either a {:s} object or with name and kwargs'.format( + self.variable_class.__name__ + )) + + self.add(vector_variable, **kwargs) + + def __add_vector_variable(self, vector_variable): + list_of_indices = [variable.index for variable in self.vector_variables] + + if vector_variable.index in list_of_indices: + raise IndexError('New variable index is already in use') + self.vector_variables.append(vector_variable) + self.update_indices() + self.update_locations() + + def update_indices(self): + + list_of_indices = [variable.index for variable in self.vector_variables] + ordered_list = sorted(list_of_indices) + + index_variable_dict = {key_index: self.vector_variables[i] for i, key_index in enumerate(list_of_indices)} + updated_list = [] + for i_var in range(self.num_variables): + + current_variable_index = ordered_list[i_var] + current_variable = index_variable_dict[current_variable_index] + current_variable.index = i_var + + updated_list.append(current_variable) + + self.vector_variables = updated_list - def __init__(self, dof_db, sys_id): - self.vector_vars = dict() + def update_locations(self): + for i_var in range(self.num_variables): + if i_var == 0: + self.vector_variables[i_var].first_position = 0 + elif i_var > 0: + # since end item is not included in range methods, set the first position to last variable end position + self.vector_variables[i_var].first_position = self.vector_variables[i_var - 1].end_position - vec_db = dict() - for item in dof_db: - vector_var = VectorVariable(item, dof_db[item], sys_id) - vec_db[item] = vector_var + def check_sametype_vars_full_vector(self): + variable_class = type(self.vector_variables[0]) - self.vector_vars = vec_db + for var in self.vector_variables: + if not isinstance(var, variable_class): + raise TypeError('All variables in the vector are not of the same kind.') - def remove(self, trim_list): - vec_db = self.vector_vars - used_vars_db = self.vector_vars.copy() + @classmethod + def merge(cls, vec1, vec2): + """ + Merges two instances of LinearVectors - # Variables to remove - removed_dofs = 0 - removed_db = dict() - for item in trim_list: + Args: + vec1 (LinearVector): Vector 1 + vec2 (LinearVector): Vector 2 + + Returns: + LinearVector: Merged vectors 1 and 2 + """ + if vec1.variable_class is not vec2.variable_class: + raise TypeError('Unable to merge two different kinds of vectors') + + list_of_variables_1 = [variable.copy() for variable in vec1] + list_of_variables_2 = [variable.copy() for variable in vec2] + + for ith, variable in enumerate(list_of_variables_1 + list_of_variables_2): + variable.index = ith + + merged_vector = cls(list_of_variables_1 + list_of_variables_2) + return merged_vector + + @classmethod + def transform(cls, linear_vector, to_type): + if not (to_type is InputVariable or to_type is StateVariable or to_type is OutputVariable): + raise TypeError('Argument should be the class to which the linear vector should be transformed') + + list_of_variables = [] + for variable in linear_vector.copy(): + list_of_variables.append(to_type(name=variable.name, + size=variable.size, + index=variable.index)) + + return cls(list_of_variables) + + @staticmethod + def check_connection(output_vector, input_vector): + """ + Checks an output_vector can be connected to an input channel. + + """ + with_error = False + err_log = '' + + for ith, out_variable in enumerate(output_vector): try: - removed_db[item] = vec_db[item] - except KeyError: - continue - removed_dofs += vec_db[item].size - del used_vars_db[item] - - # Update variables position - for rem_item in removed_db: - for item in used_vars_db: - if used_vars_db[item].first_pos < removed_db[rem_item].first_pos: - pass - else: - # Update position - used_vars_db[item].first_pos -= removed_db[rem_item].size - used_vars_db[item].end_pos -= removed_db[rem_item].size - - self.vector_vars = used_vars_db - - return removed_dofs - -def remove_variables(trim_list, dof_db, sys_id): - # Remove should be a method of class - # Create class of variables - # All variables - vec_db = dict() - for item in dof_db: - vector_var = VectorVariable(item, dof_db[item], sys_id) - vec_db[item] = vector_var - - used_vars_db = vec_db.copy() - - # Variables to remove - removed_dofs = 0 - removed_db = dict() - for item in trim_list: - removed_db[item] = vec_db[item] - removed_dofs += vec_db[item].size - del used_vars_db[item] - - # Update variables position - for rem_item in removed_db: - for item in used_vars_db: - if used_vars_db[item].first_pos < removed_db[rem_item].first_pos: - pass + in_variable = input_vector.get_variable_from_name(out_variable.name) + except ValueError: + err_log += '\nUnable to connect both systems, no input variable named {:s}\n'.format(out_variable.name) + with_error = True + else: + if not out_variable.size == in_variable.size: + err_log += 'Variable {:s} and {:s} not the same size' + with_error = True + if not out_variable.rows_loc[-1] == in_variable.cols_loc[-1]: + # checking the last index should be sufficient (and more efficient than checking the whole array) + err_log += 'Variable {:s}Output rows not coincident with input columns for variable\n'.format( + out_variable.name) + err_log += str(out_variable) + '\n' + err_log += str(in_variable) + '\n' + with_error = True + + if with_error: + raise ValueError(err_log) + + @staticmethod + def check_same_vectors(vec1, vec2): + """ + Checks that two linear vectors contain the same variables, regardless of type + Args: + vec1 (LinearVector): Vector 1 + vec2 (LinearVector): Vector 2 + """ + + assert vec1.num_variables == vec2.num_variables, 'Number of variables is not equal' + assert vec1.size == vec2.size, 'Size is not equal' + + for i_var in range(vec1.num_variables): + assert vec1[i_var].name == vec2[i_var].name, 'Variable name is not the same' + assert vec1[i_var].index == vec2[i_var].index, 'Index is not the same' + assert vec1[i_var].size == vec2[i_var].size, 'Variable size is not the same' + assert vec1[i_var] is not vec2[i_var], 'Variables in LinearVector reference the same object in memory. ' \ + 'Careful!' + + def differentiate(self): + pass + # going from second order to first order systems + + def get_variable_from_name(self, name): + list_of_variable_names = [variable.name for variable in self.vector_variables] + try: + variable_index = list_of_variable_names.index(name) + except ValueError: + raise ValueError('Variable {:s} is non existent'.format(name)) + else: + return self.vector_variables[variable_index] + + def __call__(self, variable_name): + """ + + Args: + variable_name (str): Variable name + + Returns: + VectorVariable: Vector variable within LinearVector + """ + return self.get_variable_from_name(variable_name) + + def copy(self): + return copy.deepcopy(self) + + def add_to_h5_file(self, file_handle): + """ + Given an h5 handle ``file_handle``, creates and adds a group named with the type of variable and adds the fields + ``names`` (encoded in ascii), ``sizes`` and ``indices``. + + Args: + file_handle (h5py.File): file handle of h5py.File + + Returns: + file_handle + """ + variable_data = [] + for variable in self.vector_variables: + variable_data.append(variable.save()) + + types, names, sizes, indices = zip(*variable_data) + + # encode strings to bytes + names = [name.encode('ascii', 'ignore') for name in names] + + variable_group = file_handle.create_group(types[0]) + variable_group.create_dataset(name='names', data=names) + variable_group.create_dataset(name='sizes', data=sizes, dtype=int) + variable_group.create_dataset(name='indices', data=indices, dtype=int) + + return file_handle + + @classmethod + def load_from_h5_file(cls, variable_type, variables_data): + """ + Loads data from the information saved in an h5file + + Args: + variable_type (str): Type of variable (InputVariable, OutputVariable or StateVariable) + variables_data (dict): Dictionary containing variable info from h5 with keys: names, sizes and indices + + Returns: + LinearVector: of ``variable_type`` + """ + var_class_dict = {'InputVariable': InputVariable, + 'OutputVariable': OutputVariable, + 'StateVariable': StateVariable} + n_variables = len(variables_data['names']) + list_of_variables = [] + try: + var_class = var_class_dict[variable_type] + except KeyError: + raise KeyError(f'Unknown variable type {variable_type}. Must be either InputVariable, OutputVariable ' + f'or StateVariable') + for ith_variable in range(n_variables): + name = variables_data['names'][ith_variable].astype('U13') # decode to unicode + list_of_variables.append(var_class(name, + size=variables_data['sizes'][ith_variable], + index=variables_data['indices'][ith_variable]), + ) + return cls(list_of_variables) + + def __iter__(self): + return SetIterator(self) + + def __getitem__(self, item): + return self.vector_variables[item] + + def __repr__(self): + string_out = '' + for var in self.vector_variables: + try: + out_var = str(repr(var)) + except TypeError: + print('Error printing var {:s}'.format(var.name)) else: - # Update order and position - used_vars_db[item].first_pos -= removed_db[rem_item].size - used_vars_db[item].end_pos -= removed_db[rem_item].size + string_out += '\t' + out_var + '\n' + return string_out + + +class SetIterator: + + def __init__(self, linear_vector): + self._set_cases = linear_vector + self._index = 0 + + def __next__(self): + if self._index < self._set_cases.num_variables: + res = self._set_cases.vector_variables[self._index] + self._index += 1 + return res + + raise StopIteration diff --git a/sharpy/postproc/aeroforcescalculator.py b/sharpy/postproc/aeroforcescalculator.py index bb32c1626..0e74b722f 100644 --- a/sharpy/postproc/aeroforcescalculator.py +++ b/sharpy/postproc/aeroforcescalculator.py @@ -233,6 +233,8 @@ def file_output(self, filename): header += 'fx_unsteady_G, fy_unsteady_G, fz_unsteady_G, ' header += 'fx_steady_a, fy_steady_a, fz_steady_a, ' header += 'fx_unsteady_a, fy_unsteady_a, fz_unsteady_a' + header += 'mx_total_G, my_total_G, mz_total_G' + header += 'mx_total_a, my_total_a, mz_total_a' np.savetxt(self.folder + 'forces_' + filename, force_matrix, diff --git a/sharpy/postproc/asymptoticstability.py b/sharpy/postproc/asymptoticstability.py index 6239fe9df..72e842bdd 100644 --- a/sharpy/postproc/asymptoticstability.py +++ b/sharpy/postproc/asymptoticstability.py @@ -1,5 +1,5 @@ import os -import warnings as warn +import warnings import numpy as np import scipy.linalg as sclalg import sharpy.utils.settings as settings @@ -8,22 +8,34 @@ import sharpy.utils.algebra as algebra import sharpy.solvers.lindynamicsim as lindynamicsim import sharpy.structure.utils.modalutils as modalutils -import scipy.sparse as scsp +import sharpy.utils.frequencyutils as frequencyutils +import sharpy.linear.src.libss as libss +import h5py @solver class AsymptoticStability(BaseSolver): """ - Calculates the asymptotic stability properties of the linearised aeroelastic system by computing - the corresponding eigenvalues. + Calculates the asymptotic stability properties of the linearised system by computing + the corresponding eigenvalues and eigenvectors. - To use an iterative eigenvalue solver, the setting ``iterative_eigvals`` should be set to ``on``. This - will be beneficial when deailing with very large systems. However, the direct method is - preferred and more efficient when the system is of a relatively small size (typically around 5000 states). + The output of this solver is written to the ``stability`` directory in the case output. - Warnings: - The setting ``modes_to_plot`` to plot the eigenvectors in Paraview is currently under development. + The stability of the systems specified in ``target_systems`` is performed. If the system has been previously + scaled, a ``reference_velocity`` should be provided to compute the stability at such point. + The eigenvalues can be truncated, keeping a minimum ``num_evals`` (sorted by decreasing real part) or by limiting + the higher frequency modes through ``frequency_cutoff``. + + Results can be saved to file using ``export_eigenvalues``. The setting ``display_root_locus`` shows a simple + Argand diagram where the continuous time eigenvalues are displayed. + + The eigenvectors can be displayed (in .vtu format for use in Paraview) with the ``modes_to_plot`` setting, whereby + the user specifies the mode indices that are to be plotted (sorted with in the same way as the eigenvalue table, + i.e. by decreasing real part). A snapshot of the eigenvector is produced for the 0, 45, 90 and 135 degree phases. + This feature currently supports the flexible structural modes and does not show the rigid body contribution. + The output is written to the ``stability/modes`` folder and includes the structure at the + reference linearisation state. """ solver_id = 'AsymptoticStability' solver_classification = 'post-processor' @@ -31,6 +43,7 @@ class AsymptoticStability(BaseSolver): settings_types = dict() settings_default = dict() settings_description = dict() + settings_options = dict() settings_types['print_info'] = 'bool' settings_default['print_info'] = False @@ -46,7 +59,12 @@ class AsymptoticStability(BaseSolver): settings_types['export_eigenvalues'] = 'bool' settings_default['export_eigenvalues'] = False - settings_description['export_eigenvalues'] = 'Save eigenvalues and eigenvectors to file. ' + settings_description['export_eigenvalues'] = 'Save eigenvalues and eigenvectors to file.' + + settings_types['output_file_format'] = 'str' + settings_default['output_file_format'] = 'dat' + settings_description['output_file_format'] = 'Eigenvalue/eigenvector output file format. HDF5 or text (.dat) files.' + settings_options['output_file_format'] = ['h5', 'dat'] settings_types['display_root_locus'] = 'bool' settings_default['display_root_locus'] = False @@ -57,9 +75,10 @@ class AsymptoticStability(BaseSolver): settings_description['velocity_analysis'] = 'List containing min, max and number ' \ 'of velocities to analyse the system' - settings_types['iterative_eigvals'] = 'bool' - settings_default['iterative_eigvals'] = False - settings_description['iterative_eigvals'] = 'Calculate the first ``num_evals`` using an iterative solver.' + settings_types['target_system'] = 'list(str)' + settings_default['target_system'] = ['aeroelastic'] + settings_description['target_system'] = 'System or systems for which to find frequency response.' + settings_options['target_system'] = ['aeroelastic', 'aerodynamic', 'structural'] settings_types['num_evals'] = 'int' settings_default['num_evals'] = 200 @@ -67,28 +86,20 @@ class AsymptoticStability(BaseSolver): settings_types['modes_to_plot'] = 'list(int)' settings_default['modes_to_plot'] = [] - settings_description['modes_to_plot'] = 'List of mode numbers to simulate and plot' - - settings_types['postprocessors'] = 'list(str)' - settings_default['postprocessors'] = list() - settings_description['postprocessors'] = 'To be used with ``modes_to_plot``. Under development.' - - settings_types['postprocessors_settings'] = 'dict' - settings_default['postprocessors_settings'] = dict() - settings_description['postprocessors_settings'] = 'To be used with ``modes_to_plot``. Under development.' + settings_description['modes_to_plot'] = 'List of mode numbers to plot. Plots the 0, 45, 90 and 135' \ + 'degree phases.' settings_table = settings.SettingsTable() - __doc__ += settings_table.generate(settings_types, settings_default, settings_description) + __doc__ += settings_table.generate(settings_types, settings_default, settings_description, + settings_options=settings_options) def __init__(self): self.settings = None self.data = None self.folder = None + self.print_info = False - self.eigenvalues = None - self.eigenvectors = None self.frequency_cutoff = np.inf - self.eigenvalue_table = None self.num_evals = None self.postprocessors = dict() @@ -103,7 +114,9 @@ def initialise(self, data, custom_settings=None, caller=None): else: self.settings = custom_settings - settings.to_custom_types(self.settings, self.settings_types, self.settings_default, no_ctype=True) + settings.to_custom_types(self.settings, self.settings_types, self.settings_default, + options=self.settings_options, + no_ctype=True) self.num_evals = self.settings['num_evals'] @@ -112,12 +125,17 @@ def initialise(self, data, custom_settings=None, caller=None): os.makedirs(self.folder) if self.settings['print_info']: - cout.cout_wrap('Dynamical System Eigenvalues') - eigenvalue_description_file = self.folder + '/eigenvaluetable.txt' - self.eigenvalue_table = modalutils.EigenvalueTable(filename=eigenvalue_description_file) + self.print_info = True + else: + self.print_info = False - # Output dict - self.data.linear.stability = dict() + try: + self.frequency_cutoff = self.settings['frequency_cutoff'] + except AttributeError: + self.frequency_cutoff = float(self.settings['frequency_cutoff']) + + if self.frequency_cutoff == 0: + self.frequency_cutoff = np.inf self.caller = caller @@ -130,111 +148,199 @@ def run(self, online=False): eigenvectors (np.ndarray): Corresponding mode shapes """ - try: - self.frequency_cutoff = self.settings['frequency_cutoff'] - except AttributeError: - self.frequency_cutoff = float(self.settings['frequency_cutoff']) - - if self.frequency_cutoff == 0: - self.frequency_cutoff = np.inf + # if the system is scaled, only one system can be analysed if self.settings['reference_velocity'] != 1. and self.data.linear.linear_system.uvlm.scaled: - ss = self.data.linear.linear_system.update(self.settings['reference_velocity']) + ss_list = [self.data.linear.linear_system.update(self.settings['reference_velocity'])] + not_scaled = False + system_name_list = ['aeroelastic'] + if len(self.settings['target_system']) > 1: + cout.cout_wrap('Warning: the system is scaled thus the only analysis currently supported is' + ' for the aeroelastic system', 3) else: - ss = self.data.linear.ss + ss_list = [frequencyutils.find_target_system(self.data, system_name) for system_name in + self.settings['target_system']] + not_scaled = True + system_name_list = self.settings['target_system'] - if self.settings['print_info']: - cout.cout_wrap('Calculating eigenvalues using direct method') - eigenvalues, eigenvectors = sclalg.eig(ss.A) + self.compute_eigenvalues(ss_list, system_name_list, not_scaled) - # Convert DT eigenvalues into CT - if ss.dt: - # Obtain dimensional time step - try: - ScalingFacts = self.data.linear.linear_system.uvlm.sys.ScalingFacts - if ScalingFacts['length'] != 1.0 and ScalingFacts['time'] != 1.0: - dt = ScalingFacts['length'] / self.settings['reference_velocity'] * ss.dt + return self.data + + def compute_eigenvalues(self, ss, system_name_list=None, not_scaled=True): + """ + Computes the eigenvalues and eigenvectors of the state-space + + Args: + ss (libss.StateSpace or list([libss.StateSpace]): State-space or list of state-spaces + system_name_list (list([str]): Names of systems in the case multiple systems are required + not_scaled (bool): Flag to indicate whether the systems are assembled in non-dimensional time + """ + + if type(ss) is libss.StateSpace: + ss_list = [ss] + if system_name_list is None: + system_name_list = [''] + elif type(ss) is list: + ss_list = ss + + if system_name_list is None: + system_name_list = [] + for sys, sys_number in enumerate(ss_list): + system_name_list.append(f'system{sys_number:g}') + if type(sys) is not libss.StateSpace: + raise TypeError(f'State-space {sys_number} is not type libss.StateSpace') + else: + raise TypeError('ss input must be either a libss.StateSpace instance or a list[libss.StateSpace]') + + for ith, system in enumerate(ss_list): + system_name = system_name_list[ith] + + if self.print_info: + cout.cout_wrap('Calculating %s eigenvalues using direct method' % system_name) + + eigenvalues, eigenvectors = sclalg.eig(system.A) + + # Convert DT eigenvalues into CT + if system.dt: + eigenvalues = self.convert_to_continuoustime(system.dt, eigenvalues, not_scaled) + + num_evals = min(self.num_evals, len(eigenvalues)) + + eigenvalues, eigenvectors = self.sort_eigenvalues(eigenvalues, eigenvectors, self.frequency_cutoff, + number_of_eigenvalues=num_evals) + + if self.settings['export_eigenvalues']: + self.export_eigenvalues(num_evals, eigenvalues, eigenvectors, filename=system_name) + + if self.settings['print_info']: + cout.cout_wrap(f'Dynamical System Eigenvalues - {system_name} system') + if system_name != '': + eig_table_filename = system_name + '_' else: - dt = ss.dt - except AttributeError: - dt = ss.dt - eigenvalues = np.log(eigenvalues) / dt + eig_table_filename = '' + eigenvalue_description_file = self.folder + '/{:s}eigenvaluetable.txt'.format(eig_table_filename) + eigenvalue_table = modalutils.EigenvalueTable(filename=eigenvalue_description_file) + eigenvalue_table.print_header(eigenvalue_table.headers) + eigenvalue_table.print_evals(eigenvalues[:self.num_evals]) + eigenvalue_table.close_file() - self.num_evals = min(self.num_evals, len(eigenvalues)) + if self.settings['display_root_locus']: + self.display_root_locus(eigenvalues) - self.eigenvalues, self.eigenvectors = self.sort_eigenvalues(eigenvalues, eigenvectors, self.frequency_cutoff) + if len(self.settings['velocity_analysis']) == 3 and system_name == 'aeroelastic': + assert self.data.linear.linear_system.uvlm.scaled, \ + 'The UVLM system is unscaled, unable to rescale the structural equations only. Rerun with a ' \ + 'normalised UVLM system.' + self.velocity_analysis() - if self.settings['export_eigenvalues']: - self.export_eigenvalues(self.num_evals) + # Under development + if len(self.settings['modes_to_plot']) != 0 and system_name == 'aeroelastic': + self.plot_modes(eigenvectors) - if self.settings['print_info']: - self.eigenvalue_table.print_header(self.eigenvalue_table.headers) - self.eigenvalue_table.print_evals(self.eigenvalues[:self.num_evals]) - self.eigenvalue_table.close_file() + return eigenvalues, eigenvectors - if self.settings['display_root_locus']: - self.display_root_locus() + def convert_to_continuoustime(self, dt, discrete_time_eigenvalues, not_scaled=False): + r""" + Convert eigenvalues to discrete time. The ``not_scaled`` argument can be used to bypass the search from + within SHARPy of scaling factors. For instance, when the state-space of choice is not part of a standard + SHARPy case but rather an interpolated ROM etc. - # Under development - if len(self.settings['modes_to_plot']) != 0: - warn.warn('Plotting modes is under development') - self.plot_modes() + The eigenvalues are converted to continuous time using - if len(self.settings['velocity_analysis']) == 3: - assert self.data.linear.linear_system.uvlm.scaled, 'The UVLM system is unscaled, unable to rescale the ' \ - 'structural equations only. Rerun with a normalised ' \ - 'UVLM system.' - self.velocity_analysis() + .. math:: \lambda_{ct} = \frac{\log (\lambda_{dt})}{\Delta t} - self.data.linear.stability['eigenvectors'] = self.eigenvectors - self.data.linear.stability['eigenvalues'] = self.eigenvalues + If the system is scaled, the dimensional time step is retrieved as - return self.data + .. math:: \Delta t_{dim} = \bar{\Delta t} \frac{l_{ref}}{U_{\infty, actual}} + + where :math:`l_{ref}` is the reference length and :math:`U_{\infty, actual}` is the free stream velocity at + which to calculate the eigenvalues. + + Args: + dt (float): Discrete time increment. + discrete_time_eigenvalues (np.ndarray): Array of discrete time eigenvalues. + not_scaled (bool): Treat the system as not scaled. No Scaling Factors will be searched in SHARPy. + """ + if not_scaled: + dt = dt + else: + try: + ScalingFacts = self.data.linear.linear_system.uvlm.sys.ScalingFacts + if ScalingFacts['length'] != 1.0 and ScalingFacts['time'] != 1.0: + dt *= ScalingFacts['length'] / self.settings['reference_velocity'] + else: + dt = dt + except AttributeError: + dt = dt - def export_eigenvalues(self, num_evals): + return np.log(discrete_time_eigenvalues) / dt + + def export_eigenvalues(self, num_evals, eigenvalues, eigenvectors, filename=None): """ - Saves a ``num_evals`` number of eigenvalues and eigenvectors to file. The files are saved in the output directoy - and include: + Saves a ``num_evals`` number of eigenvalues and eigenvectors to file. + + The files are saved in the output directory and include: - * ``eigenvectors.dat``: ``(num_dof, num_evals)`` array of eigenvectors + * ``{system_name}_eigenvalues.dat``: Array of eigenvalues of shape ``(num_evals, 2)`` where the first column corresponds + to the real part and the second column to the imaginary part. - * ``eigenvalues_r.dat``: ``(num_evals, 1)`` array of the real part of the eigenvalues + * ``{system_name}_stability.h5``: An ``.h5`` file containing the desired number of eigenvalues and eigenvectors of the + chosen systems. - * ``eigenvalues_i.dat``: ``(num_evals, 1)`` array of the imaginary part of the eigenvalues. + The units of the eigenvalues are ``rad/s``. - The units of the eigenvalues are ``rad/s`` + Args: + num_evals (int): Number of eigenvalues to save. + eigenvalues (np.ndarray): Eigenvalue array. + eigenvectors (np.ndarray): Matrix of eigenvectors. + filename (str (optional)): Optional prefix of the output filenames. - References: + See Also: Loading and saving complex arrays: https://stackoverflow.com/questions/6494102/how-to-save-and-load-an-array-of-complex-numbers-using-numpy-savetxt/6522396 - - Args: - num_evals: Number of eigenvalues to save """ - num_evals = min(num_evals, self.eigenvalues.shape[0]) + stability_folder_path = self.folder - np.savetxt(self.folder + '/eigenvalues.dat', self.eigenvalues[:num_evals].view(float).reshape(-1, 2)) - np.savetxt(self.folder + '/eigenvectors_r.dat', self.eigenvectors.real[:, :num_evals]) - np.savetxt(self.folder + '/eigenvectors_i.dat', self.eigenvectors.imag[:, :num_evals]) + if filename is None or filename == '': + filename = '' + else: + filename += '_' + + if self.settings['output_file_format'] == 'dat': + np.savetxt(self.folder + f'/{filename:s}eigenvalues.dat', eigenvalues.view(float).reshape(-1, 2)) + np.savetxt(self.folder + f'/{filename:s}eigenvectors_r.dat', eigenvectors.real) + np.savetxt(self.folder + f'/{filename:s}eigenvectors_i.dat', eigenvectors.imag) + elif self.settings['output_file_format'] == 'h5': + with h5py.File(stability_folder_path + f'/{filename:s}stability.h5', 'w') as f: + f.create_dataset('eigenvalues', data=eigenvalues, dtype=complex) + f.create_dataset('eigenvectors', data=eigenvectors, dtype=complex) + f.create_dataset('num_eigenvalues', data=num_evals, dtype=int) + else: + raise TypeError(f'Unrecognised file type saving option {self.settings["output_file_format"]}') + # this shouldn't happen as the settings_options should check the validity of the setting - def print_eigenvalues(self): + def velocity_analysis(self): """ - Prints the eigenvalues to a table with the corresponding natural frequency, period and damping ratios + Velocity analysis for scaled systems. - """ + Runs the stability analysis for different velocities for aeroelastic systems that have been previously scaled. - self.eigenvalue_table.print_evals(self.eigenvalues[:self.settings['num_evals']]) + For every velocity, the linear system is updated. This involves updating the structural matrices and the + coupling matrix. The eigenvalues saved are in continuous time. - def velocity_analysis(self): + It saves the results to a ``.dat`` file where the first column corresponds to the free stream velocity and the + second and third columns to the real and imaginary parts of the eigenvalues. + """ ulb, uub, num_u = self.settings['velocity_analysis'] if self.settings['print_info']: cout.cout_wrap('Velocity Asymptotic Stability Analysis', 1) - cout.cout_wrap('Initial velocity: %.2f m/s' % ulb, 1) - cout.cout_wrap('Final velocity: %.2f m/s' % uub, 1) - cout.cout_wrap('Number of evaluations: %g' % num_u, 1) + cout.cout_wrap('Initial velocity: {:02f} m/s'.format(ulb), 1) + cout.cout_wrap('Final velocity: {:02f} m/s'.format(uub), 1) + cout.cout_wrap('Number of evaluations: {:g}'.format(num_u), 1) u_inf_vec = np.linspace(ulb, uub, int(num_u)) @@ -257,32 +363,35 @@ def velocity_analysis(self): Nunst = np.sum(eigs_cont.real > 0) fn = np.abs(eigs_cont) - cout.cout_wrap('LTI\tu: %.2f m/2\tmax. CT eig. real: %.6f\t' \ - % (u_inf_vec[i], np.max(eigs_cont.real))) - cout.cout_wrap('\tN unstab.: %.3d' % (Nunst,)) - cout.cout_wrap('\tUnstable aeroelastic natural frequency CT(rad/s):' + Nunst * '\t%.2f' % tuple(fn[:Nunst])) + if self.settings['print_info']: + cout.cout_wrap('LTI\tu: %.2f m/2\tmax. CT eig. real: %.6f\t' \ + % (u_inf_vec[i], np.max(eigs_cont.real))) + cout.cout_wrap('\tN unstab.: %.3d' % (Nunst,)) + cout.cout_wrap( + '\tUnstable aeroelastic natural frequency CT(rad/s):' + Nunst * '\t%.2f' % tuple(fn[:Nunst])) # Store eigenvalues for plot real_part_plot.append(eigs_cont.real) imag_part_plot.append(eigs_cont.imag) - uinf_part_plot.append(np.ones_like(eigs_cont.real)*u_inf_vec[i]) + uinf_part_plot.append(np.ones_like(eigs_cont.real) * u_inf_vec[i]) real_part_plot = np.hstack(real_part_plot) imag_part_plot = np.hstack(imag_part_plot) uinf_part_plot = np.hstack(uinf_part_plot) - cout.cout_wrap('Saving velocity analysis results...') - np.savetxt(self.folder + '/velocity_analysis_min%04d_max%04d_nvel%04d.dat' %(ulb*10, uub*10, num_u), + velocity_file_name = self.folder + '/velocity_analysis_min{:04g}_max{:04g}_nvel{:04g}.dat'.format( + ulb * 10, + uub * 10, + num_u) + + np.savetxt(velocity_file_name, np.concatenate((uinf_part_plot, real_part_plot, imag_part_plot)).reshape((-1, 3), order='F')) - cout.cout_wrap('\tSuccessful', 1) - self.velocity_results = dict() - self.data.linear.stability['velocity_results'] = dict() - self.data.linear.stability['velocity_results']['u_inf'] = uinf_part_plot - self.data.linear.stability['velocity_results']['evals_real'] = real_part_plot - self.data.linear.stability['velocity_results']['evals_imag'] = imag_part_plot + if self.print_info: + cout.cout_wrap('\t\tSuccessfully saved velocity analysis to {:s}'.format(velocity_file_name), 2) - def display_root_locus(self): + @staticmethod + def display_root_locus(eigenvalues): """ Displays root locus diagrams. @@ -300,7 +409,7 @@ def display_root_locus(self): return fig, ax = plt.subplots() - ax.scatter(np.real(self.eigenvalues), np.imag(self.eigenvalues), + ax.scatter(np.real(eigenvalues), np.imag(eigenvalues), s=6, color='k', marker='s') @@ -311,115 +420,73 @@ def display_root_locus(self): return fig, ax - def plot_modes(self): - """ - Warnings: - Under development - + def plot_modes(self, eigenvectors): + r""" Plot the aeroelastic mode shapes for the first ``n_modes_to_plot`` + Plots the 0, 45, 90 and 135 degrees phase of the mode. Also plots the reference at the linearisation state. + + .. math:: x_{out} = Re(\Phi_i e^{i\theta}) + + for :math:`\theta \in \{0, \pi/4, \pi/2, 3\pi/4}`. + """ try: import matplotlib.pyplot as plt except ModuleNotFoundError: - cout.cout_wrap('Could not plot in asymptoticstability beacuse there is no Matplotlib', 4) + cout.cout_wrap('Could not plot in asymptoticstability because there is no Matplotlib', 4) return - mode_shape_list = self.settings['modes_to_plot'] - for mode in mode_shape_list: - # Scale mode - aero_states = self.data.linear.linear_system.uvlm.ss.states - displacement_states = self.data.linear.linear_system.beam.ss.states // 2 - amplitude_factor = modalutils.scale_mode(self.data, - self.eigenvectors[aero_states:aero_states + displacement_states-9, - mode], rot_max_deg=10, perc_max=0.1) - - fact_rbm = self.scale_rigid_body_mode(self.eigenvectors[:, mode], self.eigenvalues[mode].imag)* 100 - print(fact_rbm) - - t, x = self.mode_time_domain(amplitude_factor, fact_rbm, mode) - - # Initialise postprocessors - new folder for each mode - # initialise postprocessors - route = self.folder + '/stability/mode_%06d/' % mode - postprocessors = dict() - postprocessor_list = ['AerogridPlot', 'BeamPlot'] - postprocessors_settings = dict() - postprocessors_settings['AerogridPlot'] = {'include_rbm': 'on', - 'include_applied_forces': 'on', - 'minus_m_star': 0, - 'u_inf': 1 - } - postprocessors_settings['BeamPlot'] = {'include_rbm': 'on', - 'include_applied_forces': 'on'} - - for postproc in postprocessor_list: - postprocessors[postproc] = initialise_solver(postproc) - postprocessors[postproc].initialise( - self.data, postprocessors_settings[postproc]) - - # Plot reference - for postproc in postprocessor_list: - self.data = postprocessors[postproc].run(online=True) - for n in range(t.shape[1]): - aero_tstep, struct_tstep = lindynamicsim.state_to_timestep(self.data, x[:, n]) - self.data.aero.timestep_info.append(aero_tstep) - self.data.structure.timestep_info.append(struct_tstep) - - for postproc in postprocessor_list: - self.data = postprocessors[postproc].run(online=True) - - # Delete 'modal' timesteps ready for next mode - del self.data.structure.timestep_info[1:] - del self.data.aero.timestep_info[1:] - - def mode_time_domain(self, fact, fact_rbm, mode_num, cycles=2): - """ - Returns a single, scaled mode shape in time domain. - - Args: - fact: Structural deformation scaling - fact_rbm: Rigid body motion scaling - mode_num: Number of mode to plot - cycles: Number of periods/cycles to plot - - Returns: - tuple: Time domain array and scaled eigenvector in time. - """ - - - # Time domain representation of the mode - eigenvalue = self.eigenvalues[mode_num] - natural_freq = np.abs(eigenvalue) - damping = eigenvalue.real / natural_freq - period = 2*np.pi / natural_freq - dt = period/100 - t_dom = np.linspace(0, 2 * period, int(np.ceil(2 * cycles * period/dt))) - t_dom.shape = (1, len(t_dom)) - eigenvector = self.eigenvectors[:, mode_num] - eigenvector.shape = (len(eigenvector), 1) - # eigenvector[-10:] *= fact_rbm - # eigenvector[-self.data.linear.linear_system.beam.ss.states // 2 - 10: -self.data.linear.linear_system.beam.ss.states] *= fact_rbm + mode_shape_list = self.settings['modes_to_plot'] - # State simulation - # x_sim = np.real(fact_rbm * eigenvector.dot(np.exp(1j*eigenvalue*t_dom))) - x_sim = fact_rbm * eigenvector.real.dot(np.cos(natural_freq * t_dom) * np.exp(damping * t_dom)) + route = self.folder + '/modes/' + if not os.path.isdir(route): + os.makedirs(route, exist_ok=True) - return t_dom, x_sim + for mode in mode_shape_list: + # Scale mode - def reconstruct_mode(self, eig): - uvlm = self.data.linear.linear_system.uvlm - # beam = self.data.linear.lsys[sys_id].lsys['LinearBeam'] + beam = self.data.linear.linear_system.beam + displacement_states = beam.ss.states // 2 + structural_states = beam.ss.states + structural_modal_coords = beam.sys.modal - # for eig in range(10): - x_aero = self.eigenvectors[:uvlm.ss.states, eig] - forces, gamma, gamma_dot, gamma_star = uvlm.unpack_ss_vector(self.data, x_aero, self.data.linear.tsaero0) + for phase in [0, 1, 2, 3]: + v = eigenvectors[-structural_states:-structural_states//2, mode] + v_dot = eigenvectors[-structural_states//2:, mode] - x_struct = self.eigenvectors[uvlm.ss.states:, eig] - return gamma, gamma_dot, gamma_star, x_struct + if beam.sys.clamped: + num_dof_rig = 1 # to get the full vector (if 0 line356 returns empty array) + else: + warnings.warn('The rigid body motion contribution to the mode will not be shown, ' + 'just the flexible contributions.') + num_dof_rig = beam.sys.num_dof_rig + + if structural_modal_coords: + # project aeroelastic mode back to modal coordinates + phi = beam.sys.U + eta = phi.dot(v) + eta_dot = phi.dot(v_dot)[:-num_dof_rig] + else: + eta = v[:-num_dof_rig] + eta_dot = v_dot[:-num_dof_rig] + + eta_phase = np.real(np.exp(1j * phase * np.pi / 4) * eta) + amplitude_factor = modalutils.scale_mode(self.data, + eta_phase, + rot_max_deg=10, perc_max=0.15) + eta_phase *= amplitude_factor + zeta_mode = modalutils.get_mode_zeta(self.data, eta_phase) + modalutils.write_zeta_vtk(zeta_mode, self.data.linear.tsaero0.zeta, + filename_root=route + f'mode_{mode:06g}_phase{phase:04g}') + + # Reference - linearisation state + eta *= 0 + zeta_mode = modalutils.get_mode_zeta(self.data, eta.real) + modalutils.write_zeta_vtk(zeta_mode, self.data.linear.tsaero0.zeta, filename_root=route + 'mode_ref') @staticmethod - def sort_eigenvalues(eigenvalues, eigenvectors, frequency_cutoff=0): + def sort_eigenvalues(eigenvalues, eigenvectors, frequency_cutoff=0, number_of_eigenvalues=None): """ Sort continuous-time eigenvalues by order of magnitude. @@ -430,9 +497,10 @@ def sort_eigenvalues(eigenvalues, eigenvectors, frequency_cutoff=0): eigenvalues (np.ndarray): Continuous-time eigenvalues eigenvectors (np.ndarray): Corresponding right eigenvectors frequency_cutoff (float): Cutoff frequency for truncation ``[rad/s]`` + number_of_eigenvalues (int (optional)): Number of eigenvalues to retain Returns: - + tuple(np.array, np.array): eigenvalues and eigenvectors """ if frequency_cutoff == 0: @@ -446,29 +514,10 @@ def sort_eigenvalues(eigenvalues, eigenvectors, frequency_cutoff=0): order = np.argsort(eigenvalues_truncated.real)[::-1] + if number_of_eigenvalues is not None: + if number_of_eigenvalues > len(order): + cout.cout_wrap(f'Desired number of eigenvalues ({number_of_eigenvalues}) exceeds system size ' + f'({len(order)}) after frequency truncation.', 3) + else: + order = order[:number_of_eigenvalues] return eigenvalues_truncated[order], eigenvectors_truncated[:, order] - - @staticmethod - def scale_rigid_body_mode(eigenvector, freq_d): - rigid_body_mode = eigenvector[-10:] - - max_angle = 10 * np.pi/180 - - v = rigid_body_mode[0:3].real - omega = rigid_body_mode[3:6].real - dquat = rigid_body_mode[-4:] - euler = algebra.quat2euler(dquat) - max_euler = np.max(np.abs(euler)) - - if max_euler >= max_angle: - fact = max_euler / max_angle - else: - fact = 1 - - if np.abs(freq_d) < 1e-3: - fact = 1 / np.max(np.abs(v)) - else: - max_omega = max_angle * freq_d - fact = np.max(np.abs(omega)) / max_omega - - return fact diff --git a/sharpy/postproc/beamloads.py b/sharpy/postproc/beamloads.py index 82430fba9..9eedbe92c 100644 --- a/sharpy/postproc/beamloads.py +++ b/sharpy/postproc/beamloads.py @@ -1,5 +1,6 @@ import os import numpy as np +import os from sharpy.utils.solver_interface import solver, BaseSolver import sharpy.utils.settings as settings import sharpy.structure.utils.xbeamlib as xbeamlib @@ -98,15 +99,11 @@ def print_loads(self, online): def calculate_loads(self, online): if online: it = -1 - (self.data.structure.timestep_info[it].postproc_cell['strain'], - self.data.structure.timestep_info[it].postproc_cell['loads']) = xbeamlib.cbeam3_loads(self.data.structure, - it) + timestep_add_loads(self.data.structure, self.data.structure.timestep_info[it]) self.calculate_coords_a(self.data.structure.timestep_info[it]) else: for it in range(len(self.data.structure.timestep_info)): - (self.data.structure.timestep_info[it].postproc_cell['strain'], - self.data.structure.timestep_info[it].postproc_cell['loads']) = xbeamlib.cbeam3_loads(self.data.structure, - it) + timestep_add_loads(self.data.structure, self.data.structure.timestep_info[it]) self.calculate_coords_a(self.data.structure.timestep_info[it]) def calculate_coords_a(self, timestep_info): @@ -115,6 +112,11 @@ def calculate_coords_a(self, timestep_info): iglobal_node = self.data.structure.connectivities[ielem, 2] timestep_info.postproc_cell['coords_a'][ielem, :] = timestep_info.pos[iglobal_node, :] + +def timestep_add_loads(structure, timestep): + timestep.postproc_cell['strain'], timestep.postproc_cell['loads'] = \ + xbeamlib.cbeam3_loads(structure, timestep) + # def calculate_loads(self): # # initial (ini) loads # tstep = self.data.structure.ini_info diff --git a/sharpy/postproc/frequencyresponse.py b/sharpy/postproc/frequencyresponse.py index d43e12582..65834ebbf 100644 --- a/sharpy/postproc/frequencyresponse.py +++ b/sharpy/postproc/frequencyresponse.py @@ -53,6 +53,24 @@ class FrequencyResponse(solver_interface.BaseSolver): settings_description['frequency_unit'] = 'Units of frequency, ``w`` for rad/s, ``k`` reduced.' settings_options['frequency_unit'] = ['w', 'k'] + settings_types['frequency_scaling'] = 'dict' + settings_default['frequency_scaling'] = {} + settings_description['frequency_scaling'] = 'Dictionary containing the frequency scaling factors, if the ' \ + 'aerodynamic system has not been previously scaled. Applied also if ' \ + 'the desired unit is reduced frequency.' + + scaling_types = dict() + scaling_default = dict() + scaling_description = dict() + + scaling_types['length'] = 'float' + scaling_default['length'] = 1. + scaling_description['length'] = 'Length scaling factor.' + + scaling_types['speed'] = 'float' + scaling_default['speed'] = 1. + scaling_description['speed'] = 'Speed scaling factor.' + settings_types['frequency_bounds'] = 'list(float)' settings_default['frequency_bounds'] = [1e-3, 1] settings_description['frequency_bounds'] = 'Lower and upper frequency bounds in the corresponding unit.' @@ -77,6 +95,10 @@ class FrequencyResponse(solver_interface.BaseSolver): settings_table = settings_utils.SettingsTable() __doc__ += settings_table.generate(settings_types, settings_default, settings_description, settings_options) + scaling_table = settings_utils.SettingsTable() + __doc__ += scaling_table.generate(scaling_types, scaling_default, scaling_description, + header_line='The scaling dictionary takes the following entries:') + def __init__(self): self.settings = None @@ -84,6 +106,7 @@ def __init__(self): self.folder = None self.print_info = False + self.scaled = False self.w_to_k = 1 self.wv = None self.caller = None @@ -102,15 +125,19 @@ def initialise(self, data, custom_settings=None, caller=None): self.print_info = self.settings['print_info'] - try: - scaling = self.data.linear.linear_system.uvlm.sys.ScalingFacts - if self.settings['frequency_unit'] == 'k': - self.w_to_k = scaling['length'] / scaling['speed'] + if self.settings['frequency_unit'] == 'k': + self.scaled = True + if self.data.linear.linear_system.uvlm.scaled: + scaling = self.data.linear.linear_system.uvlm.sys.ScalingFacts else: - self.w_to_k = 1. - except AttributeError: + scaling = self.settings['frequency_scaling'] + settings_utils.to_custom_types(scaling, self.scaling_types, self.scaling_default, + no_ctype=True) + self.w_to_k = scaling['length'] / scaling['speed'] + else: self.w_to_k = 1. + # Frequency bounds in radians lb = self.settings['frequency_bounds'][0] / self.w_to_k ub = self.settings['frequency_bounds'][1] / self.w_to_k @@ -132,7 +159,7 @@ def run(self, ss=None, online=False): Computes the frequency response of the linear state-space. Args: - ss (sharpy.linear.src.libss.ss (Optional)): State-space object for which to compute the frequency response. + ss (sharpy.linear.src.libss.StateSpace (Optional)): State-space object for which to compute the frequency response. If not given, the response for the previously assembled systems and specified in ``target_system`` will be performed. """ @@ -140,12 +167,12 @@ def run(self, ss=None, online=False): if ss is None: ss_list = [find_target_system(self.data, system_name) for system_name in self.settings['target_system']] - elif type(ss) is libss.ss: + elif type(ss) is libss.StateSpace: ss_list = [ss] elif type(ss) is list: ss_list = ss else: - raise TypeError('ss input must be either a libss.ss instance or a list of libss.ss') + raise TypeError('StateSpace input must be either a libss.StateSpace instance or a list of libss.StateSpace') for ith, system in enumerate(ss_list): if self.print_info: @@ -177,7 +204,7 @@ def run(self, ss=None, online=False): else: hinf = None - self.save_freq_resp(self.wv, y_freq_fom, system_name=system_name, hinf=hinf) + self.save_freq_resp(self.wv * self.w_to_k, y_freq_fom, system_name=system_name, hinf=hinf) cout.cout_wrap('\tComputed the frequency response in %f s' % tfom, 2) @@ -204,7 +231,7 @@ def save_freq_resp(self, wv, Yfreq, system_name=None, hinf=None): with open(self.folder + '/freqdata_readme.txt', 'w') as outfile: outfile.write('Frequency Response Data Output\n\n') outfile.write('Frequency data found in the relevant .h5 file\n') - outfile.write('If the system has not been scaled, the units of frequency are rad/s\nThe frequency' \ + outfile.write('The units of frequency are rad/s\nThe frequency' \ 'response is given in complex form.') case_name = '' @@ -246,7 +273,7 @@ def quick_plot(self, y_freq_fom=None, subfolder=None): if y_freq_fom is not None: ax1[0].plot(self.wv * self.w_to_k, 20 * np.log10(np.abs(y_freq_fom[pj, mj, :])), color='C0') ax1[1].plot(self.wv * self.w_to_k, np.angle(y_freq_fom[pj, mj, :]), '-', color='C0') - if self.settings['frequency_unit'] == 'k': + if self.scaled: ax1[1].set_xlabel('Reduced Frequency, k [-]') else: ax1[1].set_xlabel(r'Frequency, $\omega$ [rad/s]') diff --git a/sharpy/postproc/savedata.py b/sharpy/postproc/savedata.py index 7cb9fab7c..360393064 100644 --- a/sharpy/postproc/savedata.py +++ b/sharpy/postproc/savedata.py @@ -182,11 +182,11 @@ def initialise(self, data, custom_settings=None, caller=None): sharpy.linear.assembler.linearaeroelastic.LinearAeroelastic, sharpy.linear.assembler.linearbeam.LinearBeam, sharpy.linear.assembler.linearuvlm.LinearUVLM, - sharpy.linear.src.libss.ss, + sharpy.linear.src.libss.ss_block, sharpy.linear.src.lingebm.FlexDynamic,) if self.settings['save_linear_uvlm']: - self.ClassesToSave += (sharpy.solvers.linearassembler.Linear, sharpy.linear.src.libss.ss) + self.ClassesToSave += (sharpy.solvers.linearassembler.Linear, sharpy.linear.src.libss.ss_block) self.caller = caller def run(self, online=False): @@ -267,28 +267,25 @@ def run(self, online=False): 'D': D} for k, v in linearisation_vectors.items(): savedict[k] = v - try: - dt = self.data.linear.ss.dt + dt = self.data.linear.ss.dt + if dt is not None: savedict['dt'] = dt - except AttributeError: - pass savemat(matfilename, savedict) if self.settings['save_linear_uvlm']: matfilename = self.filename.replace('.data.h5', '.uvlmss.mat') linearisation_vectors = self.data.linear.linear_system.uvlm.linearisation_vectors A, B, C, D = self.data.linear.linear_system.uvlm.ss.get_mats() + savedict = {'A': A, 'B': B, 'C': C, 'D': D} for k, v in linearisation_vectors.items(): savedict[k] = v - try: - dt = self.data.linear.ss.dt + dt = self.data.linear.linear_system.uvlm.ss.dt + if dt is not None: savedict['dt'] = dt - except AttributeError: - pass savemat(matfilename, savedict) return self.data diff --git a/sharpy/postproc/saveparametriccase.py b/sharpy/postproc/saveparametriccase.py index b16d733ba..f348f3a6d 100644 --- a/sharpy/postproc/saveparametriccase.py +++ b/sharpy/postproc/saveparametriccase.py @@ -3,6 +3,7 @@ import configobj import os import sharpy.utils.cout_utils as cout +import warnings @solver @@ -16,6 +17,16 @@ class SaveParametricCase(BaseSolver): If the setting ``save_case`` is selected and the post processor :class:`~sharpy.solvers.pickledata.PickleData` is not present in the SHARPy flow, this solver will pickle the data to the path given in the ``folder`` setting. + The setting ``save_pmor_items`` saves to h5 the following state-spaces and gains, if present: + * Aeroelastic state-space saved to: / save_pmor_data / _statespace.h5 + * Aerodynamic ROM reduced order bases saved to: / save_pmor_data / _aerorob.h5 + * Structural ROM reduced order bases saved to: / save_pmor_data / _modal_structrob.h5 + + The setting ``save_pmor_subsystem saves the additional state-spaces to h5 files: + * Structural matrices saved to: / save_pmor_data / _struct_matrices.h5 + * Structural state-space saved to: / save_pmor_data / _beamstatespace.h5 + * Aerodynamic state-space saved to: / save_pmor_data / _aerostatespace.h5 + Examples: In the case you are running several SHARPy cases, varying for instance the velocity, the settings would @@ -41,13 +52,25 @@ class SaveParametricCase(BaseSolver): settings_description = dict() settings_types['save_case'] = 'bool' - settings_default['save_case'] = True - settings_description['save_case'] = 'Save a .pkl of the SHARPy case. Required for PMOR.' + settings_default['save_case'] = False + settings_description['save_case'] = 'DeprecationWarning - Save a .pkl of the SHARPy case. Required for PMOR.' settings_types['parameters'] = 'dict' settings_default['parameters'] = None settings_description['parameters'] = 'Dictionary containing the chosen simulation parameters and their values.' + settings_types['save_pmor_items'] = 'bool' + settings_default['save_pmor_items'] = False + settings_description['save_pmor_items'] = 'Saves to h5 the items required for PMOR interpolation: the aerodynamic ' \ + 'reduced order bases, the structural modal matrix and the ' \ + 'reduced state-space' + + settings_types['save_pmor_subsystems'] = 'bool' + settings_default['save_pmor_subsystems'] = False + settings_description['save_pmor_subsystems'] = 'Saves to h5 the statespaces and matrices of the UVLM and beam and ' \ + 'the M, C, K matrices. The setting ``save_pmor_items`` ' \ + 'should be set to `on`' + settings_table = settings.SettingsTable() __doc__ += settings_table.generate(settings_types, settings_default, settings_description) @@ -55,6 +78,7 @@ def __init__(self): self.data = None self.settings = None self.folder = None + self.case_name = None def initialise(self, data, custom_settings=None): self.data = data @@ -70,6 +94,7 @@ def initialise(self, data, custom_settings=None): ) self.folder = data.output_folder + self.case_name = self.data.settings['SHARPy']['case'] def run(self): @@ -85,14 +110,64 @@ def run(self): sim_info['case'] = self.data.settings['SHARPy']['case'] if 'PickleData' not in self.data.settings['SHARPy']['flow'] and self.settings['save_case']: + warnings.warn('Post-proc: SaveParametricCase: Saving a pickle is not recommended - try saving required ' + 'attributes individually', + DeprecationWarning) pickle_solver = initialise_solver('PickleData') pickle_solver.initialise(self.data) self.data = pickle_solver.run() sim_info['path_to_data'] = os.path.abspath(self.folder) - else: - sim_info['path_to_data'] = os.path.abspath(self.folder) + + sim_info['path_to_data'] = os.path.abspath(self.folder) config['sim_info'] = sim_info config.write() + if self.settings['save_pmor_items']: + try: + self.data.linear + except AttributeError: + pass + else: + if not os.path.exists(self.folder + '/save_pmor_data/'): + os.makedirs(self.folder + '/save_pmor_data/') + self.save_state_space() + self.save_aero_rom_bases() + self.save_structural_modal_matrix() + + if self.settings['save_pmor_subsystems']: + self.save_structural_matrices() + self.save_aero_state_space() + return self.data + + def save_aero_rom_bases(self): + """Save the aerodynamic reduced order bases to h5 files in the output directory""" + # check rom's exist + rom_dict = self.data.linear.linear_system.uvlm.rom + if rom_dict is None: + return None + + for rom_name, rom_class in rom_dict.items(): + rom_class.save_reduced_order_bases(self.base_name + f'_{rom_name.lower()}_aerorob.h5') + + def save_structural_modal_matrix(self): + if not self.data.linear.linear_system.beam.sys.modal: + return None + + self.data.linear.linear_system.beam.save_reduced_order_bases(self.base_name + f'_modal_structrob.h5') + + def save_state_space(self): + self.data.linear.linear_system.ss.save(self.base_name + '_statespace.h5') + + def save_structural_matrices(self): + self.data.linear.linear_system.beam.ss.save(self.base_name + '_beamstatespace.h5') + self.data.linear.linear_system.beam.save_structural_matrices(self.base_name + '_struct_matrices.h5') + + def save_aero_state_space(self): + self.data.linear.linear_system.uvlm.ss.save(self.base_name + '_aerostatespace.h5') + + @property + def base_name(self): + return self.folder + '/save_pmor_data/' + self.case_name + diff --git a/sharpy/postproc/stabilityderivatives.py b/sharpy/postproc/stabilityderivatives.py new file mode 100644 index 000000000..b2c8782d6 --- /dev/null +++ b/sharpy/postproc/stabilityderivatives.py @@ -0,0 +1,215 @@ +import sharpy.utils.solver_interface as solver_interface +import os +import numpy as np +import sharpy.utils.cout_utils as cout +import sharpy.utils.settings as settings +from sharpy.linear.utils.derivatives import Derivatives, DerivativeSet + + +@solver_interface.solver +class StabilityDerivatives(solver_interface.BaseSolver): + """ + Outputs the stability derivatives of a free-flying aircraft + + """ + solver_id = 'StabilityDerivatives' + solver_classification = 'post-processor' + + settings_default = dict() + settings_description = dict() + settings_types = dict() + settings_options = dict() + + settings_types['print_info'] = 'bool' + settings_default['print_info'] = True + settings_description['print_info'] = 'Display info to screen' + + settings_types['u_inf'] = 'float' + settings_default['u_inf'] = 1. + settings_description['u_inf'] = 'Free stream reference velocity' + + settings_types['S_ref'] = 'float' + settings_default['S_ref'] = 1. + settings_description['S_ref'] = 'Reference planform area' + + settings_types['b_ref'] = 'float' + settings_default['b_ref'] = 1. + settings_description['b_ref'] = 'Reference span' + + settings_types['c_ref'] = 'float' + settings_default['c_ref'] = 1. + settings_description['c_ref'] = 'Reference chord' + + settings_table = settings.SettingsTable() + __doc__ += settings_table.generate(settings_types, settings_default, settings_description) + + def __init__(self): + self.data = None + self.settings = dict() + + self.u_inf = 1 + self.inputs = 0 + self.caller = None + self.folder = None + + self.ppal_axes = None + self.n_control_surfaces = None + + self.coefficients = dict # type: dict # name: scaling coefficient + + def initialise(self, data, custom_settings=None, caller=None): + self.data = data + + if custom_settings: + self.settings = custom_settings + else: + self.settings = self.data.settings[self.solver_id] + + settings.to_custom_types(self.settings, self.settings_types, self.settings_default, + options=self.settings_options, + no_ctype=True) + self.caller = caller + self.folder = data.output_folder + '/derivatives/' + if not os.path.isdir(self.folder): + os.makedirs(self.folder, exist_ok=True) + + u_inf = self.settings['u_inf'] + s_ref = self.settings['S_ref'] + b_ref = self.settings['b_ref'] + c_ref = self.settings['c_ref'] + rho = self.data.linear.tsaero0.rho + self.ppal_axes = self.data.settings['Modal']['rigid_modes_ppal_axes'] + + # need to decide whether coefficients stay here or goes just in Derivatives class + self.coefficients = {'force': 0.5 * rho * u_inf ** 2 * s_ref, + 'moment_lon': 0.5 * rho * u_inf ** 2 * s_ref * c_ref, + 'moment_lat': 0.5 * rho * u_inf ** 2 * s_ref * b_ref, + 'force_angular_vel': 0.5 * rho * u_inf ** 2 * s_ref * c_ref / u_inf, + 'moment_lon_angular_vel': 0.5 * rho * u_inf ** 2 * s_ref * c_ref * c_ref / u_inf} # missing rates + + reference_dimensions = {} + for k in ['S_ref', 'b_ref', 'c_ref', 'u_inf']: + reference_dimensions[k] = self.settings[k] + reference_dimensions['rho'] = rho + reference_dimensions['quat'] = self.data.linear.tsstruct0.quat + + self.data.linear.derivatives = dict() # {str:Derivatives()} (sharpy.linear.utils.derivatives.Derivatives) + self.data.linear.derivatives['aerodynamic'] = Derivatives(reference_dimensions, + static_state=self.steady_aero_forces(), + target_system='aerodynamic') + self.data.linear.derivatives['aeroelastic'] = Derivatives(reference_dimensions, + static_state=self.steady_aero_forces(), + target_system='aeroelastic') + + def run(self, online=False): + + # TODO: consider running all required solvers inside this one to keep the correct settings + # i.e: run Modal, Linear Assembly + + derivatives = self.data.linear.derivatives # {str:Derivatives()} (sharpy.linear.utils.derivatives.Derivatives) + if self.data.linear.linear_system.beam.sys.modal: + phi = self.data.linear.linear_system.linearisation_vectors['mode_shapes'].real + else: + phi = None + + steady_forces = self.data.linear.linear_system.linearisation_vectors['forces_aero_beam_dof'] + v0 = self.get_freestream_velocity() + quat = self.data.linear.tsstruct0.quat + + try: + tpa = self.data.linear.tsstruct0.modal['t_pa'] + except KeyError: + tpa = None + + if self.data.linear.linear_system.uvlm.scaled: + raise NotImplementedError('Stability Derivatives not yet implented for scaled system') + self.data.linear.linear_system.update(self.settings['u_inf']) + + for target_system in ['aerodynamic', 'aeroelastic']: + state_space = self.get_state_space(target_system) + target_system_derivatives = derivatives[target_system] + + target_system_derivatives.initialise_derivatives(state_space, + steady_forces, + quat, + v0, + phi, + self.data.linear.tsstruct0.modal['cg'], + tpa=tpa) + target_system_derivatives.dict_of_derivatives[ + 'force_angle_velocity'] = target_system_derivatives.new_derivative( + 'stability', + 'angle_derivatives', + 'Force/Angle via velocity') + + # useful to double check the effect of the ``track_body`` == 'on' setting + # current_derivative.dict_of_derivatives['force_angle_angle'] = current_derivative.new_derivative( + # 'stability', + # 'angle_derivatives_tb', + # 'Force/Angle via Track Body') + + target_system_derivatives.dict_of_derivatives['force_velocity'] = target_system_derivatives.new_derivative( + 'body', + 'body_derivatives') + + target_system_derivatives.dict_of_derivatives['force_cs'] = target_system_derivatives.new_derivative( + 'body', + 'control_surface_derivatives') + target_system_derivatives.save(self.folder) + target_system_derivatives.savetxt(self.folder) + + return self.data + + def get_freestream_velocity(self): + try: + u_inf = self.data.settings['StaticUvlm']['aero_solver_settings']['u_inf'] + u_inf_direction = self.data.settings['StaticCoupled']['aero_solver_settings']['u_inf_direction'] + except KeyError: + try: + u_inf = self.data.settings['StaticCoupled']['aero_solver_settings']['velocity_field_input']['u_inf'] + u_inf_direction = self.data.settings['StaticCoupled']['aero_solver_settings']['velocity_field_input']['u_inf_direction'] + except KeyError: + cout.cout_wrap('Unable to find free stream velocity settings in StaticUvlm or StaticCoupled,' + 'please ensure these settings are provided in the config .sharpy file. If' + 'you are running a restart simulation make sure they are included too, regardless' + 'of these solvers being present in the SHARPy flow', 4) + raise KeyError + + try: + v0 = u_inf * u_inf_direction * -1 + except TypeError: + # For restart solutions, where the settings may have not been processed and thus may + # exist but in string format + try: + u_inf_direction = np.array(u_inf_direction, dtype=float) + except ValueError: + if u_inf_direction.find(',') < 0: + u_inf_direction = np.fromstring(u_inf_direction.strip('[]'), sep=' ', dtype=float) + else: + u_inf_direction = np.fromstring(u_inf_direction.strip('[]'), sep=',', dtype=float) + finally: + v0 = np.array(u_inf_direction, dtype=float) * float(u_inf) * -1 + + return v0 + + def get_state_space(self, target_system): + if target_system == 'aerodynamic': + ss = self.data.linear.linear_system.uvlm.ss + elif target_system == 'aeroelastic': + ss = self.data.linear.ss + else: + raise NameError('Unknown target system {:s}'.format(target_system)) + + return ss + + def steady_aero_forces(self): + fx = np.sum(self.data.aero.timestep_info[0].inertial_steady_forces[:, 0], 0) + \ + np.sum(self.data.aero.timestep_info[0].inertial_unsteady_forces[:, 0], 0) + + fy = np.sum(self.data.aero.timestep_info[0].inertial_steady_forces[:, 1], 0) + \ + np.sum(self.data.aero.timestep_info[0].inertial_unsteady_forces[:, 1], 0) + + fz = np.sum(self.data.aero.timestep_info[0].inertial_steady_forces[:, 2], 0) + \ + np.sum(self.data.aero.timestep_info[0].inertial_unsteady_forces[:, 2], 0) + + return fx, fy, fz diff --git a/sharpy/rom/balanced.py b/sharpy/rom/balanced.py index cd41c1431..d092c9d13 100644 --- a/sharpy/rom/balanced.py +++ b/sharpy/rom/balanced.py @@ -21,6 +21,7 @@ import sharpy.rom.utils.librom as librom import sharpy.linear.src.libss as libss import time +from sharpy.linear.utils.ss_interface import LinearVector, StateVariable dict_of_balancing_roms = dict() @@ -116,9 +117,9 @@ def run(self, ss): Cr = C.dot(Tinv) if dtsystem: - ss_bal = libss.ss(Ar, Br, Cr, D, dt=ss.dt) + ss_bal = libss.StateSpace(Ar, Br, Cr, D, dt=ss.dt) else: - ss_bal = libss.ss(Ar, Br, Cr, D) + ss_bal = libss.StateSpace(Ar, Br, Cr, D) t1 = time.time() if self.print_info: @@ -293,7 +294,7 @@ def run(self, ss): Br = Tinv.dot(B) Cr = C.dot(T) - ssrom = libss.ss(Ar, Br, Cr, D, dt=ss.dt) + ssrom = libss.StateSpace(Ar, Br, Cr, D, dt=ss.dt) return ssrom @@ -369,14 +370,23 @@ def run(self, ss): out = self.algorithm.run(ss) - if type(out) == libss.ss: + if type(out) == libss.StateSpace: self.ssrom = out else: Ar, Br, Cr = out if self.dtsystem: - self.ssrom = libss.ss(Ar, Br, Cr, D, dt=self.ss.dt) + self.ssrom = libss.StateSpace(Ar, Br, Cr, D, dt=self.ss.dt) else: - self.ssrom = libss.ss(Ar, Br, Cr, D) + self.ssrom = libss.StateSpace(Ar, Br, Cr, D) + + try: + self.ssrom.input_variables = self.ss.input_variables.copy() + self.ssrom.output_variables = self.ss.output_variables.copy() + self.ssrom.state_variables = LinearVector( + [StateVariable('balanced_{:s}'.format(self.settings['algorithm'].lower()), + size=self.ssrom.states, index=0)]) + except AttributeError: + pass return self.ssrom diff --git a/sharpy/rom/krylov.py b/sharpy/rom/krylov.py index 0c1e5c194..2f70e8ddf 100644 --- a/sharpy/rom/krylov.py +++ b/sharpy/rom/krylov.py @@ -11,6 +11,7 @@ import sharpy.rom.utils.krylovutils as krylovutils import warnings as warn import h5py +from sharpy.linear.utils.ss_interface import LinearVector, StateVariable, InputVariable, OutputVariable @rom_interface.rom class Krylov(rom_interface.BaseRom): @@ -161,9 +162,33 @@ def save(self, filename): compress_float=True) h5.add_as_grp(self.ssrom, outfile, grpname='ssrom', - ClassesToSave=(libss.ss, ), + ClassesToSave=(libss.StateSpace, ), compress_float=True) + def save_reduced_order_bases(self, file_name): + """ + Save reduced order bases to an h5 file + + Args: + file_name (str): path to h5 file + + """ + if not isinstance(self.V, libss.Gain): + V = libss.Gain(self.V) + W = libss.Gain(self.W) + + else: + V = self.V + W = self.W + + if self.settings['single_side'] == 'observability' or self.settings['single_side'] == 'controllability': + # if single sided, the projector is V and W = V therefore no need to duplicate + libss.Gain.save_multiple_gains(file_name, ('V', V)) + cout.cout_wrap(f'Saved Krylov reduced order bases, V to file: {file_name}', 1) + else: + libss.Gain.save_multiple_gains(file_name, ('V', V), ('W', W)) + cout.cout_wrap(f'Saved Krylov reduced order bases, V and W to file: {file_name}', 1) + def run(self, ss): """ Performs Model Order Reduction employing Krylov space projection methods. @@ -181,10 +206,10 @@ def run(self, ss): ========================= ==================== ========================================================== Args: - ss (sharpy.linear.src.libss.ss): State space to reduce + ss (sharpy.linear.src.libss.StateSpace): State space to reduce Returns: - (libss.ss): Reduced state space system + (libss.StateSpace): Reduced state space system """ self.ss = ss @@ -202,7 +227,13 @@ def run(self, ss): Ar, Br, Cr = self.__getattribute__(self.algorithm)(self.frequency, self.r) - self.ssrom = libss.ss(Ar, Br, Cr, self.ss.D, self.ss.dt) + self.ssrom = libss.StateSpace(Ar, Br, Cr, self.ss.D, self.ss.dt) + try: + self.ssrom.input_variables = self.ss.input_variables.copy() + self.ssrom.output_variables = self.ss.output_variables.copy() + self.ssrom.state_variables = LinearVector([StateVariable('krylov', size=self.ssrom.states, index=0)]) + except AttributeError: + pass self.stable = self.check_stability(restart_arnoldi=self.restart_arnoldi) @@ -685,6 +716,8 @@ def mimo_rational_arnoldi(self, frequency, r): if self.settings['single_side'] == 'controllability' or self.settings['single_side'] == 'observability': if self.settings['single_side'] == 'observability': V = W + if self.settings['single_side'] == 'controllability': + W = V # needed to properly save gains Ar = V.T.dot(self.ss.A.dot(V)) Br = V.T.dot(self.ss.B) Cr = self.ss.C.dot(V) @@ -708,8 +741,17 @@ def mimo_rational_arnoldi(self, frequency, r): Br = W.T.dot(self.ss.B) Cr = self.ss.C.dot(V.dot(Tinv)) - self.W = W - self.V = V + self.W = libss.Gain(W, + input_vars=LinearVector([InputVariable('krylov', size=W.shape[1], index=0)]), + output_vars=LinearVector.transform(self.ss.state_variables, OutputVariable)) + self.V = libss.Gain(V, + input_vars=LinearVector([InputVariable('krylov', size=V.shape[1], index=0)]), + output_vars=LinearVector.transform(self.ss.state_variables, OutputVariable)) + + # for state recovery purposes + self.projection_gain = libss.Gain(V, + input_vars=LinearVector([InputVariable('krylov', size=V.shape[1], index=0)]), + output_vars=LinearVector.transform(self.ss.state_variables, OutputVariable)) return Ar, Br, Cr diff --git a/sharpy/rom/utils/librom.py b/sharpy/rom/utils/librom.py index eb5278d2e..c8e27d1cf 100644 --- a/sharpy/rom/utils/librom.py +++ b/sharpy/rom/utils/librom.py @@ -7,7 +7,6 @@ import numpy as np import scipy.linalg as scalg -# from IPython import embed import sharpy.linear.src.libsparse as libsp import sharpy.linear.src.libss as libss @@ -92,10 +91,20 @@ def balreal_direct_py(A, B, C, DLTI=True, Schur=False, full_outputs=False): else: sollyap = scalg.solve_lyapunov + # A is a sparse matrix in csr_matrix(sparse) format, can not be directly passed into functions used in scipy _solver.py + # Sparse matrices do not work well with Scipy (Version 1.7.3) in the following code, so A is transformed into a dense matrix here first. + if type(A) is not np.ndarray: + try: + A = A.todense() + except AttributeError: + raise TypeError(f'Matrix needs to be in dense form. Unable to convert A matrix of type {type(A)} to ' + f'dense using method .todense()') + # solve Lyapunov if Schur: # decompose A Atri, U = scalg.schur(A) + # solve Lyapunov BBtri = np.dot(U.T, np.dot(B, np.dot(B.T, U))) CCtri = np.dot(U.T, np.dot(C.T, np.dot(C, U))) @@ -970,7 +979,7 @@ def balfreq(SS, DictBalFreq): Ab = libsp.dot(Ti, libsp.dot(SS.A, T)) Bb = libsp.dot(Ti, SS.B) Cb = libsp.dot(SS.C, T) - SSb = libss.ss(Ab, Bb, Cb, SS.D, dt=SS.dt) + SSb = libss.StateSpace(Ab, Bb, Cb, SS.D, dt=SS.dt) ### Eliminate unstable modes - if any: if DictBalFreq['check_stability']: @@ -1007,7 +1016,7 @@ def modred(SSb, N, method='residualisation'): Nb = SSb.A.shape[0] if Nb == N: - SSrom = libss.ss(SSb.A, SSb.B, SSb.C, SSb.D, dt=SSb.dt) + SSrom = libss.StateSpace(SSb.A, SSb.B, SSb.C, SSb.D, dt=SSb.dt) return SSrom A11 = SSb.A[:N, :N] @@ -1016,7 +1025,7 @@ def modred(SSb, N, method='residualisation'): D = SSb.D if method is 'truncation': - SSrom = libss.ss(A11, B11, C11, D, dt=SSb.dt) + SSrom = libss.StateSpace(A11, B11, C11, D, dt=SSb.dt) else: Nb = SSb.A.shape[0] IA22inv = -SSb.A[N:, N:].copy() @@ -1024,7 +1033,7 @@ def modred(SSb, N, method='residualisation'): IA22inv[eevec, eevec] += 1. IA22inv = scalg.inv(IA22inv, overwrite_a=True) - SSrom = libss.ss( + SSrom = libss.StateSpace( A11 + np.dot(SSb.A[:N, N:], np.dot(IA22inv, SSb.A[N:, :N])), B11 + np.dot(SSb.A[:N, N:], np.dot(IA22inv, SSb.B[N:, :])), C11 + np.dot(SSb.C[:, N:], np.dot(IA22inv, SSb.A[N:, :N])), diff --git a/sharpy/rom/utils/librom_interp.py b/sharpy/rom/utils/librom_interp.py index 5d7981289..d84ec6711 100644 --- a/sharpy/rom/utils/librom_interp.py +++ b/sharpy/rom/utils/librom_interp.py @@ -51,7 +51,7 @@ def transfer_function(SS_list, wv): fast on-line evaluation Args: - SS_list (list): List of state-space models instances of :class:`sharpy.linear.src.libss.ss` class. + SS_list (list): List of state-space models instances of :class:`sharpy.linear.src.libss.StateSpace` class. wv (list): list of interpolatory weights. Notes: @@ -86,7 +86,7 @@ def FLB_transfer_function(SS_list, wv, U_list, VT_list, hsv_list=None, M_list=No Args: - SS_list (list): List of state-space models instances of :class:`sharpy.linear.src.libss.ss` class. + SS_list (list): List of state-space models instances of :class:`sharpy.linear.src.libss.StateSpace` class. wv (list): list of interpolatory weights. U_list (list): small size, thin SVD factors of Gramians square roots of each state space model (:math:`\mathbf{U}`). VT_list (list): small size, thin SVD factors of Gramians square roots of each state space model (:math:`\mathbf{V}^\top`). @@ -200,7 +200,7 @@ def FLB_transfer_function(SS_list, wv, U_list, VT_list, hsv_list=None, M_list=No C_int += wv[ii] * np.dot(SS_list[ii].C, T_int_list[ii]) D_int += wv[ii] * SS_list[ii].D - return libss.ss(A_int, B_int, C_int, D_int, dt=SS_list[0].dt), hsv_int + return libss.StateSpace(A_int, B_int, C_int, D_int, dt=SS_list[0].dt), hsv_int class InterpROM: @@ -228,7 +228,7 @@ class InterpROM: Inputs: - - SS: list of state-space models (instances of libss.ss class) + - SS: list of state-space models (instances of libss.StateSpace class) - VV: list of V matrices used to produce SS. If None, it is assumed that ROMs are defined over the same basis @@ -342,7 +342,7 @@ def __call__(self, wv): Cint += wv[ii] * self.CC[ii] Dint += wv[ii] * self.DD[ii] - return libss.ss(Aint, Bint, Cint, Dint, self.SS[0].dt) + return libss.StateSpace(Aint, Bint, Cint, Dint, self.SS[0].dt) def project(self): """ @@ -506,7 +506,7 @@ def setUp(self): B = np.random.rand(Nx, Nu) C = np.random.rand(Ny, Nx) D = np.random.rand(Ny, Nu) - self.SS = libss.ss(A, B, C, D, dt=dt) + self.SS = libss.StateSpace(A, B, C, D, dt=dt) # class Interp1d(): # """ @@ -577,7 +577,7 @@ def setUp(self): # def __call__(self, zint): # """ -# Evaluate at interpolation point zint. Returns a list of classes ss +# Evaluate at interpolation point zint. Returns a list of classes StateSpace # """ # Nint=len(zint) @@ -619,7 +619,7 @@ def setUp(self): # # and pack everything # SSint=[] # for ii in range(Nint): -# SSint.append( ss( Aint[ii,:,:], Bint[ii,:,:], +# SSint.append( StateSpace( Aint[ii,:,:], Bint[ii,:,:], # Cint[ii,:,:], Dint[ii,:,:], dt=self.SS[0].dt)) # return SSint diff --git a/sharpy/sharpy_main.py b/sharpy/sharpy_main.py index 5d81d37a1..046ce9cc1 100644 --- a/sharpy/sharpy_main.py +++ b/sharpy/sharpy_main.py @@ -1,6 +1,8 @@ """sharpy_main: Where it all starts """ +import warnings +import sys import dill as pickle import sharpy.utils.cout_utils as cout @@ -137,3 +139,14 @@ def main(args=None, sharpy_input_dict=None): raise e return data + + + +def sharpy_run(): + """ + This is a wrapper function for the console command "sharpy" + """ + data = None + with warnings.catch_warnings(): + warnings.simplefilter('ignore') + data = main(sys.argv) diff --git a/sharpy/solvers/lindynamicsim.py b/sharpy/solvers/lindynamicsim.py index 4284e1bf1..b28771191 100644 --- a/sharpy/solvers/lindynamicsim.py +++ b/sharpy/solvers/lindynamicsim.py @@ -7,6 +7,7 @@ import scipy.linalg as sclalg import sharpy.utils.h5utils as h5utils from sharpy.utils.datastructures import LinearTimeStepInfo +from sharpy.linear.utils.ss_interface import InputVariable, LinearVector import sharpy.utils.cout_utils as cout import time import warnings @@ -17,7 +18,14 @@ class LinDynamicSim(BaseSolver): Uses the derived linear time invariant systems and solves it in time domain. - Requires a ``case_name.lininput.h5`` file in the case root folder that contains: + The inputs are provided by means of a list of dictionaries to the setting + ``input_generators``. For each input you want, you need a dictionary entry containing + ``name`` (str) which is the name of the variable, ``index`` (int) for the index of the variable + in the case of multidimensional variables (if unspecified reverts to ``0``) and the ``file_path`` + to a text file containing the time series of the input. If the input variable is multidimensional, + ``index`` may be a list of indices for each column of time series in the array in the input file. + + Alternatively a ``case_name.lininput.h5`` file in the case root folder can be used with the following entries: * ``x0`` (optional): Initial state vector * ``input_vec``: Input vector ``(n_tsteps, n_inputs)``. @@ -51,6 +59,10 @@ class LinDynamicSim(BaseSolver): settings_default['physical_time'] = 2. settings_description['physical_time'] = 'Time to run' + settings_types['input_generators'] = 'list(dict)' + settings_default['input_generators'] = [] + settings_description['input_generators'] = 'List of dictionaries for each input' + settings_default['dt'] = 0.001 settings_types['dt'] = 'float' settings_description['dt'] = 'Time increment for the solution of systems without a specified dt' @@ -83,7 +95,7 @@ def initialise(self, data, custom_settings=None): self.settings = custom_settings else: self.settings = data.settings[self.solver_id] - settings.to_custom_types(self.settings, self.settings_types, self.settings_default) + settings.to_custom_types(self.settings, self.settings_types, self.settings_default, no_ctype=True) # Read initial state and input data and store in dictionary self.read_files() @@ -102,13 +114,51 @@ def initialise(self, data, custom_settings=None): self.postprocessors[postproc].initialise( self.data, self.settings['postprocessors_settings'][postproc], caller=self) + def input_vector(self, ss): + """ + Generates an input vector ``u`` of size ``n_tsteps x inputs`` and populates the + correct columns with the time series arrays provided as text files in the settings + ``input_generators``. + + Args: + ss (libss.StateSpace): State Space object for which to generate input + + Returns: + np.array: Input vector. + """ + n_steps = self.settings['n_tsteps'] + u_vect = np.zeros((n_steps, ss.inputs)) + + for in_settings in self.settings['input_generators']: + var_name = in_settings['name'] + index = in_settings.get('index', 0) + file_path = in_settings['file_path'] + + variable = ss.input_variables.get_variable_from_name(var_name) + input_data = np.loadtxt(file_path) + + cout.cout_wrap('Found input for {:s}'.format(str(variable))) + + if type(index) is list: + for ith, i_ind in enumerate(index): + in_channel = variable.cols_loc[i_ind] + u_vect[:, in_channel] = input_data[:, ith] + else: + in_channel = variable.cols_loc[index] + u_vect[:, in_channel] = input_data + + return u_vect + def run(self): ss = self.data.linear.ss n_steps = self.settings['n_tsteps'] x0 = self.input_data_dict.get('x0', np.zeros(ss.states)) - u = self.input_data_dict['u'] + if len(self.settings['input_generators']) != 0: + u = self.input_vector(ss) + else: + u = self.input_data_dict['u'] if len(x0) != ss.states: warnings.warn('Number of states in the initial state vector not equal to the number of states') @@ -128,14 +178,14 @@ def run(self): dt = self.settings['dt'] # Total time to run - T = n_steps*dt + T = (n_steps - 1) * dt u_ref = self.settings['reference_velocity'] # If the system is scaled: if u_ref != 1.: scaling_factors = self.data.linear.linear_system.uvlm.sys.ScalingFacts dt_dimensional = scaling_factors['length'] / u_ref - T_dimensional = n_steps * dt_dimensional + T_dimensional = (n_steps - 1) * dt_dimensional T = T_dimensional / scaling_factors['time'] ss = self.data.linear.linear_system.update(self.settings['reference_velocity']) t_dom = np.linspace(0, T, n_steps) @@ -144,7 +194,6 @@ def run(self): sys = libss.ss_to_scipy(ss) cout.cout_wrap('Solving linear system using scipy...') t0 = time.time() - # breakpoint() out = sys.output(u, t=t_dom, x0=x0) ts = time.time() - t0 cout.cout_wrap('\tSolved in %.2fs' % ts, 1) @@ -232,19 +281,13 @@ def state_to_timestep(data, x, u=None, y=None): modal = True else: modal = False - # modal = True - if data.linear.linear_system.uvlm.gust_assembler: - start_x_aero = data.linear.linear_system.uvlm.gust_assembler.ss_gust.states - else: - start_x_aero = 0 - x_aero = x[start_x_aero:data.linear.linear_system.uvlm.ss.states] - x_struct = x[-data.linear.linear_system.beam.ss.states:] - # u_aero = TODO: external velocities - phi = data.linear.linear_system.beam.sys.U - Kas = data.linear.linear_system.couplings['Kas'] + + aero_state = x[:-data.linear.linear_system.beam.ss.states] + beam_state = x[-data.linear.linear_system.beam.ss.states:] # after aero all the rest is beam, but should not use + # in case it is in DT the state variables do not mean the same! # Beam output - y_beam = x_struct + y_beam = y[-data.linear.linear_system.beam.ss.outputs:] u_q = np.zeros(data.linear.linear_system.uvlm.ss.inputs) if u is not None: @@ -253,26 +296,36 @@ def state_to_timestep(data, x, u=None, y=None): else: u_q[:y_beam.shape[0]] += y_beam + Kas = data.linear.linear_system.couplings['Kas'] if modal: - # add eye matrix for extra inputs - n_modes = phi.shape[1] - n_inputs_aero_only = len(u_q) - 2*n_modes # Inputs to the UVLM other than structural inputs - u_aero = Kas.dot(sclalg.block_diag(phi, phi, np.eye(n_inputs_aero_only)).dot(u_q)) + # Transform to aerodynamic raw inputs + uvlm_in_mode_gain = data.linear.linear_system.couplings['in_mode_gain'] + aero_input = Kas.dot(uvlm_in_mode_gain.dot(u_q)) else: - # if u_q.shape[0] != - # u_aero_zero = data.linear.tsaero0 - u_aero = Kas.dot(u_q) - - # Unpack input - zeta, zeta_dot, u_ext = data.linear.linear_system.uvlm.unpack_input_vector(u_aero) + aero_input = Kas.dot(u_q) - # Also add the beam forces. I have a feeling there is a minus there as well.... # Aero - forces, gamma, gamma_dot, gamma_star = data.linear.linear_system.uvlm.unpack_ss_vector( + forces, gamma, gamma_dot, gamma_star, gust_state_vec = data.linear.linear_system.uvlm.unpack_ss_vector( data, - x_n=x_aero, + x_n=aero_state, + u_aero=aero_input, aero_tstep=data.linear.tsaero0, - track_body=True) + track_body=True, + state_variables=data.linear.linear_system.uvlm.ss.state_variables, + gust_in=True) + + if data.linear.linear_system.uvlm.gust_assembler: + gust_in_loc = data.linear.ss.input_variables('u_gust').cols_loc + u_in_gust = u[gust_in_loc] + gust_assembler = data.linear.linear_system.uvlm.gust_assembler + u_ext_gust = gust_assembler.state_to_uext.dot(gust_state_vec) + gust_assembler.uin_to_uext.dot(u_in_gust) + else: + u_ext_gust = np.array([]) + + uvlm_input_variables = LinearVector.transform(Kas.output_variables, InputVariable) + # Unpack input + zeta, zeta_dot, u_ext = data.linear.linear_system.uvlm.unpack_input_vector(aero_input, u_ext_gust, + input_variables=uvlm_input_variables) current_aero_tstep = data.aero.timestep_info[-1].copy() current_aero_tstep.forces = [forces[i_surf] + data.linear.tsaero0.forces[i_surf] for i_surf in @@ -289,8 +342,8 @@ def state_to_timestep(data, x, u=None, y=None): # self.data.aero.timestep_info.append(current_aero_tstep) - aero_forces = data.linear.linear_system.uvlm.C_to_vertex_forces.dot(x_aero) - beam_forces = data.linear.linear_system.couplings['Ksa'].dot(aero_forces) + aero_forces_vec = np.concatenate([forces[i_surf][:3, :, :].reshape(-1, order='C') for i_surf in range(len(forces))]) + beam_forces = data.linear.linear_system.couplings['Ksa'].dot(aero_forces_vec) if u is not None: u_struct = u[-data.linear.linear_system.beam.ss.inputs:] @@ -299,9 +352,9 @@ def state_to_timestep(data, x, u=None, y=None): # Reconstruct the state if modal if modal: phi = data.linear.linear_system.beam.sys.U - x_s = sclalg.block_diag(phi, phi).dot(x_struct) + x_s = sclalg.block_diag(phi, phi).dot(y_beam) else: - x_s = x_struct + x_s = y_beam y_s = beam_forces #+ phi.dot(u_struct) # y_s = self.data.linear.lsys['LinearBeam'].sys.U.T.dot(y_struct) diff --git a/sharpy/solvers/linearassembler.py b/sharpy/solvers/linearassembler.py index aec3d8683..e65a97bed 100644 --- a/sharpy/solvers/linearassembler.py +++ b/sharpy/solvers/linearassembler.py @@ -91,6 +91,20 @@ class LinearAssembler(BaseSolver): settings_default['retain_outputs'] = [] settings_description['retain_outputs'] = 'List of output channels to retain in the chosen ``inout_coordinates``.' + settings_types['retain_input_variables'] = 'list(str)' + settings_default['retain_input_variables'] = [] + settings_description['retain_input_variables'] = 'List of input channels to retain in the chosen ' \ + '``inout_coordinates``.' + + settings_types['retain_output_variables'] = 'list(str)' + settings_default['retain_output_variables'] = [] + settings_description['retain_output_variables'] = 'List of output channels to retain in the chosen ' \ + '``inout_coordinates``.' + + settings_types['recover_accelerations'] = 'bool' + settings_default['recover_accelerations'] = False + settings_description['recover_accelerations'] = 'Recover structural system accelerations as additional outputs.' + settings_table = settings.SettingsTable() __doc__ += settings_table.generate(settings_types, settings_default, settings_description, settings_options) @@ -141,6 +155,10 @@ def run(self): self.data.linear.ss = self.data.linear.linear_system.assemble() + if self.settings['recover_accelerations']: + gain = self.data.linear.linear_system.beam.recover_accelerations(self.data.linear.ss) + self.data.linear.ss.addGain(gain, where='out') + # modify inout coordinates if self.settings['inout_coordinates'] == 'nodes': try: @@ -150,11 +168,30 @@ def run(self): # retain only selected inputs and outputs if len(self.settings['retain_inputs']) != 0: - self.data.linear.ss.remove_inout_channels(self.settings['retain_inputs'], where='in') + self.data.linear.ss.retain_inout_channels(self.settings['retain_inputs'], where='in') if len(self.settings['retain_outputs']) != 0: - self.data.linear.ss.remove_inout_channels(self.settings['retain_outputs'], where='out') + self.data.linear.ss.retain_inout_channels(self.settings['retain_outputs'], where='out') + + if len(self.settings['retain_input_variables']) != 0: + ss = self.data.linear.ss + input_vars = ss.input_variables + removed_variables = [] + for variable in input_vars: + if variable.name not in self.settings['retain_input_variables']: + removed_variables.append(variable.name) + ss.remove_inputs(*removed_variables) + + if len(self.settings['retain_output_variables']) != 0: + ss = self.data.linear.ss + output_vars = ss.output_variables + removed_variables = [] + for variable in output_vars: + if variable.name not in self.settings['retain_output_variables']: + removed_variables.append(variable.name) + ss.remove_outputs(*removed_variables) + cout.cout_wrap('Final system is:', 1) - cout.cout_wrap(self.data.linear.ss.summary(), 1) + cout.cout_wrap(str(self.data.linear.ss), 2) return self.data diff --git a/sharpy/solvers/modal.py b/sharpy/solvers/modal.py index 553c8d8d0..a9c5ea69e 100644 --- a/sharpy/solvers/modal.py +++ b/sharpy/solvers/modal.py @@ -45,10 +45,6 @@ class Modal(BaseSolver): settings_default['NumLambda'] = 20 # doubles if use_undamped_modes is False settings_description['NumLambda'] = 'Number of modes to retain' - settings_types['keep_linear_matrices'] = 'bool' # attach linear M,C,K matrices to output dictionary - settings_default['keep_linear_matrices'] = True - settings_description['keep_linear_matrices'] = 'Save M, C and K matrices to output dictionary' - # output options settings_types['write_modes_vtk'] = 'bool' # write displacements mode shapes in vtk file settings_default['write_modes_vtk'] = True @@ -58,9 +54,9 @@ class Modal(BaseSolver): settings_default['print_matrices'] = False settings_description['print_matrices'] = 'Write M, C and K matrices to file' - settings_types['write_dat'] = 'bool' # write modes shapes/freq./damp. to dat file - settings_default['write_dat'] = True - settings_description['write_dat'] = 'Write mode shapes, frequencies and damping to file' + settings_types['save_data'] = 'bool' # write modes shapes/freq./damp. to dat file + settings_default['save_data'] = True + settings_description['save_data'] = 'Write mode shapes, frequencies and damping to file' settings_types['continuous_eigenvalues'] = 'bool' settings_default['continuous_eigenvalues'] = False @@ -90,9 +86,14 @@ class Modal(BaseSolver): settings_default['use_custom_timestep'] = -1 settings_description['use_custom_timestep'] = 'If > -1, it will use that time step geometry for calculating the modes' + settings_types['rigid_modes_ppal_axes'] = 'bool' + settings_default['rigid_modes_ppal_axes'] = False + settings_description['rigid_modes_ppal_axes'] = 'Modify the ridid body modes such that they are defined wrt ' \ + 'to the CG and aligned with the principal axes of inertia' + settings_types['rigid_modes_cg'] = 'bool' settings_default['rigid_modes_cg'] = False - settings_description['rigid_modes_cg'] = 'Modify the ridid body modes such that they are defined wrt to the CG' + settings_description['rigid_modes_cg'] = 'Not implemente yet' settings_table = settings.SettingsTable() __doc__ += settings_table.generate(settings_types, settings_default, settings_description) @@ -242,10 +243,13 @@ def run(self): FullMglobal, FullCglobal, FullKglobal, FullQ = xbeamlib.xbeam3_asbly_dynamic(self.data.structure, self.data.structure.timestep_info[self.data.ts], full_matrix_settings) + + cg = modalutils.cg(FullMglobal) else: xbeamlib.cbeam3_solv_modal(self.data.structure, self.settings, self.data.ts, FullMglobal, FullCglobal, FullKglobal) + cg = None # Print matrices if self.settings['print_matrices']: @@ -347,11 +351,15 @@ def run(self): eigenvectors_left = eigenvectors_left[:, order].conj() # Modify rigid body modes for them to be defined wrt the CG - eigenvectors = modalutils.mode_sign_convention(self.data.structure.boundary_conditions, eigenvectors, self.rigid_body_motion) - if self.settings['rigid_modes_cg']: - if not eigenvectors_left: - eigenvectors = modalutils.free_modes_principal_axes(eigenvectors, FullMglobal) - + eigenvectors = modalutils.mode_sign_convention(self.data.structure.boundary_conditions, eigenvectors, + self.rigid_body_motion) + if not eigenvectors_left: + if self.settings['rigid_modes_ppal_axes']: + eigenvectors, t_pa, r_pa = modalutils.free_modes_principal_axes(eigenvectors, FullMglobal, + return_transform=True) + else: + t_pa = None # Transformation matrix from the A frame to the P frame (principal axes of inertia) + r_pa = None # Scaling eigenvectors, eigenvectors_left = self.scale_modes_unit_mass_matrix(eigenvectors, FullMglobal, eigenvectors_left) @@ -381,7 +389,7 @@ def run(self): warnings.warn('Unable to import matplotlib, skipping plot') # Write dat files - if self.settings['write_dat']: + if self.settings['save_data']: if type(eigenvalues) == complex: np.savetxt(self.folder + "eigenvalues.dat", eigenvalues.view(float).reshape(-1, 2), fmt='%.12f', delimiter='\t', newline='\n') @@ -405,12 +413,9 @@ def run(self): if self.settings['write_modes_vtk']: try: self.data.aero - aero_model = True except AttributeError: warnings.warn('No aerodynamic model found - unable to project the mode onto aerodynamic grid') - aero_model = False - - if aero_model: + else: modalutils.write_modes_vtk( self.data, eigenvectors[:num_dof], @@ -444,10 +449,16 @@ def run(self): if not self.settings['use_undamped_modes']: outdict['eigenvectors_left'] = eigenvectors_left - if self.settings['keep_linear_matrices']: - outdict['M'] = FullMglobal - outdict['C'] = FullCglobal - outdict['K'] = FullKglobal + if cg is not None: + outdict['cg'] = cg + + outdict['M'] = FullMglobal + outdict['C'] = FullCglobal + outdict['K'] = FullKglobal + + if t_pa is not None: + outdict['t_pa'] = t_pa + outdict['r_pa'] = r_pa self.data.structure.timestep_info[self.data.ts].modal = outdict if self.settings['print_info']: diff --git a/sharpy/solvers/nonlineardynamicmultibody.py b/sharpy/solvers/nonlineardynamicmultibody.py index a93abef41..a1fd04eb7 100644 --- a/sharpy/solvers/nonlineardynamicmultibody.py +++ b/sharpy/solvers/nonlineardynamicmultibody.py @@ -43,7 +43,7 @@ class NonLinearDynamicMultibody(_BaseStructural): settings_types['write_lm'] = 'bool' settings_default['write_lm'] = False - settings_description['write_lm'] = 'Write lagrange multipliers' + settings_description['write_lm'] = 'Write lagrange multipliers to file' settings_types['relax_factor_lm'] = 'float' settings_default['relax_factor_lm'] = 0. @@ -71,6 +71,8 @@ def __init__(self): self.prev_Dq = None + self.out_files = None # dict: containing output_variable:file_path if desired to write output + def initialise(self, data, custom_settings=None): self.data = data @@ -99,13 +101,18 @@ def initialise(self, data, custom_settings=None): self.Lambda_ddot = np.zeros((self.num_LM_eq,), dtype=ct.c_double, order='F') if self.settings['write_lm']: - dire = './output/' + self.data.settings['SHARPy']['case'] + '/NonLinearDynamicMultibody/' + dire = self.data.output_folder + '/NonLinearDynamicMultibody/' if not os.path.isdir(dire): os.makedirs(dire) - self.fid_lambda = open(dire + 'lambda.dat', "w") - self.fid_lambda_dot = open(dire + 'lambda_dot.dat', "w") - self.fid_lambda_ddot = open(dire + 'lambda_ddot.dat', "w") - self.fid_cond_num = open(dire + 'cond_num.dat', "w") + + self.out_files = {'lambda': dire + 'lambda.dat', + 'lambda_dot': dire + 'lambda_dot.dat', + 'lambda_ddot': dire + 'lambda_ddot.dat', + 'cond_number': dire + 'cond_num.dat'} + # clean up files + for file in self.out_files.values(): + if os.path.isfile(file): + os.remove(file) # Define the number of dofs self.define_sys_size() @@ -296,20 +303,24 @@ def compute_forces_constraints(self, MB_beam, MB_tstep, ts, dt, Lambda, Lambda_d # TODO: right now, these forces are only used as an output, they are not read when the multibody is splitted def write_lm_cond_num(self, iteration, Lambda, Lambda_dot, Lambda_ddot, cond_num, cond_num_lm): - - self.fid_lambda.write("%d %d " % (self.data.ts, iteration)) - self.fid_lambda_dot.write("%d %d " % (self.data.ts, iteration)) - self.fid_lambda_ddot.write("%d %d " % (self.data.ts, iteration)) - self.fid_cond_num.write("%d %d " % (self.data.ts, iteration)) - for ilm in range(self.num_LM_eq): - self.fid_lambda.write("%f " % Lambda[ilm]) - self.fid_lambda_dot.write("%f " % Lambda_dot[ilm]) - self.fid_lambda_ddot.write("%f " % Lambda_ddot[ilm]) - self.fid_lambda.write("\n") - self.fid_lambda_dot.write("\n") - self.fid_lambda_ddot.write("\n") - self.fid_cond_num.write("%e %e\n" % (cond_num, cond_num_lm)) - + # Maybe not the most efficient way to output this, as files are opened and closed every time data is written + # However, containing the writing in the with statement prevents from files remaining open in the previous + # implementation + out_data = {'lambda': Lambda, + 'lambda_dot': Lambda_dot, + 'lambda_ddot': Lambda_ddot} + + for out_var, data in out_data.items(): + file_name = self.out_files[out_var] + with open(file_name, 'a') as fid: + fid.write(f'{self.data.ts:g} {iteration:g} ') + for ilm in range(self.num_LM_eq): + fid.write(f'{data[ilm]} ') + fid.write(f'\n') + + with open(self.out_files['cond_number'], 'a') as fid: + fid.write(f'{self.data.ts:g} {iteration:g} ') + fid.write(f'{cond_num:e} {cond_num_lm:e} \n') def run(self, structural_step=None, dt=None): if structural_step is None: @@ -364,6 +375,7 @@ def run(self, structural_step=None, dt=None): LM_old_Dq = 1.0 converged = False + for iteration in range(self.settings['max_iterations']): # Check if the maximum of iterations has been reached if iteration == self.settings['max_iterations'] - 1: diff --git a/sharpy/structure/models/beam.py b/sharpy/structure/models/beam.py index 614531529..7b167da68 100644 --- a/sharpy/structure/models/beam.py +++ b/sharpy/structure/models/beam.py @@ -328,9 +328,9 @@ def add_lumped_mass_to_element(self, i_lumped_node, inertia_tensor, replace=Fals if replace: self.elements[i_lumped_master_elem].rbmass[i_lumped_master_node_local, :, :] = ( - inertia_tensor) # += necessary in case multiple masses defined per node + inertia_tensor) else: - self.elements[i_lumped_master_elem].rbmass[i_lumped_master_node_local, :, :] = ( + self.elements[i_lumped_master_elem].rbmass[i_lumped_master_node_local, :, :] += ( inertia_tensor) # += necessary in case multiple masses defined per node # def generate_master_structure(self): diff --git a/sharpy/structure/utils/modalutils.py b/sharpy/structure/utils/modalutils.py index e0dd0db3a..bdcbd7781 100644 --- a/sharpy/structure/utils/modalutils.py +++ b/sharpy/structure/utils/modalutils.py @@ -181,21 +181,18 @@ def get_mode_zeta(data, eigvect): def write_zeta_vtk(zeta, zeta_ref, filename_root): - ''' + """ Given a list of arrays representing the coordinates of a set of n_surf UVLM lattices and organised as: zeta[n_surf][3,M+1,N=1] this function writes a vtk for each of the n_surf surfaces. - Input: - - zeta: lattice coordinates to plot - - zeta_ref: reference lattice used to compute the magnitude of displacements - - filename_root: initial part of filename (full path) without file - extension (.vtk) - ''' + Args: + zeta (np.array): lattice coordinates to plot + zeta_ref (np.array): reference lattice used to compute the magnitude of displacements + filename_root (str): initial part of filename (full path) without file extension (.vtk) + """ - # from IPython import embed - # embed() for i_surf in range(len(zeta)): filename = filename_root + "_%02u.vtu" % (i_surf,) @@ -295,11 +292,23 @@ def write_modes_vtk(data, eigenvectors, NumLambda, filename_root, write_zeta_vtk(zeta_mode, tsaero.zeta, filename_root + "_%06u" % (mode,)) -def free_modes_principal_axes(phi, mass_matrix, use_euler=False): +def free_modes_principal_axes(phi, mass_matrix, use_euler=False, **kwargs): """ Transforms the rigid body modes defined at with the A frame as reference to the centre of mass position and aligned with the principal axes of inertia. + Args: + phi (np.array): Eigenvectors defined at the ``A`` frame. + mass_matrix (np.array): System mass matrix + use_euler (bool): Use Euler rotation parametrisation rather than quaternions. + + Keyword Args: + return_transform (bool): Return tuple containing transformed modes and the transformation from the ``A`` frame + to the ``P`` frame. + + Returns: + np.array: Mass normalised modes with rigid modes defined at the centre of gravity and aligned with the + principal axes of inertia. References: Marc Artola, 2020 @@ -314,7 +323,7 @@ def free_modes_principal_axes(phi, mass_matrix, use_euler=False): m = mrr[0, 0] # mass # principal axes of inertia matrix and transformation matrix - j_cm, t_rb = np.linalg.eig(mrr[-3:, -3:] + algebra.multiply_matrices(algebra.skew(r_cg), algebra.skew(r_cg)) * m) + j_cm, t_rb = principal_axes_inertia(mrr[-3:, -3:], r_cg, m) # rigid body mass matrix about CM and inertia in principal axes m_cm = np.eye(6) * m @@ -335,7 +344,43 @@ def free_modes_principal_axes(phi, mass_matrix, use_euler=False): phit[-num_rigid_modes + 6:, 6:num_rigid_modes] = np.eye(num_rigid_modes - 6) # euler or quaternion modes - return phit + if kwargs.get('return_transform', False): + return phit, t_rb, np.block([[np.eye(3), algebra.skew(r_cg)], [np.zeros((3, 3)), np.eye(3)]]).dot(trb_diag) + else: + return phit + + +def principal_axes_inertia(j_a, r_cg, m): + r""" + Transform the inertia tensor :math:`\boldsymbol{j}_a` defined about the ``A`` frame of reference to the centre of + gravity and aligned with the principal axes of inertia. + + The inertia tensor about the centre of gravity is obtained using the parallel axes theorem + + .. math:: \boldsymbol{j}_{cm} = \boldsymbol{j}_a + \tilde{r}_{cg}\tilde{r}_{cg}m + + and rotated such that it is aligned with its eigenvectors and thus represents the inertia tensor about the principal + axes of inertia + + .. math:: \boldsymbol{j}_p = T_{pa}^\top \boldsymbol{j}_{cm} T^{pa} + + where :math:`T^{pa}` is the transformation matrix from the ``A`` frame to the principal axes ``P`` frame. + + Args: + j_a (np.array): Inertia tensor defined about the ``A`` frame. + r_cg (np.array): Centre of gravity position defined in ``A`` coordinates. + m (float): Mass. + + Returns: + tuple: Containing :math:`\boldsymbol{j}_p` and :math:`T^{pa}` + + """ + + j_p, t_pa = np.linalg.eig(j_a + algebra.multiply_matrices(algebra.skew(r_cg), algebra.skew(r_cg)) * m) + + t_pa, j_p = order_eigenvectors(t_pa, j_p) + + return j_p, t_pa def mode_sign_convention(bocos, eigenvectors, rigid_body_motion=False, use_euler=False): @@ -428,6 +473,24 @@ def order_rigid_body_modes(eigenvectors, use_euler): return eigenvectors +def order_eigenvectors(eigenvectors, eigenvalues): + ordered_eigenvectors = np.zeros_like(eigenvectors) + new_order = [] + for i in range(eigenvectors.shape[1]): + index_max_node = np.where(np.abs(eigenvectors[:, i]) == np.max(np.abs(eigenvectors[:, i])))[0][0] + ordered_eigenvectors[:, index_max_node] = eigenvectors[:, i] * np.sign(eigenvectors[index_max_node, i]) + new_order.append(index_max_node) + + try: + eigenvalues.shape[1] + except IndexError: + new_eigenvalues = eigenvalues[new_order] + else: + new_eigenvalues = eigenvalues[:, new_order] + + return ordered_eigenvectors, new_eigenvalues + + def scale_mass_normalised_modes(eigenvectors, mass_matrix): r""" Scales eigenvector matrix such that the modes are mass normalised: @@ -500,3 +563,96 @@ def assert_modes_mass_normalised(phi, m, tolerance, raise_error=False): raise e else: cout.cout_wrap('Eigenvectors are not mass normalised', 3) + + +def modes_to_cg_ref(phi, M, rigid_body_motion=False, use_euler=False): + r""" + + Returns the rigid body modes defined with respect to the centre of gravity + + The transformation from the modes defined at the FoR A origin, :math:`\boldsymbol{\Phi}`, to the modes defined + using the centre of gravity as a reference is + + + .. math:: \boldsymbol{\Phi}_{rr,CG}|_{TRA} = \boldsymbol{\Phi}_{RR}|_{TRA} + \tilde{\mathbf{r}}_{CG} + \boldsymbol{\Phi}_{RR}|_{ROT} + + .. math:: \boldsymbol{\Phi}_{rr,CG}|_{ROT} = \boldsymbol{\Phi}_{RR}|_{ROT} + + Returns: + (np.array): Transformed eigenvectors + """ + # if not rigid_body_motion: + # return phi + # NG - 26/7/19 This is the transformation being performed by K_vec + # Leaving this here for now in case it becomes necessary + # .. math:: \boldsymbol{\Phi}_{ss,CG}|_{TRA} = \boldsymbol{\Phi}_{SS}|_{TRA} +\boldsymbol{\Phi}_{RS}|_{TRA} - + # \tilde{\mathbf{r}}_{A}\boldsymbol{\Phi}_{RS}|_{ROT} + # + # .. math:: \boldsymbol{\Phi}_{ss,CG}|_{ROT} = \boldsymbol{\Phi}_{SS}|_{ROT} + # + (\mathbf{T}(\boldsymbol{\Psi})^\top)^{-1}\boldsymbol{\Phi}_{RS}|_{ROT} + # pos = self.data.structure.timestep_info[self.data.ts].pos + r_cg = cg(M) + + # jj = 0 + K_vec = np.zeros((phi.shape[0], phi.shape[0])) + + # jj_for_vel = range(self.data.structure.num_dof.value, self.data.structure.num_dof.value + 3) + # jj_for_rot = range(self.data.structure.num_dof.value + 3, self.data.structure.num_dof.value + 6) + + # for node_glob in range(self.data.structure.num_node): + # ### detect bc at node (and no. of dofs) + # bc_here = self.data.structure.boundary_conditions[node_glob] + # + # if bc_here == 1: # clamp (only rigid-body) + # dofs_here = 0 + # jj_tra, jj_rot = [], [] + # continue + # + # elif bc_here == -1 or bc_here == 0: # (rigid+flex body) + # dofs_here = 6 + # jj_tra = 6 * self.data.structure.vdof[node_glob] + np.array([0, 1, 2], dtype=int) + # jj_rot = 6 * self.data.structure.vdof[node_glob] + np.array([3, 4, 5], dtype=int) + # # jj_tra=[jj ,jj+1,jj+2] + # # jj_rot=[jj+3,jj+4,jj+5] + # else: + # raise NameError('Invalid boundary condition (%d) at node %d!' \ + # % (bc_here, node_glob)) + # + # jj += dofs_here + # + # ee, node_loc = self.data.structure.node_master_elem[node_glob, :] + # psi = self.data.structure.timestep_info[self.data.ts].psi[ee, node_loc, :] + # + # Ra = pos[node_glob, :] # in A FoR with respect to G + # + # K_vec[np.ix_(jj_tra, jj_tra)] += np.eye(3) + # K_vec[np.ix_(jj_tra, jj_for_vel)] += np.eye(3) + # K_vec[np.ix_(jj_tra, jj_for_rot)] -= algebra.skew(Ra) + # + # K_vec[np.ix_(jj_rot, jj_rot)] += np.eye(3) + # K_vec[np.ix_(jj_rot, jj_for_rot)] += np.linalg.inv(algebra.crv2tan(psi).T) + # NG - 26/7/19 - Transformation of the rigid part of the elastic modes ended up not being necessary but leaving + # here in case it becomes useful in the future (using K_vec) + + # Rigid-Rigid modes transform + if use_euler: + num_rig_dof = 9 + else: + num_rig_dof = 10 + Krr = np.eye(num_rig_dof) + Krr[np.ix_([0, 1, 2], [3, 4, 5])] += algebra.skew(r_cg) + + # Assemble transformed modes + phirr = Krr.dot(phi[-num_rig_dof:, :num_rig_dof]) + # phiss = K_vec.dot(phi[:, 10:]) + + # Get rigid body modes to be positive in translation and rotation + for i in range(num_rig_dof): + ind = np.argmax(np.abs(phirr[:, i])) + phirr[:, i] = np.sign(phirr[ind, i]) * phirr[:, i] + + phit = np.block([np.zeros((phi.shape[0], num_rig_dof)), phi[:, num_rig_dof:]]) + phit[-num_rig_dof:, :num_rig_dof] = phirr + + return phit diff --git a/sharpy/structure/utils/xbeamlib.py b/sharpy/structure/utils/xbeamlib.py index 936685069..82979b06c 100644 --- a/sharpy/structure/utils/xbeamlib.py +++ b/sharpy/structure/utils/xbeamlib.py @@ -9,7 +9,18 @@ # from sharpy.utils.datastructures import StructTimeStepInfo import sharpy.utils.cout_utils as cout -xbeam_lib_path = SharpyDir + '/lib/xbeam/lib/' + +try: + xbeamlib = ct_utils.import_ctypes_lib(SharpyDir + '/xbeam', 'libxbeam') +except OSError: + xbeamlib = ct_utils.import_ctypes_lib(SharpyDir + '/lib/xbeam/lib', + 'libxbeam') + +# ctypes pointer types +doubleP = ct.POINTER(ct.c_double) +intP = ct.POINTER(ct.c_int) +charP = ct.POINTER(ct.c_char_p) + class Xbopts(ct.Structure): """Structure skeleton for options input in xbeam @@ -61,14 +72,6 @@ def __init__(self): self.relaxation_factor = ct.c_double(0.3) -xbeamlib = ct_utils.import_ctypes_lib(xbeam_lib_path, 'libxbeam') - -# ctypes pointer types -doubleP = ct.POINTER(ct.c_double) -intP = ct.POINTER(ct.c_int) -charP = ct.POINTER(ct.c_char_p) - - def cbeam3_solv_nlnstatic(beam, settings, ts): """@brief Python wrapper for f_cbeam3_solv_nlnstatic Alfonso del Carre @@ -129,9 +132,15 @@ def cbeam3_solv_nlnstatic(beam, settings, ts): ) -def cbeam3_loads(beam, ts): - """@brief Python wrapper for f_cbeam3_loads - Alfonso del Carre +def cbeam3_loads(beam, timestep): + """Python wrapper for f_cbeam3_loads + + Args: + beam (sharpy.structure.models.beam.Beam): Structural info class + timestep (sharpy.utils.datastructures.StructTimeStepInfo): Structural time step class + + Returns: + tuple: Tuple containing the ``strains`` and ``loads``. """ f_cbeam3_loads = xbeamlib.cbeam3_loads f_cbeam3_loads.restype = None @@ -147,9 +156,9 @@ def cbeam3_loads(beam, ts): ct.byref(n_nodes), beam.fortran['connectivities'].ctypes.data_as(intP), beam.ini_info.pos.ctypes.data_as(doubleP), - beam.timestep_info[ts].pos.ctypes.data_as(doubleP), + timestep.pos.ctypes.data_as(doubleP), beam.ini_info.psi.ctypes.data_as(doubleP), - beam.timestep_info[ts].psi.ctypes.data_as(doubleP), + timestep.psi.ctypes.data_as(doubleP), beam.fortran['stiffness_indices'].ctypes.data_as(intP), ct.byref(n_stiff), beam.fortran['stiffness'].ctypes.data_as(doubleP), @@ -859,7 +868,6 @@ def cbeam3_asbly_dynamic(beam, tstep, settings): """ # library load - xbeamlib = ct_utils.import_ctypes_lib(xbeam_lib_path, 'libxbeam') f_cbeam3_asbly_dynamic_python = xbeamlib.cbeam3_asbly_dynamic_python f_cbeam3_asbly_dynamic_python.restype = None @@ -963,7 +971,6 @@ def xbeam3_asbly_dynamic(beam, tstep, settings): """ # library load - xbeamlib = ct_utils.import_ctypes_lib(xbeam_lib_path, 'libxbeam') f_xbeam3_asbly_dynamic_python = xbeamlib.xbeam3_asbly_dynamic_python f_xbeam3_asbly_dynamic_python.restype = None @@ -1056,7 +1063,6 @@ def cbeam3_correct_gravity_forces(beam, tstep, settings): """ # library load - xbeamlib = ct_utils.import_ctypes_lib(xbeam_lib_path, 'libxbeam') f_cbeam3_correct_gravity_forces_python = xbeamlib.cbeam3_correct_gravity_forces_python f_cbeam3_correct_gravity_forces_python.restype = None @@ -1110,7 +1116,6 @@ def cbeam3_asbly_static(beam, tstep, settings, iLoadStep): """ # library load - xbeamlib = ct_utils.import_ctypes_lib(xbeam_lib_path, 'libxbeam') f_cbeam3_asbly_static_python = xbeamlib.cbeam3_asbly_static_python f_cbeam3_asbly_static_python.restype = None diff --git a/sharpy/utils/cout_utils.py b/sharpy/utils/cout_utils.py index eaf24febb..3a14bcc4f 100644 --- a/sharpy/utils/cout_utils.py +++ b/sharpy/utils/cout_utils.py @@ -161,8 +161,9 @@ def __init__(self, n_fields=3, field_length=12, field_types=[['g']]*100, filenam if cout_wrap is None: start_writer() - if filename is not None: - self.file = open(filename, 'w') + # if filename is not None: + # self.file = open(filename, 'w') + self.file = filename def print_header(self, field_names): self.field_names = field_names @@ -194,9 +195,10 @@ def print_header(self, field_names): cout_wrap(divider_line) if self.file is not None: - self.file.write(divider_line) - self.file.write('\n' + string.format(*(self.field_names))) - self.file.write('\n' + divider_line) + with open(self.file, 'a+') as f: + f.write(divider_line) + f.write('\n' + string.format(*(self.field_names))) + f.write('\n' + divider_line) def print_line(self, line_data): string = '' @@ -214,18 +216,25 @@ def print_line(self, line_data): string += self.divider_char cout_wrap(string.format(line_data)) if self.file is not None: - self.file.write('\n'+string.format(line_data)) + with open(self.file, 'a') as f: + f.write('\n'+string.format(line_data)) def close_file(self): if self.file is not None: - try: - self.file.write('\n' + self.divider_line) - except ValueError: - pass - self.file.close() + with open(self.file, 'a+') as f: + f.write('\n' + self.divider_line) def print_divider_line(self): cout_wrap(self.divider_line) + if self.file is not None: + with open(self.file, 'a+') as f: + f.write('\n' + self.divider_line) + + def character_return(self, n_lines=1): + cout_wrap(n_lines * '\n') + if self.file is not None: + with open(self.file, 'a+') as f: + f.write(n_lines * '\n') # version tracker and output diff --git a/sharpy/utils/ctypes_utils.py b/sharpy/utils/ctypes_utils.py index fb4d1cb79..b8d3f18cc 100644 --- a/sharpy/utils/ctypes_utils.py +++ b/sharpy/utils/ctypes_utils.py @@ -4,7 +4,7 @@ def import_ctypes_lib(route, libname): - lib_path = route + libname + lib_path = os.path.join(route, libname) if platform.system() == 'Darwin': ext = '.dylib' elif platform.system() == 'Linux': @@ -14,11 +14,7 @@ def import_ctypes_lib(route, libname): lib_path += ext lib_path = os.path.abspath(lib_path) - try: - library = ct.CDLL(lib_path, mode=ct.RTLD_GLOBAL) - except: - import traceback - import sys - traceback.print_exc(file=sys.stderr) - sys.exit(1) + + library = ct.CDLL(lib_path, mode=ct.RTLD_GLOBAL) + return library diff --git a/sharpy/utils/datastructures.py b/sharpy/utils/datastructures.py index d3ee1ac6c..c3c5408fe 100644 --- a/sharpy/utils/datastructures.py +++ b/sharpy/utils/datastructures.py @@ -920,7 +920,7 @@ class Linear(object): as class attributes the following classes that describe the linearised problem. Attributes: - ss (sharpy.linear.src.libss.ss): State-space system + ss (sharpy.linear.src.libss.StateSpace): State-space system linear_system (sharpy.linear.utils.ss_interface.BaseElement): Assemble system properties tsaero0 (sharpy.utils.datastructures.AeroTimeStepInfo): Linearisation aerodynamic timestep tsstruct0 (sharpy.utils.datastructures.StructTimeStepInfo): Linearisation structural timestep diff --git a/sharpy/utils/docutils.py b/sharpy/utils/docutils.py index fe256ce73..f9f6e4894 100644 --- a/sharpy/utils/docutils.py +++ b/sharpy/utils/docutils.py @@ -67,6 +67,8 @@ def write_folder(folder, ignore_list): files, mtitle, mbody = open_folder(folder) for file in files: if os.path.isfile(file) and not check_folder_in_ignore(file, ignore_list): + if file[-3:] != '.py': + continue write_file(file) elif os.path.isdir(file) and not check_folder_in_ignore(file, ignore_list): mtitlesub, mbodysub = write_folder(file, ignore_list) diff --git a/sharpy/utils/frequencyutils.py b/sharpy/utils/frequencyutils.py index ad59929af..a69656bc7 100644 --- a/sharpy/utils/frequencyutils.py +++ b/sharpy/utils/frequencyutils.py @@ -216,7 +216,7 @@ def hamiltonian(gamma, ss): Args: gamma (float): Evaluation point. - ss (sharpy.linear.src.libss.ss): Linear system. + ss (sharpy.linear.src.libss.StateSpace): Linear system. Returns: np.ndarray: Hamiltonian evaluated at ``gamma``. @@ -255,7 +255,7 @@ def h_infinity_norm(ss, **kwargs): matrix. Systems and Control Letters, 14(4), 287–293. https://doi.org/10.1016/0167-6911(90)90049-Z Args: - ss (sharpy.linear.src.libss.ss): Multi input multi output system. + ss (sharpy.linear.src.libss.StateSpace): Multi input multi output system. **kwargs: Key-word arguments. Keyword Args: @@ -395,7 +395,7 @@ def find_target_system(data, target_system): target_system (str): Desired target system. Returns: - sharpy.linear.src.libss.ss: State-space object of target system + sharpy.linear.src.libss.StateSpace: State-space object of target system """ if target_system == 'aeroelastic': diff --git a/sharpy/utils/linearutils.py b/sharpy/utils/linearutils.py new file mode 100644 index 000000000..ec4ec7462 --- /dev/null +++ b/sharpy/utils/linearutils.py @@ -0,0 +1,110 @@ +"""Linear state-space vector manipulation utilities""" +import numpy as np +import sharpy.utils.algebra as algebra + + +def structural_vector_to_timestep(vector, tstruct, structure, phi=None, num_rig_dof=0, copy_tstep=True): + """Transform a state-space structural vector into a time step object + + This adds to a reference time step the following variables extracted from the vector: + + * ``pos`` and ``pos_dot`` + * ``psi`` and ``psi_dot`` + * ``quat`` + * ``for_pos`` and ``for_vel`` + + The rest of the structural time step variables are left unchanged + + Args: + vector(np.ndarray): Vector to plot + tstruct (sharpy.utils.datastructures.StructTimeStepInfo): Reference time step + structure (sharpy.structure.models.beam.Beam): Structural information class + phi (np.ndarray, optional): Eigenvector matrix to transform vector back to nodal coordinates + num_rig_dof (int): Number of rigid degrees of freedom + copy_tstep (bool): Return a copy of the reference time step. Else, modify the input one. + + Returns: + sharpy.utils.datastructures.StructTimeStepInfo: Time step with the aforementioned variables populated from + state-space vector. + """ + n_dof = vector.shape[0] + v = vector[:n_dof//2] + v_dot = vector[n_dof//2:] + if phi is not None: # modal coordinates + eta = phi.dot(v) + eta_dot = phi.dot(v_dot) + else: + eta = v + eta_dot = v_dot + + if num_rig_dof != 0: + eta = eta[:-num_rig_dof] + eta_dot = eta_dot[:-num_rig_dof] + beta = eta_dot[-num_rig_dof:] + beta_bar = np.zeros_like(beta) + else: + beta = np.array([]) + beta_bar = np.array([]) + + if copy_tstep: + tstep = tstruct.copy() + else: + tstep = tstruct + + vdof = structure.vdof + num_dof = 6*sum(vdof >= 0) + + q = np.zeros((num_dof + num_rig_dof)) + dqdt = np.zeros_like(q) + dqddt = np.zeros_like(q) + + pos = np.zeros_like(tstep.pos) + pos_dot = np.zeros_like(tstep.pos_dot) + + psi = np.zeros_like(tstep.psi) + psi_dot = np.zeros_like(tstep.psi_dot) + + for_pos = np.zeros_like(tstep.for_pos) + for_vel = np.zeros_like(tstep.for_vel) + for_acc = np.zeros_like(tstep.for_acc) + quat = np.zeros_like(tstep.quat) + + q[:num_dof + num_rig_dof] = np.concatenate((eta, beta_bar)) + dqdt[:num_dof + num_rig_dof] = np.concatenate((eta_dot, beta)) + + for i_node in vdof[vdof >= 0]: + pos[i_node + 1, :] = q[6*i_node: 6*i_node + 3] + pos_dot[i_node + 1, :] = dqdt[6*i_node + 0: 6*i_node + 3] + + for i_elem in range(tstep.num_elem): + for i_node in range(tstep.num_node_elem): + psi[i_elem, i_node, :] = np.linalg.inv(algebra.crv2tan(tstep.psi[i_elem, i_node]).T).dot(q[i_node + 3: i_node + 6]) + psi_dot[i_elem, i_node, :] = dqdt[i_node + 3: i_node + 6] + + if num_rig_dof != 0: # beam is clamped + for_vel = beta[:6] + if num_rig_dof == 9: + quat = algebra.euler2quat(beta[-3:]) + elif num_rig_dof == 10: + quat = beta[-4:] + else: + raise NotImplementedError('Structural vector to timestep for cases without 9 or 10 rigid' + 'degrees of freedom not yet supported.') + + tstep.q[:len(q)] += q + tstep.dqdt[:len(q)] += dqdt + + tstep.pos += pos + tstep.pos_dot += pos_dot + + tstep.psi += psi + tstep.psi_dot += psi_dot + + tstep.for_pos += for_pos + tstep.for_vel += for_vel + + tstep.quat += quat + tstep.quat /= np.linalg.norm(tstep.quat) # normalise quaternion + + return tstep + diff --git a/sharpy/utils/settings.py b/sharpy/utils/settings.py index 50955d2d7..140a1c494 100644 --- a/sharpy/utils/settings.py +++ b/sharpy/utils/settings.py @@ -171,7 +171,7 @@ def to_custom_types(dictionary, types, default, options=dict(), no_ctype=True): continue if isinstance(dictionary[k], list): for i in range(len(dictionary[k])): - dictionary[k][i] = float(dictionary[k][i]) + dictionary[k][i] = complex(dictionary[k][i]) dictionary[k] = np.array(dictionary[k]) continue # dictionary[k] = dictionary[k].split(',') diff --git a/tests/coupled/dynamic/__init__.py b/tests/coupled/dynamic/__init__.py index 9e368c66b..e69de29bb 100644 --- a/tests/coupled/dynamic/__init__.py +++ b/tests/coupled/dynamic/__init__.py @@ -1 +0,0 @@ -from tests.coupled.dynamic.dynamic_test import * diff --git a/tests/coupled/dynamic/dynamic_test.py b/tests/coupled/dynamic/dynamic_test.py deleted file mode 100644 index d2e266624..000000000 --- a/tests/coupled/dynamic/dynamic_test.py +++ /dev/null @@ -1,24 +0,0 @@ -import numpy as np -import importlib -import unittest -import os -import sharpy.utils.cout_utils as cout - - -class TestCoupledDynamic(unittest.TestCase): - """ - Placeholder for dynamic coupled tests - """ - - @classmethod - def setUpClass(cls): - # run all the cases generators - pass - - @classmethod - def tearDownClass(cls): - pass - - def test_coupled_dynamic1(self): - cout.cout_wrap('No tests for dynamic coupled simulations (yet)!', 1) - pass diff --git a/tests/coupled/dynamic/hale/generate_hale.py b/tests/coupled/dynamic/hale/generate_hale.py new file mode 100644 index 000000000..b7399f273 --- /dev/null +++ b/tests/coupled/dynamic/hale/generate_hale.py @@ -0,0 +1,708 @@ +#! /usr/bin/env python3 +import h5py as h5 +import numpy as np +import os +import sharpy.utils.algebra as algebra + +case_name = 'hale' +route = os.path.dirname(os.path.realpath(__file__)) + '/' + +# EXECUTION +flow = ['BeamLoader', + 'AerogridLoader', + 'StaticTrim', + 'DynamicCoupled', + 'BeamLoads' + ] + +# if free_flight is False, the motion of the centre of the wing is prescribed. +free_flight = True +if not free_flight: + case_name += '_prescribed' + amplitude = 0 * np.pi / 180 + period = 3 + case_name += '_amp_' + str(amplitude).replace('.', '') + '_period_' + str(period) + +# FLIGHT CONDITIONS +# the simulation is set such that the aircraft flies at a u_inf velocity while +# the air is calm. +u_inf = 10 +rho = 1.225 + +# trim sigma = 1.5 +alpha = 4.31 * np.pi / 180 +beta = 0 +roll = 0 +gravity = 'on' +cs_deflection = -2.08 * np.pi / 180 +rudder_static_deflection = 0.0 +rudder_step = 0.0 * np.pi / 180 +thrust = 6.16 +sigma = 1.5 +lambda_dihedral = 20 * np.pi / 180 + +# gust settings +gust_intensity = 0.20 +gust_length = 1 * u_inf +gust_offset = 0.2 * u_inf + +# numerics +n_step = 5 +structural_relaxation_factor = 0.6 +relaxation_factor = 0.35 +tolerance = 1e-6 +fsi_tolerance = 1e-4 + +num_cores = 2 + +# MODEL GEOMETRY +# beam +span_main = 16.0 +lambda_main = 0.25 +ea_main = 0.3 + +ea = 1e7 +ga = 1e5 +gj = 1e4 +eiy = 2e4 +eiz = 4e6 +m_bar_main = 0.75 +j_bar_main = 0.075 + +length_fuselage = 10 +offset_fuselage = 0 +sigma_fuselage = 10 +m_bar_fuselage = 0.2 +j_bar_fuselage = 0.08 + +span_tail = 2.5 +ea_tail = 0.5 +fin_height = 2.5 +ea_fin = 0.5 +sigma_tail = 100 +m_bar_tail = 0.3 +j_bar_tail = 0.08 + +# lumped masses +n_lumped_mass = 1 +lumped_mass_nodes = np.zeros((n_lumped_mass,), dtype=int) +lumped_mass = np.zeros((n_lumped_mass,)) +lumped_mass[0] = 50 +lumped_mass_inertia = np.zeros((n_lumped_mass, 3, 3)) +lumped_mass_position = np.zeros((n_lumped_mass, 3)) + +# aero +chord_main = 1.0 +chord_tail = 0.5 +chord_fin = 0.5 + +# DISCRETISATION +# spatial discretisation +# chordiwse panels +m = 4 +# spanwise elements +n_elem_multiplier = 2 +n_elem_main = int(4 * n_elem_multiplier) +n_elem_tail = int(2 * n_elem_multiplier) +n_elem_fin = int(2 * n_elem_multiplier) +n_elem_fuselage = int(2 * n_elem_multiplier) +n_surfaces = 5 + +# temporal discretisation +physical_time = 30 +tstep_factor = 1. +dt = 1.0 / m / u_inf * tstep_factor +n_tstep = 20 + +# END OF INPUT----------------------------------------------------------------- + +# beam processing +n_node_elem = 3 +span_main1 = (1.0 - lambda_main) * span_main +span_main2 = lambda_main * span_main + +n_elem_main1 = round(n_elem_main * (1 - lambda_main)) +n_elem_main2 = n_elem_main - n_elem_main1 + +# total number of elements +n_elem = 0 +n_elem += n_elem_main1 + n_elem_main1 +n_elem += n_elem_main2 + n_elem_main2 +n_elem += n_elem_fuselage +n_elem += n_elem_fin +n_elem += n_elem_tail + n_elem_tail + +# number of nodes per part +n_node_main1 = n_elem_main1 * (n_node_elem - 1) + 1 +n_node_main2 = n_elem_main2 * (n_node_elem - 1) + 1 +n_node_main = n_node_main1 + n_node_main2 - 1 +n_node_fuselage = n_elem_fuselage * (n_node_elem - 1) + 1 +n_node_fin = n_elem_fin * (n_node_elem - 1) + 1 +n_node_tail = n_elem_tail * (n_node_elem - 1) + 1 + +# total number of nodes +n_node = 0 +n_node += n_node_main1 + n_node_main1 - 1 +n_node += n_node_main2 - 1 + n_node_main2 - 1 +n_node += n_node_fuselage - 1 +n_node += n_node_fin - 1 +n_node += n_node_tail - 1 +n_node += n_node_tail - 1 + +# stiffness and mass matrices +n_stiffness = 3 +base_stiffness_main = sigma * np.diag([ea, ga, ga, gj, eiy, eiz]) +base_stiffness_fuselage = base_stiffness_main.copy() * sigma_fuselage +base_stiffness_fuselage[4, 4] = base_stiffness_fuselage[5, 5] +base_stiffness_tail = base_stiffness_main.copy() * sigma_tail +base_stiffness_tail[4, 4] = base_stiffness_tail[5, 5] + +n_mass = 3 +base_mass_main = np.diag([m_bar_main, m_bar_main, m_bar_main, j_bar_main, 0.5 * j_bar_main, 0.5 * j_bar_main]) +base_mass_fuselage = np.diag([m_bar_fuselage, + m_bar_fuselage, + m_bar_fuselage, + j_bar_fuselage, + j_bar_fuselage * 0.5, + j_bar_fuselage * 0.5]) +base_mass_tail = np.diag([m_bar_tail, + m_bar_tail, + m_bar_tail, + j_bar_tail, + j_bar_tail * 0.5, + j_bar_tail * 0.5]) + +# PLACEHOLDERS +# beam +x = np.zeros((n_node,)) +y = np.zeros((n_node,)) +z = np.zeros((n_node,)) +beam_number = np.zeros((n_elem,), dtype=int) +frame_of_reference_delta = np.zeros((n_elem, n_node_elem, 3)) +structural_twist = np.zeros((n_elem, 3)) +conn = np.zeros((n_elem, n_node_elem), dtype=int) +stiffness = np.zeros((n_stiffness, 6, 6)) +elem_stiffness = np.zeros((n_elem,), dtype=int) +mass = np.zeros((n_mass, 6, 6)) +elem_mass = np.zeros((n_elem,), dtype=int) +boundary_conditions = np.zeros((n_node,), dtype=int) +app_forces = np.zeros((n_node, 6)) + +# aero +airfoil_distribution = np.zeros((n_elem, n_node_elem), dtype=int) +surface_distribution = np.zeros((n_elem,), dtype=int) - 1 +surface_m = np.zeros((n_surfaces,), dtype=int) +m_distribution = 'uniform' +aero_node = np.zeros((n_node,), dtype=bool) +twist = np.zeros((n_elem, n_node_elem)) +sweep = np.zeros((n_elem, n_node_elem)) +chord = np.zeros((n_elem, n_node_elem,)) +elastic_axis = np.zeros((n_elem, n_node_elem,)) + + +# FUNCTIONS------------------------------------------------------------- +def clean_test_files(): + fem_file_name = route + '/' + case_name + '.fem.h5' + if os.path.isfile(fem_file_name): + os.remove(fem_file_name) + + dyn_file_name = route + '/' + case_name + '.dyn.h5' + if os.path.isfile(dyn_file_name): + os.remove(dyn_file_name) + + aero_file_name = route + '/' + case_name + '.aero.h5' + if os.path.isfile(aero_file_name): + os.remove(aero_file_name) + + solver_file_name = route + '/' + case_name + '.sharpy' + if os.path.isfile(solver_file_name): + os.remove(solver_file_name) + + flightcon_file_name = route + '/' + case_name + '.flightcon.txt' + if os.path.isfile(flightcon_file_name): + os.remove(flightcon_file_name) + +def generate_fem(): + stiffness[0, ...] = base_stiffness_main + stiffness[1, ...] = base_stiffness_fuselage + stiffness[2, ...] = base_stiffness_tail + + mass[0, ...] = base_mass_main + mass[1, ...] = base_mass_fuselage + mass[2, ...] = base_mass_tail + + we = 0 + wn = 0 + # inner right wing + beam_number[we:we + n_elem_main1] = 0 + y[wn:wn + n_node_main1] = np.linspace(0.0, span_main1, n_node_main1) + + for ielem in range(n_elem_main1): + conn[we + ielem, :] = ((np.ones((3,)) * (we + ielem) * (n_node_elem - 1)) + + [0, 2, 1]) + for inode in range(n_node_elem): + frame_of_reference_delta[we + ielem, inode, :] = [-1.0, 0.0, 0.0] + + elem_stiffness[we:we + n_elem_main1] = 0 + elem_mass[we:we + n_elem_main1] = 0 + boundary_conditions[0] = 1 + # remember this is in B FoR + app_forces[0] = [0, thrust, 0, 0, 0, 0] + we += n_elem_main1 + wn += n_node_main1 + + # outer right wing + beam_number[we:we + n_elem_main1] = 0 + y[wn:wn + n_node_main2 - 1] = y[wn - 1] + np.linspace(0.0, np.cos(lambda_dihedral) * span_main2, n_node_main2)[1:] + z[wn:wn + n_node_main2 - 1] = z[wn - 1] + np.linspace(0.0, np.sin(lambda_dihedral) * span_main2, n_node_main2)[1:] + for ielem in range(n_elem_main2): + conn[we + ielem, :] = ((np.ones((3,)) * (we + ielem) * (n_node_elem - 1)) + + [0, 2, 1]) + for inode in range(n_node_elem): + frame_of_reference_delta[we + ielem, inode, :] = [-1.0, 0.0, 0.0] + elem_stiffness[we:we + n_elem_main2] = 0 + elem_mass[we:we + n_elem_main2] = 0 + boundary_conditions[wn + n_node_main2 - 2] = -1 + we += n_elem_main2 + wn += n_node_main2 - 1 + + # inner left wing + beam_number[we:we + n_elem_main1 - 1] = 1 + y[wn:wn + n_node_main1 - 1] = np.linspace(0.0, -span_main1, n_node_main1)[1:] + for ielem in range(n_elem_main1): + conn[we + ielem, :] = ((np.ones((3,)) * (we + ielem) * (n_node_elem - 1)) + + [0, 2, 1]) + for inode in range(n_node_elem): + frame_of_reference_delta[we + ielem, inode, :] = [1.0, 0.0, 0.0] + conn[we, 0] = 0 + elem_stiffness[we:we + n_elem_main1] = 0 + elem_mass[we:we + n_elem_main1] = 0 + we += n_elem_main1 + wn += n_node_main1 - 1 + + # outer left wing + beam_number[we:we + n_elem_main2] = 1 + y[wn:wn + n_node_main2 - 1] = y[wn - 1] + np.linspace(0.0, -np.cos(lambda_dihedral) * span_main2, n_node_main2)[1:] + z[wn:wn + n_node_main2 - 1] = z[wn - 1] + np.linspace(0.0, np.sin(lambda_dihedral) * span_main2, n_node_main2)[1:] + for ielem in range(n_elem_main2): + conn[we + ielem, :] = ((np.ones((3,)) * (we + ielem) * (n_node_elem - 1)) + + [0, 2, 1]) + for inode in range(n_node_elem): + frame_of_reference_delta[we + ielem, inode, :] = [1.0, 0.0, 0.0] + elem_stiffness[we:we + n_elem_main2] = 0 + elem_mass[we:we + n_elem_main2] = 0 + boundary_conditions[wn + n_node_main2 - 2] = -1 + we += n_elem_main2 + wn += n_node_main2 - 1 + + # fuselage + beam_number[we:we + n_elem_fuselage] = 2 + x[wn:wn + n_node_fuselage - 1] = np.linspace(0.0, length_fuselage, n_node_fuselage)[1:] + z[wn:wn + n_node_fuselage - 1] = np.linspace(0.0, offset_fuselage, n_node_fuselage)[1:] + for ielem in range(n_elem_fuselage): + conn[we + ielem, :] = ((np.ones((3,)) * (we + ielem) * (n_node_elem - 1)) + + [0, 2, 1]) + for inode in range(n_node_elem): + frame_of_reference_delta[we + ielem, inode, :] = [0.0, 1.0, 0.0] + conn[we, 0] = 0 + elem_stiffness[we:we + n_elem_fuselage] = 1 + elem_mass[we:we + n_elem_fuselage] = 1 + we += n_elem_fuselage + wn += n_node_fuselage - 1 + global end_of_fuselage_node + end_of_fuselage_node = wn - 1 + + # fin + beam_number[we:we + n_elem_fin] = 3 + x[wn:wn + n_node_fin - 1] = x[end_of_fuselage_node] + z[wn:wn + n_node_fin - 1] = z[end_of_fuselage_node] + np.linspace(0.0, fin_height, n_node_fin)[1:] + for ielem in range(n_elem_fin): + conn[we + ielem, :] = ((np.ones((3,)) * (we + ielem) * (n_node_elem - 1)) + + [0, 2, 1]) + for inode in range(n_node_elem): + frame_of_reference_delta[we + ielem, inode, :] = [-1.0, 0.0, 0.0] + conn[we, 0] = end_of_fuselage_node + elem_stiffness[we:we + n_elem_fin] = 2 + elem_mass[we:we + n_elem_fin] = 2 + we += n_elem_fin + wn += n_node_fin - 1 + end_of_fin_node = wn - 1 + + # right tail + beam_number[we:we + n_elem_tail] = 4 + x[wn:wn + n_node_tail - 1] = x[end_of_fin_node] + y[wn:wn + n_node_tail - 1] = np.linspace(0.0, span_tail, n_node_tail)[1:] + z[wn:wn + n_node_tail - 1] = z[end_of_fin_node] + for ielem in range(n_elem_tail): + conn[we + ielem, :] = ((np.ones((3,)) * (we + ielem) * (n_node_elem - 1)) + + [0, 2, 1]) + for inode in range(n_node_elem): + frame_of_reference_delta[we + ielem, inode, :] = [-1.0, 0.0, 0.0] + conn[we, 0] = end_of_fin_node + elem_stiffness[we:we + n_elem_tail] = 2 + elem_mass[we:we + n_elem_tail] = 2 + boundary_conditions[wn + n_node_tail - 2] = -1 + we += n_elem_tail + wn += n_node_tail - 1 + + # left tail + beam_number[we:we + n_elem_tail] = 5 + x[wn:wn + n_node_tail - 1] = x[end_of_fin_node] + y[wn:wn + n_node_tail - 1] = np.linspace(0.0, -span_tail, n_node_tail)[1:] + z[wn:wn + n_node_tail - 1] = z[end_of_fin_node] + for ielem in range(n_elem_tail): + conn[we + ielem, :] = ((np.ones((3,)) * (we + ielem) * (n_node_elem - 1)) + + [0, 2, 1]) + for inode in range(n_node_elem): + frame_of_reference_delta[we + ielem, inode, :] = [1.0, 0.0, 0.0] + conn[we, 0] = end_of_fin_node + elem_stiffness[we:we + n_elem_tail] = 2 + elem_mass[we:we + n_elem_tail] = 2 + boundary_conditions[wn + n_node_tail - 2] = -1 + we += n_elem_tail + wn += n_node_tail - 1 + + with h5.File(route + '/' + case_name + '.fem.h5', 'a') as h5file: + coordinates = h5file.create_dataset('coordinates', data=np.column_stack((x, y, z))) + conectivities = h5file.create_dataset('connectivities', data=conn) + num_nodes_elem_handle = h5file.create_dataset( + 'num_node_elem', data=n_node_elem) + num_nodes_handle = h5file.create_dataset( + 'num_node', data=n_node) + num_elem_handle = h5file.create_dataset( + 'num_elem', data=n_elem) + stiffness_db_handle = h5file.create_dataset( + 'stiffness_db', data=stiffness) + stiffness_handle = h5file.create_dataset( + 'elem_stiffness', data=elem_stiffness) + mass_db_handle = h5file.create_dataset( + 'mass_db', data=mass) + mass_handle = h5file.create_dataset( + 'elem_mass', data=elem_mass) + frame_of_reference_delta_handle = h5file.create_dataset( + 'frame_of_reference_delta', data=frame_of_reference_delta) + structural_twist_handle = h5file.create_dataset( + 'structural_twist', data=structural_twist) + bocos_handle = h5file.create_dataset( + 'boundary_conditions', data=boundary_conditions) + beam_handle = h5file.create_dataset( + 'beam_number', data=beam_number) + app_forces_handle = h5file.create_dataset( + 'app_forces', data=app_forces) + lumped_mass_nodes_handle = h5file.create_dataset( + 'lumped_mass_nodes', data=lumped_mass_nodes) + lumped_mass_handle = h5file.create_dataset( + 'lumped_mass', data=lumped_mass) + lumped_mass_inertia_handle = h5file.create_dataset( + 'lumped_mass_inertia', data=lumped_mass_inertia) + lumped_mass_position_handle = h5file.create_dataset( + 'lumped_mass_position', data=lumped_mass_position) + + +def generate_aero_file(): + global x, y, z + # control surfaces + n_control_surfaces = 2 + control_surface = np.zeros((n_elem, n_node_elem), dtype=int) - 1 + control_surface_type = np.zeros((n_control_surfaces,), dtype=int) + control_surface_deflection = np.zeros((n_control_surfaces,)) + control_surface_chord = np.zeros((n_control_surfaces,), dtype=int) + control_surface_hinge_coord = np.zeros((n_control_surfaces,), dtype=float) + + # control surface type 0 = static + # control surface type 1 = dynamic + control_surface_type[0] = 0 + control_surface_deflection[0] = cs_deflection + control_surface_chord[0] = m + control_surface_hinge_coord[0] = -0.25 # nondimensional wrt elastic axis (+ towards the trailing edge) + + control_surface_type[1] = 0 + control_surface_deflection[1] = rudder_static_deflection + control_surface_chord[1] = 1 + control_surface_hinge_coord[1] = -0. # nondimensional wrt elastic axis (+ towards the trailing edge) + + we = 0 + wn = 0 + # right wing (surface 0, beam 0) + i_surf = 0 + airfoil_distribution[we:we + n_elem_main, :] = 0 + surface_distribution[we:we + n_elem_main] = i_surf + surface_m[i_surf] = m + aero_node[wn:wn + n_node_main] = True + temp_chord = np.linspace(chord_main, chord_main, n_node_main) + temp_sweep = np.linspace(0.0, 0 * np.pi / 180, n_node_main) + node_counter = 0 + for i_elem in range(we, we + n_elem_main): + for i_local_node in range(n_node_elem): + if not i_local_node == 0: + node_counter += 1 + chord[i_elem, i_local_node] = temp_chord[node_counter] + elastic_axis[i_elem, i_local_node] = ea_main + sweep[i_elem, i_local_node] = temp_sweep[node_counter] + + we += n_elem_main + wn += n_node_main + + # left wing (surface 1, beam 1) + i_surf = 1 + airfoil_distribution[we:we + n_elem_main, :] = 0 + # airfoil_distribution[wn:wn + n_node_main - 1] = 0 + surface_distribution[we:we + n_elem_main] = i_surf + surface_m[i_surf] = m + aero_node[wn:wn + n_node_main - 1] = True + # chord[wn:wn + num_node_main - 1] = np.linspace(main_chord, main_tip_chord, num_node_main)[1:] + # chord[wn:wn + num_node_main - 1] = main_chord + # elastic_axis[wn:wn + num_node_main - 1] = main_ea + temp_chord = np.linspace(chord_main, chord_main, n_node_main) + node_counter = 0 + for i_elem in range(we, we + n_elem_main): + for i_local_node in range(n_node_elem): + if not i_local_node == 0: + node_counter += 1 + chord[i_elem, i_local_node] = temp_chord[node_counter] + elastic_axis[i_elem, i_local_node] = ea_main + sweep[i_elem, i_local_node] = -temp_sweep[node_counter] + + we += n_elem_main + wn += n_node_main - 1 + + we += n_elem_fuselage + wn += n_node_fuselage - 1 - 1 + # + # # fin (surface 2, beam 3) + i_surf = 2 + airfoil_distribution[we:we + n_elem_fin, :] = 1 + # airfoil_distribution[wn:wn + n_node_fin] = 0 + surface_distribution[we:we + n_elem_fin] = i_surf + surface_m[i_surf] = m + aero_node[wn:wn + n_node_fin] = True + # chord[wn:wn + num_node_fin] = fin_chord + for i_elem in range(we, we + n_elem_fin): + for i_local_node in range(n_node_elem): + chord[i_elem, i_local_node] = chord_fin + elastic_axis[i_elem, i_local_node] = ea_fin + control_surface[i_elem, i_local_node] = 1 + # twist[end_of_fuselage_node] = 0 + # twist[wn:] = 0 + # elastic_axis[wn:wn + num_node_main] = fin_ea + we += n_elem_fin + wn += n_node_fin - 1 + # + # # # right tail (surface 3, beam 4) + i_surf = 3 + airfoil_distribution[we:we + n_elem_tail, :] = 2 + # airfoil_distribution[wn:wn + n_node_tail] = 0 + surface_distribution[we:we + n_elem_tail] = i_surf + surface_m[i_surf] = m + # XXX not very elegant + aero_node[wn:] = True + # chord[wn:wn + num_node_tail] = tail_chord + # elastic_axis[wn:wn + num_node_main] = tail_ea + for i_elem in range(we, we + n_elem_tail): + for i_local_node in range(n_node_elem): + twist[i_elem, i_local_node] = -0 + for i_elem in range(we, we + n_elem_tail): + for i_local_node in range(n_node_elem): + chord[i_elem, i_local_node] = chord_tail + elastic_axis[i_elem, i_local_node] = ea_tail + control_surface[i_elem, i_local_node] = 0 + + we += n_elem_tail + wn += n_node_tail + # + # # left tail (surface 4, beam 5) + i_surf = 4 + airfoil_distribution[we:we + n_elem_tail, :] = 2 + # airfoil_distribution[wn:wn + n_node_tail - 1] = 0 + surface_distribution[we:we + n_elem_tail] = i_surf + surface_m[i_surf] = m + aero_node[wn:wn + n_node_tail - 1] = True + # chord[wn:wn + num_node_tail] = tail_chord + # elastic_axis[wn:wn + num_node_main] = tail_ea + # twist[we:we + num_elem_tail] = -tail_twist + for i_elem in range(we, we + n_elem_tail): + for i_local_node in range(n_node_elem): + twist[i_elem, i_local_node] = -0 + for i_elem in range(we, we + n_elem_tail): + for i_local_node in range(n_node_elem): + chord[i_elem, i_local_node] = chord_tail + elastic_axis[i_elem, i_local_node] = ea_tail + control_surface[i_elem, i_local_node] = 0 + we += n_elem_tail + wn += n_node_tail + + with h5.File(route + '/' + case_name + '.aero.h5', 'a') as h5file: + airfoils_group = h5file.create_group('airfoils') + # add one airfoil + naca_airfoil_main = airfoils_group.create_dataset('0', data=np.column_stack( + generate_naca_camber(P=0, M=0))) + naca_airfoil_tail = airfoils_group.create_dataset('1', data=np.column_stack( + generate_naca_camber(P=0, M=0))) + naca_airfoil_fin = airfoils_group.create_dataset('2', data=np.column_stack( + generate_naca_camber(P=0, M=0))) + + # chord + chord_input = h5file.create_dataset('chord', data=chord) + dim_attr = chord_input.attrs['units'] = 'm' + + # twist + twist_input = h5file.create_dataset('twist', data=twist) + dim_attr = twist_input.attrs['units'] = 'rad' + + # sweep + sweep_input = h5file.create_dataset('sweep', data=sweep) + dim_attr = sweep_input.attrs['units'] = 'rad' + + # airfoil distribution + airfoil_distribution_input = h5file.create_dataset('airfoil_distribution', data=airfoil_distribution) + + surface_distribution_input = h5file.create_dataset('surface_distribution', data=surface_distribution) + surface_m_input = h5file.create_dataset('surface_m', data=surface_m) + m_distribution_input = h5file.create_dataset('m_distribution', data=m_distribution.encode('ascii', 'ignore')) + + aero_node_input = h5file.create_dataset('aero_node', data=aero_node) + elastic_axis_input = h5file.create_dataset('elastic_axis', data=elastic_axis) + + control_surface_input = h5file.create_dataset('control_surface', data=control_surface) + control_surface_deflection_input = h5file.create_dataset('control_surface_deflection', + data=control_surface_deflection) + control_surface_chord_input = h5file.create_dataset('control_surface_chord', data=control_surface_chord) + control_surface_hinge_coord_input = h5file.create_dataset('control_surface_hinge_coord', + data=control_surface_hinge_coord) + control_surface_types_input = h5file.create_dataset('control_surface_type', data=control_surface_type) + + +def generate_naca_camber(M=0, P=0): + mm = M * 1e-2 + p = P * 1e-1 + + def naca(x, mm, p): + if x < 1e-6: + return 0.0 + elif x < p: + return mm / (p * p) * (2 * p * x - x * x) + elif x > p and x < 1 + 1e-6: + return mm / ((1 - p) * (1 - p)) * (1 - 2 * p + 2 * p * x - x * x) + + x_vec = np.linspace(0, 1, 1000) + y_vec = np.array([naca(x, mm, p) for x in x_vec]) + return x_vec, y_vec + + +def generate_solver_file(): + file_name = route + '/' + case_name + '.sharpy' + settings = dict() + settings['SHARPy'] = {'case': case_name, + 'route': route, + 'flow': flow, + 'write_screen': 'off', + 'write_log': 'off', + 'log_folder': route + '/output/', + 'log_file': case_name + '.log'} + + settings['NonLinearStatic'] = {'print_info': 'off', + 'max_iterations': 150, + 'num_load_steps': 1, + 'delta_curved': 1e-1, + 'min_delta': tolerance, + 'gravity_on': gravity, + 'gravity': 9.81} + + settings['StaticUvlm'] = {'print_info': 'on', + 'horseshoe': 'off', + 'num_cores': num_cores, + 'n_rollup': 0, + 'rollup_dt': dt, + 'rollup_aic_refresh': 1, + 'rollup_tolerance': 1e-4, + 'velocity_field_generator': 'SteadyVelocityField', + 'velocity_field_input': {'u_inf': u_inf, + 'u_inf_direction': [1., 0, 0]}, + 'rho': rho} + + settings['StaticCoupled'] = {'print_info': 'off', + 'structural_solver': 'NonLinearStatic', + 'structural_solver_settings': settings['NonLinearStatic'], + 'aero_solver': 'StaticUvlm', + 'aero_solver_settings': settings['StaticUvlm'], + 'max_iter': 100, + 'n_load_steps': n_step, + 'tolerance': fsi_tolerance, + 'relaxation_factor': structural_relaxation_factor} + + settings['StaticTrim'] = {'solver': 'StaticCoupled', + 'solver_settings': settings['StaticCoupled'], + 'initial_alpha': alpha, + 'initial_deflection': cs_deflection, + 'initial_thrust': thrust} + + settings['NonLinearDynamicCoupledStep'] = {'print_info': 'off', + 'max_iterations': 950, + 'delta_curved': 1e-1, + 'min_delta': tolerance, + 'newmark_damp': 5e-3, + 'gravity_on': gravity, + 'gravity': 9.81, + 'num_steps': n_tstep, + 'dt': dt, + 'initial_velocity': u_inf} + + relative_motion = 'off' + settings['StepUvlm'] = {'print_info': 'off', + 'num_cores': num_cores, + 'convection_scheme': 2, + 'gamma_dot_filtering': 7, + 'velocity_field_generator': 'GustVelocityField', + 'velocity_field_input': {'u_inf': int(not free_flight) * u_inf, + 'u_inf_direction': [1., 0, 0], + 'gust_shape': '1-cos', + 'gust_parameters': {'gust_length': gust_length, + 'gust_intensity': gust_intensity * u_inf}, + 'offset': gust_offset, + 'relative_motion': relative_motion}, + 'rho': rho, + 'n_time_steps': n_tstep, + 'dt': dt} + settings['BeamLoads'] = {'csv_output': True} + solver = 'NonLinearDynamicCoupledStep' + settings['DynamicCoupled'] = {'structural_solver': solver, + 'structural_solver_settings': settings[solver], + 'aero_solver': 'StepUvlm', + 'aero_solver_settings': settings['StepUvlm'], + 'fsi_substeps': 200, + 'fsi_tolerance': fsi_tolerance, + 'relaxation_factor': relaxation_factor, + 'minimum_steps': 1, + 'relaxation_steps': 150, + 'final_relaxation_factor': 0.5, + 'n_time_steps': n_tstep, + 'dt': dt, + 'include_unsteady_force_contribution': 'on',} + + settings['BeamLoader'] = {'unsteady': 'on', + 'orientation': algebra.euler2quat(np.array([roll, + alpha, + beta]))} + + settings['AerogridLoader'] = {'unsteady': 'on', + 'aligned_grid': 'on', + 'mstar': int(20 / tstep_factor), + 'freestream_dir': ['1', '0', '0'], + 'wake_shape_generator': 'StraightWake', + 'wake_shape_generator_input': {'u_inf': u_inf, + 'u_inf_direction': ['1', '0', '0'], + 'dt': dt}} + + + import configobj + config = configobj.ConfigObj() + config.filename = file_name + for k, v in settings.items(): + config[k] = v + config.write() + + +clean_test_files() +generate_fem() +generate_aero_file() +generate_solver_file() diff --git a/tests/coupled/dynamic/test_dynamic.py b/tests/coupled/dynamic/test_dynamic.py new file mode 100644 index 000000000..99cbca807 --- /dev/null +++ b/tests/coupled/dynamic/test_dynamic.py @@ -0,0 +1,68 @@ +import numpy as np +import importlib +import unittest +import os +import sharpy.utils.cout_utils as cout + + +class TestCoupledDynamic(unittest.TestCase): + """ + Tests for dynamic coupled problems to identify errors in the unsteady solvers. + Implemented tests: + - Gust response of the hale aircraft + """ + + @classmethod + def setUpClass(cls): + # run all the cases generators + case = 'hale' + mod = importlib.import_module('tests.coupled.dynamic.' + case + '.generate_' + case) + pass + + def test_hale_dynamic(self): + """ + Case and results from: + tests/coupled/dynamic/hale + reference results produced with SHARPy version 1.3 + :return: + """ + import sharpy.sharpy_main + + case_name = 'hale' + route_test_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + cases_folder = os.path.join(route_test_dir, case_name) + output_folder = cases_folder + '/output/' + + sharpy.sharpy_main.main(['', cases_folder + '/hale.sharpy']) + n_tstep = 20 + + # compare results with reference values + ref_Fz = 50.4986064826483 + ref_My = -1833.91402522644 + file = os.path.join(output_folder, case_name, 'beam/beam_loads_%i.csv' % (n_tstep)) + beam_loads_ts = np.loadtxt(file, delimiter=',') + np.testing.assert_almost_equal(float(beam_loads_ts[0, 6]), ref_Fz, + decimal=3, + err_msg='Vertical load on wing root not within 3 decimal points of reference.', + verbose=True) + np.testing.assert_almost_equal(float(beam_loads_ts[0, 8]), ref_My, + decimal=3, + err_msg='Pitching moment on wing root not within 3 decimal points of reference.', + verbose=True) + + @classmethod + def tearDownClass(cls): + + import shutil + list_cases = ['hale'] + list_file_extensions = ['.fem.h5', '.aero.h5', '.sharpy'] + list_folders = ['output', '__pycache__'] + for case in list_cases: + file_path = os.path.join(os.path.abspath(os.path.dirname(os.path.realpath(__file__))), + case) + for folder in list_folders: + if os.path.isdir(folder): + shutil.rmtree(folder) + for extension in list_file_extensions: + os.remove(os.path.join(file_path, case + extension)) + pass diff --git a/tests/coupled/prescribed/WindTurbine/test_rotor.py b/tests/coupled/prescribed/WindTurbine/test_rotor.py index d5c675453..2d29faf76 100644 --- a/tests/coupled/prescribed/WindTurbine/test_rotor.py +++ b/tests/coupled/prescribed/WindTurbine/test_rotor.py @@ -18,7 +18,7 @@ class TestRotor(unittest.TestCase): def setUp(self): import sharpy.utils.generate_cases as gc - import cases.templates.template_wt as template_wt + import sharpy.cases.templates.template_wt as template_wt from sharpy.utils.constants import deg2rad ###################################################################### @@ -100,7 +100,7 @@ def setUp(self): SimInfo.solvers['BeamLoader']['unsteady'] = 'on' SimInfo.solvers['Modal']['write_modes_vtk'] = False - SimInfo.solvers['Modal']['write_dat'] = True + SimInfo.solvers['Modal']['save_data'] = True ###################################################################### ####################### GENERATE FILES ############################# diff --git a/tests/coupled/prescribed/prescribed_test.py b/tests/coupled/prescribed/test_prescribed.py similarity index 92% rename from tests/coupled/prescribed/prescribed_test.py rename to tests/coupled/prescribed/test_prescribed.py index f707013f0..0ab740f0e 100644 --- a/tests/coupled/prescribed/prescribed_test.py +++ b/tests/coupled/prescribed/test_prescribed.py @@ -8,6 +8,7 @@ import sharpy.utils.cout_utils as cout +@unittest.skip('Placeholder - No test yet to run') class TestCoupledPrescribed(unittest.TestCase): """ """ @@ -41,5 +42,5 @@ def test_rotating_wing(self): # solver_path = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + # '/rotating_wing/rotating_wing.sharpy') # sharpy.sharpy_main.main(['', solver_path]) - cout.cout_wrap('No tests for prescribed dynamic configurations (yet)!', 1) + print('No tests for prescribed dynamic configurations (yet)!', 1) pass diff --git a/tests/coupled/static/pazy/generate_pazy.py b/tests/coupled/static/pazy/generate_pazy.py index 1105dede4..f756915d7 100644 --- a/tests/coupled/static/pazy/generate_pazy.py +++ b/tests/coupled/static/pazy/generate_pazy.py @@ -1,4 +1,4 @@ -import cases.templates.flying_wings as wings +import sharpy.cases.templates.flying_wings as wings import sharpy.sharpy_main @@ -128,4 +128,3 @@ def generate_pazy(u_inf, case_name, output_folder='/output/', cases_folder='', * ws.config.write() sharpy.sharpy_main.main(['', ws.route + ws.case_name + '.sharpy']) - diff --git a/tests/io/Example_simple_hale/Client_HALE.py b/tests/io/Example_simple_hale/Client_HALE.py new file mode 100644 index 000000000..d395a5619 --- /dev/null +++ b/tests/io/Example_simple_hale/Client_HALE.py @@ -0,0 +1,113 @@ +import socket +import select +import time +import logging +import struct +import sharpy.io.message_interface as message_interface +import numpy as np +""" +This is not a test but is to be used as client when testing the development of the input +output capabilities of sharpy. It will just give back the initial control surface deflection. + +Run this script as client. + +Run ``python generate_hale_io.py`` as server from the folder tests/io/Example_simple_hale +""" + +# sel = selectors.DefaultSelector() + +logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + level=20) +logger = logging.getLogger(__name__) + +sharpy_incoming = ('127.0.0.1', 64011) # control side socket +sharpy_outgoing = ('127.0.0.1', 64010) # output side socket + +own_control = ('127.0.0.1', 64000) +own_receive = ('127.0.0.1', 64001) + +in_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +in_sock.bind(own_control) + +out_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +out_sock.bind(own_receive) + +# from https://stackoverflow.com/questions/2719017/how-to-set-timeout-on-pythons-socket-recv-method +# ready_to_read = select.select([out_sock], [], [], 2) +out_sock.settimeout(300) + +#initial values +cs_deflection = [-2.08 * np.pi / 180, 0] +thrust = 6.16 +dt = 0.025 +n_tstep = 0 #Counter for time + +x_pos = [] +y_pos = [] +z_pos = [] + +x_vel = [] +y_vel = [] +z_vel = [] +p = [] +q = [] +r = [] + +root_OOP_moment = [] +root_OOP_strain = [] + +time_vec = [] + +while True: + if n_tstep > 401: + break + + # send control input to sharpy + ctrl_value = struct.pack('<5sif', b'RREF0', 0, cs_deflection[0]) + ctrl_value += struct.pack('if', 1, cs_deflection[1]) + ctrl_value += struct.pack('if', 2, thrust) + logger.info('Sending control input of size {} bytes'.format(len(ctrl_value))) + in_sock.sendto(ctrl_value, sharpy_incoming) + logger.info('Sent control input to {}'.format(sharpy_incoming)) + + # time.sleep(2) + # input('Continue loop') + + # receive output data. set msg_len to whatever length SHARPy is sending + msg_len = 93 + try: + msg, conn = out_sock.recvfrom(msg_len) + except socket.timeout: + logger.info('Socket time out') + break + logger.info('Received {} data from {}'.format(msg, conn)) + logger.info('Received data is {} bytes long'.format(len(msg))) + # else: + # break + # decoding + values = message_interface.decoder(msg) + + # Add the received values to the variables + x_pos.append(-values[0][1]) + y_pos.append(values[1][1]) + z_pos.append(values[2][1]) + + x_vel.append(-values[3][1]) + y_vel.append(values[4][1]) + z_vel.append(values[5][1]) + p.append(values[6][1] * 180 / np.pi) + q.append(values[7][1] * 180 / np.pi) + r.append(values[8][1] * 180 / np.pi) + + root_OOP_moment.append(values[9][1]) + root_OOP_strain.append(values[10][1]) + + time_vec.append(dt * n_tstep) + + n_tstep += 1 + +## + +out_sock.close() +in_sock.close() +logger.info('Closed input and output sockets') \ No newline at end of file diff --git a/tests/io/Example_simple_hale/__init__.py b/tests/io/Example_simple_hale/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/io/Example_simple_hale/generate_hale_io.py b/tests/io/Example_simple_hale/generate_hale_io.py new file mode 100644 index 000000000..80cca6db7 --- /dev/null +++ b/tests/io/Example_simple_hale/generate_hale_io.py @@ -0,0 +1,875 @@ +#! /usr/bin/env python3 +import h5py as h5 +import numpy as np +import os +import sharpy.utils.algebra as algebra +import sharpy.sharpy_main + +case_name = 'simple_HALE' +route = os.path.dirname(os.path.realpath(__file__)) + '/' + +# EXECUTION +flow = ['BeamLoader', + 'AerogridLoader', + # 'NonLinearStatic', + # 'StaticUvlm', + 'StaticTrim', + # 'StaticCoupled', + 'BeamLoads', + 'AerogridPlot', + 'BeamPlot', + 'DynamicCoupled', + 'SaveData' + # 'Modal', + # 'LinearAssember', + # 'AsymptoticStability', + ] + +# if free_flight is False, the motion of the centre of the wing is prescribed. +free_flight = True +if not free_flight: + case_name += '_prescribed' + amplitude = 0 * np.pi / 180 + period = 3 + case_name += '_amp_' + str(amplitude).replace('.', '') + '_period_' + str(period) + +# FLIGHT CONDITIONS +# the simulation is set such that the aircraft flies at a u_inf velocity while +# the air is calm. +u_inf = 10 +rho = 1.225 + +# trim sigma = 1.5 +alpha = 4.31 * np.pi / 180 +beta = 0 +roll = 0 +gravity = 'on' +cs_deflection = -2.08 * np.pi / 180 +rudder_static_deflection = 0.0 +rudder_step = 0.0 * np.pi / 180 +thrust = 6.16 +sigma = 1.5 +lambda_dihedral = 20 * np.pi / 180 + +# gust settings +gust_intensity = 0.20 +gust_length = 1 * u_inf +gust_offset = 0.5 * u_inf + +# numerics +n_step = 5 +structural_relaxation_factor = 0.6 +relaxation_factor = 0.35 +tolerance = 1e-6 +fsi_tolerance = 1e-4 + +num_cores = 2 + +# MODEL GEOMETRY +# beam +span_main = 16.0 +lambda_main = 0.25 +ea_main = 0.3 + +ea = 1e7 +ga = 1e5 +gj = 1e4 +eiy = 2e4 +eiz = 4e6 +m_bar_main = 0.75 +j_bar_main = 0.075 + +length_fuselage = 10 +offset_fuselage = 0 +sigma_fuselage = 10 +m_bar_fuselage = 0.2 +j_bar_fuselage = 0.08 + +span_tail = 2.5 +ea_tail = 0.5 +fin_height = 2.5 +ea_fin = 0.5 +sigma_tail = 100 +m_bar_tail = 0.3 +j_bar_tail = 0.08 + +# lumped masses +n_lumped_mass = 1 +lumped_mass_nodes = np.zeros((n_lumped_mass,), dtype=int) +lumped_mass = np.zeros((n_lumped_mass,)) +lumped_mass[0] = 50 +lumped_mass_inertia = np.zeros((n_lumped_mass, 3, 3)) +lumped_mass_position = np.zeros((n_lumped_mass, 3)) + +# aero +chord_main = 1.0 +chord_tail = 0.5 +chord_fin = 0.5 + +# DISCRETISATION +# spatial discretisation +# chordiwse panels +m = 4 +# spanwise elements +n_elem_multiplier = 2 +n_elem_main = int(4 * n_elem_multiplier) +n_elem_tail = int(2 * n_elem_multiplier) +n_elem_fin = int(2 * n_elem_multiplier) +n_elem_fuselage = int(2 * n_elem_multiplier) +n_surfaces = 5 + +# temporal discretisation +physical_time = 5 +tstep_factor = 1. +dt = 1.0 / m / u_inf * tstep_factor +n_tstep = round(physical_time / dt) + +# END OF INPUT----------------------------------------------------------------- + +# beam processing +n_node_elem = 3 +span_main1 = (1.0 - lambda_main) * span_main +span_main2 = lambda_main * span_main + +n_elem_main1 = round(n_elem_main * (1 - lambda_main)) +n_elem_main2 = n_elem_main - n_elem_main1 + +# total number of elements +n_elem = 0 +n_elem += n_elem_main1 + n_elem_main1 +n_elem += n_elem_main2 + n_elem_main2 +n_elem += n_elem_fuselage +n_elem += n_elem_fin +n_elem += n_elem_tail + n_elem_tail + +# number of nodes per part +n_node_main1 = n_elem_main1 * (n_node_elem - 1) + 1 +n_node_main2 = n_elem_main2 * (n_node_elem - 1) + 1 +n_node_main = n_node_main1 + n_node_main2 - 1 +n_node_fuselage = n_elem_fuselage * (n_node_elem - 1) + 1 +n_node_fin = n_elem_fin * (n_node_elem - 1) + 1 +n_node_tail = n_elem_tail * (n_node_elem - 1) + 1 + +# total number of nodes +n_node = 0 +n_node += n_node_main1 + n_node_main1 - 1 +n_node += n_node_main2 - 1 + n_node_main2 - 1 +n_node += n_node_fuselage - 1 +n_node += n_node_fin - 1 +n_node += n_node_tail - 1 +n_node += n_node_tail - 1 + +# stiffness and mass matrices +n_stiffness = 3 +base_stiffness_main = sigma * np.diag([ea, ga, ga, gj, eiy, eiz]) +base_stiffness_fuselage = base_stiffness_main.copy() * sigma_fuselage +base_stiffness_fuselage[4, 4] = base_stiffness_fuselage[5, 5] +base_stiffness_tail = base_stiffness_main.copy() * sigma_tail +base_stiffness_tail[4, 4] = base_stiffness_tail[5, 5] + +n_mass = 3 +base_mass_main = np.diag([m_bar_main, m_bar_main, m_bar_main, j_bar_main, 0.5 * j_bar_main, 0.5 * j_bar_main]) +base_mass_fuselage = np.diag([m_bar_fuselage, + m_bar_fuselage, + m_bar_fuselage, + j_bar_fuselage, + j_bar_fuselage * 0.5, + j_bar_fuselage * 0.5]) +base_mass_tail = np.diag([m_bar_tail, + m_bar_tail, + m_bar_tail, + j_bar_tail, + j_bar_tail * 0.5, + j_bar_tail * 0.5]) + +# PLACEHOLDERS +# beam +x = np.zeros((n_node,)) +y = np.zeros((n_node,)) +z = np.zeros((n_node,)) +beam_number = np.zeros((n_elem,), dtype=int) +frame_of_reference_delta = np.zeros((n_elem, n_node_elem, 3)) +structural_twist = np.zeros((n_elem, 3)) +conn = np.zeros((n_elem, n_node_elem), dtype=int) +stiffness = np.zeros((n_stiffness, 6, 6)) +elem_stiffness = np.zeros((n_elem,), dtype=int) +mass = np.zeros((n_mass, 6, 6)) +elem_mass = np.zeros((n_elem,), dtype=int) +boundary_conditions = np.zeros((n_node,), dtype=int) +app_forces = np.zeros((n_node, 6)) + +# aero +airfoil_distribution = np.zeros((n_elem, n_node_elem), dtype=int) +surface_distribution = np.zeros((n_elem,), dtype=int) - 1 +surface_m = np.zeros((n_surfaces,), dtype=int) +m_distribution = 'uniform' +aero_node = np.zeros((n_node,), dtype=bool) +twist = np.zeros((n_elem, n_node_elem)) +sweep = np.zeros((n_elem, n_node_elem)) +chord = np.zeros((n_elem, n_node_elem,)) +elastic_axis = np.zeros((n_elem, n_node_elem,)) + + +# FUNCTIONS------------------------------------------------------------- +def clean_test_files(): + fem_file_name = route + '/' + case_name + '.fem.h5' + if os.path.isfile(fem_file_name): + os.remove(fem_file_name) + + dyn_file_name = route + '/' + case_name + '.dyn.h5' + if os.path.isfile(dyn_file_name): + os.remove(dyn_file_name) + + aero_file_name = route + '/' + case_name + '.aero.h5' + if os.path.isfile(aero_file_name): + os.remove(aero_file_name) + + solver_file_name = route + '/' + case_name + '.sharpy' + if os.path.isfile(solver_file_name): + os.remove(solver_file_name) + + flightcon_file_name = route + '/' + case_name + '.flightcon.txt' + if os.path.isfile(flightcon_file_name): + os.remove(flightcon_file_name) + + +def generate_dyn_file(): + global dt + global n_tstep + global route + global case_name + global num_elem + global num_node_elem + global num_node + global amplitude + global period + global free_flight + + dynamic_forces_time = None + with_dynamic_forces = False + with_forced_vel = False + if not free_flight: + with_forced_vel = True + + if with_dynamic_forces: + f1 = 100 + dynamic_forces = np.zeros((num_node, 6)) + app_node = [int(num_node_main - 1), int(num_node_main)] + dynamic_forces[app_node, 2] = f1 + force_time = np.zeros((n_tstep,)) + limit = round(0.05 / dt) + force_time[50:61] = 1 + + dynamic_forces_time = np.zeros((n_tstep, num_node, 6)) + for it in range(n_tstep): + dynamic_forces_time[it, :, :] = force_time[it] * dynamic_forces + + forced_for_vel = None + if with_forced_vel: + forced_for_vel = np.zeros((n_tstep, 6)) + forced_for_acc = np.zeros((n_tstep, 6)) + for it in range(n_tstep): + # if dt*it < period: + # forced_for_vel[it, 2] = 2*np.pi/period*amplitude*np.sin(2*np.pi*dt*it/period) + # forced_for_acc[it, 2] = (2*np.pi/period)**2*amplitude*np.cos(2*np.pi*dt*it/period) + + forced_for_vel[it, 3] = 2 * np.pi / period * amplitude * np.sin(2 * np.pi * dt * it / period) + forced_for_acc[it, 3] = (2 * np.pi / period) ** 2 * amplitude * np.cos(2 * np.pi * dt * it / period) + + if with_dynamic_forces or with_forced_vel: + with h5.File(route + '/' + case_name + '.dyn.h5', 'a') as h5file: + if with_dynamic_forces: + h5file.create_dataset( + 'dynamic_forces', data=dynamic_forces_time) + if with_forced_vel: + h5file.create_dataset( + 'for_vel', data=forced_for_vel) + h5file.create_dataset( + 'for_acc', data=forced_for_acc) + h5file.create_dataset( + 'num_steps', data=n_tstep) + + +def generate_fem(): + stiffness[0, ...] = base_stiffness_main + stiffness[1, ...] = base_stiffness_fuselage + stiffness[2, ...] = base_stiffness_tail + + mass[0, ...] = base_mass_main + mass[1, ...] = base_mass_fuselage + mass[2, ...] = base_mass_tail + + we = 0 + wn = 0 + # inner right wing + beam_number[we:we + n_elem_main1] = 0 + y[wn:wn + n_node_main1] = np.linspace(0.0, span_main1, n_node_main1) + + for ielem in range(n_elem_main1): + conn[we + ielem, :] = ((np.ones((3,)) * (we + ielem) * (n_node_elem - 1)) + + [0, 2, 1]) + for inode in range(n_node_elem): + frame_of_reference_delta[we + ielem, inode, :] = [-1.0, 0.0, 0.0] + + elem_stiffness[we:we + n_elem_main1] = 0 + elem_mass[we:we + n_elem_main1] = 0 + boundary_conditions[0] = 1 + # remember this is in B FoR + app_forces[0] = [0, thrust, 0, 0, 0, 0] + we += n_elem_main1 + wn += n_node_main1 + + # outer right wing + beam_number[we:we + n_elem_main1] = 0 + y[wn:wn + n_node_main2 - 1] = y[wn - 1] + np.linspace(0.0, np.cos(lambda_dihedral) * span_main2, n_node_main2)[1:] + z[wn:wn + n_node_main2 - 1] = z[wn - 1] + np.linspace(0.0, np.sin(lambda_dihedral) * span_main2, n_node_main2)[1:] + for ielem in range(n_elem_main2): + conn[we + ielem, :] = ((np.ones((3,)) * (we + ielem) * (n_node_elem - 1)) + + [0, 2, 1]) + for inode in range(n_node_elem): + frame_of_reference_delta[we + ielem, inode, :] = [-1.0, 0.0, 0.0] + elem_stiffness[we:we + n_elem_main2] = 0 + elem_mass[we:we + n_elem_main2] = 0 + boundary_conditions[wn + n_node_main2 - 2] = -1 + we += n_elem_main2 + wn += n_node_main2 - 1 + + # inner left wing + beam_number[we:we + n_elem_main1 - 1] = 1 + y[wn:wn + n_node_main1 - 1] = np.linspace(0.0, -span_main1, n_node_main1)[1:] + for ielem in range(n_elem_main1): + conn[we + ielem, :] = ((np.ones((3,)) * (we + ielem) * (n_node_elem - 1)) + + [0, 2, 1]) + for inode in range(n_node_elem): + frame_of_reference_delta[we + ielem, inode, :] = [1.0, 0.0, 0.0] + conn[we, 0] = 0 + elem_stiffness[we:we + n_elem_main1] = 0 + elem_mass[we:we + n_elem_main1] = 0 + we += n_elem_main1 + wn += n_node_main1 - 1 + + # outer left wing + beam_number[we:we + n_elem_main2] = 1 + y[wn:wn + n_node_main2 - 1] = y[wn - 1] + np.linspace(0.0, -np.cos(lambda_dihedral) * span_main2, n_node_main2)[1:] + z[wn:wn + n_node_main2 - 1] = z[wn - 1] + np.linspace(0.0, np.sin(lambda_dihedral) * span_main2, n_node_main2)[1:] + for ielem in range(n_elem_main2): + conn[we + ielem, :] = ((np.ones((3,)) * (we + ielem) * (n_node_elem - 1)) + + [0, 2, 1]) + for inode in range(n_node_elem): + frame_of_reference_delta[we + ielem, inode, :] = [1.0, 0.0, 0.0] + elem_stiffness[we:we + n_elem_main2] = 0 + elem_mass[we:we + n_elem_main2] = 0 + boundary_conditions[wn + n_node_main2 - 2] = -1 + we += n_elem_main2 + wn += n_node_main2 - 1 + + # fuselage + beam_number[we:we + n_elem_fuselage] = 2 + x[wn:wn + n_node_fuselage - 1] = np.linspace(0.0, length_fuselage, n_node_fuselage)[1:] + z[wn:wn + n_node_fuselage - 1] = np.linspace(0.0, offset_fuselage, n_node_fuselage)[1:] + for ielem in range(n_elem_fuselage): + conn[we + ielem, :] = ((np.ones((3,)) * (we + ielem) * (n_node_elem - 1)) + + [0, 2, 1]) + for inode in range(n_node_elem): + frame_of_reference_delta[we + ielem, inode, :] = [0.0, 1.0, 0.0] + conn[we, 0] = 0 + elem_stiffness[we:we + n_elem_fuselage] = 1 + elem_mass[we:we + n_elem_fuselage] = 1 + we += n_elem_fuselage + wn += n_node_fuselage - 1 + global end_of_fuselage_node + end_of_fuselage_node = wn - 1 + + # fin + beam_number[we:we + n_elem_fin] = 3 + x[wn:wn + n_node_fin - 1] = x[end_of_fuselage_node] + z[wn:wn + n_node_fin - 1] = z[end_of_fuselage_node] + np.linspace(0.0, fin_height, n_node_fin)[1:] + for ielem in range(n_elem_fin): + conn[we + ielem, :] = ((np.ones((3,)) * (we + ielem) * (n_node_elem - 1)) + + [0, 2, 1]) + for inode in range(n_node_elem): + frame_of_reference_delta[we + ielem, inode, :] = [-1.0, 0.0, 0.0] + conn[we, 0] = end_of_fuselage_node + elem_stiffness[we:we + n_elem_fin] = 2 + elem_mass[we:we + n_elem_fin] = 2 + we += n_elem_fin + wn += n_node_fin - 1 + end_of_fin_node = wn - 1 + + # right tail + beam_number[we:we + n_elem_tail] = 4 + x[wn:wn + n_node_tail - 1] = x[end_of_fin_node] + y[wn:wn + n_node_tail - 1] = np.linspace(0.0, span_tail, n_node_tail)[1:] + z[wn:wn + n_node_tail - 1] = z[end_of_fin_node] + for ielem in range(n_elem_tail): + conn[we + ielem, :] = ((np.ones((3,)) * (we + ielem) * (n_node_elem - 1)) + + [0, 2, 1]) + for inode in range(n_node_elem): + frame_of_reference_delta[we + ielem, inode, :] = [-1.0, 0.0, 0.0] + conn[we, 0] = end_of_fin_node + elem_stiffness[we:we + n_elem_tail] = 2 + elem_mass[we:we + n_elem_tail] = 2 + boundary_conditions[wn + n_node_tail - 2] = -1 + we += n_elem_tail + wn += n_node_tail - 1 + + # left tail + beam_number[we:we + n_elem_tail] = 5 + x[wn:wn + n_node_tail - 1] = x[end_of_fin_node] + y[wn:wn + n_node_tail - 1] = np.linspace(0.0, -span_tail, n_node_tail)[1:] + z[wn:wn + n_node_tail - 1] = z[end_of_fin_node] + for ielem in range(n_elem_tail): + conn[we + ielem, :] = ((np.ones((3,)) * (we + ielem) * (n_node_elem - 1)) + + [0, 2, 1]) + for inode in range(n_node_elem): + frame_of_reference_delta[we + ielem, inode, :] = [1.0, 0.0, 0.0] + conn[we, 0] = end_of_fin_node + elem_stiffness[we:we + n_elem_tail] = 2 + elem_mass[we:we + n_elem_tail] = 2 + boundary_conditions[wn + n_node_tail - 2] = -1 + we += n_elem_tail + wn += n_node_tail - 1 + + with h5.File(route + '/' + case_name + '.fem.h5', 'a') as h5file: + coordinates = h5file.create_dataset('coordinates', data=np.column_stack((x, y, z))) + conectivities = h5file.create_dataset('connectivities', data=conn) + num_nodes_elem_handle = h5file.create_dataset( + 'num_node_elem', data=n_node_elem) + num_nodes_handle = h5file.create_dataset( + 'num_node', data=n_node) + num_elem_handle = h5file.create_dataset( + 'num_elem', data=n_elem) + stiffness_db_handle = h5file.create_dataset( + 'stiffness_db', data=stiffness) + stiffness_handle = h5file.create_dataset( + 'elem_stiffness', data=elem_stiffness) + mass_db_handle = h5file.create_dataset( + 'mass_db', data=mass) + mass_handle = h5file.create_dataset( + 'elem_mass', data=elem_mass) + frame_of_reference_delta_handle = h5file.create_dataset( + 'frame_of_reference_delta', data=frame_of_reference_delta) + structural_twist_handle = h5file.create_dataset( + 'structural_twist', data=structural_twist) + bocos_handle = h5file.create_dataset( + 'boundary_conditions', data=boundary_conditions) + beam_handle = h5file.create_dataset( + 'beam_number', data=beam_number) + app_forces_handle = h5file.create_dataset( + 'app_forces', data=app_forces) + lumped_mass_nodes_handle = h5file.create_dataset( + 'lumped_mass_nodes', data=lumped_mass_nodes) + lumped_mass_handle = h5file.create_dataset( + 'lumped_mass', data=lumped_mass) + lumped_mass_inertia_handle = h5file.create_dataset( + 'lumped_mass_inertia', data=lumped_mass_inertia) + lumped_mass_position_handle = h5file.create_dataset( + 'lumped_mass_position', data=lumped_mass_position) + + +def generate_aero_file(): + global x, y, z + # control surfaces + n_control_surfaces = 2 + control_surface = np.zeros((n_elem, n_node_elem), dtype=int) - 1 + control_surface_type = np.zeros((n_control_surfaces,), dtype=int) + control_surface_deflection = np.zeros((n_control_surfaces,)) + control_surface_chord = np.zeros((n_control_surfaces,), dtype=int) + control_surface_hinge_coord = np.zeros((n_control_surfaces,), dtype=float) + + # control surface type 0 = static + # control surface type 1 = dynamic + control_surface_type[0] = 2 + control_surface_deflection[0] = cs_deflection + control_surface_chord[0] = m + control_surface_hinge_coord[0] = -0.25 # nondimensional wrt elastic axis (+ towards the trailing edge) + + control_surface_type[1] = 2 + control_surface_deflection[1] = rudder_static_deflection + control_surface_chord[1] = 1 + control_surface_hinge_coord[1] = -0. # nondimensional wrt elastic axis (+ towards the trailing edge) + + we = 0 + wn = 0 + # right wing (surface 0, beam 0) + i_surf = 0 + airfoil_distribution[we:we + n_elem_main, :] = 0 + surface_distribution[we:we + n_elem_main] = i_surf + surface_m[i_surf] = m + aero_node[wn:wn + n_node_main] = True + temp_chord = np.linspace(chord_main, chord_main, n_node_main) + temp_sweep = np.linspace(0.0, 0 * np.pi / 180, n_node_main) + node_counter = 0 + for i_elem in range(we, we + n_elem_main): + for i_local_node in range(n_node_elem): + if not i_local_node == 0: + node_counter += 1 + chord[i_elem, i_local_node] = temp_chord[node_counter] + elastic_axis[i_elem, i_local_node] = ea_main + sweep[i_elem, i_local_node] = temp_sweep[node_counter] + + we += n_elem_main + wn += n_node_main + + # left wing (surface 1, beam 1) + i_surf = 1 + airfoil_distribution[we:we + n_elem_main, :] = 0 + # airfoil_distribution[wn:wn + n_node_main - 1] = 0 + surface_distribution[we:we + n_elem_main] = i_surf + surface_m[i_surf] = m + aero_node[wn:wn + n_node_main - 1] = True + # chord[wn:wn + num_node_main - 1] = np.linspace(main_chord, main_tip_chord, num_node_main)[1:] + # chord[wn:wn + num_node_main - 1] = main_chord + # elastic_axis[wn:wn + num_node_main - 1] = main_ea + temp_chord = np.linspace(chord_main, chord_main, n_node_main) + node_counter = 0 + for i_elem in range(we, we + n_elem_main): + for i_local_node in range(n_node_elem): + if not i_local_node == 0: + node_counter += 1 + chord[i_elem, i_local_node] = temp_chord[node_counter] + elastic_axis[i_elem, i_local_node] = ea_main + sweep[i_elem, i_local_node] = -temp_sweep[node_counter] + + we += n_elem_main + wn += n_node_main - 1 + + we += n_elem_fuselage + wn += n_node_fuselage - 1 - 1 + # + # # fin (surface 2, beam 3) + i_surf = 2 + airfoil_distribution[we:we + n_elem_fin, :] = 1 + # airfoil_distribution[wn:wn + n_node_fin] = 0 + surface_distribution[we:we + n_elem_fin] = i_surf + surface_m[i_surf] = m + aero_node[wn:wn + n_node_fin] = True + # chord[wn:wn + num_node_fin] = fin_chord + for i_elem in range(we, we + n_elem_fin): + for i_local_node in range(n_node_elem): + chord[i_elem, i_local_node] = chord_fin + elastic_axis[i_elem, i_local_node] = ea_fin + control_surface[i_elem, i_local_node] = 1 + # twist[end_of_fuselage_node] = 0 + # twist[wn:] = 0 + # elastic_axis[wn:wn + num_node_main] = fin_ea + we += n_elem_fin + wn += n_node_fin - 1 + # + # # # right tail (surface 3, beam 4) + i_surf = 3 + airfoil_distribution[we:we + n_elem_tail, :] = 2 + # airfoil_distribution[wn:wn + n_node_tail] = 0 + surface_distribution[we:we + n_elem_tail] = i_surf + surface_m[i_surf] = m + # XXX not very elegant + aero_node[wn:] = True + # chord[wn:wn + num_node_tail] = tail_chord + # elastic_axis[wn:wn + num_node_main] = tail_ea + for i_elem in range(we, we + n_elem_tail): + for i_local_node in range(n_node_elem): + twist[i_elem, i_local_node] = -0 + for i_elem in range(we, we + n_elem_tail): + for i_local_node in range(n_node_elem): + chord[i_elem, i_local_node] = chord_tail + elastic_axis[i_elem, i_local_node] = ea_tail + control_surface[i_elem, i_local_node] = 0 + + we += n_elem_tail + wn += n_node_tail + # + # # left tail (surface 4, beam 5) + i_surf = 4 + airfoil_distribution[we:we + n_elem_tail, :] = 2 + # airfoil_distribution[wn:wn + n_node_tail - 1] = 0 + surface_distribution[we:we + n_elem_tail] = i_surf + surface_m[i_surf] = m + aero_node[wn:wn + n_node_tail - 1] = True + # chord[wn:wn + num_node_tail] = tail_chord + # elastic_axis[wn:wn + num_node_main] = tail_ea + # twist[we:we + num_elem_tail] = -tail_twist + for i_elem in range(we, we + n_elem_tail): + for i_local_node in range(n_node_elem): + twist[i_elem, i_local_node] = -0 + for i_elem in range(we, we + n_elem_tail): + for i_local_node in range(n_node_elem): + chord[i_elem, i_local_node] = chord_tail + elastic_axis[i_elem, i_local_node] = ea_tail + control_surface[i_elem, i_local_node] = 0 + we += n_elem_tail + wn += n_node_tail + + with h5.File(route + '/' + case_name + '.aero.h5', 'a') as h5file: + airfoils_group = h5file.create_group('airfoils') + # add one airfoil + naca_airfoil_main = airfoils_group.create_dataset('0', data=np.column_stack( + generate_naca_camber(P=0, M=0))) + naca_airfoil_tail = airfoils_group.create_dataset('1', data=np.column_stack( + generate_naca_camber(P=0, M=0))) + naca_airfoil_fin = airfoils_group.create_dataset('2', data=np.column_stack( + generate_naca_camber(P=0, M=0))) + + # chord + chord_input = h5file.create_dataset('chord', data=chord) + dim_attr = chord_input.attrs['units'] = 'm' + + # twist + twist_input = h5file.create_dataset('twist', data=twist) + dim_attr = twist_input.attrs['units'] = 'rad' + + # sweep + sweep_input = h5file.create_dataset('sweep', data=sweep) + dim_attr = sweep_input.attrs['units'] = 'rad' + + # airfoil distribution + airfoil_distribution_input = h5file.create_dataset('airfoil_distribution', data=airfoil_distribution) + + surface_distribution_input = h5file.create_dataset('surface_distribution', data=surface_distribution) + surface_m_input = h5file.create_dataset('surface_m', data=surface_m) + m_distribution_input = h5file.create_dataset('m_distribution', data=m_distribution.encode('ascii', 'ignore')) + + aero_node_input = h5file.create_dataset('aero_node', data=aero_node) + elastic_axis_input = h5file.create_dataset('elastic_axis', data=elastic_axis) + + control_surface_input = h5file.create_dataset('control_surface', data=control_surface) + control_surface_deflection_input = h5file.create_dataset('control_surface_deflection', + data=control_surface_deflection) + control_surface_chord_input = h5file.create_dataset('control_surface_chord', data=control_surface_chord) + control_surface_hinge_coord_input = h5file.create_dataset('control_surface_hinge_coord', + data=control_surface_hinge_coord) + control_surface_types_input = h5file.create_dataset('control_surface_type', data=control_surface_type) + + +def generate_naca_camber(M=0, P=0): + mm = M * 1e-2 + p = P * 1e-1 + + def naca(x, mm, p): + if x < 1e-6: + return 0.0 + elif x < p: + return mm / (p * p) * (2 * p * x - x * x) + elif x > p and x < 1 + 1e-6: + return mm / ((1 - p) * (1 - p)) * (1 - 2 * p + 2 * p * x - x * x) + + x_vec = np.linspace(0, 1, 1000) + y_vec = np.array([naca(x, mm, p) for x in x_vec]) + return x_vec, y_vec + + +def generate_solver_file(): + file_name = route + '/' + case_name + '.sharpy' + settings = dict() + settings['SHARPy'] = {'case': case_name, + 'route': route, + 'flow': flow, + 'write_screen': 'on', + 'write_log': 'on', + 'log_folder': route + '/output/', + 'log_file': case_name + '.log'} + + settings['BeamLoader'] = {'unsteady': 'on', + 'orientation': algebra.euler2quat(np.array([roll, + alpha, + beta]))} + settings['AerogridLoader'] = {'unsteady': 'on', + 'aligned_grid': 'on', + 'mstar': int(20 / tstep_factor), + 'freestream_dir': ['1', '0', '0'], + 'wake_shape_generator': 'StraightWake', + 'wake_shape_generator_input': {'u_inf': u_inf, + 'u_inf_direction': ['1', '0', '0'], + 'dt': dt}} + + settings['NonLinearStatic'] = {'print_info': 'off', + 'max_iterations': 150, + 'num_load_steps': 1, + 'delta_curved': 1e-1, + 'min_delta': tolerance, + 'gravity_on': gravity, + 'gravity': 9.81} + + settings['StaticUvlm'] = {'print_info': 'on', + 'horseshoe': 'off', + 'num_cores': num_cores, + 'n_rollup': 0, + 'rollup_dt': dt, + 'rollup_aic_refresh': 1, + 'rollup_tolerance': 1e-4, + 'velocity_field_generator': 'SteadyVelocityField', + 'velocity_field_input': {'u_inf': u_inf, + 'u_inf_direction': [1., 0, 0]}, + 'rho': rho} + + settings['StaticCoupled'] = {'print_info': 'off', + 'structural_solver': 'NonLinearStatic', + 'structural_solver_settings': settings['NonLinearStatic'], + 'aero_solver': 'StaticUvlm', + 'aero_solver_settings': settings['StaticUvlm'], + 'max_iter': 100, + 'n_load_steps': n_step, + 'tolerance': fsi_tolerance, + 'relaxation_factor': structural_relaxation_factor} + + settings['StaticTrim'] = {'solver': 'StaticCoupled', + 'solver_settings': settings['StaticCoupled'], + 'initial_alpha': alpha, + 'initial_deflection': cs_deflection, + 'initial_thrust': thrust} + + settings['NonLinearDynamicCoupledStep'] = {'print_info': 'off', + 'max_iterations': 950, + 'delta_curved': 1e-1, + 'min_delta': tolerance, + 'newmark_damp': 5e-3, + 'gravity_on': gravity, + 'gravity': 9.81, + 'num_steps': n_tstep, + 'dt': dt, + 'initial_velocity': u_inf} + + settings['NonLinearDynamicPrescribedStep'] = {'print_info': 'off', + 'max_iterations': 950, + 'delta_curved': 1e-1, + 'min_delta': tolerance, + 'newmark_damp': 5e-3, + 'gravity_on': gravity, + 'gravity': 9.81, + 'num_steps': n_tstep, + 'dt': dt, + 'initial_velocity': u_inf * int(free_flight)} + settings['SaveData'] = { 'save_aero': 'on', + 'save_struct': 'on', + 'save_linear': 'off', + 'save_linear_uvlm': 'off'} + + relative_motion = 'off' + if not free_flight: + relative_motion = 'on' + settings['StepUvlm'] = {'print_info': 'off', + 'num_cores': num_cores, + 'convection_scheme': 2, + 'gamma_dot_filtering': 6, + 'velocity_field_generator': 'GustVelocityField', + 'velocity_field_input': {'u_inf': int(not free_flight) * u_inf, + 'u_inf_direction': [1., 0, 0], + 'gust_shape': '1-cos', + 'gust_parameters': {'gust_length': gust_length, + 'gust_intensity': gust_intensity * u_inf}, + 'offset': gust_offset, + 'relative_motion': relative_motion}, + 'rho': rho, + 'n_time_steps': n_tstep, + 'dt': dt} + + if free_flight: + solver = 'NonLinearDynamicCoupledStep' + else: + solver = 'NonLinearDynamicPrescribedStep' + settings['DynamicCoupled'] = {'structural_solver': solver, + 'structural_solver_settings': settings[solver], + 'aero_solver': 'StepUvlm', + 'aero_solver_settings': settings['StepUvlm'], + 'fsi_substeps': 200, + 'fsi_tolerance': fsi_tolerance, + 'relaxation_factor': relaxation_factor, + 'minimum_steps': 1, + 'relaxation_steps': 150, + 'final_relaxation_factor': 0.5, + 'n_time_steps': n_tstep, + 'dt': dt, + 'include_unsteady_force_contribution': 'on', + 'network_settings': { + 'variables_filename': './variables_hale.yaml', + 'send_output_to_all_clients': 'on', + 'byte_ordering': 'little', + 'received_data_filename': './output/' + case_name + '/input.dat', + 'log_name': './output/' + case_name + '/sharpy_network.log', + 'file_log_level': 'debug', + 'console_log_level': 'debug', + 'input_network_settings': {'address': '127.0.0.1', + 'port': 64011}, + 'output_network_settings': {'send_on_demand': 'off', + 'destination_address': ['127.0.0.1'], + 'address': '127.0.0.1', + 'port': 64010, + 'destination_ports': [64001]}, }, + 'postprocessors': ['BeamLoads', 'BeamPlot', 'AerogridPlot'], + 'postprocessors_settings': {'BeamLoads': {'csv_output': 'off'}, + 'BeamPlot': {'include_rbm': 'on', + 'include_applied_forces': 'on'}, + 'AerogridPlot': { + 'include_rbm': 'on', + 'include_applied_forces': 'on', + 'minus_m_star': 0}, + }} + + settings['BeamLoads'] = {'csv_output': 'off'} + + settings['BeamPlot'] = {'include_rbm': 'on', + 'include_applied_forces': 'on'} + + + settings['AerogridPlot'] = {'include_rbm': 'on', + 'include_forward_motion': 'off', + 'include_applied_forces': 'on', + 'minus_m_star': 0, + 'u_inf': u_inf, + 'dt': dt} + + settings['Modal'] = {'print_info': True, + 'use_undamped_modes': True, + 'NumLambda': 30, + 'rigid_body_modes': True, + 'write_modes_vtk': 'on', + 'print_matrices': 'on', + 'continuous_eigenvalues': 'off', + 'dt': dt, + 'plot_eigenvalues': False} + + settings['LinearAssembler'] = {'linear_system': 'LinearAeroelastic', + 'linear_system_settings': { + 'beam_settings': {'modal_projection': False, + 'inout_coords': 'nodes', + 'discrete_time': True, + 'newmark_damp': 0.05, + 'discr_method': 'newmark', + 'dt': dt, + 'proj_modes': 'undamped', + 'use_euler': 'off', + 'num_modes': 40, + 'print_info': 'on', + 'gravity': 'on', + 'remove_dofs': []}, + 'aero_settings': {'dt': dt, + 'integr_order': 2, + 'density': rho, + 'remove_predictor': False, + 'use_sparse': True, + 'remove_inputs': ['u_gust']} + }} + + settings['AsymptoticStability'] = {'print_info': 'on', + 'modes_to_plot': [], + 'display_root_locus': 'off', + 'frequency_cutoff': 0, + 'export_eigenvalues': 'off', + 'num_evals': 40} + + import configobj + config = configobj.ConfigObj() + config.filename = file_name + for k, v in settings.items(): + config[k] = v + config.write() + + +clean_test_files() +generate_fem() +generate_aero_file() +generate_solver_file() +generate_dyn_file() + +case_route = '' + +sharpy.sharpy_main.main(['', case_route + '' + case_name + '.sharpy']) \ No newline at end of file diff --git a/tests/io/Example_simple_hale/variables_hale.yaml b/tests/io/Example_simple_hale/variables_hale.yaml new file mode 100644 index 000000000..95394694b --- /dev/null +++ b/tests/io/Example_simple_hale/variables_hale.yaml @@ -0,0 +1,61 @@ +--- +- name: 'control_surface_deflection' + var_type: 'control_surface' + inout: 'in' + position: 0 +- name: 'control_surface_deflection' + var_type: 'control_surface' + inout: 'in' + position: 1 +- name: 'app_forces' + var_type: 'node' + inout: 'in' + position: 0 + index: 1 +- name: 'for_pos' + inout: 'out' + position: 0 + var_type: 'node' +- name: 'for_pos' + var_type: 'node' + inout: 'out' + position: 1 +- name: 'for_pos' + var_type: 'node' + inout: 'out' + position: 2 +- name: 'for_vel' + var_type: 'node' + inout: 'out' + position: 0 +- name: 'for_vel' + var_type: 'node' + inout: 'out' + position: 1 +- name: 'for_vel' + var_type: 'node' + inout: 'out' + position: 2 +- name: 'for_vel' + var_type: 'node' + inout: 'out' + position: 3 +- name: 'for_vel' + var_type: 'node' + inout: 'out' + position: 4 +- name: 'for_vel' + var_type: 'node' + inout: 'out' + position: 5 +- name: 'loads' + var_type: 'node' + inout: 'out' + position: 0 + index: 4 +- name: 'strain' + var_type: 'node' + inout: 'out' + position: 0 + index: 4 +... \ No newline at end of file diff --git a/tests/io/generate_pazy_udpout.py b/tests/io/generate_pazy_udpout.py index ad48e9a15..2cb47caad 100644 --- a/tests/io/generate_pazy_udpout.py +++ b/tests/io/generate_pazy_udpout.py @@ -1,7 +1,7 @@ import numpy as np import os import unittest -import cases.templates.flying_wings as wings +import sharpy.cases.templates.flying_wings as wings import sharpy.sharpy_main # Problem Set up @@ -170,8 +170,7 @@ def generate_pazy_udp(u_inf, case_name, output_folder='/output/', cases_folder=' ws.config['Modal'] = {'NumLambda': 20, 'rigid_body_modes': 'off', 'print_matrices': 'on', - 'keep_linear_matrices': 'on', - 'write_dat': 'off', + 'save_data': 'off', 'rigid_modes_cg': 'off', 'continuous_eigenvalues': 'off', 'dt': 0, diff --git a/tests/io/sample_udp_inout/generate_pazy_test_io_local.py b/tests/io/sample_udp_inout/generate_pazy_test_io_local.py index c9d225da2..f74d93f8b 100644 --- a/tests/io/sample_udp_inout/generate_pazy_test_io_local.py +++ b/tests/io/sample_udp_inout/generate_pazy_test_io_local.py @@ -163,8 +163,7 @@ def generate_pazy(u_inf, case_name, output_folder='/output/', cases_folder='', * ws.config['Modal'] = {'NumLambda': 20, 'rigid_body_modes': 'off', 'print_matrices': 'on', - 'keep_linear_matrices': 'on', - 'write_dat': 'off', + 'save_data': 'off', 'rigid_modes_cg': 'off', 'continuous_eigenvalues': 'off', 'dt': 0, diff --git a/tests/linear/control_surfaces/test_control_surfaces.py b/tests/linear/control_surfaces/test_control_surfaces.py index 95c5d1452..a8f8e64f2 100644 --- a/tests/linear/control_surfaces/test_control_surfaces.py +++ b/tests/linear/control_surfaces/test_control_surfaces.py @@ -1,19 +1,22 @@ import numpy as np import os import unittest -import cases.templates.flying_wings as wings +import sharpy.cases.templates.flying_wings as wings import sharpy.sharpy_main +import pickle -@unittest.skip('Control Surface Test for visual inspection') +# @unittest.skip('Control Surface Test for visual inspection') class TestGolandControlSurface(unittest.TestCase): + def setup(self): + self.deflection_degrees = 5 # Problem Set up u_inf = 1. alpha_deg = 0. rho = 1.02 - num_modes = 4 + num_modes = 2 # Lattice Discretisation M = 4 @@ -56,6 +59,11 @@ def setup(self): ws.update_derived_params() ws.set_default_config_dict() + # Full moving control surface on the right wing + ws.control_surface_chord[0] = M + ws.control_surface_hinge_coord[0] = 0.5 + ws.control_surface[4, :] = 1 + ws.generate_aero_file() ws.generate_fem_file() @@ -63,7 +71,8 @@ def setup(self): lin_tsteps = 10 u_vec = np.zeros((lin_tsteps, 8)) # elevator - u_vec[:, 4] = 10 * np.pi / 180 + u_vec[:, 4] = self.deflection_degrees * np.pi / 180 + np.savetxt(self.route_test_dir + '/cases/elevator.txt', u_vec[:, 4]) ws.create_linear_files(x0, u_vec) ws.config['SHARPy'] = { @@ -74,11 +83,12 @@ def setup(self): 'BeamPlot', 'Modal', 'LinearAssembler', - 'FrequencyResponse', + # 'FrequencyResponse', 'LinDynamicSim', + 'PickleData', ], 'case': ws.case_name, 'route': ws.route, - 'write_screen': 'on', 'write_log': 'on', + 'write_screen': 'off', 'write_log': 'on', 'log_folder': self.route_test_dir + '/output/' + ws.case_name + '/', 'log_file': ws.case_name + '.log'} @@ -154,8 +164,7 @@ def setup(self): ws.config['Modal'] = {'NumLambda': 20, 'rigid_body_modes': 'off', 'print_matrices': 'on', - 'keep_linear_matrices': 'on', - 'write_dat': 'off', + 'save_data': 'off', 'rigid_modes_cg': 'off', 'continuous_eigenvalues': 'off', 'dt': 0, @@ -186,24 +195,59 @@ def setup(self): 'remove_predictor': remove_predictor, 'use_sparse': use_sparse, 'remove_inputs': ['u_gust']}, - 'rigid_body_motion': 'off'}} + } + } ws.config['LinDynamicSim'] = {'n_tsteps': lin_tsteps, - 'dt': ws.dt, - 'postprocessors': ['AerogridPlot'], - 'postprocessors_settings': - {'AerogridPlot': {'include_rbm': 'on', - 'include_applied_forces': 'on', - 'minus_m_star': 0}, } - } + 'dt': ws.dt, + 'input_generators': [{'name': 'control_surface_deflection', + 'index': 0, + 'file_path': self.route_test_dir + '/cases/elevator.txt'}, + {'name': 'control_surface_deflection', + 'index': 1, + 'file_path': self.route_test_dir + '/cases/elevator.txt'}], + 'postprocessors': ['AerogridPlot'], + 'postprocessors_settings': + {'AerogridPlot': {'include_rbm': 'on', + 'include_applied_forces': 'on', + 'minus_m_star': 0}, } + } + + ws.config['PickleData'] = {'folder': self.route_test_dir + '/output/'} ws.config.write() - self.data = sharpy.sharpy_main.main(['', ws.route + ws.case_name + '.sharpy']) + restart = False # useful for debugging to load pickle + if not restart: + self.data = sharpy.sharpy_main.main(['', ws.route + ws.case_name + '.sharpy']) + else: + with open(self.route_test_dir + '/output/' + ws.case_name + '.pkl', 'rb') as f: + self.data = pickle.load(f) def test_control_surface(self): self.setup() + for i_surf in range(2): + with self.subTest(i_surf=i_surf): + zeta = self.data.aero.timestep_info[-1].zeta[i_surf] + zeta0 = self.data.linear.tsaero0.zeta[i_surf] + + # zeta indices [(xyz), chord, span] + if i_surf == 0: + # Full moving control surface with hinge at c=0.5 + zeta_elev = zeta[:, -1, -1] - zeta[:, 0, -1] # elevator starts at chordwise node 0 + zeta_0elev = zeta0[:, -1, -1] - zeta0[:, 0, -1] + elif i_surf == 1: + # mirrored surface. span index 0 is the wing tip + zeta_elev = zeta[:, -1, 0] - zeta[:, 2, 0] # elevator starts at chordwise node 2 + zeta_0elev = zeta0[:, -1, 0] - zeta0[:, 2, 0] + + deflection = np.arccos((zeta_elev.dot(zeta_0elev)) / np.linalg.norm(zeta_elev) / np.linalg.norm(zeta_0elev)) + + deflection_actual_deg = deflection * 180 / np.pi + np.testing.assert_array_almost_equal(self.deflection_degrees, deflection_actual_deg, + decimal=2) + def tearDown(self): import shutil folders = ['cases', 'figures', 'output'] diff --git a/tests/linear/derivatives/test_stabilityderivatives.py b/tests/linear/derivatives/test_stabilityderivatives.py new file mode 100644 index 000000000..60213caa8 --- /dev/null +++ b/tests/linear/derivatives/test_stabilityderivatives.py @@ -0,0 +1,511 @@ +import numpy as np +import os +import unittest +import sharpy.cases.templates.flying_wings as wings +import sharpy.sharpy_main +import h5py +import sharpy.utils.h5utils as h5utils +import sharpy.utils.algebra as algebra +import sharpy.linear.src.libss as libss + + +class TestLinearDerivatives(unittest.TestCase): + + print_info = False + + def run_sharpy(self, alpha_deg, flow, target_system, not_run=False): + # Problem Set up + u_inf = 10. + rho = 1.02 + num_modes = 4 + + # Lattice Discretisation + M = 4 + N = 4 + M_star_fact = 10 + + # Linear UVLM settings + integration_order = 2 + remove_predictor = False + use_sparse = True + + # ROM Properties + rom_settings = dict() + rom_settings['algorithm'] = 'mimo_rational_arnoldi' + rom_settings['r'] = 6 + rom_settings['single_side'] = 'observability' + frequency_continuous_k = np.array([0.]) + + # Case Admin - Create results folders + case_name = 'wing' + if target_system == 'aerodynamic': + case_name += '_uvlm' + elif target_system == 'aeroelastic': + case_name += '_coupled' + else: + NameError('Unrecognised system') + + case_nlin_info = '_uinf{:04g}_a{:04g}'.format(u_inf*10, alpha_deg*100) + case_name += case_nlin_info + + self.route_test_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + + # SHARPy nonlinear reference solution + ws = wings.QuasiInfinite(M=M, + aspect_ratio=10, + N=N, + Mstar_fact=M_star_fact, + u_inf=u_inf, + alpha=alpha_deg, + rho=rho, + sweep=0, + n_surfaces=2, + route=self.route_test_dir + '/cases', + case_name=case_name) + + ws.gust_intensity = 0.01 + ws.sigma = 1e-1 + + ws.clean_test_files() + ws.update_derived_params() + ws.set_default_config_dict() + + ws.generate_aero_file() + ws.generate_fem_file() + + frequency_continuous_w = 2 * u_inf * frequency_continuous_k / ws.c_ref + rom_settings['frequency'] = frequency_continuous_w + + ws.config['SHARPy'] = { + 'flow': flow, + 'case': ws.case_name, 'route': ws.route, + 'write_screen': 'off', 'write_log': 'on', + 'log_folder': self.route_test_dir + '/output/', + 'log_file': ws.case_name + '.log'} + + ws.config['BeamLoader'] = { + 'unsteady': 'off', + 'orientation': ws.quat} + + ws.config['AerogridLoader'] = { + 'unsteady': 'off', + 'aligned_grid': 'on', + 'mstar': ws.Mstar_fact * ws.M, + 'freestream_dir': ws.u_inf_direction, + 'wake_shape_generator': 'StraightWake', + 'wake_shape_generator_input': {'u_inf': ws.u_inf, + 'u_inf_direction': ws.u_inf_direction, + 'dt': ws.dt}} + + ws.config['StaticUvlm'] = { + 'rho': ws.rho, + 'velocity_field_generator': 'SteadyVelocityField', + 'velocity_field_input': { + 'u_inf': ws.u_inf, + 'u_inf_direction': ws.u_inf_direction}, + 'rollup_dt': ws.dt, + 'print_info': 'on', + 'horseshoe': 'off', + 'num_cores': 4, + 'n_rollup': 0, + 'rollup_aic_refresh': 0, + 'rollup_tolerance': 1e-4} + + ws.config['StaticCoupled'] = { + 'print_info': 'on', + 'max_iter': 200, + 'n_load_steps': 1, + 'tolerance': 1e-10, + 'relaxation_factor': 0., + 'aero_solver': 'StaticUvlm', + 'aero_solver_settings': { + 'rho': ws.rho, + 'print_info': 'off', + 'horseshoe': 'off', + 'num_cores': 4, + 'n_rollup': 0, + 'rollup_dt': ws.dt, + 'rollup_aic_refresh': 1, + 'rollup_tolerance': 1e-4, + 'velocity_field_generator': 'SteadyVelocityField', + 'velocity_field_input': { + 'u_inf': ws.u_inf, + 'u_inf_direction': ws.u_inf_direction}}, + 'structural_solver': 'NonLinearStatic', + 'structural_solver_settings': {'print_info': 'off', + 'max_iterations': 150, + 'num_load_steps': 4, + 'delta_curved': 1e-1, + 'min_delta': 1e-10, + 'gravity_on': 'off', + 'gravity': 9.81}} + + ws.config['AerogridPlot'] = {'include_rbm': 'off', + 'include_applied_forces': 'on', + 'minus_m_star': 0} + + ws.config['AeroForcesCalculator'] = {'write_text_file': 'on', + 'screen_output': 'on'} + + ws.config['BeamPlot'] = {'include_rbm': 'off', + 'include_applied_forces': 'on'} + + ws.config['BeamCsvOutput'] = {'output_pos': 'on', + 'output_psi': 'on', + 'screen_output': 'on'} + + ws.config['Modal'] = {'print_info': 'on', + 'use_undamped_modes': 'on', + 'NumLambda': 20, + 'rigid_body_modes': 'on', + 'write_modes_vtk': 'on', + 'print_matrices': 'off', + 'save_data': 'on', + 'rigid_modes_cg': 'off'} + + settings = {} + settings['NonLinearDynamicCoupledStep'] = {'print_info': 'off', + 'max_iterations': 950, + 'delta_curved': 1e-1, + 'min_delta': ws.tolerance, + 'newmark_damp': 5e-3, + 'gravity_on': 'on', + 'gravity': 9.81, + 'num_steps': 4, + 'dt': ws.dt, + 'initial_velocity': u_inf} + + relative_motion = 'off' + settings['StepUvlm'] = {'print_info': 'off', + 'horseshoe': 'off', + 'num_cores': 4, + 'n_rollup': 0, + 'convection_scheme': 2, + 'rollup_dt': ws.dt, + 'rollup_aic_refresh': 1, + 'rollup_tolerance': 1e-4, + 'gamma_dot_filtering': 6, + 'vortex_radius': 1e-8, + 'velocity_field_generator': 'SteadyVelocityField', + 'velocity_field_input': {'u_inf': 0 * u_inf, + 'u_inf_direction': [1., 0, 0]}, + 'rho': rho, + 'n_time_steps': 1, + 'dt': ws.dt} + + solver = 'NonLinearDynamicCoupledStep' + ws.config['DynamicCoupled'] = {'structural_solver': solver, + 'structural_solver_settings': settings[solver], + 'aero_solver': 'StepUvlm', + 'aero_solver_settings': settings['StepUvlm'], + 'fsi_substeps': 200, + 'fsi_tolerance': ws.tolerance, + 'relaxation_factor': ws.relaxation_factor, + 'minimum_steps': 1, + 'relaxation_steps': 150, + 'final_relaxation_factor': 0.5, + 'n_time_steps': 1, + 'dt': ws.dt, + 'include_unsteady_force_contribution': 'off', + } + + ws.config['LinearAssembler'] = {'linear_system': 'LinearAeroelastic', + 'linear_system_settings': { + 'track_body': 'off', + 'beam_settings': {'modal_projection': 'on', + 'inout_coords': 'modes', + 'discrete_time': 'off', + 'newmark_damp': 5e-4, + 'discr_method': 'newmark', + 'dt': ws.dt, + 'proj_modes': 'undamped', + 'use_euler': 'on', + 'num_modes': 20, + 'print_info': 'on', + 'gravity': 'off', + 'remove_dofs': [], + 'remove_rigid_states': 'on'}, + 'aero_settings': {'dt': ws.dt, + # 'ScalingDict': {'density': rho, + # 'length': ws.c_ref * 0.5, + # 'speed': u_inf}, + 'integr_order': 2, + 'density': rho, + 'remove_predictor': 'off', + 'use_sparse': 'off', + 'vortex_radius': 1e-8, + 'convert_to_ct': 'on', + 'remove_inputs': ['u_gust'], + # 'rom_method': ['Krylov'], + 'rom_method_settings': { + 'Krylov': {'algorithm': 'mimo_rational_arnoldi', + 'frequency': [0.], + 'r': 4, + 'single_side': 'observability'}} + }, + 'use_euler': 'on', + }, + } + + ws.config['FrequencyResponse'] = {'quick_plot': 'off', + 'frequency_unit': 'k', + 'frequency_bounds': [0.0001, 1.0], + 'num_freqs': 100, + 'frequency_spacing': 'log', + 'target_system': ['aeroelastic'], + } + + ws.config['StabilityDerivatives'] = {'u_inf': ws.u_inf, + 'c_ref': ws.main_chord, + 'b_ref': ws.wing_span, + 'S_ref': ws.wing_span * ws.main_chord, + } + + ws.config['AsymptoticStability'] = {'print_info': 'on'} + + ws.config['SaveParametricCase'] = {'save_case': 'off', + 'parameters': {'alpha': alpha_deg}, + 'save_pmor_items': 'on', + 'save_pmor_subsystems': 'on'} + + ws.config['SaveData'] = {'save_aero': 'off', + 'save_struct': 'off', + 'save_linear': 'on', + 'save_linear_uvlm': 'off', + 'format': 'mat', + } + + ws.config.write() + + if not not_run: + self.data = sharpy.sharpy_main.main(['', ws.route + ws.case_name + '.sharpy']) + + return ws + + def test_derivatives(self): + # target_system_list = ['aerodynamic']#, 'aeroelastic'] + # target_system_list = ['aeroelastic'] + target_system_list = ['aerodynamic', 'aeroelastic'] + for system in target_system_list: + with self.subTest(target_system=system): + self.run_case(target_system=system) + + def run_case(self, target_system): + + if target_system == 'aerodynamic': + nonlinear_solver = ['StaticUvlm'] + elif target_system == 'aeroelastic': + nonlinear_solver = ['StaticCoupled'] + else: + NameError('Unrecognised system') + + case_name_db = [] + not_run = False # for debugging + # Reference Case at 4 degrees + # Run nonlinear simulation and save linerised ROM + alpha_deg_ref = 0 + alpha0 = alpha_deg_ref * np.pi/180 + ref = self.run_sharpy(alpha_deg=alpha_deg_ref, + flow=['BeamLoader', + 'AerogridLoader', ] + + nonlinear_solver + + ['AeroForcesCalculator', + 'Modal', + 'LinearAssembler', + # 'AsymptoticStability', + 'StabilityDerivatives', + 'SaveData', + 'SaveParametricCase'], + target_system=target_system, + not_run=False) + u_inf = ref.u_inf + ref_case_name = ref.case_name + case_name_db.append(ref_case_name) + qS = 0.5 * ref.rho * u_inf ** 2 * ref.c_ref * ref.wing_span + if self.print_info: + print(f'Reference span: {ref.wing_span} m') + print(f'Reference chord: {ref.main_chord} m') + print(f'Aspect ratio: {ref.aspect_ratio}') + + # Run nonlinear cases in the vicinity + nonlinear_sim_flow = ['BeamLoader', + 'AerogridLoader'] + nonlinear_solver + ['AeroForcesCalculator', 'SaveParametricCase'] + alpha_deg_min = alpha_deg_ref - 5e-3 + alpha_deg_max = alpha_deg_ref + 5e-3 + n_evals = 11 + alpha_vec = np.linspace(alpha_deg_min, alpha_deg_max, n_evals) + nlin_forces_g = np.zeros((n_evals, 3)) + nlin_forces_a = np.zeros((n_evals, 3)) + for ith, alpha in enumerate(alpha_vec): + if alpha == alpha_deg_ref: + case_name = ref_case_name + continue + else: + case = self.run_sharpy(alpha, flow=nonlinear_sim_flow, target_system=target_system, not_run=not_run) + case_name_db.append(case.case_name) + case_name = case.case_name + nlin_forces = np.loadtxt(self.route_test_dir + + '/output/{:s}/forces/forces_aeroforces.txt'.format(case_name), + skiprows=1, + delimiter=',') + nlin_forces_g[ith, :3] = nlin_forces[1:4] + nlin_forces_a[ith, :3] = nlin_forces[7:10] + nlin_forces_g /= qS + nlin_forces_a /= qS + + lift_poly = np.polyfit(alpha_vec * np.pi/180, nlin_forces_g[:, 2], deg=1) + nonlin_cla = lift_poly[0] + drag_poly = np.polyfit(alpha_vec * np.pi/180, nlin_forces_g[:, 0], deg=2) + nonlin_cda = 2 * drag_poly[0] * alpha0 + drag_poly[1] + if self.print_info: + print('Nonlinear coefficients') + print('CLa', nonlin_cla) + print('CDa', nonlin_cda) + + lift_poly = np.polyfit(alpha_vec * np.pi/180, nlin_forces_a[:, 2], deg=1) + nonlin_cza = lift_poly[0] + drag_poly = np.polyfit(alpha_vec * np.pi/180, nlin_forces_a[:, 0], deg=2) + nonlin_cxa = 2 * drag_poly[0] * alpha0 + drag_poly[1] + if self.print_info: + print('Nonlinear coefficients - body axes') + print('CZa', nonlin_cza) + print('CXa', nonlin_cxa) + + # Get Linear ROM at reference case + import scipy.io as scio + linss_data = scio.loadmat(self.route_test_dir + '/output/{:s}/savedata/{:s}.linss.mat'.format(ref_case_name, + ref_case_name)) + + # Steady State transfer function + if target_system == 'aerodynamic': + ss = libss.StateSpace.load_from_h5(self.route_test_dir + + f'/output/{ref_case_name}/' + + f'save_pmor_data/{ref_case_name}_aerostatespace.h5') + else: + ss = libss.StateSpace.load_from_h5(self.route_test_dir + + f'/output/{ref_case_name}/' + + f'save_pmor_data/{ref_case_name}_statespace.h5') + + H0 = ss.freqresp(np.array([1e-5]))[:, :, 0].real + + cga = algebra.quat2rotation(algebra.euler2quat(np.array([0, alpha0, 0]))) + + vx_ind = 20 # x_a input index + vz_ind = 20 + 2 # z_a input index + + n_evals = 2 + forces = np.zeros((n_evals, 4)) + moments = np.zeros_like(forces) + body_forces = np.zeros_like(forces) + + phi = libss.Gain.load_from_h5(self.route_test_dir + + f'/output/{ref_case_name}/' + + f'save_pmor_data/{ref_case_name}_modal_structrob.h5').value.real # other Gain features not needed + + eps = 1e-5 + dalpha_vec = np.array([-eps, +eps]) + for i_alpha, dalpha in enumerate(dalpha_vec): + alpha = alpha0 + dalpha # rad + deuler = np.array([0, dalpha, 0]) + euler0 = np.array([0, alpha0, 0]) + + u = np.zeros(ss.inputs) # input vector + V0 = np.array([-1, 0, 0], dtype=float) * u_inf # G + Vp = u_inf * np.array([-np.cos(dalpha), 0, -np.sin(dalpha)]) # G + + dvg = Vp - V0 # G + dva = cga.T.dot(dvg) # A + dvz = dva[2] + dvx = dva[0] + + # print(Vp) + # print(algebra.euler2rot(deuler).T.dot(V0)) + # print(algebra.der_Peuler_by_v(euler0*0, V0)) + Vp2 = algebra.euler2rot(euler0 * 0).T.dot(V0) + algebra.der_Peuler_by_v(euler0 * 0, V0).dot(deuler) + dva2 = cga.T.dot(algebra.euler2rot(deuler).T.dot(V0) - V0) + dva3 = cga.T.dot(Vp2 - V0) + # print('{:.4f}\t'.format((alpha0 + dalpha) * 180 / np.pi), dva2, dva3, dva3-dva2) + dvz = dva3[2] + dvx = dva3[0] + + # Need to scale the mode shapes by the rigid body mode factor + u[vx_ind] = dvx / phi[-9, 0] + u[vz_ind] = dvz / phi[-7, 2] + + # and the same with the output forces + flin = H0.dot(u)[:3].real / phi[-9, 0] # A + mlin = np.linalg.inv(phi[-6:-3, 3:6].T).dot(H0.dot(u)[3:6].real) # A + F0A = linss_data['forces_aero_beam_dof'][0, :3].real / phi[-9, 0] # A - forces at the linearisation + M0A = linss_data['forces_aero_beam_dof'][0, 3:6].real / phi[-6, 3] # A - forces at the linearisation + LD0 = cga.dot(F0A) # Lift and drag at the linearisation point + M0G = cga.dot(M0A) + + forces[i_alpha, 0] = (alpha0 + dalpha) * 180 / np.pi # deg + LD = LD0 + algebra.der_Ceuler_by_v(euler0, F0A).dot(deuler) + cga.dot(flin) # stability axes + forces[i_alpha, 1:] = LD / qS + + u_body = np.zeros_like(u) + u_body[vz_ind] = dvz / phi[-7, 2] + body_forces[i_alpha, 1:] = H0.dot(u_body)[:3].real / phi[-9, 0] / qS # C_Z_w in A frame + if self.print_info: + print('Transfer function coefficients (normalised by qS)') + print(H0[:3, vx_ind:vx_ind+3].real / phi[-7, 2] / phi[-9, 0] / qS) + + MD = M0G + algebra.der_Ceuler_by_v(euler0, M0A).dot(deuler) + cga.dot(mlin) + moments[i_alpha, 0] = forces[i_alpha, 0] + moments[i_alpha, 1:] = MD / qS / ref.main_chord + + cla = (forces[-1, -1] - forces[0, -1]) / (forces[-1, 0] - forces[0, 0]) * 180 / np.pi + cda = (forces[-1, 1] - forces[0, 1]) / (forces[-1, 0] - forces[0, 0]) * 180 / np.pi + cma = (moments[-1, 2] - moments[0, 2]) / (moments[-1, 0] - moments[0, 0]) * 180 / np.pi + + if self.print_info: + print('Lift curve slope perturbation {:.6e}'.format(cla)) + print('Drag curve slope perturbation {:.6e}'.format(cda)) + print('Moment curve slope perturbation {:.6e}'.format(cma)) + + # body derivative + # czwa = (body_forces[-1, -1] - body_forces[0, -1]) / (forces[-1, 0] - forces[0, 0]) * 180 / np.pi + # print('Vertical force with vertical velocity perturbation {:.6e}'.format(czwa)) + + with h5py.File(self.route_test_dir + '/output/' + ref_case_name + + '/derivatives/aerodynamic_stability_derivatives.h5', 'r') as f: + sharpy_force_angle = h5utils.load_h5_in_dict(f)['force_angle_velocity'] + + linsubtests = ((2, cla, 'lift'), + (0, cda, 'drag'), + (4, cma, 'moment')) + for test in linsubtests: + with self.subTest(test): + try: + np.testing.assert_almost_equal(sharpy_force_angle[test[0], 1], test[1], decimal=3) + except AssertionError as e: + print('Error Linear perturbation in {:s}'.format(test[2])) + print(e) + print('Pct error', np.abs(sharpy_force_angle[test[0], 1] - test[1]) / test[1] * 100) + raise AssertionError + + nonlinsubtests = ((2, nonlin_cla, 'lift'), + (0, nonlin_cda, 'drag'), + ) + + for test in nonlinsubtests: + with self.subTest(test): + try: + np.testing.assert_almost_equal(sharpy_force_angle[test[0], 1], test[1], decimal=2) + except AssertionError as e: + print('Error nonlinear perturbation in {:s}'.format(test[2])) + print(e) + print('Pct error', np.abs(sharpy_force_angle[test[0], 1] - test[1]) / test[1] * 100) + raise AssertionError + return forces, moments + + def tearDown(self): + import shutil + folders = ['cases', 'output'] + for folder in folders: + shutil.rmtree(self.route_test_dir + '/' + folder) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/linear/frequencyutils/test_frequency_utils.py b/tests/linear/frequencyutils/test_frequency_utils.py index 312de7830..bb4194d5f 100644 --- a/tests/linear/frequencyutils/test_frequency_utils.py +++ b/tests/linear/frequencyutils/test_frequency_utils.py @@ -21,7 +21,7 @@ def setUp(self): c = np.load(self.test_dir + '/src/c.npy') d = np.load(self.test_dir + '/src/d.npy') - self.sys = libss.ss(a, b, c, d, dt=None) + self.sys = libss.StateSpace(a, b, c, d, dt=None) # self.sys = libss.random_ss(10, 4, 3, dt=0.1, stable=True) # self.sys = libss.disc2cont(self.sys) diff --git a/tests/linear/goland_wing/test_goland_flutter.py b/tests/linear/goland_wing/test_goland_flutter.py index 530ce3fe5..7bec6fcb9 100644 --- a/tests/linear/goland_wing/test_goland_flutter.py +++ b/tests/linear/goland_wing/test_goland_flutter.py @@ -1,7 +1,7 @@ import numpy as np import os import unittest -import cases.templates.flying_wings as wings +import sharpy.cases.templates.flying_wings as wings import sharpy.sharpy_main @@ -160,8 +160,7 @@ def setup(self): ws.config['Modal'] = {'NumLambda': 20, 'rigid_body_modes': 'off', 'print_matrices': 'on', - 'keep_linear_matrices': 'on', - 'write_dat': 'off', + 'save_data': 'off', 'rigid_modes_cg': 'off', 'continuous_eigenvalues': 'off', 'dt': 0, diff --git a/tests/linear/gusts/__init__.py b/tests/linear/gusts/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/linear/gusts/test_external_gust.py b/tests/linear/gusts/test_external_gust.py new file mode 100644 index 000000000..d397f059d --- /dev/null +++ b/tests/linear/gusts/test_external_gust.py @@ -0,0 +1,475 @@ +import numpy as np +import sharpy.utils.algebra as algebra +import sharpy.sharpy_main as smain +import unittest +import sharpy.cases.templates.flying_wings as wings +import os +import shutil + + +def generate_sharpy(alpha=0., case_name='hale_static', case_route='./', **kwargs): + + output_route = kwargs.get('output_route', './output/') + m = kwargs.get('M', 4) + rho = kwargs.get('rho', 1.225) + tolerance = kwargs.get('tolerance', 1e-5) + u_inf = kwargs.get('u_inf', 10) + tsteps = kwargs.get('tsteps', 100) + + ws = wings.QuasiInfinite(M=m, + aspect_ratio=10, + N=8, + Mstar_fact=10, + u_inf=u_inf, + alpha=alpha, + rho=rho, + sweep=0, + n_surfaces=2, + route=case_route, + case_name=case_name) + + ws.gust_intensity = 0.01 + ws.sigma = 1e-1 + + ws.clean_test_files() + ws.update_derived_params() + ws.set_default_config_dict() + + ws.generate_aero_file() + ws.generate_fem_file() + + settings = dict() + + settings['SHARPy'] = {'case': case_name, + 'route': case_route, + 'flow': kwargs.get('flow', []), + 'write_screen': 'off', + 'write_log': 'on', + 'log_folder': output_route, + 'log_file': case_name + '.log'} + + settings['BeamLoader'] = {'unsteady': 'on', + 'orientation': algebra.euler2quat(np.array([0., + alpha, + 0.]))} + + settings['AerogridLoader'] = {'unsteady': 'on', + 'aligned_grid': 'on', + 'mstar': int(kwargs.get('wake_length', 10) * m), + 'control_surface_deflection': ['', ''], + 'control_surface_deflection_generator_settings': + {'0': {}, + '1': {}}, + 'wake_shape_generator': 'StraightWake', + 'wake_shape_generator_input': { + 'u_inf': u_inf, + 'u_inf_direction': [1., 0., 0.], + 'dt': ws.dt, + }, + } + + settings['NonLinearStatic'] = {'print_info': 'off', + 'max_iterations': 150, + 'num_load_steps': 1, + 'delta_curved': 1e-1, + 'min_delta': tolerance, + 'gravity_on': kwargs.get('gravity', 'on'), + 'gravity': 9.81, + 'initial_position': [0., 0., 0.]} + + settings['StaticUvlm'] = {'print_info': 'on', + 'horseshoe': kwargs.get('horseshoe', 'off'), + 'num_cores': 4, + 'velocity_field_generator': 'SteadyVelocityField', + 'velocity_field_input': {'u_inf': u_inf, + 'u_inf_direction': [1., 0, 0]}, + 'rho': rho} + + settings['StaticCoupled'] = {'print_info': 'off', + 'structural_solver': 'NonLinearStatic', + 'structural_solver_settings': settings['NonLinearStatic'], + 'aero_solver': 'StaticUvlm', + 'aero_solver_settings': settings['StaticUvlm'], + 'max_iter': 100, + 'n_load_steps': kwargs.get('n_load_steps', 1), + 'tolerance': kwargs.get('fsi_tolerance', 1e-5), + 'relaxation_factor': kwargs.get('relaxation_factor', 0.2)} + + settings['BeamPlot'] = {'include_FoR': 'on'} + + settings['AerogridPlot'] = {'include_rbm': 'off', + 'include_applied_forces': 'on', + 'minus_m_star': 0, + 'u_inf': u_inf + } + + settings['AeroForcesCalculator'] = {'write_text_file': 'on', + 'text_file_name': 'aeroforces.txt', + 'screen_output': 'on', + 'coefficients': True, + 'q_ref': 0.5 * rho * u_inf ** 2, + 'S_ref': 12.809, + } + + settings['BeamPlot'] = {'include_rbm': 'on', + 'include_applied_forces': 'on', + 'include_FoR': 'on'} + + struct_solver_settings = {'print_info': 'off', + 'max_iterations': 950, + 'delta_curved': 1e-6, + 'min_delta': tolerance, + 'newmark_damp': 5e-3, + 'gravity_on': kwargs.get('gravity', 'on'), + 'gravity': 9.81, + 'num_steps': 1, + 'dt': ws.dt} + + gust_vec = kwargs.get('nl_gust', None) + if gust_vec is not None: + t_dom = np.linspace(0, ws.dt * (tsteps-1), tsteps) + np.savetxt(ws.route + '/gust.txt', np.column_stack(( + t_dom, + np.zeros_like(t_dom), + np.zeros_like(t_dom), + gust_vec + ))) + + step_uvlm_settings = {'print_info': 'on', + 'num_cores': 4, + 'convection_scheme': 0, #ws.wake_type, + 'vortex_radius': 1e-7, + 'velocity_field_generator': 'GustVelocityField', + 'velocity_field_input': {'u_inf': u_inf * 1, + 'u_inf_direction': [1., 0., 0.], + 'relative_motion': 'on', + 'gust_shape': 'time varying', + 'gust_parameters': { + 'file': ws.route + '/gust.txt', + }}, + 'rho': rho, + 'n_time_steps': tsteps, + 'dt': ws.dt, + 'gamma_dot_filtering': 3} + + settings['DynamicCoupled'] = {'print_info': 'on', + 'structural_solver': 'NonLinearDynamicPrescribedStep', + 'structural_solver_settings': struct_solver_settings, + 'aero_solver': 'StepUvlm', + 'aero_solver_settings': step_uvlm_settings, + 'fsi_substeps': 200, + 'fsi_tolerance': tolerance, + 'relaxation_factor': 0.3, + 'minimum_steps': 1, + 'relaxation_steps': 150, + 'final_relaxation_factor': 0.5, + 'n_time_steps': tsteps, # ws.n_tstep, + 'dt': ws.dt, + 'include_unsteady_force_contribution': 'on', + 'steps_without_unsteady_force': 5, + 'postprocessors': ['BeamPlot', 'AerogridPlot', 'WriteVariablesTime'], + 'postprocessors_settings': {'BeamLoads': {'csv_output': 'off'}, + 'BeamPlot': {'include_rbm': 'on', + 'include_applied_forces': 'on'}, + 'AerogridPlot': { + 'u_inf': u_inf, + 'include_rbm': 'on', + 'include_applied_forces': 'on', + 'minus_m_star': 0}, + 'WriteVariablesTime': { + 'cleanup_old_solution': 'on', + 'delimiter': ',', + 'FoR_variables': ['total_forces', + 'total_gravity_forces', + 'for_pos', 'quat'], + }}} + + settings['Modal'] = {'print_info': True, + 'use_undamped_modes': True, + 'NumLambda': 50, + 'rigid_body_modes': 'off', + 'write_modes_vtk': 'on', + 'print_matrices': 'on', + 'save_data': 'on', + 'continuous_eigenvalues': 'off', + 'dt': ws.dt, + 'plot_eigenvalues': False, + 'rigid_modes_ppal_axes': 'on'} + # ROM settings + rom_settings = dict() + rom_settings['algorithm'] = 'mimo_rational_arnoldi' + rom_settings['r'] = 10 + rom_settings['frequency'] = np.array([0], dtype=float) + rom_settings['single_side'] = 'observability' + + settings['LinearAssembler'] = {'linear_system': 'LinearAeroelastic', + 'linearisation_tstep': -1, + 'linear_system_settings': { + 'beam_settings': {'modal_projection': 'off', + 'inout_coords': 'modes', + 'discrete_time': 'on', + 'newmark_damp': 0.5e-4, + 'discr_method': 'newmark', + 'dt': ws.dt, + 'proj_modes': 'undamped', + 'use_euler': 'on', + 'num_modes': 20, + 'print_info': 'on', + 'gravity': kwargs.get('gravity', 'on'), + 'remove_dofs': [], + 'remove_rigid_states': 'off'}, + 'aero_settings': {'dt': ws.dt, + 'integr_order': 2, + 'density': rho, + 'remove_predictor': 'off', + 'use_sparse': False, + 'vortex_radius': 1e-7, + 'convert_to_ct': 'off', + 'gust_assembler': kwargs.get('gust_name', ''), + 'gust_assembler_inputs': kwargs.get('gust_settings', {}), + }, + 'track_body': 'on', + 'use_euler': 'on', + }, + } + + settings['AsymptoticStability'] = { + 'print_info': 'on', + 'modes_to_plot': [], + # 'velocity_analysis': [27, 29, 3], + 'display_root_locus': 'off', + 'frequency_cutoff': 0, + 'export_eigenvalues': 'on', + 'num_evals': 100} + + settings['FrequencyResponse'] = {'target_system': ['aeroelastic', 'aerodynamic', 'structural'], + 'quick_plot': 'off', + 'frequency_spacing': 'log', + 'frequency_unit': 'w', + 'frequency_bounds': [1e-3, 1e3], + 'num_freqs': 200, + 'print_info': 'on'} + + settings['PickleData'] = {} + + settings['LinDynamicSim'] = {'n_tsteps': tsteps, + 'dt': ws.dt, + 'write_dat': ['x', 'y', 'u'], + 'input_generators': kwargs.get('linear_input_generators', []), + 'postprocessors': ['AerogridPlot'], + 'postprocessors_settings': + {'AerogridPlot': {'include_rbm': 'on', + 'include_applied_forces': 'on', + 'minus_m_star': 0}, } + } + + case_file = ws.settings_to_config(settings) + return case_file + + +def run_case(case_file, pickle_file=None): + if pickle_file is not None: + restart = True + else: + restart = False + if restart: + data = smain.main(['', case_file, '-r', pickle_file]) + else: + data = smain.main(['', case_file]) + + return data + + +class TestGustAssembly(unittest.TestCase): + + route_test_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + directories = ['cases', 'output', 'inputs'] + + time_steps = 80 + gust_intensity = 0.01 + gust_ramp = 5 + + M = 4 + u_inf = 1 + + gust_vec = np.zeros(time_steps) + + @classmethod + def setUpClass(cls): + for folder in cls.directories: + os.makedirs(cls.route_test_dir + '/' + folder, exist_ok=True) + + # Gust input profile + cls.gust_vec[5:cls.gust_ramp+5] = np.linspace(0, cls.gust_intensity, cls.gust_ramp) + cls.gust_vec[5+ cls.gust_ramp:] = cls.gust_intensity + + @classmethod + def tearDownClass(cls): + for folder in cls.directories: + if os.path.isdir(cls.route_test_dir + '/' + folder): + shutil.rmtree(cls.route_test_dir + '/' + folder) + + def linear_response(self): + + np.savetxt(self.route_test_dir + '/inputs/gust_linear.txt', self.gust_vec) + + input_generators = [{'name': 'u_gust', + 'index': 0, + 'file_path': self.route_test_dir + '/inputs/gust_linear.txt'}] + + data = self.run_sharpy_linear(gust_name='LeadingEdge', linear_input_generators=input_generators)# + + return data + + def nonlinear_response(self): + + data = self.run_sharpy_nonlinear(nl_gust=self.gust_vec) + + return data + + def test_gust(self): + + linear = self.linear_response() + nonlinear = self.nonlinear_response() + final_linear = linear.aero.timestep_info[-1] + final_nonlinear = nonlinear.aero.timestep_info[-1] + with self.subTest('zeta'): + np.testing.assert_almost_equal(final_linear.zeta[0][2, 0, -1], + final_nonlinear.zeta[0][2, 0, -1], decimal=4) + with self.subTest('forces'): + np.testing.assert_almost_equal(np.sum(final_linear.forces[0][2, :, 0]), + np.sum(final_nonlinear.forces[0][2, :, 0]), decimal=4) + + with self.subTest('gamma'): + np.testing.assert_almost_equal(final_linear.gamma[0][2, 2], + final_nonlinear.gamma[0][2, 2], decimal=4) + + def wingtip_timeseries(self, data): + nsteps = len(data.aero.timestep_info) + + wingtip = np.zeros(nsteps) + + for i in range(nsteps): + wingtip[i] = data.aero.timestep_info[i].zeta[0][2, 0, -1] + + return wingtip + + def gamma_timeseries(self, data): + nsteps = len(data.aero.timestep_info) + + wingtip = np.zeros(nsteps) + + for i in range(nsteps): + wingtip[i] = data.aero.timestep_info[i].gamma[0][0, -1] + + return wingtip + + def run_sharpy_nonlinear(self, **kwargs): + flow = [ + 'BeamLoader', + 'AerogridLoader', + 'StaticUvlm', + 'DynamicCoupled', + # 'AeroForcesCalculator', + 'PickleData', + ] + + + alpha = 0 #2 * np.pi / 180 + + # Discretisation + n_elem_multiplier = 1.5 + wake_length = 10 + horseshoe = False + + case_file = generate_sharpy(alpha=alpha, + case_name='nonlinear', + case_route=self.route_test_dir + '/cases/', + output_route=self.route_test_dir + '/output/', + flow=flow, + u_inf=self.u_inf, + M=self.M, + n_elem_multiplier=n_elem_multiplier, + horseshoe=horseshoe, + wake_length=wake_length, + relaxation_factor=0.6, + tolerance=1e-5, + gravity='off', + fsi_tolerance=1e-5, + tsteps=self.time_steps, + **kwargs, + ) + + data = run_case(case_file) + + return data + + def run_sharpy_linear(self, **kwargs): + + flow = [ + 'BeamLoader', + 'AerogridLoader', + 'StaticCoupled', + 'Modal', + 'AeroForcesCalculator', + 'LinearAssembler', + 'LinDynamicSim', + 'PickleData', + ] + + restart = False + if restart: + flow = ['LinDynamicSim'] + pickle_file = self.route_test_dir + '/output/linear.pkl' + else: + pickle_file = None + alpha = 0 #2 * np.pi / 180 + elevator = 0 #-0.5 * np.pi / 180 + thrust = 0 #5 + + # Discretisation + M = 4 + n_elem_multiplier = 1.5 + wake_length = 10 + horseshoe = False + + case_file = generate_sharpy(alpha=alpha, + case_name='linear', + case_route=self.route_test_dir + '/cases/', + output_route=self.route_test_dir + '/output', + flow=flow, + u_inf=self.u_inf, + M=self.M, + n_elem_multiplier=n_elem_multiplier, + horseshoe=horseshoe, + wake_length=wake_length, + relaxation_factor=0.6, + tolerance=1e-5, + gravity='off', + fsi_tolerance=1e-5, + tsteps=self.time_steps, + **kwargs, + ) + + data = run_case(case_file, pickle_file) + + return data + + def assert_gust_propagation(self, path_to_input, output_route): + x_sharpy = np.loadtxt(output_route + '/lindynamicsim/x_out.dat') + u_in = np.loadtxt(path_to_input) + + np.testing.assert_array_almost_equal(x_sharpy[1:, 0], u_in[:-1]) + + def tearDown(self): + folders = ['cases', 'output'] + import shutil + + for folder in folders: + path = self.route_test_dir + '/' + folder + if os.path.isdir(path): + shutil.rmtree(path) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/linear/gusts/test_linear_gusts.py b/tests/linear/gusts/test_linear_gusts.py new file mode 100644 index 000000000..5f6079a3e --- /dev/null +++ b/tests/linear/gusts/test_linear_gusts.py @@ -0,0 +1,305 @@ +import numpy as np +import os +import unittest +import sharpy.cases.templates.flying_wings as wings +import sharpy.sharpy_main +from sharpy.linear.assembler.lineargustassembler import campbell +import pickle + + +class TestGolandControlSurface(unittest.TestCase): + + def setUp(self): + self.route_test_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + + def run_sharpy(self, flow, **kwargs): + # Problem Set up + u_inf = 2. + alpha_deg = 0. + rho = 1.02 + num_modes = 4 + restart = kwargs.get('restart', False) + + # Lattice Discretisation + M = 4 + N = 16 + M_star_fact = 1 + + # Linear UVLM settings + integration_order = 2 + remove_predictor = kwargs.get('remove_predictor', False) + use_sparse = True + + # Case Admin - Create results folders + case_name = 'goland_cs' + case_nlin_info = 'M%dN%dMs%d_nmodes%d' % (M, N, M_star_fact, num_modes) + + case_name += case_nlin_info + + fig_folder = self.route_test_dir + '/figures/' + os.makedirs(fig_folder, exist_ok=True) + + # SHARPy nonlinear reference solution + ws = wings.GolandControlSurface(M=M, + N=N, + Mstar_fact=M_star_fact, + u_inf=u_inf, + alpha=alpha_deg, + # cs_deflection=[0, 0], + rho=rho, + sweep=0, + physical_time=2, + n_surfaces=2, + route=self.route_test_dir + '/cases', + case_name=case_name) + + ws.gust_intensity = 0.01 + ws.sigma = 1 + + ws.clean_test_files() + ws.update_derived_params() + ws.set_default_config_dict() + + ws.generate_aero_file() + ws.generate_fem_file() + + x0 = np.zeros(256) + lin_tsteps = 40 + u_vec = np.zeros((lin_tsteps, num_modes // 2 * 3 + 1 + 2)) + # elevator + u_vec[5:, 5] = 10 * np.pi / 180 + # gust + u_vec[:, 4] = np.sin(np.linspace(0, 2*np.pi, lin_tsteps)) + ws.create_linear_files(x0, u_vec) + np.savetxt(self.route_test_dir + '/cases/elevator.txt', u_vec[:, 5]) + np.savetxt(self.route_test_dir + '/cases/gust.txt', u_vec[:, 4]) + + ws.config['SHARPy'] = { + 'flow': flow, + 'case': ws.case_name, 'route': ws.route, + 'write_screen': 'off', 'write_log': 'on', + 'log_folder': self.route_test_dir + '/output/' + ws.case_name + '/', + 'log_file': ws.case_name + '.log'} + + ws.config['BeamLoader'] = { + 'unsteady': 'off', + 'orientation': ws.quat} + + ws.config['AerogridLoader'] = { + 'unsteady': 'off', + 'aligned_grid': 'on', + 'mstar': ws.Mstar_fact * ws.M, + 'freestream_dir': ws.u_inf_direction, + 'wake_shape_generator': 'StraightWake', + 'wake_shape_generator_input': {'u_inf': ws.u_inf, + 'u_inf_direction': ws.u_inf_direction, + 'dt': ws.dt}} + + ws.config['StaticUvlm'] = { + 'rho': ws.rho, + 'velocity_field_generator': 'SteadyVelocityField', + 'velocity_field_input': { + 'u_inf': ws.u_inf, + 'u_inf_direction': ws.u_inf_direction}, + 'rollup_dt': ws.dt, + 'print_info': 'on', + 'horseshoe': 'off', + 'num_cores': 4, + 'n_rollup': 0, + 'rollup_aic_refresh': 0, + 'rollup_tolerance': 1e-4} + + ws.config['StaticCoupled'] = { + 'print_info': 'on', + 'max_iter': 200, + 'n_load_steps': 1, + 'tolerance': 1e-10, + 'relaxation_factor': 0., + 'aero_solver': 'StaticUvlm', + 'aero_solver_settings': { + 'rho': ws.rho, + 'print_info': 'off', + 'horseshoe': 'off', + 'num_cores': 4, + 'n_rollup': 0, + 'rollup_dt': ws.dt, + 'rollup_aic_refresh': 1, + 'rollup_tolerance': 1e-4, + 'velocity_field_generator': 'SteadyVelocityField', + 'velocity_field_input': { + 'u_inf': ws.u_inf, + 'u_inf_direction': ws.u_inf_direction}}, + 'structural_solver': 'NonLinearStatic', + 'structural_solver_settings': {'print_info': 'off', + 'max_iterations': 150, + 'num_load_steps': 4, + 'delta_curved': 1e-1, + 'min_delta': 1e-10, + 'gravity_on': 'on', + 'gravity': 9.81}} + + ws.config['AerogridPlot'] = {'include_rbm': 'off', + 'include_applied_forces': 'on', + 'minus_m_star': 0} + + ws.config['AeroForcesCalculator'] = {'write_text_file': 'on', + 'text_file_name': ws.case_name + '_aeroforces.csv', + 'screen_output': 'on', + 'unsteady': 'off'} + + ws.config['BeamPlot'] = {'include_rbm': 'off', + 'include_applied_forces': 'on'} + + ws.config['Modal'] = {'NumLambda': 20, + 'rigid_body_modes': 'off', + 'print_matrices': 'on', + 'save_data': 'off', + 'rigid_modes_cg': 'off', + 'continuous_eigenvalues': 'off', + 'dt': 0, + 'plot_eigenvalues': False, + 'max_rotation_deg': 15., + 'max_displacement': 0.15, + 'write_modes_vtk': True, + 'use_undamped_modes': True} + + ws.config['LinearAssembler'] = {'linear_system': 'LinearAeroelastic', + 'linear_system_settings': { + 'beam_settings': {'modal_projection': 'on', + 'inout_coords': 'modes', + 'discrete_time': 'on', + 'newmark_damp': 0.5e-4, + 'discr_method': 'newmark', + 'dt': ws.dt, + 'proj_modes': 'undamped', + 'use_euler': 'off', + 'num_modes': num_modes, + 'print_info': 'off', + 'gravity': 'on', + 'remove_sym_modes': 'on', + 'remove_dofs': []}, + 'aero_settings': {'dt': ws.dt, + 'integr_order': integration_order, + 'density': ws.rho, + 'remove_predictor': remove_predictor, + 'use_sparse': use_sparse, + 'remove_inputs': [], + 'gust_assembler': 'LeadingEdge', + }, + } + } + + ws.config['LinDynamicSim'] = {'n_tsteps': lin_tsteps, + 'dt': ws.dt, + 'input_generators': [ + {'name': 'control_surface_deflection', + 'index': 0, + 'file_path': self.route_test_dir + '/cases/elevator.txt'}, + {'name': 'u_gust', + 'index': 0, + 'file_path': self.route_test_dir + '/cases/gust.txt'} + ], + 'postprocessors': ['AerogridPlot'], + 'postprocessors_settings': + {'AerogridPlot': {'include_rbm': 'on', + 'include_applied_forces': 'on', + 'minus_m_star': 0}, } + } + + ws.config['PickleData'] = {} + ws.config.write() + + if not restart: + data = sharpy.sharpy_main.main(['', ws.route + ws.case_name + '.sharpy']) + else: + with open(self.route_test_dir + '/output/{:s}.pkl'.format(ws.case_name), 'rb') as f: + data = pickle.load(f) + + return data + + def run_linear_sharpy(self, **kwargs): + flow = ['BeamLoader', 'AerogridLoader', + 'StaticCoupled', + 'Modal', + 'LinearAssembler', + 'LinDynamicSim', + 'PickleData'] + + restart = False + if restart: + flow = ['LinDynamicSim'] + + data = self.run_sharpy(flow, restart=restart, **kwargs) + + return data + + def extract_u_inf(self, data): + ts = len(data.aero.timestep_info) + _, m_chord, n_span = data.aero.timestep_info[0].u_ext[0].shape + u_inf_ext = np.zeros((ts, m_chord, n_span)) + for i_ts in range(ts): + u_inf_ext[i_ts, :, :] = data.aero.timestep_info[i_ts].u_ext[0][2, :, :] + + return u_inf_ext + + def test_linear_gust(self): + test_conditions = [ + {'remove_predictor': True}, + {'remove_predictor': False}, + ] + for test in test_conditions: + with self.subTest(test): + data = self.run_linear_sharpy(**test) + u_gust_in = np.loadtxt(self.route_test_dir + '/cases/gust.txt') + u_inf_ext = self.extract_u_inf(data) + if test['remove_predictor']: + predictor_offset = 0 + else: + # input defined at time step n+1 + predictor_offset = 1 + + # test leading edge value is equal to input + np.testing.assert_array_almost_equal(u_inf_ext[1 + predictor_offset:, 0, 0], + u_gust_in[:-1-predictor_offset]) + + # check convection in panels downstream + for i_chord in range(1, u_inf_ext.shape[1]): + np.testing.assert_array_almost_equal(u_inf_ext[predictor_offset + 1 + i_chord:-i_chord, i_chord, 0], + u_inf_ext[predictor_offset + 1 + i_chord - 1:-(i_chord + 1), i_chord - 1, 0]) + + def tearDown(self): + import shutil + folders = ['cases', 'figures', 'output'] + for folder in folders: + if os.path.isdir(self.route_test_dir + '/' + folder): + shutil.rmtree(self.route_test_dir + '/' + folder) + + +class TestGusts(unittest.TestCase): + + def test_campbell(self): + """ + Test that the Campell approximation to the Von Karman filter is equivalent in continuous and + discrete time + + """ + sigma_w = 1 + length_scale = 1 + velocity = 1 + dt = 1e-1 + omega_w = np.logspace(-3, 0, 10) + + ss_ct = campbell(sigma_w, length_scale, velocity) + + ss_dt = campbell(sigma_w, length_scale, velocity, dt=dt) + + G_ct = ss_ct.freqresp(omega_w) + G_dt = ss_dt.freqresp(omega_w) + + np.testing.assert_array_almost_equal(G_ct[0, 0, :].real, G_dt[0, 0, :].real, decimal=3) + + +if __name__ == '__main__': + unittest.main() + + diff --git a/tests/linear/horten/__init__.py b/tests/linear/horten/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/linear/horten/test_horten.py b/tests/linear/horten/test_horten.py new file mode 100644 index 000000000..5c8eef3e5 --- /dev/null +++ b/tests/linear/horten/test_horten.py @@ -0,0 +1,366 @@ +import sharpy.utils.algebra as algebra +from sharpy.cases.hangar.richards_wing import Baseline +import sharpy.sharpy_main +import numpy as np +import configobj +import unittest +import os +import shutil + +route_test_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + + +def run_rom_convergence(case_name, case_route='./cases/', output_folder='./output/', **kwargs): + M = kwargs.get('M', 4) + N = kwargs.get('N', 11) + Msf = kwargs.get('Msf', 10) + + trim = kwargs.get('trim', True) + + rho_fact = 1. + track_body = True + payload = 0 + u_inf = kwargs.get('u_inf', 30) + + use_euler = True + if use_euler: + orient = 'euler' + else: + orient = 'quat' + + case_name += 'M%gN%gMsf%g_u%g' % (M, N, Msf, u_inf) + + # M4N11Msf5 + alpha_deg = 3.974 + cs_deflection = 0.3582 + thrust = 4.8062 + + # ROM settings + rom_settings = dict() + rom_settings['algorithm'] = 'mimo_rational_arnoldi' + rom_settings['r'] = 10 + rom_settings['frequency'] = np.array([0], dtype=float) + rom_settings['single_side'] = 'observability' + + case_name += 'rom_%g_%s' % (rom_settings['r'], rom_settings['single_side'][:3]) + + ws = Baseline(M=M, + N=N, + Mstarfactor=Msf, + u_inf=u_inf, + rho=1.02, + alpha_deg=alpha_deg, # 7.7563783342984385, + roll_deg=0, + cs_deflection_deg=cs_deflection, # -6.733360628875144, + thrust=thrust, # 10.140622253017584, + physical_time=20, + case_name=case_name, + case_route=case_route, + case_name_format=2) + + ws.set_properties() + ws.initialise() + ws.clean_test_files() + + # ws.update_mass_stiffness(sigma=1., sigma_mass=1.5) + ws.update_mass_stiffness(sigma=.5, sigma_mass=1.0, payload=payload) + ws.update_fem_prop() + ws.generate_fem_file() + ws.update_aero_properties() + ws.generate_aero_file() + + flow = ['BeamLoader', + 'AerogridLoader', + 'StaticTrim', + 'BeamPlot', + 'AerogridPlot', + 'AeroForcesCalculator', + 'DynamicCoupled', + 'Modal', + 'LinearAssembler', + 'AsymptoticStability', + 'SaveParametricCase' + ] + + if not trim: + flow[2] = 'StaticCoupled' + + settings = dict() + settings['SHARPy'] = {'case': ws.case_name, + 'route': ws.case_route, + 'flow': flow, + 'write_screen': 'off', + 'write_log': 'on', + 'log_folder': output_folder, + 'log_file': ws.case_name + '.log'} + + settings['BeamLoader'] = {'unsteady': 'off', + 'orientation': algebra.euler2quat(np.array([ws.roll, + ws.alpha, + ws.beta]))} + + settings['AerogridLoader'] = {'unsteady': 'off', + 'aligned_grid': 'on', + 'mstar': int(ws.M * ws.Mstarfactor), + 'freestream_dir': ['1', '0', '0'], + 'control_surface_deflection': [''], + 'wake_shape_generator': 'StraightWake', + 'wake_shape_generator_input': {'u_inf': u_inf, + 'u_inf_direction': [1., 0., 0.], + 'dt': ws.dt}, + } + + if ws.horseshoe is True: + settings['AerogridLoader']['mstar'] = 1 + + settings['StaticCoupled'] = {'print_info': 'on', + 'structural_solver': 'NonLinearStatic', + 'structural_solver_settings': {'print_info': 'off', + 'max_iterations': 200, + 'num_load_steps': 1, + 'delta_curved': 1e-5, + 'min_delta': ws.tolerance, + 'gravity_on': 'on', + 'gravity': 9.81}, + 'aero_solver': 'StaticUvlm', + 'aero_solver_settings': {'print_info': 'on', + 'horseshoe': ws.horseshoe, + 'num_cores': 4, + 'n_rollup': int(0), + 'rollup_dt': ws.dt, + 'rollup_aic_refresh': 1, + 'rollup_tolerance': 1e-4, + 'vortex_radius': 1e-6, + 'velocity_field_generator': 'SteadyVelocityField', + 'velocity_field_input': {'u_inf': ws.u_inf, + 'u_inf_direction': [1., 0, 0]}, + 'rho': ws.rho}, + 'max_iter': 200, + 'n_load_steps': 1, + 'tolerance': ws.tolerance, + 'relaxation_factor': 0.2} + + settings['StaticTrim'] = {'solver': 'StaticCoupled', + 'solver_settings': settings['StaticCoupled'], + 'thrust_nodes': ws.thrust_nodes, + 'initial_alpha': ws.alpha, + 'initial_deflection': ws.cs_deflection, + 'initial_thrust': ws.thrust, + 'max_iter': 200, + 'fz_tolerance': 1e-2, + 'fx_tolerance': 1e-2, + 'm_tolerance': 1e-2, + 'save_info': 'on'} + + settings['AerogridPlot'] = {'include_rbm': 'off', + 'include_applied_forces': 'on', + 'minus_m_star': 0, + 'u_inf': ws.u_inf + } + settings['AeroForcesCalculator'] = {'write_text_file': 'off', + 'text_file_name': ws.case_name + '_aeroforces.csv', + 'screen_output': 'on', + 'coefficients': True, + 'q_ref': 0.5 * ws.rho * ws.u_inf ** 2, + 'S_ref': 12.809, + } + + settings['BeamPlot'] = {'include_rbm': 'on', + 'include_applied_forces': 'on', + 'include_FoR': 'on'} + + struct_solver_settings = {'print_info': 'off', + 'initial_velocity_direction': [-1., 0., 0.], + 'max_iterations': 950, + 'delta_curved': 1e-6, + 'min_delta': ws.tolerance, + 'newmark_damp': 5e-3, + 'gravity_on': True, + 'gravity': 9.81, + 'num_steps': ws.n_tstep, + 'dt': ws.dt, + 'initial_velocity': ws.u_inf * 1} + + step_uvlm_settings = {'print_info': 'on', + 'num_cores': 4, + 'convection_scheme': ws.wake_type, + 'vortex_radius': 1e-6, + 'velocity_field_generator': 'SteadyVelocityField', + 'velocity_field_input': {'u_inf': ws.u_inf * 0, + 'u_inf_direction': [1., 0., 0.]}, + 'rho': ws.rho, + 'n_time_steps': ws.n_tstep, + 'dt': ws.dt, + 'gamma_dot_filtering': 3} + + settings['DynamicCoupled'] = {'print_info': 'on', + 'structural_solver': 'NonLinearDynamicCoupledStep', + 'structural_solver_settings': struct_solver_settings, + 'aero_solver': 'StepUvlm', + 'aero_solver_settings': step_uvlm_settings, + 'fsi_substeps': 200, + 'fsi_tolerance': ws.fsi_tolerance, + 'relaxation_factor': ws.relaxation_factor, + 'minimum_steps': 1, + 'relaxation_steps': 150, + 'final_relaxation_factor': 0.5, + 'n_time_steps': 1, # ws.n_tstep, + 'dt': ws.dt, + 'include_unsteady_force_contribution': 'off', + 'postprocessors': ['BeamLoads', 'BeamPlot', 'AerogridPlot', 'WriteVariablesTime'], + 'postprocessors_settings': {'BeamLoads': {'csv_output': 'off'}, + 'BeamPlot': {'include_rbm': 'on', + 'include_applied_forces': 'on'}, + 'AerogridPlot': { + 'u_inf': ws.u_inf, + 'include_rbm': 'on', + 'include_applied_forces': 'on', + 'minus_m_star': 0}, + 'WriteVariablesTime': { + 'cleanup_old_solution': 'on', + 'delimiter': ',', + 'FoR_variables': ['total_forces', + 'total_gravity_forces', + 'for_pos', 'quat'], + }}} + + settings['Modal'] = {'print_info': True, + 'use_undamped_modes': True, + 'NumLambda': 30, + 'rigid_body_modes': True, + 'write_modes_vtk': 'on', + 'print_matrices': 'on', + 'save_data': 'on', + 'continuous_eigenvalues': 'off', + 'dt': ws.dt, + 'plot_eigenvalues': False, + 'rigid_modes_cg': False} + + settings['LinearAssembler'] = {'linear_system': 'LinearAeroelastic', + 'linearisation_tstep': -1, + 'linear_system_settings': { + 'beam_settings': {'modal_projection': 'on', + 'inout_coords': 'modes', + 'discrete_time': True, + 'newmark_damp': 0.5e-2, + 'discr_method': 'newmark', + 'dt': ws.dt, + 'proj_modes': 'undamped', + 'use_euler': use_euler, + 'num_modes': 20, + 'print_info': 'on', + 'gravity': 'on', + 'remove_dofs': []}, + 'aero_settings': {'dt': ws.dt, + 'integr_order': 2, + 'density': ws.rho * rho_fact, + 'remove_predictor': False, + 'use_sparse': False, + 'vortex_radius': 1e-6, + 'remove_inputs': ['u_gust'], + 'rom_method': ['Krylov'], + 'rom_method_settings': {'Krylov': rom_settings}}, + 'track_body': track_body, + 'use_euler': use_euler, + }} + + settings['AsymptoticStability'] = { + 'print_info': 'on', + 'modes_to_plot': [], + # 'velocity_analysis': [27, 29, 3], + 'display_root_locus': 'off', + 'frequency_cutoff': 0, + 'export_eigenvalues': 'on', + 'target_system': ['aeroelastic', 'aerodynamic', 'structural'], + 'output_file_format': 'dat', + 'num_evals': 100} + + settings['FrequencyResponse'] = {'print_info': 'on', + 'frequency_bounds': [0.1, 100]} + + settings['LinDynamicSim'] = {'dt': ws.dt, + 'n_tsteps': ws.n_tstep, + 'sys_id': 'LinearAeroelastic', + # 'reference_velocity': ws.u_inf, + 'write_dat': ['x', 'y', 't', 'u'], + # 'write_dat': 'on', + 'postprocessors': ['BeamPlot', 'AerogridPlot'], + 'postprocessors_settings': {'AerogridPlot': { + 'u_inf': ws.u_inf, + 'include_rbm': 'on', + 'include_applied_forces': 'on', + 'minus_m_star': 0}, + 'BeamPlot': {'include_rbm': 'on', + 'include_applied_forces': 'on'}}} + + settings['StabilityDerivatives'] = {'u_inf': ws.u_inf, + 'S_ref': 12.809, + 'b_ref': ws.span, + 'c_ref': 0.719} + + settings['SaveData'] = {'save_aero': 'off', + 'save_struct': 'off', + 'save_linear': 'on', + 'save_linear_uvlm': 'on'} + + settings['PickleData'] = {} + + settings['SaveParametricCase'] = {'parameters': {'r': rom_settings['r']}} + + config = configobj.ConfigObj() + file_name = ws.case_route + '/' + ws.case_name + '.solver.txt' + config.filename = file_name + for k, v in settings.items(): + config[k] = v + config.write() + + return sharpy.sharpy_main.main(['', ws.case_route + '/' + ws.case_name + '.solver.txt']) + + +class TestHortenWing(unittest.TestCase): + + + def test_horten(self): + + M = 4 + N = 11 + Msf = 5 + + trim = False + + case_name = 'horten_' + + case_route = route_test_dir + '/cases/' + output_route = route_test_dir + '/output/' + + data = run_rom_convergence(case_name=case_name, case_route=case_route, + output_folder=output_route, + M=M, N=N, Msf=Msf, trim=trim) + + # check first 10 eigs are zero (9 integro states + yaw) + path_to_eigs = data.output_folder + '/stability/aeroelastic_eigenvalues.dat' + eigs = np.loadtxt(path_to_eigs) + + # check that aerodynamic and structural eigs are also written + target_system = ['aerodynamic', 'structural'] + for sys in target_system: + np.loadtxt(data.output_folder + f'/stability/{sys}_eigenvalues.dat') + + np.testing.assert_allclose(np.abs(eigs[:10]), 0, atol=1e-8) + + # check that the phugoid mode is there (more or less, very high tolerance) + phugoid_eigs = eigs[11] + wn_phugoid = np.linalg.norm(phugoid_eigs) + period_phugoid = 2 * np.pi / wn_phugoid + np.testing.assert_allclose(period_phugoid, 20.7, atol=0.1, rtol=0.1) + + def tearDown(self): + folders = ['output/', 'cases/'] + + for folder in folders: + if os.path.isdir(route_test_dir + '/' + folder): + shutil.rmtree(route_test_dir + '/' + folder) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/linear/rom/test_krylov.py b/tests/linear/rom/test_krylov.py index c43ec618e..24ce8d7d4 100644 --- a/tests/linear/rom/test_krylov.py +++ b/tests/linear/rom/test_krylov.py @@ -31,7 +31,7 @@ def setUp(self): A = A.todense() - self.ss = libss.ss(A, B, C, D) + self.ss = libss.StateSpace(A, B, C, D) self.rom = krylov.Krylov() diff --git a/tests/linear/rom/test_rom_framework.py b/tests/linear/rom/test_rom_framework.py index 9bfc06ddb..7d187b54d 100644 --- a/tests/linear/rom/test_rom_framework.py +++ b/tests/linear/rom/test_rom_framework.py @@ -1,7 +1,7 @@ import numpy as np import os import unittest -import cases.templates.flying_wings as wings +import sharpy.cases.templates.flying_wings as wings import sharpy.sharpy_main @@ -149,8 +149,7 @@ def setUp(self): ws.config['Modal'] = {'NumLambda': 20, 'rigid_body_modes': 'off', 'print_matrices': 'on', - 'keep_linear_matrices': 'on', - 'write_dat': 'off', + 'save_data': 'off', 'rigid_modes_cg': 'off', 'continuous_eigenvalues': 'off', 'dt': 0, diff --git a/tests/linear/rom/test_springmasssystem.py b/tests/linear/rom/test_springmasssystem.py index f43c06b5a..77adcd7bd 100644 --- a/tests/linear/rom/test_springmasssystem.py +++ b/tests/linear/rom/test_springmasssystem.py @@ -129,14 +129,14 @@ def build_system(self, system_inputs, system_time): # Build State Space if system_time == 'ct': - system = libss.ss(A, b, c, d, dt=None) + system = libss.StateSpace(A, b, c, d, dt=None) else: # Discrete time system dt = 1e-2 Adt, Bdt, Cdt, Ddt = lingebm.newmark_ss(Minv, C, k, dt=dt, num_damp=0) - system = libss.ss(Adt, Bdt, Cdt, Ddt, dt=dt) + system = libss.StateSpace(Adt, Bdt, Cdt, Ddt, dt=dt) # SISO Gains for DT system if system_inputs == 'SISO': diff --git a/tests/linear/statespace/__init__.py b/tests/linear/statespace/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/linear/statespace/test_statespace.py b/tests/linear/statespace/test_statespace.py new file mode 100644 index 000000000..0e436b189 --- /dev/null +++ b/tests/linear/statespace/test_statespace.py @@ -0,0 +1,427 @@ +import copy +import unittest +import os + +import numpy as np + +from sharpy.linear.src import libsparse as libsp +from sharpy.linear.src.libss import StateSpace, SSconv, compare_ss, scale_SS, Gain, random_ss, couple, join, disc2cont, series +from sharpy.linear.utils.ss_interface import LinearVector, InputVariable, StateVariable, OutputVariable + + +class Test_dlti(unittest.TestCase): + """ Test methods into this module for DLTI systems """ + + def setUp(self): + # allocate some state-space model (dense and sparse) + dt = 0.3 + Ny, Nx, Nu = 8, 3, 5 + A = np.random.rand(Nx, Nx) + B = np.random.rand(Nx, Nu) + C = np.random.rand(Ny, Nx) + D = np.random.rand(Ny, Nu) + self.SS = StateSpace(A, B, C, D, dt=dt) + self.SSsp = StateSpace(libsp.csc_matrix(A), libsp.csc_matrix(B), C, D, dt=dt) + + self.SS.input_variables = LinearVector([InputVariable('input1', size=3, index=0), + InputVariable('input2', size=2, index=1)]) + self.SS.state_variables = LinearVector([StateVariable('state1', size=3, index=0)]) + self.SS.output_variables = LinearVector([OutputVariable('output1', size=3, index=0), + OutputVariable('output2', size=5, index=1)]) + + self.SSsp.input_variables = self.SS.input_variables + self.SSsp.output_variables = self.SS.output_variables + self.SSsp.state_variables = self.SS.state_variables + + def test_SSconv(self): + + SS = self.SS + SSsp = self.SSsp + Nu, Nx, Ny = SS.inputs, SS.states, SS.outputs + A, B, C, D = SS.get_mats() + + # remove predictor: try different scenario + B1 = np.random.rand(Nx, Nu) + SSpr0 = StateSpace(*SSconv(A, B, B1, C, D), dt=0.3) + SSpr1 = StateSpace(*SSconv(A, B, libsp.csc_matrix(B1), C, D), dt=0.3) + SSpr2 = StateSpace(*SSconv( + libsp.csc_matrix(A), B, libsp.csc_matrix(B1), C, D), dt=0.3) + SSpr3 = StateSpace(*SSconv( + libsp.csc_matrix(A), libsp.csc_matrix(B), B1, C, D), dt=0.3) + SSpr4 = StateSpace(*SSconv( + libsp.csc_matrix(A), libsp.csc_matrix(B), libsp.csc_matrix(B1), C, D), dt=0.3) + compare_ss(SSpr0, SSpr1) + compare_ss(SSpr0, SSpr2) + compare_ss(SSpr0, SSpr3) + compare_ss(SSpr0, SSpr4) + + def test_scale_SS(self): + + SS = self.SS + SSsp = self.SSsp + Nu, Nx, Ny = SS.inputs, SS.states, SS.outputs + + # scale (hard-copy) + insc = np.random.rand(Nu) + stsc = np.random.rand(Nx) + outsc = np.random.rand(Ny) + SSadim = scale_SS(SS, insc, outsc, stsc, byref=False) + SSadim_sp = scale_SS(SSsp, insc, outsc, stsc, byref=False) + compare_ss(SSadim, SSadim_sp) + + # scale (by reference) + SS.scale(insc, outsc, stsc) + SSsp.scale(insc, outsc, stsc) + compare_ss(SS, SSsp) + + def test_addGain(self): + + SS = self.SS + SSsp = self.SSsp + Nu, Nx, Ny = SS.inputs, SS.states, SS.outputs + + # add gains + Kin = np.random.rand(Nu, 5) + Kout = np.random.rand(4, Ny) + + gain_in = Gain(Kin) + gain_in.input_variables = LinearVector([InputVariable('input1', size=5, index=0)]) + gain_in.output_variables = LinearVector([OutputVariable('output1', size=Nu, index=0)]) + + gain_out = Gain(Kout) + gain_out.input_variables = LinearVector.transform(self.SS.output_variables, InputVariable) + gain_out.output_variables = LinearVector([OutputVariable('final_output', size=gain_out.outputs, index=0)]) + + SS.addGain(gain_in, 'in') + SS.addGain(gain_out, 'out') + SSsp.addGain(gain_in, 'in') + SSsp.addGain(gain_out, 'out') + compare_ss(SS, SSsp) + + def test_freqresp(self): + # freq response: try different scenario + + SS = self.SS + SSsp = self.SSsp + Nu, Nx, Ny = SS.inputs, SS.states, SS.outputs + + kv = np.linspace(0, 1, 8) + Y = SS.freqresp(kv) + Ysp = SSsp.freqresp(kv) + er = np.max(np.abs(Y - Ysp)) + assert er < 1e-10, 'Test on freqresp failed' + + SS.D = libsp.csc_matrix(SS.D) + Y1 = SS.freqresp(kv) + er = np.max(np.abs(Y - Y1)) + assert er < 1e-10, 'Test on freqresp failed' + + def test_couple(self): + dt = .2 + Nx1, Nu1, Ny1 = 3, 4, 2 + Nx2, Nu2, Ny2 = 4, 3, 2 + K12 = np.random.rand(Nu1, Ny2) + K21 = np.random.rand(Nu2, Ny1) + SS1 = random_ss(Nx1, Nu1, Ny1, dt=.2) + SS2 = random_ss(Nx2, Nu2, Ny2, dt=.2) + + SS1sp = StateSpace(libsp.csc_matrix(SS1.A), + libsp.csc_matrix(SS1.B), + libsp.csc_matrix(SS1.C), + libsp.csc_matrix(SS1.D), dt=dt) + SS2sp = StateSpace(libsp.csc_matrix(SS2.A), + libsp.csc_matrix(SS2.B), + libsp.csc_matrix(SS2.C), + libsp.csc_matrix(SS2.D), dt=dt) + K12sp = libsp.csc_matrix(K12) + K21sp = libsp.csc_matrix(K21) + + # SCref=couple_full(SS1,SS2,K12,K21) + SC0 = couple(SS1, SS2, K12, K21) + # compare_ss(SCref,SC0) + for SSa in [SS1, SS1sp]: + for SSb in [SS2, SS2sp]: + for k12 in [K12, K12sp]: + for k21 in [K21, K21sp]: + SChere = couple(SSa, SSb, k12, k21) + compare_ss(SC0, SChere) + + def test_join(self): + + Nx, Nu, Ny = 4, 3, 2 + SS_list = [random_ss(Nx, Nu, Ny, dt=.2) for ii in range(3)] + + wv = [.3, .5, .2] + SSjoin = join(SS_list, wv) + + kv = np.array([0., 1., 3.]) + Yjoin = SSjoin.freqresp(kv) + + Yref = np.zeros_like(Yjoin) + for ii in range(3): + Yref += wv[ii] * SS_list[ii].freqresp(kv) + + er = np.max(np.abs(Yjoin - Yref)) + assert er < 1e-14, 'test_join error %.3e too large' % er + + def test_disc2cont(self): + # not the best test given that eigenvalue comparison is not great with random systems. (error grows near + # nyquist frequency) + + # this test is for execution purposes only. + sys = copy.deepcopy(self.SS) + self.SS.disc2cont() + + ct_sys = disc2cont(sys) + + def test_remove_inputs(self): + dt = 0.3 + Ny, Nx, Nu = 4, 3, 10 + A = np.random.rand(Nx, Nx) + B = np.random.rand(Nx, Nu) + C = np.random.rand(Ny, Nx) + D = np.random.rand(Ny, Nu) + self.SS = StateSpace(A, B, C, D, dt=dt) + self.SSsp = StateSpace(libsp.csc_matrix(A), libsp.csc_matrix(B), C, D, dt=dt) + + self.SS.input_variables = LinearVector([InputVariable('input1', size=3, index=0), + InputVariable('input2', size=4, index=1), + InputVariable('input3', size=2, index=2), + InputVariable('input4', size=1, index=3)]) + self.SSsp.input_variables = self.SS.input_variables.copy() + + rows_loc = self.SS.input_variables.num_variables * [None] + for ith, variable in enumerate(self.SS.input_variables): + rows_loc[ith] = variable.rows_loc + + self.SS.remove_inputs('input2', 'input4') + + assert self.SS.B.shape == (Nx, self.SS.input_variables.size), 'B matrix not trimmed correctly' + assert self.SS.D.shape == (Ny, self.SS.input_variables.size), 'D matrix not trimmed correctly' + + assert self.SS.input_variables[0].rows_loc == rows_loc[0], \ + 'Rows of input 1 not retained correctly' + assert self.SS.input_variables[1].rows_loc == rows_loc[2], \ + 'Rows of input 3 not retained correctly' + + # sparse system + self.SSsp.remove_inputs('input2', 'input4') + assert self.SSsp.B.shape == (Nx, self.SSsp.input_variables.size), 'Bsp matrix not trimmed correctly' + assert self.SSsp.D.shape == (Ny, self.SSsp.input_variables.size), 'Dsp matrix not trimmed correctly' + + assert self.SSsp.input_variables[0].rows_loc == rows_loc[0], \ + 'Rows of input 1 not retained correctly in sparse system' + assert self.SSsp.input_variables[1].rows_loc == rows_loc[2], \ + 'Rows of input 3 not retained correctly in sparse system' + + def test_series(self): + """ + Test Series connection + + input11 -- >>> SS2 ----> input1, input2 -----> self.SS ----> y + """ + Nx2, Nu2, Ny2 = 4, 3, self.SS.inputs + SS2 = random_ss(Nx2, Nu2, Ny2, dt=self.SS.dt) + SS2.input_variables = LinearVector([InputVariable('input11', size=3, index=0)]) + SS2.state_variables = LinearVector([StateVariable('state11', size=4, index=0)]) + SS2.output_variables = LinearVector([OutputVariable('input1', size=3, index=0), + OutputVariable('input2', size=2, index=1)]) + + SSnew = series(SS2, self.SS) + state_vars = LinearVector.merge(SS2.state_variables, self.SS.state_variables) + + LinearVector.check_same_vectors(SSnew.state_variables, state_vars) + + assert SSnew.outputs == self.SS.outputs, 'Number of outputs not the same as self.SS' + assert SSnew.inputs == SS2.inputs, 'Number of inputs not the same as SS2' + + +class TestStateSpaceManipulation(unittest.TestCase): + + route_test_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + files_created_by_test = [] + + def setUp(self): + Ny, Nx, Nu = 6, 3, 10 + A = np.random.rand(Nx, Nx) + B = np.random.rand(Nx, Nu) + C = np.random.rand(Ny, Nx) + D = np.random.rand(Ny, Nu) + self.ss = StateSpace(A, B, C, D) + self.SSsp = StateSpace(libsp.csc_matrix(A), libsp.csc_matrix(B), C, D) + + self.ss.input_variables = LinearVector([InputVariable('input1', size=3, index=0), + InputVariable('input2', size=4, index=1), + InputVariable('input3', size=2, index=2), + InputVariable('input4', size=1, index=3)]) + + self.ss.state_variables = LinearVector([StateVariable('state1', size=Nx, index=0)]) + self.ss.output_variables = LinearVector([OutputVariable('output1', size=1, index=0), + OutputVariable('output2', size=3, index=1), + OutputVariable('output3', size=2, index=2)]) + + def test_remove_outputs(self): + self.ss.remove_outputs('output1') # size 1 + self.ss.remove_outputs('output2') # size 3 + + assert self.ss.outputs == 2, 'Number of outputs not correct' + + def test_retain_channels(self): + """ + Retain certain input and output channels only + """ + retain_input_channels = [0, 7] + self.ss.retain_inout_channels(retain_input_channels, where='in') + + assert self.ss.inputs == len(retain_input_channels), 'Number of inputs not correct' + assert self.ss.input_variables[0].size == 1, 'input1 not correct size' + assert self.ss.input_variables[1].name == 'input3', 'input2 not properly removed' + + retain_output_channels = [0, 5] + self.ss.retain_inout_channels(retain_output_channels, where='out') + assert self.ss.outputs == len(retain_output_channels), 'Number of outputs not correct' + assert self.ss.output_variables[0].size == 1, 'output1 not correct size' + assert self.ss.output_variables[1].name == 'output3', 'output2 not properly removed' + assert self.ss.output_variables[1].size == 1, 'output3 not properly trimmed' + assert self.ss.output_variables[1].index == 1, 'output3 not properly indexed' + + def test_input_output(self): + + h5_filename = self.route_test_dir + '/file_test_statespace_inout.h5' + self.files_created_by_test.append(h5_filename) + + self.ss.save(h5_filename) + + loaded_ss = StateSpace.load_from_h5(h5_filename) + + LinearVector.check_same_vectors(self.ss.input_variables, + loaded_ss.input_variables) + + LinearVector.check_same_vectors(self.ss.output_variables, + loaded_ss.output_variables) + + LinearVector.check_same_vectors(self.ss.state_variables, + loaded_ss.state_variables) + + np.testing.assert_array_almost_equal(self.ss.A, loaded_ss.A) + np.testing.assert_array_almost_equal(self.ss.B, loaded_ss.B) + np.testing.assert_array_almost_equal(self.ss.C, loaded_ss.C) + np.testing.assert_array_almost_equal(self.ss.D, loaded_ss.D) + + @classmethod + def tearDownClass(cls): + for file in cls.files_created_by_test: + if os.path.exists(file): + os.remove(file) + + +class TestGains(unittest.TestCase): + + route_test_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + files_created_by_test = [] + + def test_dot(self): + """ + u ---> K1 ----> K2 ---->y + Returns: + + """ + m1 = 4 + p1 = 3 + k1 = np.random.rand(p1, m1) + gain1 = Gain(k1, + input_vars=LinearVector([InputVariable('input', size=m1, index=0)]), + output_vars=LinearVector([OutputVariable('output1', size=p1, index=0)])) + + m2 = p1 + p2 = 5 + k2 = np.random.rand(p2, m2) + gain2 = Gain(k2, + input_vars=LinearVector([InputVariable('output1', size=m2, index=0)]), + output_vars=LinearVector([OutputVariable('output', size=p2, index=0)])) + + gain = gain2.dot(gain1) + np.testing.assert_array_almost_equal(gain.value, k2.dot(k1)) + + @unittest.expectedFailure + def test_fail_connection(self): + """ + This one should fail because of dimension mismatch + + u ---> K1 ----> K2 ---->y + + """ + m1 = 4 + p1 = 3 + k1 = np.random.rand(p1, m1) + gain1 = Gain(k1, + input_vars=LinearVector([InputVariable('input', size=m1, index=0)]), + output_vars=LinearVector([OutputVariable('output1', size=p1, index=0)])) + + m2 = 2 + p2 = 5 + k2 = np.random.rand(p2, m2) + gain2 = Gain(k2, + input_vars=LinearVector([InputVariable('output1', size=m2, index=0)]), + output_vars=LinearVector([OutputVariable('output', size=p2, index=0)])) + + # check the check fails :) + with self.subTest('ss_interface_check'): + LinearVector.check_connection(gain1.output_variables, gain2.input_variables) + + with self.subTest('connection_check'): + gain = gain2.dot(gain1) + np.testing.assert_array_almost_equal(gain.value, k2.dot(k1)) + + def test_input_output_gains(self): + m1 = 4 + p1 = 3 + k1 = np.random.rand(p1, m1) + gain1 = Gain(k1, + input_vars=LinearVector([InputVariable('input', size=m1, index=0)]), + output_vars=LinearVector([OutputVariable('output1', size=p1, index=0)])) + + m2 = 2 + p2 = 5 + k2 = np.random.rand(p2, m2) + gain2 = Gain(k2, + input_vars=LinearVector([InputVariable('output1', size=m2, index=0)]), + output_vars=LinearVector([OutputVariable('output', size=p2, index=0)])) + + with self.subTest('Save to single h5 file'): + h5_file_name = self.route_test_dir + '/gain1.h5' + self.files_created_by_test.append(h5_file_name) + gain1.save(h5_file_name) + + with self.subTest('Load from single h5 file'): + loaded_gain = Gain.load_from_h5(h5_file_name) + self.check_gains_equal(gain1, loaded_gain) + + with self.subTest('Save both gains to single h5'): + h5_file_name = self.route_test_dir + '/multi_gain.h5' + self.files_created_by_test.append(h5_file_name) + + Gain.save_multiple_gains(h5_file_name, ('gain1', gain1), ('gain2', gain2)) + + with self.subTest('Load multiple gains from a single h5'): + out_gains = Gain.load_multiple_gains(h5_file_name) + self.check_gains_equal(gain1, out_gains['gain1']) + self.check_gains_equal(gain2, out_gains['gain2']) + + @staticmethod + def check_gains_equal(gain1, gain2): + np.testing.assert_array_almost_equal(gain1.value, gain2.value) + LinearVector.check_same_vectors(gain1.input_variables, + gain2.input_variables) + LinearVector.check_same_vectors(gain1.output_variables, + gain2.output_variables) + + @classmethod + def tearDownClass(cls): + for file in cls.files_created_by_test: + if os.path.exists(file): + os.remove(file) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/linear/statespace/test_variable_tracker.py b/tests/linear/statespace/test_variable_tracker.py new file mode 100644 index 000000000..35d66cfe2 --- /dev/null +++ b/tests/linear/statespace/test_variable_tracker.py @@ -0,0 +1,168 @@ +import unittest +import os +import h5py +import sharpy.utils.h5utils as h5utils + +from sharpy.linear.utils.ss_interface import InputVariable, LinearVector, OutputVariable + + +class TestVariables(unittest.TestCase): + + route_test_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + files_created_by_test = [] + + def setUp(self): + # initialising with index out of order + Kzeta = 4 + input_variables_list = [InputVariable('zeta', size=3 * Kzeta, index=0), + InputVariable('zeta_dot', size=3 * Kzeta, index=1), # this should be one + InputVariable('u_gust', size=3 * Kzeta, index=2)] + + self.input_variables = LinearVector(input_variables_list) + + def test_initialisation(self): + Kzeta = 4 + input_variables_list = [InputVariable('zeta', size=3 * Kzeta, index=0), + InputVariable('u_gust', size=3 * Kzeta, index=2), + InputVariable('zeta_dot', size=3 * Kzeta, index=1)] + + input_variables = LinearVector(input_variables_list) + + sorted_indices = [variable.index for variable in input_variables] + variable_names = [variable.name for variable in input_variables] + assert sorted_indices == [0, 1, 2], 'Error sorting indices in initialisation' + assert variable_names == ['zeta', 'zeta_dot', 'u_gust'], 'Error sorting indices in initialisation' + + def test_remove(self): + + self.input_variables.remove('zeta_dot') + + sorted_indices = [variable.index for variable in self.input_variables] + assert sorted_indices == [0, 1], 'Error removing variable' + + def test_modify(self): + + new_values = {'name': 'new_u_gust', + 'size': 5, + } + + self.input_variables.modify('u_gust', **new_values) + + assert self.input_variables.get_variable_from_name('new_u_gust').size == 5, 'Variable not modified correctly' + + def test_add(self): + + new_variable = InputVariable('control_surface', size=2, index=3) + + self.input_variables.add(new_variable) + self.input_variables.update_locations() + + # test it's properly included - it will raise an error internally if not + included_var = self.input_variables.get_variable_from_name('control_surface') + + new_variable_repeated_index = InputVariable('control_surface_2', size=2, index=3) + try: + self.input_variables.add(new_variable_repeated_index) + except IndexError: + # correct behaviour + pass + else: + raise IndexError('Variable was added when it should have been rejected because of repeated index') + + new_variable_inbetween = InputVariable('first_variable', size=2, index=-1) + self.input_variables.add(new_variable_inbetween) + self.input_variables.update_locations() + + assert self.input_variables[0].name == new_variable_inbetween.name, 'New variable not added at the start of' \ + ' the list' + + self.input_variables.add('var_from_string', size=3, index=10) + assert self.input_variables[-1].name == 'var_from_string', 'Variable from string not added correctly' + + self.input_variables.update_indices() + + def test_append(self): + self.input_variables.append('last_var', size=3) + self.input_variables.update_indices() + + assert self.input_variables[-1].name == 'last_var', 'Variable not properly appended' + + # test adding an incorrect input + out_var = OutputVariable('out_var', size=1, index=4) + + try: + self.input_variables.append(out_var) + except TypeError: + pass # this is what should happen + else: + raise TypeError('Error. Able to append an output variable to an input variable') + + self.input_variables.append(InputVariable(name='last_last_var', size=2, index=0)) + + assert self.input_variables[-1].name == 'last_last_var', 'Variable not properly appended' + + def test_merge(self): + second_variables_list = [InputVariable('eta', size=3, index=0), + InputVariable('eta_dot', size=3, index=1)] + + second_vector = LinearVector(second_variables_list) + + merged_vector = LinearVector.merge(self.input_variables, second_vector) + + assert merged_vector[-1].name == 'eta_dot', 'Vectors not coupled properly' + assert merged_vector[2].name == 'u_gust', 'Vectors not coupled properly' + + def test_copy(self): + vec1 = self.input_variables + vec2 = vec1.copy() + + assert vec1 is not vec2, 'Object not deep copied' + + def test_transform(self): + + new_vector = LinearVector.transform(self.input_variables, to_type=OutputVariable) + + LinearVector.check_same_vectors(new_vector, self.input_variables) + assert new_vector.num_variables == self.input_variables.num_variables, 'Number of variables not equal' + assert new_vector.size == self.input_variables.size, 'Size not equal' + + for i_var in range(new_vector.num_variables): + assert new_vector[i_var].name == self.input_variables[i_var].name, 'Name does not match' + assert new_vector[i_var].index == self.input_variables[i_var].index, 'Index does not match' + assert new_vector[i_var].size == self.input_variables[i_var].size, 'Size does not match' + + new_vector.append(OutputVariable('last_var', size=2, index=0)) + + assert new_vector.num_variables == self.input_variables.num_variables + 1, 'Number of variables got ' \ + 'updated in the original object' + assert new_vector[-1].name == 'last_var', 'Variable not correctly appended' + assert new_vector[-1].index == new_vector.num_variables - 1, 'Variable not correctly appended' + + def test_save_to_h5(self): + Kzeta = 4 + input_variables_list = [InputVariable('zeta', size=3 * Kzeta, index=0), + InputVariable('zeta_dot', size=3 * Kzeta, index=1), # this should be one + InputVariable('u_gust', size=3 * Kzeta, index=2)] + + input_variables = LinearVector(input_variables_list) + + h5_file_name = self.route_test_dir + '/test_save_variables.h5' + self.files_created_by_test.append(h5_file_name) + with h5py.File(h5_file_name, 'w') as fid: + input_variables.add_to_h5_file(fid) + + with h5py.File(h5_file_name, 'r') as fid: + data_dict = h5utils.load_h5_in_dict(fid) + + loaded_variable = LinearVector.load_from_h5_file('InputVariable', data_dict['InputVariable']) + + LinearVector.check_same_vectors(input_variables, loaded_variable) + + def tearDown(self): + for file in self.files_created_by_test: + if os.path.exists(file): + os.remove(file) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/linear/uvlm/test_infinite_span.py b/tests/linear/uvlm/test_infinite_span.py index b22001fb7..70a857e4c 100644 --- a/tests/linear/uvlm/test_infinite_span.py +++ b/tests/linear/uvlm/test_infinite_span.py @@ -16,7 +16,7 @@ import sharpy.utils.algebra as algebra import sharpy.utils.analytical as an import sharpy.linear.src.linuvlm as linuvlm -import cases.templates.flying_wings as flying_wings +import sharpy.cases.templates.flying_wings as flying_wings import sharpy.utils.sharpydir as sharpydir import sharpy.utils.cout_utils as cout diff --git a/tests/utils/test_generate_cases.py b/tests/utils/test_generate_cases.py index 2e4fb1083..95f127b18 100644 --- a/tests/utils/test_generate_cases.py +++ b/tests/utils/test_generate_cases.py @@ -4,7 +4,7 @@ import shutil import sharpy.utils.generate_cases as gc -import cases.templates.template_wt as template_wt +import sharpy.cases.templates.template_wt as template_wt from sharpy.utils.constants import deg2rad diff --git a/tests/uvlm/__init__.py b/tests/uvlm/__init__.py index 0049f82cb..e69de29bb 100644 --- a/tests/uvlm/__init__.py +++ b/tests/uvlm/__init__.py @@ -1,2 +0,0 @@ -# from tests.uvlm.static import * -# from tests.uvlm.dynamic import * diff --git a/tests/uvlm/dynamic/test_wake_cfl_n1.py b/tests/uvlm/dynamic/test_wake_cfl_n1.py index 52fa6040b..06fff066a 100755 --- a/tests/uvlm/dynamic/test_wake_cfl_n1.py +++ b/tests/uvlm/dynamic/test_wake_cfl_n1.py @@ -109,7 +109,7 @@ def generate_geometry(self): nodes_y[:num_node_semispan] = -1*nodes_y_semispan[::-1] nodes_y[num_node_semispan - 1:] = nodes_y_semispan - assert not (np.diff(nodes_y) == 0).any(), "Repited nodes" + assert not (np.diff(nodes_y) == 0).any(), "Repeated nodes" nodes = np.zeros((len(nodes_y), 3)) nodes[:, 1] = nodes_y @@ -187,7 +187,7 @@ def generate_files(self, case_header, airfoil): SimInfo.solvers['BeamLoader']['unsteady'] = 'on' import sharpy.utils.generator_interface as gi - if case_header == 'traditional': + if case_header == 'traditional' or case_header == 'statespace_cfl1': SimInfo.solvers['AerogridLoader']['wake_shape_generator'] = 'StraightWake' SimInfo.solvers['AerogridLoader']['wake_shape_generator_input'] = {'u_inf': self.uinf, @@ -214,7 +214,7 @@ def generate_files(self, case_header, airfoil): SimInfo.solvers['AerogridLoader']['mstar'] = mstar SimInfo.solvers['AerogridLoader']['freestream_dir'] = np.array([0.,0.,0.]) - elif case_header in ['new_nonlinear', 'new_linear']: + elif case_header in ['new_nonlinear', 'new_linear', 'statespace']: SimInfo.solvers['AerogridLoader']['wake_shape_generator_input'] = {'u_inf': self.uinf, 'u_inf_direction': self.uinf_dir, @@ -273,7 +273,7 @@ def generate_files(self, case_header, airfoil): SimInfo.solvers['StepLinearUVLM']['dt'] = self.dt SimInfo.solvers['StepLinearUVLM']['integr_order'] = 2 - SimInfo.solvers['StepLinearUVLM']['remove_predictor'] = True + SimInfo.solvers['StepLinearUVLM']['remove_predictor'] = 'off' SimInfo.solvers['StepLinearUVLM']['use_sparse'] = True SimInfo.solvers['StepLinearUVLM']['density'] = self.air_density SimInfo.solvers['StepLinearUVLM']['vortex_radius'] = 1e-6 @@ -314,17 +314,67 @@ def generate_files(self, case_header, airfoil): SimInfo.solvers['DynamicCoupled']['dynamic_relaxation'] = False SimInfo.solvers['DynamicCoupled']['relaxation_steps'] = 0 SimInfo.solvers['DynamicCoupled']['fsi_tolerance'] = 1e-6 - + # Time discretization time_steps = int(self.final_ndtime / self.nd_dt - 1) SimInfo.define_num_steps(time_steps) - + SimInfo.with_forced_vel = True SimInfo.for_vel = np.zeros((time_steps,6), dtype=float) SimInfo.for_acc = np.zeros((time_steps,6), dtype=float) SimInfo.with_dynamic_forces = True SimInfo.dynamic_forces = np.zeros((time_steps,airfoil.StructuralInformation.num_node,6), dtype=float) - + + SimInfo.solvers['PickleData'] = {'folder': route + '/output/'} + if 'statespace' in case_header: + SimInfo.solvers['SHARPy']['flow'] = ['BeamLoader', + 'AerogridLoader', + 'StaticCoupled', + 'Modal', + 'LinearAssembler', + 'AsymptoticStability', + ] + + SimInfo.solvers['SHARPy']['write_screen'] = 'on' + SimInfo.solvers['Modal'] = {'save_data': 'off', + 'NumLambda': 50, + 'rigid_body_modes': 'off'} + + SimInfo.solvers['LinearAssembler'] = {'linear_system': 'LinearAeroelastic', + 'linear_system_settings': { + 'beam_settings': {'modal_projection': 'off', + 'inout_coords': 'nodes', + 'discrete_time': 'on', + 'newmark_damp': 0.5e-4, + 'discr_method': 'newmark', + 'dt': self.dt, + 'proj_modes': 'undamped', + 'use_euler': 'on', + 'num_modes': 2, + 'print_info': 'on', + 'gravity': 'on', + 'remove_dofs': [], + 'remove_rigid_states': 'off'}, + 'aero_settings': {'dt': self.dt, + 'integr_order': 2, + 'density': self.air_density, + 'remove_predictor': 'off', + 'use_sparse': 'off', + 'rigid_body_motion': 'off', + 'use_euler': 'on', + 'vortex_radius': 1e-6, + 'convert_to_ct': 'off', + 'gust_assembler': 'LeadingEdge', + 'gust_assembler_inputs': {}, + }, + 'rigid_body_motion': 'off', + 'track_body': 'on', + 'use_euler': 'on', + 'linearisation_tstep': -1 + }, + } + SimInfo.solvers['AsymptoticStability'] = {'num_evals': 100} + gc.clean_test_files(SimInfo.solvers['SHARPy']['route'], SimInfo.solvers['SHARPy']['case']) airfoil.generate_h5_files(SimInfo.solvers['SHARPy']['route'], SimInfo.solvers['SHARPy']['case']) SimInfo.generate_solver_file() @@ -332,7 +382,6 @@ def generate_files(self, case_header, airfoil): return case, route - def run_test(self, case_header): airfoil = self.generate_geometry() @@ -351,25 +400,37 @@ def run_test(self, case_header): factor = 0.5*self.air_density*self.uinf**2*self.chord*2.5e6 sharpy_cl_clss = -lift/factor/clsteady kussner_cl_clss = 2*np.pi*self.aoa_ini_deg*deg2rad - kussner_cl_clss += 2*np.pi*daoa*deg2rad*np.interp(nd_time, - kussner_function[:, 0], - kussner_function[:, 1]) + kussner_cl_clss += 2 * np.pi * daoa * deg2rad * np.interp(nd_time, + kussner_function[:, 0], + kussner_function[:, 1]) kussner_cl_clss /= clsteady self.assertAlmostEqual(sharpy_cl_clss, kussner_cl_clss, 1) - def test_traditional(self): self.run_test('traditional') - - + + def test_new_nonlinear(self): self.run_test('new_nonlinear') - + def test_new_linear(self): self.run_test('new_linear') - + + def test_statespace(self): + results = {} + for case in ['statespace', 'statespace_cfl1']: + with self.subTest(case): + results[case] = self.run_statespace(case) + + def run_statespace(self, case_header): + airfoil = self.generate_geometry() + + case, route = self.generate_files(case_header, airfoil) + sharpy_file = route + case + '.sharpy' + return sharpy.sharpy_main.main(['', sharpy_file]) + @classmethod def tearDownClass(cls): solver_path = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) diff --git a/tests/uvlm/static/polars/generate_wing.py b/tests/uvlm/static/polars/generate_wing.py index 0d6138da1..349b1cecc 100644 --- a/tests/uvlm/static/polars/generate_wing.py +++ b/tests/uvlm/static/polars/generate_wing.py @@ -1,5 +1,5 @@ import numpy as np -import cases.templates.flying_wings as wings +import sharpy.cases.templates.flying_wings as wings import sharpy.utils.algebra as algebra import sharpy.sharpy_main