From 474f1e3fa0a220bbd2bea8c7a70c97424f500c9b Mon Sep 17 00:00:00 2001 From: camUrban Date: Wed, 4 Dec 2024 13:17:04 -0500 Subject: [PATCH] I reformatted my code with Black. --- BUILD.md | 15 +- CODE_OF_CONDUCT.md | 126 +- README.md | 185 +- SECURITY.md | 4 +- benchmarks/unsteady_benchmark.py | 108 +- benchmarks/unsteady_benchmark_converge.py | 106 +- benchmarks/unsteady_benchmark_timed.py | 3 +- examples/analyze_steady_trim_example.py | 74 +- examples/analyze_unsteady_trim_example.py | 137 +- examples/steady_convergence_example.py | 162 +- ..._horseshoe_vortex_lattice_method_solver.py | 256 +- ...teady_ring_vortex_lattice_method_solver.py | 290 +- ...ing_vortex_lattice_method_solver_static.py | 182 +- ...g_vortex_lattice_method_solver_variable.py | 176 +- ...attice_method_solver_variable_formation.py | 301 +- .../unsteady_static_convergence_example.py | 80 +- .../unsteady_variable_convergence_example.py | 82 +- formation flight/formation_flight.py | 145 +- .../formation_flight_convergence.py | 295 +- main.py | 1 - pterasoftware/aerodynamics.py | 373 +- pterasoftware/convergence.py | 6 +- pterasoftware/functions.py | 4 - pterasoftware/geometry.py | 225 +- ..._horseshoe_vortex_lattice_method_solver.py | 130 +- ...teady_ring_vortex_lattice_method_solver.py | 290 +- ...ing_vortex_lattice_method_solver_static.py | 182 +- ...g_vortex_lattice_method_solver_variable.py | 176 +- ...attice_method_solver_variable_formation.py | 310 +- pterasoftware/movement.py | 531 +-- pterasoftware/operating_point.py | 38 +- pterasoftware/output.py | 783 ++--- pterasoftware/panel.py | 24 +- pterasoftware/problems.py | 10 +- .../steady_horseshoe_vortex_lattice_method.py | 92 +- .../steady_ring_vortex_lattice_method.py | 195 +- pterasoftware/trim.py | 162 +- pterasoftware/ui_resources/main_window.py | 3 +- pterasoftware/ui_resources/main_window.ui | 3092 ++++++++++------- pterasoftware/ui_resources/textdialog.py | 15 +- .../unsteady_ring_vortex_lattice_method.py | 712 ++-- tests/integration/__init__.py | 7 +- .../integration/fixtures/airplane_fixtures.py | 313 +- .../integration/fixtures/movement_fixtures.py | 283 +- .../integration/fixtures/problem_fixtures.py | 33 +- tests/integration/test_output.py | 27 +- tests/integration/test_steady_convergence.py | 15 +- ..._steady_horseshoe_vortex_lattice_method.py | 56 +- .../test_steady_ring_vortex_lattice_method.py | 23 +- ...ce_method_multiple_wing_static_geometry.py | 14 +- ..._method_multiple_wing_variable_geometry.py | 17 +- ...g_vortex_lattice_method_static_geometry.py | 18 +- ...vortex_lattice_method_variable_geometry.py | 18 +- tests/unit/fixtures/vortex_fixtures.py | 17 +- tests/unit/test_horseshoe_vortex.py | 102 +- tests/unit/test_line_vortex.py | 21 +- tests/unit/test_ring_vortex.py | 252 +- 57 files changed, 4251 insertions(+), 7046 deletions(-) diff --git a/BUILD.md b/BUILD.md index dba04411..a0d9cbb4 100644 --- a/BUILD.md +++ b/BUILD.md @@ -1,15 +1,18 @@ # Packaging -There are 2 available packaging formats: Installer or wheel. The former is what is distributed, while the latter is +There are 2 available packaging formats: Installer or wheel. The former is what is +distributed, while the latter is required for use of the CLI for instance. -When packaging don't forget to check versions in both `setup.py` and `make_installer.iss` and ensure they match! +When packaging don't forget to check versions in both `setup.py` and +`make_installer.iss` and ensure they match! ### Installer **Currently, this is only supported on Windows.** -The installer uses `PyInstaller` to extract all the dependencies and `InnoSetup` to create a Windows installer package +The installer uses `PyInstaller` to extract all the dependencies and `InnoSetup` to +create a Windows installer package out of them. 1. Ensure you have installed `InnoSetup` [here](https://jrsoftware.org/isdl.php). @@ -20,8 +23,10 @@ out of them. python -O -m PyInstaller --noconfirm "pterasoftware.spec" ``` -We run `python` with the first level optimise flag `-O` to slim down some now unnecessary debug code. *Do not use second +We run `python` with the first level optimise flag `-O` to slim down some now +unnecessary debug code. *Do not use second level optimisation `-OO`, as this removes some docstrings that break dependencies.* -3. Open `make_installer.iss` in InnoSetup and run the packaging process. This can take a while, but should output an +3. Open `make_installer.iss` in InnoSetup and run the packaging process. This can take a + while, but should output an installer in the `Output` folder. \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 656a3c23..2bdbc634 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -2,120 +2,120 @@ ## Our Pledge -We as members, contributors, and leaders pledge to make participation in our community -a harassment-free experience for everyone, regardless of age, body size, visible or -invisible disability, ethnicity, sex characteristics, gender identity and expression, -level of experience, education, socio-economic status, nationality, personal -appearance, race, religion, or sexual identity and orientation. +We as members, contributors, and leaders pledge to make participation in our community +a harassment-free experience for everyone, regardless of age, body size, visible or +invisible disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. -We pledge to act and interact in ways that contribute to an open, welcoming, diverse, -inclusive, and healthy community. +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, +inclusive, and healthy community. ## Our Standards -Examples of behavior that contributes to a positive environment for our community -include: +Examples of behavior that contributes to a positive environment for our community +include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, and -learning from the experience +* Accepting responsibility and apologizing to those affected by our mistakes, and + learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or advances of any -kind +* The use of sexualized language or imagery, and sexual attention or advances of any + kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment -* Publishing others' private information, such as a physical or email address, without -their explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional -setting +* Publishing others' private information, such as a physical or email address, without + their explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional + setting ## Enforcement Responsibilities -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in response to -any behavior that they deem inappropriate, threatening, offensive, or harmful. +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in response to +any behavior that they deem inappropriate, threatening, offensive, or harmful. -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are not -aligned to this Code of Conduct, and will communicate reasons for moderation decisions -when appropriate. +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are not +aligned to this Code of Conduct, and will communicate reasons for moderation decisions +when appropriate. ## Scope -This Code of Conduct applies within all community spaces, and also applies when an -individual is officially representing the community in public spaces. Examples of -representing our community include using an official e-mail address, posting via an -official social media account, or acting as an appointed representative at an online -or offline event. +This Code of Conduct applies within all community spaces, and also applies when an +individual is officially representing the community in public spaces. Examples of +representing our community include using an official e-mail address, posting via an +official social media account, or acting as an appointed representative at an online +or offline event. ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to -the community leaders responsible for enforcement at camerongurban@gmail.com. All -complaints will be reviewed and investigated promptly and fairly. +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to +the community leaders responsible for enforcement at camerongurban@gmail.com. All +complaints will be reviewed and investigated promptly and fairly. -All community leaders are obligated to respect the privacy and security of the reporter -of any incident. +All community leaders are obligated to respect the privacy and security of the reporter +of any incident. ## Enforcement Guidelines -Community leaders will follow these Community Impact Guidelines in determining the -consequences for any action they deem in violation of this Code of Conduct: +Community leaders will follow these Community Impact Guidelines in determining the +consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. -**Consequence**: A private, written warning from community leaders, providing clarity -around the nature of the violation and an explanation of why the behavior was -inappropriate. A public apology may be requested. +**Consequence**: A private, written warning from community leaders, providing clarity +around the nature of the violation and an explanation of why the behavior was +inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. -**Consequence**: A warning with consequences for continued behavior. No interaction -with the people involved, including unsolicited interaction with those enforcing the -Code of Conduct, for a specified period of time. This includes avoiding interactions in -community spaces as well as external channels like social media. Violating these terms -may lead to a temporary or permanent ban. +**Consequence**: A warning with consequences for continued behavior. No interaction +with the people involved, including unsolicited interaction with those enforcing the +Code of Conduct, for a specified period of time. This includes avoiding interactions in +community spaces as well as external channels like social media. Violating these terms +may lead to a temporary or permanent ban. ### 3. Temporary Ban -**Community Impact**: A serious violation of community standards, including sustained -inappropriate behavior. +**Community Impact**: A serious violation of community standards, including sustained +inappropriate behavior. -**Consequence**: A temporary ban from any sort of interaction or public communication -with the community for a specified period of time. No public or private interaction -with the people involved, including unsolicited interaction with those enforcing the -Code of Conduct, is allowed during this period. Violating these terms may lead to a -permanent ban. +**Consequence**: A temporary ban from any sort of interaction or public communication +with the community for a specified period of time. No public or private interaction +with the people involved, including unsolicited interaction with those enforcing the +Code of Conduct, is allowed during this period. Violating these terms may lead to a +permanent ban. ### 4. Permanent Ban -**Community Impact**: Demonstrating a pattern of violation of community standards, -including sustained inappropriate behavior, harassment of an individual, or aggression -toward or disparagement of classes of individuals. +**Community Impact**: Demonstrating a pattern of violation of community standards, +including sustained inappropriate behavior, harassment of an individual, or aggression +toward or disparagement of classes of individuals. -**Consequence**: A permanent ban from any sort of public interaction within the -community. +**Consequence**: A permanent ban from any sort of public interaction within the +community. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, -available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, +available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by -[Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). +[Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org -For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. diff --git a/README.md b/README.md index a08fcc3a..602cad30 100644 --- a/README.md +++ b/README.md @@ -13,105 +13,106 @@ ![Example Unsteady Formation Flight](https://raw.githubusercontent.com/camUrban/PteraSoftware/master/docs/examples%20expected%20output/unsteady%20ring%20vortex%20lattice%20method%20solver%20variable%20formation/Animate.webp) -This is Ptera Software: a fast, easy-to-use, and open-source package for analyzing +This is Ptera Software: a fast, easy-to-use, and open-source package for analyzing flapping-wing flight. ## Motivation -In late 2018, I became curious about biological flight. To sate this curiosity, I -wanted to computationally simulate some flapping-wing fliers. I quickly realized I had +In late 2018, I became curious about biological flight. To sate this curiosity, I +wanted to computationally simulate some flapping-wing fliers. I quickly realized I had two options: -1. Spend thousands of dollars on a closed-source CFD program, which would take hours to -solve a simple case. -2. Try to learn someone else's open-source, unsteady solver written in a language I -didn't know, or using a framework that is overly complicated for my use case. +1. Spend thousands of dollars on a closed-source CFD program, which would take hours to + solve a simple case. +2. Try to learn someone else's open-source, unsteady solver written in a language I + didn't know, or using a framework that is overly complicated for my use case. Neither of these seemed like the right choice. -Thankfully, my friend, Peter Sharpe, had just released his own open-source aerodynamics -solver: AeroSandbox. With his support, I have used AeroSandbox as a jumping-off point +Thankfully, my friend, Peter Sharpe, had just released his own open-source aerodynamics +solver: AeroSandbox. With his support, I have used AeroSandbox as a jumping-off point to develop a solver package capable of unsteady simulations. -Through the combined efforts of Peter Sharpe, Suhas Kodali, and me, Ptera Software was -born. It is an easy-to-use, open-source, and actively-maintained UVLM package capable -of analyzing flapping-wing flight. Moreover, it's written in Python, is well +Through the combined efforts of Peter Sharpe, Suhas Kodali, and me, Ptera Software was +born. It is an easy-to-use, open-source, and actively-maintained UVLM package capable +of analyzing flapping-wing flight. Moreover, it's written in Python, is well documented, tested, and validated. Beginning with version 3.0.0, Ptera Software also includes a GUI developed by Zach Tait. Although it is still rudimentary, we hope that it will help make this tool accessible to even more users. -With your help, I hope we will increase the open-source community's interest and +With your help, I hope we will increase the open-source community's interest and understanding of biological flight. ## Features 1. Various Aerodynamic Simulation Methods - * Steady simulations can be run with a standard horseshoe vortex-lattice method - (VLM) or a ring VLM. - * Unsteady simulations use a ring unsteady VLM (UVLM) solver. - * Unsteady simulations support both fixed and free wakes. - * Unsteady simulations implement vortex aging to reduce numerical instabilities. + * Steady simulations can be run with a standard horseshoe vortex-lattice method + (VLM) or a ring VLM. + * Unsteady simulations use a ring unsteady VLM (UVLM) solver. + * Unsteady simulations support both fixed and free wakes. + * Unsteady simulations implement vortex aging to reduce numerical instabilities. 2. Customizable Aircraft Geometry - * Aircraft can be defined as a collection of one or more wings of any dimensions and - positions. - * Wings can be defined as a collection of two or more wing cross sections of any - dimensions and positions. - * Wing cross sections can be specified to match the mean camber line of an airfoil. - * The package comes with a massive database of airfoil to chose from. - * Wings are automatically discretized into panels with customizable sizes and - spacings. + * Aircraft can be defined as a collection of one or more wings of any dimensions and + positions. + * Wings can be defined as a collection of two or more wing cross sections of any + dimensions and positions. + * Wing cross sections can be specified to match the mean camber line of an airfoil. + * The package comes with a massive database of airfoil to chose from. + * Wings are automatically discretized into panels with customizable sizes and + spacings. 3. Customizable Aircraft Motion - * The relative motion of wings and wing cross sections can be defined using any - time-dependent functions of sweep, pitch, and heave angles. + * The relative motion of wings and wing cross sections can be defined using any + time-dependent functions of sweep, pitch, and heave angles. 4. Customizable Operating Points - * Parameters such as the free-stream velocity, density, angle of attack, angle of - sideslip, etc. can be changed by the user. + * Parameters such as the free-stream velocity, density, angle of attack, angle of + sideslip, etc. can be changed by the user. 5. High-Speed Simulations - * Using Just-In-Time compilation, Ptera Software can solve many unsteady - flapping-wing simulations in less than a minute! - * Steady simulations take only seconds! + * Using Just-In-Time compilation, Ptera Software can solve many unsteady + flapping-wing simulations in less than a minute! + * Steady simulations take only seconds! 6. Simulations of Formation Flight - * Since v2.0.0, Ptera Software has supported simulations with more than one - airplane. - * This feature can be used to analyze the aerodynamics of flapping-wing formation - flight! + * Since v2.0.0, Ptera Software has supported simulations with more than one + airplane. + * This feature can be used to analyze the aerodynamics of flapping-wing formation + flight! 7. Features for Flapping-Wing Vehicle Design - * Ptera Software is focused on developing features to facilitate designing - flapping-wing vehicles. - * For example, use the functions in the trim module to automatically search for a - trim operating point for steady and unsteady simulations of aircraft. + * Ptera Software is focused on developing features to facilitate designing + flapping-wing vehicles. + * For example, use the functions in the trim module to automatically search for a + trim operating point for steady and unsteady simulations of aircraft. 8. A Basic GUI - * This is still in its beta stage, but we will be adding more functionality over the - next several releases. + * This is still in its beta stage, but we will be adding more functionality over the + next several releases. ## Installation and Use -First things first, you will need a copy of Python 3.10, which you can download from the official Python website. -At this time, I do not recommend using a version from the Anaconda distribution as it +First things first, you will need a copy of Python 3.10, which you can download from the +official Python website. +At this time, I do not recommend using a version from the Anaconda distribution as it could introduce compatibility issues with PyPI. -There are two ways to use Ptera Software. The first is by downloading GitHub release, -which will provide you your own copy of the source code, in which you can get a feel -for how it works (this can also be accomplished by forking the main branch). The second -is by importing the Ptera Software package using PyPI, which will allow you to call -Ptera Software's functions in your own scripts. If you are new to this tool, I -recommend first downloading a release, as this will give you access to the "examples" +There are two ways to use Ptera Software. The first is by downloading GitHub release, +which will provide you your own copy of the source code, in which you can get a feel +for how it works (this can also be accomplished by forking the main branch). The second +is by importing the Ptera Software package using PyPI, which will allow you to call +Ptera Software's functions in your own scripts. If you are new to this tool, I +recommend first downloading a release, as this will give you access to the "examples" directory. -Next, make sure you have an IDE in which you can run Ptera Software. I recommend using -the Community Edition of PyCharm, which is free, powerful, and well documented. If -you've never set up a Python project before, follow -[this guide](https://www.jetbrains.com/help/pycharm/quick-start-guide.html) to set up a -new project in PyCharm. If you'll be downloading a release, follow that tutorial's +Next, make sure you have an IDE in which you can run Ptera Software. I recommend using +the Community Edition of PyCharm, which is free, powerful, and well documented. If +you've never set up a Python project before, follow +[this guide](https://www.jetbrains.com/help/pycharm/quick-start-guide.html) to set up a +new project in PyCharm. If you'll be downloading a release, follow that tutorial's "Open an existing project guide." Otherwise, follow the "Create a new project guide." ### Downloading A Release -To download a release, navigate to -[the releases page](https://github.com/camUrban/PteraSoftware/releases) and download -the latest zipped directory. Extract the contents, and set up a python project as +To download a release, navigate to +[the releases page](https://github.com/camUrban/PteraSoftware/releases) and download +the latest zipped directory. Extract the contents, and set up a python project as described in the PyCharm tutorial. Then, open a command prompt window in your project's directory and enter: @@ -124,8 +125,8 @@ via the command prompt in your fork's directory. You may also want to run: if you plan on making significant changes to the software. -Finally, open the "examples" folder, which contains several heavily commented scripts -that demonstrate different features and simulations. Read through each example, and +Finally, open the "examples" folder, which contains several heavily commented scripts +that demonstrate different features and simulations. Read through each example, and then run them to admire their pretty output! ### Importing As A Package @@ -134,30 +135,32 @@ If you wish to use this package as a dependency in your own project, simply run: ```pip install pterasoftware``` -via the command prompt in your project's directory. Then, in a script that you'd like +via the command prompt in your project's directory. Then, in a script that you'd like to use features from Ptera Software, add: ```import pterasoftware as ps``` -If you haven't previously downloaded Ptera Software's source code, you can also learn -about the available functions by reading their docstrings, which should be fetched -automatically by many IDEs. Otherwise, you can return to the GitHub and read through +If you haven't previously downloaded Ptera Software's source code, you can also learn +about the available functions by reading their docstrings, which should be fetched +automatically by many IDEs. Otherwise, you can return to the GitHub and read through the docstrings there. -I am hoping to implement a web-based documentation guide soon! If you'd like to +I am hoping to implement a web-based documentation guide soon! If you'd like to contribute to this, feel free to open a feature request issue and start a conversation! ### What If I'm Having Trouble Getting Set Up? -Not to worry! I've made [a video](https://www.youtube.com/watch?v=oX8u2ZflJM4) that walks through getting Ptera Software up and -running. It includes every step, from downloading Python for the first time to setting -up your IDE to running the software. However, I recorded this video a while ago, so in it -I download Python 3.8. For all versions of Ptera Software 3.1.0 and on, use Python 3.10 +Not to worry! I've made [a video](https://www.youtube.com/watch?v=oX8u2ZflJM4) that +walks through getting Ptera Software up and +running. It includes every step, from downloading Python for the first time to setting +up your IDE to running the software. However, I recorded this video a while ago, so in +it +I download Python 3.8. For all versions of Ptera Software 3.1.0 and on, use Python 3.10 instead. If you still run into problems, feel free to open an issue for guidance. ## Example Code -The following code snippet is all that is needed (after running pip install +The following code snippet is all that is needed (after running pip install pterasoftware) to run the steady horseshoe solver on an airplane with custom geometry. ``` @@ -199,8 +202,8 @@ ps.output.draw(solver=solver, scalar_type="lift", show_streamlines=True) ## Example Output -This package currently supports three different solvers, a steady horseshoe vortex -lattice method (VLM), a steady ring VLM, and an unsteady ring VLM (UVLM). Here are +This package currently supports three different solvers, a steady horseshoe vortex +lattice method (VLM), a steady ring VLM, and an unsteady ring VLM (UVLM). Here are examples of the output you can expect to receive from each of them. ### Steady Horseshoe VLM @@ -246,25 +249,25 @@ Additionally, these packages are useful for continued development of the softwar ## Validation -Since the release of version 1.0.0, Ptera Software is now validated against -experimental flapping-wing data! See the "validation" directory to run the test case +Since the release of version 1.0.0, Ptera Software is now validated against +experimental flapping-wing data! See the "validation" directory to run the test case and read a report on the software's accuracy. ## How to Contribute -As I said before, the primary goal of this project is to increase the open-source -community's understanding and appreciation for unsteady aerodynamics in general and -flapping-wing flight in particular. This will only happen through your participation. -Feel free to request features, report bugs or security issues, and provide suggestions. +As I said before, the primary goal of this project is to increase the open-source +community's understanding and appreciation for unsteady aerodynamics in general and +flapping-wing flight in particular. This will only happen through your participation. +Feel free to request features, report bugs or security issues, and provide suggestions. No comment is too big or small! -Here is a list of changes I would like to make in the coming releases. If you want to +Here is a list of changes I would like to make in the coming releases. If you want to contribute and don't know where to start, this is for you! ### Testing -* We should make sure that all the integration tests compare output against expected -results. This means getting rid of all the "test_method_does_not_throw" tests. +* We should make sure that all the integration tests compare output against expected + results. This means getting rid of all the "test_method_does_not_throw" tests. * We should maintain the repository's testing coverage to be at least 80%. ### Style and Documentation @@ -276,13 +279,13 @@ results. This means getting rid of all the "test_method_does_not_throw" tests. ### Features -* We should implement a leading-edge model to account for flow separation. See -"Modified Unsteady Vortex-Lattice Method to Study Flapping Wings in Hover Flight." by -Bruno Roccia, Sergio Preidikman, Julio Massa, and Dean Mook for details. +* We should implement a leading-edge model to account for flow separation. See + "Modified Unsteady Vortex-Lattice Method to Study Flapping Wings in Hover Flight." by + Bruno Roccia, Sergio Preidikman, Julio Massa, and Dean Mook for details. * We should try to implement aeroelastic effects in Ptera Software's solvers. -* Flapping wing controls is both fascinating and complicated. We should try to create a -workflow in Ptera Software for controls systems identification for flapping-wing -vehicles. +* Flapping wing controls is both fascinating and complicated. We should try to create a + workflow in Ptera Software for controls systems identification for flapping-wing + vehicles. ## Credits @@ -321,7 +324,7 @@ where applicable. ## Notes -To the best of my ability, I am following SemVer conventions in naming my releases. I -am also using the GitFlow method of branching for this project's development. This -means that nightly builds will be available on the develop branch. The latest stable +To the best of my ability, I am following SemVer conventions in naming my releases. I +am also using the GitFlow method of branching for this project's development. This +means that nightly builds will be available on the develop branch. The latest stable releases can be found on the master branch. diff --git a/SECURITY.md b/SECURITY.md index 4912c10d..d3446d86 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,8 +2,8 @@ ## Supported Versions -Due to time constraints, only the most recent version will be updated with security -patches. +Due to time constraints, only the most recent version will be updated with security +patches. ## Reporting a Vulnerability diff --git a/benchmarks/unsteady_benchmark.py b/benchmarks/unsteady_benchmark.py index 250492a5..f21aef8e 100644 --- a/benchmarks/unsteady_benchmark.py +++ b/benchmarks/unsteady_benchmark.py @@ -9,105 +9,59 @@ num_chordwise_panels = 5 num_spanwise_panels = 20 -example_airplane = ps.geometry.Airplane( - name="Example Airplane", - wings=[ - ps.geometry.Wing( - name="Main Wing", - symmetric=True, - num_chordwise_panels=num_chordwise_panels, - chordwise_spacing="uniform", - wing_cross_sections=[ - ps.geometry.WingCrossSection( - num_spanwise_panels=num_spanwise_panels, - spanwise_spacing="uniform", - chord=1.75, - airfoil=ps.geometry.Airfoil( - name="naca0000", - ), - ), - ps.geometry.WingCrossSection( - num_spanwise_panels=num_spanwise_panels, - spanwise_spacing="uniform", - x_le=0.625, - y_le=5.0, - chord=0.5, - airfoil=ps.geometry.Airfoil( - name="naca0000", - ), - ), - ], - ), - ], -) +example_airplane = ps.geometry.Airplane(name="Example Airplane", wings=[ + ps.geometry.Wing(name="Main Wing", symmetric=True, + num_chordwise_panels=num_chordwise_panels, chordwise_spacing="uniform", + wing_cross_sections=[ + ps.geometry.WingCrossSection(num_spanwise_panels=num_spanwise_panels, + spanwise_spacing="uniform", chord=1.75, + airfoil=ps.geometry.Airfoil(name="naca0000", ), ), + ps.geometry.WingCrossSection(num_spanwise_panels=num_spanwise_panels, + spanwise_spacing="uniform", x_le=0.625, y_le=5.0, chord=0.5, + airfoil=ps.geometry.Airfoil(name="naca0000", ), ), ], ), ], ) upper_wing_root_wing_cross_section_movement = ps.movement.WingCrossSectionMovement( - base_wing_cross_section=example_airplane.wings[0].wing_cross_sections[0], -) + base_wing_cross_section=example_airplane.wings[0].wing_cross_sections[0], ) upper_wing_tip_wing_cross_section_movement = ps.movement.WingCrossSectionMovement( base_wing_cross_section=example_airplane.wings[0].wing_cross_sections[1], - sweeping_amplitude=15.0, - sweeping_period=1 / flapping_frequency, - sweeping_spacing="sine", - pitching_amplitude=5.0, - pitching_period=1 / flapping_frequency, - pitching_spacing="sine", - heaving_amplitude=5.0, - heaving_period=1 / flapping_frequency, - heaving_spacing="sine", -) - -upper_wing_movement = ps.movement.WingMovement( - base_wing=example_airplane.wings[0], - wing_cross_sections_movements=[ - upper_wing_root_wing_cross_section_movement, - upper_wing_tip_wing_cross_section_movement, - ], -) + sweeping_amplitude=15.0, sweeping_period=1 / flapping_frequency, + sweeping_spacing="sine", pitching_amplitude=5.0, + pitching_period=1 / flapping_frequency, pitching_spacing="sine", + heaving_amplitude=5.0, heaving_period=1 / flapping_frequency, + heaving_spacing="sine", ) + +upper_wing_movement = ps.movement.WingMovement(base_wing=example_airplane.wings[0], + wing_cross_sections_movements=[upper_wing_root_wing_cross_section_movement, + upper_wing_tip_wing_cross_section_movement, ], ) del upper_wing_root_wing_cross_section_movement del upper_wing_tip_wing_cross_section_movement -airplane_movement = ps.movement.AirplaneMovement( - base_airplane=example_airplane, - wing_movements=[upper_wing_movement], -) +airplane_movement = ps.movement.AirplaneMovement(base_airplane=example_airplane, + wing_movements=[upper_wing_movement], ) del upper_wing_movement -example_operating_point = ps.operating_point.OperatingPoint( - density=1.225, - beta=0.0, - velocity=10.0, - alpha=0.0, -) +example_operating_point = ps.operating_point.OperatingPoint(density=1.225, beta=0.0, + velocity=10.0, alpha=0.0, ) operating_point_movement = ps.movement.OperatingPointMovement( - base_operating_point=example_operating_point, -) + base_operating_point=example_operating_point, ) -movement = ps.movement.Movement( - airplane_movements=[airplane_movement], - operating_point_movement=operating_point_movement, -) +movement = ps.movement.Movement(airplane_movements=[airplane_movement], + operating_point_movement=operating_point_movement, ) del airplane_movement del operating_point_movement -example_problem = ps.problems.UnsteadyProblem( - movement=movement, only_final_results=True -) +example_problem = ps.problems.UnsteadyProblem(movement=movement, + only_final_results=True) example_solver = ( ps.unsteady_ring_vortex_lattice_method.UnsteadyRingVortexLatticeMethodSolver( - unsteady_problem=example_problem, - ) -) + unsteady_problem=example_problem, )) del example_problem -example_solver.run( - prescribed_wake=True, - calculate_streamlines=False, -) +example_solver.run(prescribed_wake=True, calculate_streamlines=False, ) diff --git a/benchmarks/unsteady_benchmark_converge.py b/benchmarks/unsteady_benchmark_converge.py index 4ad98122..30e4062f 100644 --- a/benchmarks/unsteady_benchmark_converge.py +++ b/benchmarks/unsteady_benchmark_converge.py @@ -4,105 +4,59 @@ num_chordwise_panels = 5 num_spanwise_panels = 20 -example_airplane = ps.geometry.Airplane( - name="Example Airplane", - wings=[ - ps.geometry.Wing( - name="Main Wing", - symmetric=True, - num_chordwise_panels=num_chordwise_panels, - chordwise_spacing="uniform", - wing_cross_sections=[ - ps.geometry.WingCrossSection( - num_spanwise_panels=num_spanwise_panels, - spanwise_spacing="uniform", - chord=1.75, - airfoil=ps.geometry.Airfoil( - name="naca0000", - ), - ), - ps.geometry.WingCrossSection( - num_spanwise_panels=num_spanwise_panels, - spanwise_spacing="uniform", - x_le=0.625, - y_le=5.0, - chord=0.5, - airfoil=ps.geometry.Airfoil( - name="naca0000", - ), - ), - ], - ), - ], -) +example_airplane = ps.geometry.Airplane(name="Example Airplane", wings=[ + ps.geometry.Wing(name="Main Wing", symmetric=True, + num_chordwise_panels=num_chordwise_panels, chordwise_spacing="uniform", + wing_cross_sections=[ + ps.geometry.WingCrossSection(num_spanwise_panels=num_spanwise_panels, + spanwise_spacing="uniform", chord=1.75, + airfoil=ps.geometry.Airfoil(name="naca0000", ), ), + ps.geometry.WingCrossSection(num_spanwise_panels=num_spanwise_panels, + spanwise_spacing="uniform", x_le=0.625, y_le=5.0, chord=0.5, + airfoil=ps.geometry.Airfoil(name="naca0000", ), ), ], ), ], ) upper_wing_root_wing_cross_section_movement = ps.movement.WingCrossSectionMovement( - base_wing_cross_section=example_airplane.wings[0].wing_cross_sections[0], -) + base_wing_cross_section=example_airplane.wings[0].wing_cross_sections[0], ) upper_wing_tip_wing_cross_section_movement = ps.movement.WingCrossSectionMovement( base_wing_cross_section=example_airplane.wings[0].wing_cross_sections[1], - sweeping_amplitude=15.0, - sweeping_period=1 / flapping_frequency, - sweeping_spacing="sine", - pitching_amplitude=5.0, - pitching_period=1 / flapping_frequency, - pitching_spacing="sine", - heaving_amplitude=5.0, - heaving_period=1 / flapping_frequency, - heaving_spacing="sine", -) + sweeping_amplitude=15.0, sweeping_period=1 / flapping_frequency, + sweeping_spacing="sine", pitching_amplitude=5.0, + pitching_period=1 / flapping_frequency, pitching_spacing="sine", + heaving_amplitude=5.0, heaving_period=1 / flapping_frequency, + heaving_spacing="sine", ) -upper_wing_movement = ps.movement.WingMovement( - base_wing=example_airplane.wings[0], - wing_cross_sections_movements=[ - upper_wing_root_wing_cross_section_movement, - upper_wing_tip_wing_cross_section_movement, - ], -) +upper_wing_movement = ps.movement.WingMovement(base_wing=example_airplane.wings[0], + wing_cross_sections_movements=[upper_wing_root_wing_cross_section_movement, + upper_wing_tip_wing_cross_section_movement, ], ) del upper_wing_root_wing_cross_section_movement del upper_wing_tip_wing_cross_section_movement -airplane_movement = ps.movement.AirplaneMovement( - base_airplane=example_airplane, - wing_movements=[upper_wing_movement], -) +airplane_movement = ps.movement.AirplaneMovement(base_airplane=example_airplane, + wing_movements=[upper_wing_movement], ) del example_airplane del upper_wing_movement -example_operating_point = ps.operating_point.OperatingPoint( - density=1.225, - beta=0.0, - velocity=10.0, - alpha=0.0, -) +example_operating_point = ps.operating_point.OperatingPoint(density=1.225, beta=0.0, + velocity=10.0, alpha=0.0, ) operating_point_movement = ps.movement.OperatingPointMovement( - base_operating_point=example_operating_point, -) + base_operating_point=example_operating_point, ) del example_operating_point -movement = ps.movement.Movement( - airplane_movements=[airplane_movement], - operating_point_movement=operating_point_movement, -) +movement = ps.movement.Movement(airplane_movements=[airplane_movement], + operating_point_movement=operating_point_movement, ) del airplane_movement del operating_point_movement -ps.convergence.analyze_unsteady_convergence( - ref_movement=movement, - prescribed_wake=True, - free_wake=True, - num_cycles_bounds=(1, 3), - num_chords_bounds=None, - panel_aspect_ratio_bounds=(4, 1), - num_chordwise_panels_bounds=(5, 10), - convergence_criteria=5.0, -) +ps.convergence.analyze_unsteady_convergence(ref_movement=movement, prescribed_wake=True, + free_wake=True, num_cycles_bounds=(1, 3), num_chords_bounds=None, + panel_aspect_ratio_bounds=(4, 1), num_chordwise_panels_bounds=(5, 10), + convergence_criteria=5.0, ) # Converged result: # wake=false diff --git a/benchmarks/unsteady_benchmark_timed.py b/benchmarks/unsteady_benchmark_timed.py index 17c504b7..c21ad257 100644 --- a/benchmarks/unsteady_benchmark_timed.py +++ b/benchmarks/unsteady_benchmark_timed.py @@ -96,7 +96,8 @@ del airplane_movement del operating_point_movement -example_problem = ps.problems.UnsteadyProblem(movement=movement, only_final_results=True) +example_problem = ps.problems.UnsteadyProblem(movement=movement, +only_final_results=True) unsteady_solver = ps.unsteady_ring_vortex_lattice_method.UnsteadyRingVortexLatticeMethodSolver( unsteady_problem=example_problem, diff --git a/examples/analyze_steady_trim_example.py b/examples/analyze_steady_trim_example.py index 8a4be2de..4b551db3 100644 --- a/examples/analyze_steady_trim_example.py +++ b/examples/analyze_steady_trim_example.py @@ -12,55 +12,16 @@ # Create an airplane object. Read through the solver examples for more details on # creating this object. -default_airplane = ps.geometry.Airplane( - weight=250, - wings=[ - ps.geometry.Wing( - symmetric=True, - wing_cross_sections=[ - ps.geometry.WingCrossSection( - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - ), - ps.geometry.WingCrossSection( - x_le=0.0, - y_le=5.0, - z_le=0.0, - chord=1.0, - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - ), - ], - ), - ps.geometry.Wing( - x_le=7.50, - z_le=0.25, - symmetric=True, - wing_cross_sections=[ - ps.geometry.WingCrossSection( - x_le=0.0, - y_le=0.0, - chord=0.5, - twist=-5.0, - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ps.geometry.WingCrossSection( - x_le=0.0, - y_le=1.0, - chord=0.5, - twist=-5.0, - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ], - ), - ], -) +default_airplane = ps.geometry.Airplane(weight=250, wings=[ + ps.geometry.Wing(symmetric=True, wing_cross_sections=[ + ps.geometry.WingCrossSection(airfoil=ps.geometry.Airfoil(name="naca2412", ), ), + ps.geometry.WingCrossSection(x_le=0.0, y_le=5.0, z_le=0.0, chord=1.0, + airfoil=ps.geometry.Airfoil(name="naca2412", ), ), ], ), + ps.geometry.Wing(x_le=7.50, z_le=0.25, symmetric=True, wing_cross_sections=[ + ps.geometry.WingCrossSection(x_le=0.0, y_le=0.0, chord=0.5, twist=-5.0, + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), + ps.geometry.WingCrossSection(x_le=0.0, y_le=1.0, chord=0.5, twist=-5.0, + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), ], ), ], ) # Create an operating point object for this example's problem. Be sure to specify an # external thrust because this aircraft is not flapping, and will therefore generate @@ -69,20 +30,15 @@ default_operating_point = ps.operating_point.OperatingPoint(external_thrust=5) # Construct this example's problem object. -default_problem = ps.problems.SteadyProblem( - airplanes=[default_airplane], operating_point=default_operating_point -) +default_problem = ps.problems.SteadyProblem(airplanes=[default_airplane], + operating_point=default_operating_point) # Call the analyze_steady_trim function to search for a trim condition (thrust # balances drag, weight balances lift, and all moments are close to zero) within a # certain set of bounds. -trim_conditions = ps.trim.analyze_steady_trim( - problem=default_problem, - velocity_bounds=(5, 15), - alpha_bounds=(-10, 10), - beta_bounds=(-1, 1), - external_thrust_bounds=(0, 10), -) +trim_conditions = ps.trim.analyze_steady_trim(problem=default_problem, + velocity_bounds=(5, 15), alpha_bounds=(-10, 10), beta_bounds=(-1, 1), + external_thrust_bounds=(0, 10), ) # Log the trim conditions. If these display "nan", then the trim function couldn't # find a trimmed state. diff --git a/examples/analyze_unsteady_trim_example.py b/examples/analyze_unsteady_trim_example.py index 28d4035e..ed2c76c2 100644 --- a/examples/analyze_unsteady_trim_example.py +++ b/examples/analyze_unsteady_trim_example.py @@ -12,98 +12,35 @@ # Create an airplane object. Read through the solver examples for more details on # creating this object. -example_airplane = ps.geometry.Airplane( - x_ref=0.14, - weight=420, - wings=[ - ps.geometry.Wing( - symmetric=True, - num_chordwise_panels=5, - wing_cross_sections=[ - ps.geometry.WingCrossSection( - num_spanwise_panels=5, - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - ), - ps.geometry.WingCrossSection( - x_le=0.0, - y_le=5.0, - z_le=0.0, - chord=1.0, - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - ), - ], - ), - ps.geometry.Wing( - x_le=5, - symmetric=True, - num_chordwise_panels=5, - wing_cross_sections=[ - ps.geometry.WingCrossSection( - num_spanwise_panels=5, - twist=-5.0, - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ps.geometry.WingCrossSection( - x_le=0.0, - y_le=1.0, - chord=1.0, - twist=-5.0, - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ], - ), - ], -) +example_airplane = ps.geometry.Airplane(x_ref=0.14, weight=420, wings=[ + ps.geometry.Wing(symmetric=True, num_chordwise_panels=5, wing_cross_sections=[ + ps.geometry.WingCrossSection(num_spanwise_panels=5, + airfoil=ps.geometry.Airfoil(name="naca2412", ), ), + ps.geometry.WingCrossSection(x_le=0.0, y_le=5.0, z_le=0.0, chord=1.0, + airfoil=ps.geometry.Airfoil(name="naca2412", ), ), ], ), + ps.geometry.Wing(x_le=5, symmetric=True, num_chordwise_panels=5, + wing_cross_sections=[ + ps.geometry.WingCrossSection(num_spanwise_panels=5, twist=-5.0, + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), + ps.geometry.WingCrossSection(x_le=0.0, y_le=1.0, chord=1.0, twist=-5.0, + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), ], ), ], ) # Create a movement for this example's airplane. Read through the unsteady solver # examples for more details on this type of object. -example_airplane_movement = ps.movement.AirplaneMovement( - base_airplane=example_airplane, - wing_movements=[ - ps.movement.WingMovement( - base_wing=example_airplane.wings[0], - wing_cross_sections_movements=[ - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=example_airplane.wings[ - 0 - ].wing_cross_sections[0] - ), - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=example_airplane.wings[ - 0 - ].wing_cross_sections[1], - sweeping_period=1.0, - sweeping_amplitude=5.0, - heaving_period=1.0, - heaving_amplitude=10.0, - ), - ], - ), - ps.movement.WingMovement( - base_wing=example_airplane.wings[1], - wing_cross_sections_movements=[ - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=example_airplane.wings[ - 1 - ].wing_cross_sections[0] - ), - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=example_airplane.wings[ - 1 - ].wing_cross_sections[1] - ), - ], - ), - ], -) +example_airplane_movement = ps.movement.AirplaneMovement(base_airplane=example_airplane, + wing_movements=[ps.movement.WingMovement(base_wing=example_airplane.wings[0], + wing_cross_sections_movements=[ps.movement.WingCrossSectionMovement( + base_wing_cross_section=example_airplane.wings[0].wing_cross_sections[0]), + ps.movement.WingCrossSectionMovement( + base_wing_cross_section=example_airplane.wings[0].wing_cross_sections[ + 1], sweeping_period=1.0, sweeping_amplitude=5.0, heaving_period=1.0, + heaving_amplitude=10.0, ), ], ), + ps.movement.WingMovement(base_wing=example_airplane.wings[1], + wing_cross_sections_movements=[ps.movement.WingCrossSectionMovement( + base_wing_cross_section=example_airplane.wings[1].wing_cross_sections[ + 0]), ps.movement.WingCrossSectionMovement( + base_wing_cross_section=example_airplane.wings[1].wing_cross_sections[ + 1]), ], ), ], ) # Create an operating point object for this example's problem using the default values. example_operating_point = ps.operating_point.OperatingPoint() @@ -111,30 +48,22 @@ # Create an operating point movement object. Read through the unsteady solver # examples for more details on this type of object. example_operating_point_movement = ps.movement.OperatingPointMovement( - base_operating_point=example_operating_point -) + base_operating_point=example_operating_point) # Construct this example's movement and problem object. Only calculate final results # to speed up the solver. -example_movement = ps.movement.Movement( - airplane_movements=[example_airplane_movement], - operating_point_movement=example_operating_point_movement, -) -example_problem = ps.problems.UnsteadyProblem( - movement=example_movement, - only_final_results=False, -) +example_movement = ps.movement.Movement(airplane_movements=[example_airplane_movement], + operating_point_movement=example_operating_point_movement, ) +example_problem = ps.problems.UnsteadyProblem(movement=example_movement, + only_final_results=False, ) # Call the analyze_unsteady_trim function to search for a trim condition (thrust # balances drag, weight balances lift, and all moments are close to zero) within a # certain set of bounds. trim_conditions = ps.trim.analyze_unsteady_trim( airplane_movement=example_airplane_movement, - operating_point=example_operating_point, - velocity_bounds=(5, 15), - alpha_bounds=(-10, 10), - beta_bounds=(-0.1, 0.1), -) + operating_point=example_operating_point, velocity_bounds=(5, 15), + alpha_bounds=(-10, 10), beta_bounds=(-0.1, 0.1), ) # Log the trim conditions. If these display "nan", then the trim function couldn't # find a trimmed state. diff --git a/examples/steady_convergence_example.py b/examples/steady_convergence_example.py index d6516cc8..d20c6bf0 100644 --- a/examples/steady_convergence_example.py +++ b/examples/steady_convergence_example.py @@ -6,132 +6,42 @@ # Create two airplane objects. Read through the solver and formation examples for # more details on creating these objects. -leading_airplane = ps.geometry.Airplane( - wings=[ - ps.geometry.Wing( - symmetric=True, - chordwise_spacing="uniform", - wing_cross_sections=[ - ps.geometry.WingCrossSection( - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - spanwise_spacing="cosine", - ), - ps.geometry.WingCrossSection( - x_le=0.0, - y_le=5.0, - z_le=0.0, - chord=1.0, - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - ), - ps.geometry.WingCrossSection( - x_le=0.25, - y_le=10.0, - z_le=0.0, - chord=1.0, - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - ), - ], - ), - ps.geometry.Wing( - x_le=5.0, - symmetric=True, - chordwise_spacing="uniform", - wing_cross_sections=[ - ps.geometry.WingCrossSection( - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - spanwise_spacing="cosine", - ), - ps.geometry.WingCrossSection( - x_le=0.0, - y_le=5.0, - z_le=0.0, - chord=1.0, - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ], - ), - ], -) -trailing_airplane = ps.geometry.Airplane( - x_ref=10, - y_ref=-5, - wings=[ - ps.geometry.Wing( - x_le=10, - y_le=-5, - symmetric=True, - chordwise_spacing="uniform", - wing_cross_sections=[ - ps.geometry.WingCrossSection( - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - spanwise_spacing="cosine", - ), - ps.geometry.WingCrossSection( - x_le=0.0, - y_le=5.0, - z_le=0.0, - chord=1.0, - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - ), - ps.geometry.WingCrossSection( - x_le=0.25, - y_le=10.0, - z_le=0.0, - chord=1.0, - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - ), - ], - ), - ps.geometry.Wing( - x_le=15.0, - y_le=-5, - symmetric=True, - chordwise_spacing="uniform", - wing_cross_sections=[ - ps.geometry.WingCrossSection( - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - spanwise_spacing="cosine", - ), - ps.geometry.WingCrossSection( - x_le=0.0, - y_le=5.0, - z_le=0.0, - chord=1.0, - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ], - ), - ], -) +leading_airplane = ps.geometry.Airplane(wings=[ + ps.geometry.Wing(symmetric=True, chordwise_spacing="uniform", wing_cross_sections=[ + ps.geometry.WingCrossSection(airfoil=ps.geometry.Airfoil(name="naca2412", ), + spanwise_spacing="cosine", ), + ps.geometry.WingCrossSection(x_le=0.0, y_le=5.0, z_le=0.0, chord=1.0, + airfoil=ps.geometry.Airfoil(name="naca2412", ), ), + ps.geometry.WingCrossSection(x_le=0.25, y_le=10.0, z_le=0.0, chord=1.0, + airfoil=ps.geometry.Airfoil(name="naca2412", ), ), ], ), + ps.geometry.Wing(x_le=5.0, symmetric=True, chordwise_spacing="uniform", + wing_cross_sections=[ + ps.geometry.WingCrossSection(airfoil=ps.geometry.Airfoil(name="naca0012", ), + spanwise_spacing="cosine", ), + ps.geometry.WingCrossSection(x_le=0.0, y_le=5.0, z_le=0.0, chord=1.0, + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), ], ), ], ) +trailing_airplane = ps.geometry.Airplane(x_ref=10, y_ref=-5, wings=[ + ps.geometry.Wing(x_le=10, y_le=-5, symmetric=True, chordwise_spacing="uniform", + wing_cross_sections=[ + ps.geometry.WingCrossSection(airfoil=ps.geometry.Airfoil(name="naca2412", ), + spanwise_spacing="cosine", ), + ps.geometry.WingCrossSection(x_le=0.0, y_le=5.0, z_le=0.0, chord=1.0, + airfoil=ps.geometry.Airfoil(name="naca2412", ), ), + ps.geometry.WingCrossSection(x_le=0.25, y_le=10.0, z_le=0.0, chord=1.0, + airfoil=ps.geometry.Airfoil(name="naca2412", ), ), ], ), + ps.geometry.Wing(x_le=15.0, y_le=-5, symmetric=True, chordwise_spacing="uniform", + wing_cross_sections=[ + ps.geometry.WingCrossSection(airfoil=ps.geometry.Airfoil(name="naca0012", ), + spanwise_spacing="cosine", ), + ps.geometry.WingCrossSection(x_le=0.0, y_le=5.0, z_le=0.0, chord=1.0, + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), ], ), ], ) # Create an operating point object. operating_point = ps.operating_point.OperatingPoint() # Create a steady problem. We will pass this into the convergence function. -problem = ps.problems.SteadyProblem( - airplanes=[leading_airplane, trailing_airplane], - operating_point=operating_point, -) +problem = ps.problems.SteadyProblem(airplanes=[leading_airplane, trailing_airplane], + operating_point=operating_point, ) del leading_airplane del trailing_airplane @@ -144,13 +54,9 @@ # return the parameters it found to result in a converged solution. See the # analyze_steady_convergence function docstring for more details. The progress and # results are displayed to the console. -ps.convergence.analyze_steady_convergence( - ref_problem=problem, - solver_type="steady ring vortex lattice method", - panel_aspect_ratio_bounds=(4, 1), - num_chordwise_panels_bounds=(3, 8), - convergence_criteria=1.0, -) +ps.convergence.analyze_steady_convergence(ref_problem=problem, + solver_type="steady ring vortex lattice method", panel_aspect_ratio_bounds=(4, 1), + num_chordwise_panels_bounds=(3, 8), convergence_criteria=1.0, ) # Check the console that the convergence analysis found that the solution converged # with the following parameters: diff --git a/examples/steady_horseshoe_vortex_lattice_method_solver.py b/examples/steady_horseshoe_vortex_lattice_method_solver.py index 46bf9ad6..d7b259ba 100644 --- a/examples/steady_horseshoe_vortex_lattice_method_solver.py +++ b/examples/steady_horseshoe_vortex_lattice_method_solver.py @@ -18,159 +18,110 @@ # program is in SI units. Note: these values are relative to the global # coordinate system fixed front left corner of the first airplane's first wing's # root wing cross section. - x_ref=0.0, - y_ref=0.0, - z_ref=0.0, + x_ref=0.0, y_ref=0.0, z_ref=0.0, # Give the reference dimensions of this aircraft. "s_ref" is the reference area # in meters squared, "b_ref" is the reference span in meters, and "c_ref" is the # reference chord in meters. I set these values to None, which is their default, # so that they will be populated by the first wing object's calculated # characteristics. Note that the reference area used in this program is the # wetted area of the wing's mean-camberline surface. - s_ref=None, - b_ref=None, - c_ref=None, - wings=[ - ps.geometry.Wing( - name="Main Wing", - # Define the location of the leading edge of the wing relative to the - # global coordinate system fixed front left corner of the first - # airplane's first wing's root wing cross section. These values all + s_ref=None, b_ref=None, c_ref=None, wings=[ps.geometry.Wing(name="Main Wing", + # Define the location of the leading edge of the wing relative to the + # global coordinate system fixed front left corner of the first + # airplane's first wing's root wing cross section. These values all + # default to 0.0 meters. + x_le=0.0, y_le=0.0, z_le=0.0, + # Declare that this wing is symmetric. This means that the geometry will + # be reflected across plane of this wing's root wing cross section. Note + # that the geometry coordinates are defined as such: If you were riding + # in the airplane, the positive x direction would point behind you, + # the positive y direction would point out of your right wing, and the + # positive z direction would point upwards, out of your chair. These + # directions form a right-handed coordinate system. The default value of + # "symmetric" is False. + symmetric=True, + # Define the number of chordwise panels on the wing, and the spacing + # between them. The number of chordwise panels defaults to 8 panels. The + # spacing defaults to "cosine", which makes the panels relatively finer, + # in the chordwise direction, near the leading and trailing edges. The + # other option is "uniform". + num_chordwise_panels=6, chordwise_spacing="cosine", + # Every wing has a list of wing cross sections. In order for the geometry + # output to be sensible, each wing must have at least two wing cross + # sections. + wing_cross_sections=[ps.geometry.WingCrossSection( + # Define the location of the leading edge of the wing cross + # section relative to the wing's leading edge. These values all # default to 0.0 meters. - x_le=0.0, - y_le=0.0, - z_le=0.0, - # Declare that this wing is symmetric. This means that the geometry will - # be reflected across plane of this wing's root wing cross section. Note - # that the geometry coordinates are defined as such: If you were riding - # in the airplane, the positive x direction would point behind you, - # the positive y direction would point out of your right wing, and the - # positive z direction would point upwards, out of your chair. These - # directions form a right-handed coordinate system. The default value of - # "symmetric" is False. - symmetric=True, - # Define the number of chordwise panels on the wing, and the spacing - # between them. The number of chordwise panels defaults to 8 panels. The - # spacing defaults to "cosine", which makes the panels relatively finer, - # in the chordwise direction, near the leading and trailing edges. The - # other option is "uniform". - num_chordwise_panels=6, - chordwise_spacing="cosine", - # Every wing has a list of wing cross sections. In order for the geometry - # output to be sensible, each wing must have at least two wing cross - # sections. - wing_cross_sections=[ - ps.geometry.WingCrossSection( - # Define the location of the leading edge of the wing cross - # section relative to the wing's leading edge. These values all - # default to 0.0 meters. - x_le=0.0, - y_le=0.0, - z_le=0.0, - # Define the twist of the wing cross section in degrees. This is - # equivalent to incidence angle of cross section. The twist is - # about the leading edge. Note that the twist is only stable up - # to 45.0 degrees. Values above that produce unexpected results. - # This will be fixed in a future release. The default value is - # 0.0 degrees. Positive twist corresponds to positive rotation - # about the y axis, as defined by the right-hand rule. - twist=0.0, - # Define the type of control surface. The options are "symmetric" - # and "asymmetric". This is only applicable if your wing is also - # symmetric. If so, symmetric control surfaces will deflect in - # the same direction, like flaps, while asymmetric control - # surfaces will deflect in opposite directions, like ailerons. - # The default value is "symmetric". - control_surface_type="symmetric", - # Define the point on the airfoil where the control surface - # hinges. This is expressed as a faction of the chord length, - # back from the leading edge. The default value is 0.75. - control_surface_hinge_point=0.75, - # Define the deflection of the control surface in degrees. The - # default is 0.0 degrees. - control_surface_deflection=0.0, - # Define the number of spanwise panels on the wing cross section, - # and the spacing between them. The number of spanwise panels - # defaults to 8 panels. The spacing defaults to "cosine", - # which makes the panels relatively finer, in the spanwise - # direction, near the cross section ends. The other option is - # "uniform". - num_spanwise_panels=8, - spanwise_spacing="cosine", - # Set the chord of this cross section to be 1.75 meters. This - # value defaults to 1.0 meter. - chord=1.75, - airfoil=ps.geometry.Airfoil( - # Give the airfoil a name. This defaults to "Untitled - # Airfoil". This name should correspond to a name in the - # airfoil directory or a NACA four series airfoil, unless you - # are passing in your own coordinates. - name="naca2412", - # If you wish to pass in coordinates, set this to an N x 2 - # array of the airfoil's coordinates, where N is the number - # of coordinates. Treat this as an immutable, don't edit - # directly after initialization. If you wish to load - # coordinates from the airfoil directory, leave this as None. - # The default is None. Make sure that any airfoil coordinates - # used range in x from 0 to 1. - coordinates=None, - # This is the variable that determines whether you would like - # to repanel the airfoil coordinates. This applies to - # coordinates passed in by the user or to the directory - # coordinates. I highly recommended setting this to True. The - # default is True. - repanel=True, - # This is number of points to use if repaneling the airfoil. - # It is ignored if the repanel is False. The default is 400. - n_points_per_side=400, - ), - ), - # Define the next wing cross section. From here on out, - # the declarations will not be as commented as the previous. See the - # above comments if you have questions. - ps.geometry.WingCrossSection( - x_le=0.75, - y_le=6.0, - z_le=1.0, - chord=1.5, - twist=5.0, - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - ), - ], - ), + x_le=0.0, y_le=0.0, z_le=0.0, + # Define the twist of the wing cross section in degrees. This is + # equivalent to incidence angle of cross section. The twist is + # about the leading edge. Note that the twist is only stable up + # to 45.0 degrees. Values above that produce unexpected results. + # This will be fixed in a future release. The default value is + # 0.0 degrees. Positive twist corresponds to positive rotation + # about the y axis, as defined by the right-hand rule. + twist=0.0, # Define the type of control surface. The options are "symmetric" + # and "asymmetric". This is only applicable if your wing is also + # symmetric. If so, symmetric control surfaces will deflect in + # the same direction, like flaps, while asymmetric control + # surfaces will deflect in opposite directions, like ailerons. + # The default value is "symmetric". + control_surface_type="symmetric", + # Define the point on the airfoil where the control surface + # hinges. This is expressed as a faction of the chord length, + # back from the leading edge. The default value is 0.75. + control_surface_hinge_point=0.75, + # Define the deflection of the control surface in degrees. The + # default is 0.0 degrees. + control_surface_deflection=0.0, + # Define the number of spanwise panels on the wing cross section, + # and the spacing between them. The number of spanwise panels + # defaults to 8 panels. The spacing defaults to "cosine", + # which makes the panels relatively finer, in the spanwise + # direction, near the cross section ends. The other option is + # "uniform". + num_spanwise_panels=8, spanwise_spacing="cosine", + # Set the chord of this cross section to be 1.75 meters. This + # value defaults to 1.0 meter. + chord=1.75, airfoil=ps.geometry.Airfoil( + # Give the airfoil a name. This defaults to "Untitled + # Airfoil". This name should correspond to a name in the + # airfoil directory or a NACA four series airfoil, unless you + # are passing in your own coordinates. + name="naca2412", + # If you wish to pass in coordinates, set this to an N x 2 + # array of the airfoil's coordinates, where N is the number + # of coordinates. Treat this as an immutable, don't edit + # directly after initialization. If you wish to load + # coordinates from the airfoil directory, leave this as None. + # The default is None. Make sure that any airfoil coordinates + # used range in x from 0 to 1. + coordinates=None, + # This is the variable that determines whether you would like + # to repanel the airfoil coordinates. This applies to + # coordinates passed in by the user or to the directory + # coordinates. I highly recommended setting this to True. The + # default is True. + repanel=True, + # This is number of points to use if repaneling the airfoil. + # It is ignored if the repanel is False. The default is 400. + n_points_per_side=400, ), ), + # Define the next wing cross section. From here on out, + # the declarations will not be as commented as the previous. See the + # above comments if you have questions. + ps.geometry.WingCrossSection(x_le=0.75, y_le=6.0, z_le=1.0, chord=1.5, + twist=5.0, airfoil=ps.geometry.Airfoil(name="naca2412", ), ), ], ), # Define the next wing. - ps.geometry.Wing( - name="V-Tail", - x_le=6.75, - z_le=0.25, - symmetric=True, + ps.geometry.Wing(name="V-Tail", x_le=6.75, z_le=0.25, symmetric=True, # Define this wing's root wing cross section. - wing_cross_sections=[ - ps.geometry.WingCrossSection( - chord=1.5, - # Give the root wing cross section an airfoil. - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - twist=-5.0, - ), + wing_cross_sections=[ps.geometry.WingCrossSection(chord=1.5, + # Give the root wing cross section an airfoil. + airfoil=ps.geometry.Airfoil(name="naca0012", ), twist=-5.0, ), # Define the wing's tip wing cross section. - ps.geometry.WingCrossSection( - x_le=0.5, - y_le=2.0, - z_le=1.0, - chord=1.0, + ps.geometry.WingCrossSection(x_le=0.5, y_le=2.0, z_le=1.0, chord=1.0, twist=-5.0, - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ], - ), - ], -) + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), ], ), ], ) # Define a new operating point object. This defines the state at which the airplane # object is operating. @@ -186,8 +137,7 @@ velocity=10.0, # Define the angle of attack the airplane is experiencing. This defaults to 5.0 # degrees. - alpha=1.0, -) + alpha=1.0, ) # Define a new steady problem. A steady problem contains an airplane object and an # operating point object. @@ -195,8 +145,7 @@ # Set this steady problem's airplane object to be the one we just created. airplanes=[example_airplane], # Set this steady problem's operating point object ot be the one we just created. - operating_point=example_operating_point, -) + operating_point=example_operating_point, ) # Now, the airplane and operating point object exist within the steady problem # object. I like to delete the external pointers to these objects to ease debugging. @@ -206,10 +155,10 @@ # Define a new solver. The available solver objects are the steady horseshoe vortex # lattice method solver, the steady ring vortex lattice method solver, and the # unsteady ring vortex lattice method solver. -example_solver = ps.steady_horseshoe_vortex_lattice_method.SteadyHorseshoeVortexLatticeMethodSolver( +example_solver = (ps.steady_horseshoe_vortex_lattice_method +.SteadyHorseshoeVortexLatticeMethodSolver( # Solvers just take in one attribute: the problem they are going to solve. - steady_problem=example_problem -) + steady_problem=example_problem)) # Delete the extraneous pointer to the problem as it is now contained within the # solver. Again, this is unnecessary, I just like to do this to ease debugging. @@ -220,15 +169,13 @@ # This parameter determines the detail of information that the solver's logger # will output while running. The options are, in order of detail and severity, # "Debug", "Info", "Warning", "Error", "Critical". The default value is "Warning". - logging_level="Warning", -) + logging_level="Warning", ) # Call this function from the output module to print the results. ps.output.print_steady_results(steady_solver=example_solver) # Call the software's draw function on the solver. -ps.output.draw( - solver=example_solver, +ps.output.draw(solver=example_solver, # Tell the draw function to color the aircraft's wing panels with the local lift # coefficient. The valid arguments for this parameter are None, "induced drag", # "side force", or "lift". @@ -241,8 +188,7 @@ show_wake_vortices=False, # Tell the draw function to not save the drawing as an image file. This way, # the drawing will still be displayed but not saved. This value defaults to false. - save=False, -) + save=False, ) # Compare the output you see with the expected outputs saved in the "docs/examples # expected output" directory. diff --git a/examples/steady_ring_vortex_lattice_method_solver.py b/examples/steady_ring_vortex_lattice_method_solver.py index ba494e56..04c321e1 100644 --- a/examples/steady_ring_vortex_lattice_method_solver.py +++ b/examples/steady_ring_vortex_lattice_method_solver.py @@ -18,187 +18,122 @@ # program is in SI units. Note: these values are relative to the global # coordinate system fixed front left corner of the first airplane's first wing's # root wing cross section. - x_ref=0.0, - y_ref=0.0, - z_ref=0.0, + x_ref=0.0, y_ref=0.0, z_ref=0.0, # Give the reference dimensions of this aircraft. "s_ref" is the reference area # in meters squared, "b_ref" is the reference span in meters, and "c_ref" is the # reference chord in meters. I set these values to None, which is their default, # so that they will be populated by the first wing object's calculated # characteristics. Note that the reference area used in this program is the # wetted area of the wing's mean-camberline surface. - s_ref=None, - b_ref=None, - c_ref=None, - wings=[ - ps.geometry.Wing( - name="Main Wing", - # Define the location of the leading edge of the wing relative to the - # global coordinate system fixed front left corner of the first - # airplane's first wing's root wing cross section. These values all + s_ref=None, b_ref=None, c_ref=None, wings=[ps.geometry.Wing(name="Main Wing", + # Define the location of the leading edge of the wing relative to the + # global coordinate system fixed front left corner of the first + # airplane's first wing's root wing cross section. These values all + # default to 0.0 meters. + x_le=0.0, y_le=0.0, z_le=0.0, + # Declare that this wing is symmetric. This means that the geometry will + # be reflected across plane of this wing's root wing cross section. Note + # that the geometry coordinates are defined as such: If you were riding + # in the airplane, the positive x direction would point behind you, + # the positive y direction would point out of your right wing, and the + # positive z direction would point upwards, out of your chair. These + # directions form a right-handed coordinate system. The default value of + # "symmetric" is false. + symmetric=True, + # Define the number of chordwise panels on the wing, and the spacing + # between them. The number of chordwise panels defaults to 8 panels. The + # spacing defaults to "cosine", which makes the panels relatively finer, + # in the chordwise direction, near the leading and trailing edges. The + # other option is "uniform". + num_chordwise_panels=8, chordwise_spacing="cosine", + # Every wing has a list of wing cross sections. In order for the geometry + # output to be sensible, each wing must have at least two wing cross + # sections. + wing_cross_sections=[ps.geometry.WingCrossSection( + # Define the location of the leading edge of the wing cross + # section relative to the wing's leading edge. These values all # default to 0.0 meters. - x_le=0.0, - y_le=0.0, - z_le=0.0, - # Declare that this wing is symmetric. This means that the geometry will - # be reflected across plane of this wing's root wing cross section. Note - # that the geometry coordinates are defined as such: If you were riding - # in the airplane, the positive x direction would point behind you, - # the positive y direction would point out of your right wing, and the - # positive z direction would point upwards, out of your chair. These - # directions form a right-handed coordinate system. The default value of - # "symmetric" is false. - symmetric=True, - # Define the number of chordwise panels on the wing, and the spacing - # between them. The number of chordwise panels defaults to 8 panels. The - # spacing defaults to "cosine", which makes the panels relatively finer, - # in the chordwise direction, near the leading and trailing edges. The - # other option is "uniform". - num_chordwise_panels=8, - chordwise_spacing="cosine", - # Every wing has a list of wing cross sections. In order for the geometry - # output to be sensible, each wing must have at least two wing cross - # sections. - wing_cross_sections=[ - ps.geometry.WingCrossSection( - # Define the location of the leading edge of the wing cross - # section relative to the wing's leading edge. These values all - # default to 0.0 meters. - x_le=0.0, - y_le=0.0, - z_le=0.0, - # Define the twist of the wing cross section in degrees. This is - # equivalent to incidence angle of cross section. The twist is - # about the leading edge. Note that the twist is only stable up - # to 45.0 degrees. Values above that produce unexpected results. - # This will be fixed in a future release. The default value is - # 0.0 degrees. Positive twist corresponds to positive rotation - # about the y axis, as defined by the right-hand rule. - twist=0.0, - # Define the type of control surface. The options are "symmetric" - # and "asymmetric". This is only applicable if your wing is also - # symmetric. If so, symmetric control surfaces will deflect in - # the same direction, like flaps, while asymmetric control - # surfaces will deflect in opposite directions, like ailerons. - # The default value is "symmetric". - control_surface_type="asymmetric", - # Define the point on the airfoil where the control surface - # hinges. This is expressed as a faction of the chord length, - # back from the leading edge. The default value is 0.75. - control_surface_hinge_point=0.75, - # Define the deflection of the control surface in degrees. The - # default is 0.0 degrees. We'll set it to 10.0 degrees to show an - # example of an aileron deflection. - control_surface_deflection=10.0, - # Define the number of spanwise panels on the wing cross section, - # and the spacing between them. The number of spanwise panels - # defaults to 8 panels. The spacing defaults to "cosine", - # which makes the panels relatively finer, in the spanwise - # direction, near the cross section ends. The other option is - # "uniform". - num_spanwise_panels=8, - spanwise_spacing="cosine", - # Set the chord of this cross section to be 1.75 meters. This - # value defaults to 1.0 meter. - chord=1.5, - airfoil=ps.geometry.Airfoil( - # Give the airfoil a name. This defaults to "Untitled - # Airfoil". This name should correspond to a name in the - # airfoil directory or a NACA four series airfoil, unless you - # are passing in your own coordinates. - name="naca2412", - # If you wish to pass in coordinates, set this to an N x 2 - # array of the airfoil's coordinates, where N is the number - # of coordinates. Treat this as an immutable, don't edit - # directly after initialization. If you wish to load - # coordinates from the airfoil directory, leave this as None. - # The default is None. Make sure that any airfoil coordinates - # used range in x from 0 to 1. - coordinates=None, - # This is the variable that determines whether you would like - # to repanel the airfoil coordinates. This applies to - # coordinates passed in by the user or to the directory - # coordinates. I highly recommended setting this to True. The - # default is True. - repanel=True, - # This is number of points to use if repaneling the airfoil. - # It is ignored if the repanel is False. The default is 400. - n_points_per_side=400, - ), - ), - # Define the next wing cross section. From here on out, - # the declarations will not be as commented as the previous. See the - # above comments if you have questions. - ps.geometry.WingCrossSection( - x_le=1.5, - y_le=6.0, - z_le=0.5, - chord=0.75, - control_surface_type="asymmetric", - control_surface_hinge_point=0.75, - control_surface_deflection=10.0, - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - ), - ], - ), + x_le=0.0, y_le=0.0, z_le=0.0, + # Define the twist of the wing cross section in degrees. This is + # equivalent to incidence angle of cross section. The twist is + # about the leading edge. Note that the twist is only stable up + # to 45.0 degrees. Values above that produce unexpected results. + # This will be fixed in a future release. The default value is + # 0.0 degrees. Positive twist corresponds to positive rotation + # about the y axis, as defined by the right-hand rule. + twist=0.0, # Define the type of control surface. The options are "symmetric" + # and "asymmetric". This is only applicable if your wing is also + # symmetric. If so, symmetric control surfaces will deflect in + # the same direction, like flaps, while asymmetric control + # surfaces will deflect in opposite directions, like ailerons. + # The default value is "symmetric". + control_surface_type="asymmetric", + # Define the point on the airfoil where the control surface + # hinges. This is expressed as a faction of the chord length, + # back from the leading edge. The default value is 0.75. + control_surface_hinge_point=0.75, + # Define the deflection of the control surface in degrees. The + # default is 0.0 degrees. We'll set it to 10.0 degrees to show an + # example of an aileron deflection. + control_surface_deflection=10.0, + # Define the number of spanwise panels on the wing cross section, + # and the spacing between them. The number of spanwise panels + # defaults to 8 panels. The spacing defaults to "cosine", + # which makes the panels relatively finer, in the spanwise + # direction, near the cross section ends. The other option is + # "uniform". + num_spanwise_panels=8, spanwise_spacing="cosine", + # Set the chord of this cross section to be 1.75 meters. This + # value defaults to 1.0 meter. + chord=1.5, airfoil=ps.geometry.Airfoil( + # Give the airfoil a name. This defaults to "Untitled + # Airfoil". This name should correspond to a name in the + # airfoil directory or a NACA four series airfoil, unless you + # are passing in your own coordinates. + name="naca2412", + # If you wish to pass in coordinates, set this to an N x 2 + # array of the airfoil's coordinates, where N is the number + # of coordinates. Treat this as an immutable, don't edit + # directly after initialization. If you wish to load + # coordinates from the airfoil directory, leave this as None. + # The default is None. Make sure that any airfoil coordinates + # used range in x from 0 to 1. + coordinates=None, + # This is the variable that determines whether you would like + # to repanel the airfoil coordinates. This applies to + # coordinates passed in by the user or to the directory + # coordinates. I highly recommended setting this to True. The + # default is True. + repanel=True, + # This is number of points to use if repaneling the airfoil. + # It is ignored if the repanel is False. The default is 400. + n_points_per_side=400, ), ), + # Define the next wing cross section. From here on out, + # the declarations will not be as commented as the previous. See the + # above comments if you have questions. + ps.geometry.WingCrossSection(x_le=1.5, y_le=6.0, z_le=0.5, chord=0.75, + control_surface_type="asymmetric", control_surface_hinge_point=0.75, + control_surface_deflection=10.0, + airfoil=ps.geometry.Airfoil(name="naca2412", ), ), ], ), # Define the next wing. - ps.geometry.Wing( - name="Horizontal Stabilizer", - x_le=6.75, - z_le=0.25, - symmetric=True, - wing_cross_sections=[ - ps.geometry.WingCrossSection( - chord=1.5, - # Give the root wing cross section an airfoil. - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - twist=-5.0, - ), + ps.geometry.Wing(name="Horizontal Stabilizer", x_le=6.75, z_le=0.25, + symmetric=True, wing_cross_sections=[ps.geometry.WingCrossSection(chord=1.5, + # Give the root wing cross section an airfoil. + airfoil=ps.geometry.Airfoil(name="naca0012", ), twist=-5.0, ), # Define the wing's tip wing cross section. - ps.geometry.WingCrossSection( - x_le=0.5, - y_le=2.0, - chord=1.0, - twist=-5.0, + ps.geometry.WingCrossSection(x_le=0.5, y_le=2.0, chord=1.0, twist=-5.0, # Give the tip wing cross section an airfoil. - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ], - ), + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), ], ), # Define the next wing. - ps.geometry.Wing( - name="Vertical Stabilizer", - x_le=6.75, - z_le=0.5, - symmetric=False, - wing_cross_sections=[ - ps.geometry.WingCrossSection( - chord=1.5, + ps.geometry.Wing(name="Vertical Stabilizer", x_le=6.75, z_le=0.5, + symmetric=False, wing_cross_sections=[ + ps.geometry.WingCrossSection(chord=1.5, # Give the root wing cross section an airfoil. - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), # Define the wing's tip wing cross section. - ps.geometry.WingCrossSection( - x_le=0.5, - z_le=2.0, - chord=1.0, + ps.geometry.WingCrossSection(x_le=0.5, z_le=2.0, chord=1.0, # Give the tip wing cross section an airfoil. - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ], - ), - ], -) + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), ], ), ], ) # Define a new operating point object. This defines the state at which the airplane # object is operating. @@ -214,8 +149,7 @@ velocity=10.0, # Define the angle of attack the airplane is experiencing. This defaults to 5.0 # degrees. - alpha=1.0, -) + alpha=1.0, ) # Define a new steady problem. A steady problem contains an airplane object and an # operating point object. @@ -223,8 +157,7 @@ # Set this steady problem's list of airplane objects to be the one we just created. airplanes=[example_airplane], # Set this steady problem's operating point object ot be the one we just created. - operating_point=example_operating_point, -) + operating_point=example_operating_point, ) # Now, the airplane and operating point object exist within the steady problem # object. I like to delete the external pointers to these objects to ease debugging. @@ -234,10 +167,10 @@ # Define a new solver. The available solver objects are the steady horseshoe vortex # lattice method solver, the steady ring vortex lattice method solver, and the # unsteady ring vortex lattice method solver. -example_solver = ps.steady_ring_vortex_lattice_method.SteadyRingVortexLatticeMethodSolver( +example_solver = (ps.steady_ring_vortex_lattice_method +.SteadyRingVortexLatticeMethodSolver( # Solvers just take in one attribute: the problem they are going to solve. - steady_problem=example_problem -) + steady_problem=example_problem)) # Delete the extraneous pointer to the problem as it is now contained within the # solver. Again, this is unnecessary, I just like to do this to ease debugging. @@ -248,15 +181,13 @@ # This parameter determines the detail of information that the solver's logger # will output while running. The options are, in order of detail and severity, # "Debug", "Info", "Warning", "Error", "Critical". The default value is "Warning". - logging_level="Warning", -) + logging_level="Warning", ) # Call this function from the output module to print the results. ps.output.print_steady_results(steady_solver=example_solver) # Call the software's draw function on the solver. -ps.output.draw( - solver=example_solver, +ps.output.draw(solver=example_solver, # Tell the draw function to color the aircraft's wing panels with the local # lift coefficient. The valid arguments for this parameter are None, "induced drag", # "side force", or "lift". @@ -269,8 +200,7 @@ show_wake_vortices=False, # Tell the draw function to not save the drawing as an image file. This way, # the drawing will still be displayed but not saved. This value defaults to false. - save=False, -) + save=False, ) # Compare the output you see with the expected outputs saved in the "docs/examples # expected output" directory. diff --git a/examples/unsteady_ring_vortex_lattice_method_solver_static.py b/examples/unsteady_ring_vortex_lattice_method_solver_static.py index 322b968f..1133f73e 100644 --- a/examples/unsteady_ring_vortex_lattice_method_solver_static.py +++ b/examples/unsteady_ring_vortex_lattice_method_solver_static.py @@ -18,31 +18,22 @@ # program is in SI units. Note: these values are relative to the global # coordinate system fixed front left corner of the first airplane's first wing's # root wing cross section. - x_ref=0.0, - y_ref=0.0, - z_ref=0.0, + x_ref=0.0, y_ref=0.0, z_ref=0.0, # Give the reference dimensions of this aircraft. "s_ref" is the reference area # in meters squared, "b_ref" is the reference span in meters, and "c_ref" is the # reference chord in meters. I set these values to None, which is their default, # so that they will be populated by the first wing object's calculated # characteristics. Note that the reference area used in this program is the # wetted area of the wing's mean-camberline surface. - s_ref=None, - b_ref=None, - c_ref=None, - # All airplane objects have a list of wings. - wings=[ - # Create the first wing object in this airplane. - ps.geometry.Wing( - # Give the wing a name, this defaults to "Untitled Wing". + s_ref=None, b_ref=None, c_ref=None, # All airplane objects have a list of wings. + wings=[# Create the first wing object in this airplane. + ps.geometry.Wing(# Give the wing a name, this defaults to "Untitled Wing". name="Main Wing", # Define the location of the leading edge of the wing relative to the # global coordinate system fixed front left corner of the first # airplane's first wing's root wing cross section. These values all # default to 0.0 meters. - x_le=0.0, - y_le=0.0, - z_le=0.0, + x_le=0.0, y_le=0.0, z_le=0.0, # Declare that this wing is symmetric. This means that the geometry will # be reflected across plane of this wing's root wing cross section. Note # that the geometry coordinates are defined as such: If you were riding @@ -58,20 +49,16 @@ # in the chordwise direction, near the leading and trailing edges. The # other option is "uniform". I set this value to "uniform" here as it # increase the accuracy of unsteady solvers. - num_chordwise_panels=6, - chordwise_spacing="uniform", + num_chordwise_panels=6, chordwise_spacing="uniform", # Every wing has a list of wing cross sections. In order for the geometry # output to be sensible, each wing must have at least two wing cross # sections. - wing_cross_sections=[ - # Create a new wing cross section object. + wing_cross_sections=[# Create a new wing cross section object. ps.geometry.WingCrossSection( # Define the location of the leading edge of the wing cross # section relative to the wing's leading edge. These values all # default to 0.0 meters. - x_le=0.0, - y_le=0.0, - z_le=0.0, + x_le=0.0, y_le=0.0, z_le=0.0, # Define the twist of the wing cross section in degrees. This is # equivalent to incidence angle of cross section. The twist is # about the leading edge. Note that the twist is only stable up @@ -100,12 +87,10 @@ # which makes the panels relatively finer, in the spanwise # direction, near the cross section ends. The other option is # "uniform". - num_spanwise_panels=8, - spanwise_spacing="cosine", + num_spanwise_panels=8, spanwise_spacing="cosine", # Set the chord of this cross section to be 1.75 meters. This # value defaults to 1.0 meter. - chord=1.75, - # Every wing cross section has an airfoil object. + chord=1.75, # Every wing cross section has an airfoil object. airfoil=ps.geometry.Airfoil( # Give the airfoil a name. This defaults to "Untitled # Airfoil". This name should correspond to a name in the @@ -128,59 +113,24 @@ repanel=True, # This is number of points to use if repaneling the airfoil. # It is ignored if the repanel is False. The default is 400. - n_points_per_side=400, - ), - ), + n_points_per_side=400, ), ), # Define the next wing cross section. From here on out, # the declarations will not be as commented as the previous. See the # above comments if you have questions. - ps.geometry.WingCrossSection( - x_le=0.75, - y_le=6.0, - z_le=1.0, - chord=1.5, - twist=5.0, - # Give this wing cross section an airfoil. - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - ), - ], - ), + ps.geometry.WingCrossSection(x_le=0.75, y_le=6.0, z_le=1.0, chord=1.5, + twist=5.0, # Give this wing cross section an airfoil. + airfoil=ps.geometry.Airfoil(name="naca2412", ), ), ], ), # Define the next wing. - ps.geometry.Wing( - name="V-Tail", - x_le=6.75, - z_le=0.25, - num_chordwise_panels=6, - chordwise_spacing="uniform", - symmetric=True, + ps.geometry.Wing(name="V-Tail", x_le=6.75, z_le=0.25, num_chordwise_panels=6, + chordwise_spacing="uniform", symmetric=True, # Define this wing's root wing cross section. - wing_cross_sections=[ - ps.geometry.WingCrossSection( - chord=1.5, - # Give the root wing cross section an airfoil. - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - twist=-5.0, - ), + wing_cross_sections=[ps.geometry.WingCrossSection(chord=1.5, + # Give the root wing cross section an airfoil. + airfoil=ps.geometry.Airfoil(name="naca0012", ), twist=-5.0, ), # Define the wing's tip wing cross section. - ps.geometry.WingCrossSection( - x_le=0.5, - y_le=2.0, - z_le=1.0, - chord=1.0, - twist=-5.0, - # Give the tip wing cross section an airfoil. - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ], - ), - ], -) + ps.geometry.WingCrossSection(x_le=0.5, y_le=2.0, z_le=1.0, chord=1.0, + twist=-5.0, # Give the tip wing cross section an airfoil. + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), ], ), ], ) # Now define the main wing's root wing cross section's movement. Cross sections can # move in three ways: sweeping, pitching, and heaving. Sweeping is defined as the @@ -220,39 +170,33 @@ heaving_period=0.0, # Define the time step spacing of the heaving. This is "sine" by default. The # options are "sine" and "uniform". - heaving_spacing="sine", -) + heaving_spacing="sine", ) # Define the main wing's tip wing cross section's movement. As the example has static # geometry, the movement attributes can be excluded, and the default values will # suffice. main_wing_tip_wing_cross_section_movement = ps.movement.WingCrossSectionMovement( - base_wing_cross_section=example_airplane.wings[0].wing_cross_sections[1], -) + base_wing_cross_section=example_airplane.wings[0].wing_cross_sections[1], ) # Define the v-tail's root wing cross section's movement. As the example has static # geometry, the movement attributes can be excluded, and the default values will # suffice. v_tail_root_wing_cross_section_movement = ps.movement.WingCrossSectionMovement( - base_wing_cross_section=example_airplane.wings[1].wing_cross_sections[0], -) + base_wing_cross_section=example_airplane.wings[1].wing_cross_sections[0], ) # Define the v-tail's tip wing cross section's movement. As the example has static # geometry, the movement attributes can be excluded, and the default values will # suffice. v_tail_tip_wing_cross_section_movement = ps.movement.WingCrossSectionMovement( - base_wing_cross_section=example_airplane.wings[1].wing_cross_sections[1], -) + base_wing_cross_section=example_airplane.wings[1].wing_cross_sections[1], ) # Now define the main wing's movement. In addition to their wing cross sections' # relative movements, wings' leading edge positions can move as well. main_wing_movement = ps.movement.WingMovement( # Define the base wing object. base_wing=example_airplane.wings[0], # Add the list of wing cross section movement objects. - wing_cross_sections_movements=[ - main_wing_root_wing_cross_section_movement, - main_wing_tip_wing_cross_section_movement, - ], + wing_cross_sections_movements=[main_wing_root_wing_cross_section_movement, + main_wing_tip_wing_cross_section_movement, ], # Define the amplitude of the leading edge's change in x position. This value is # in meters. This is set to 0.0 meters, which is the default value. x_le_amplitude=0.0, @@ -279,8 +223,7 @@ z_le_period=0.0, # Define the time step spacing of the leading edge's change in z position. This # is "sine" by default. The options are "sine" and "uniform". - z_le_spacing="sine", -) + z_le_spacing="sine", ) # Delete the extraneous wing cross section movement objects, as these are now # contained within the wing movement object. This is unnecessary, but it can make @@ -289,15 +232,11 @@ del main_wing_tip_wing_cross_section_movement # Make the v-tail's wing movement object. -v_tail_movement = ps.movement.WingMovement( - # Define the base wing object. +v_tail_movement = ps.movement.WingMovement(# Define the base wing object. base_wing=example_airplane.wings[1], # Add the list of wing cross section movement objects. - wing_cross_sections_movements=[ - v_tail_root_wing_cross_section_movement, - v_tail_tip_wing_cross_section_movement, - ], -) + wing_cross_sections_movements=[v_tail_root_wing_cross_section_movement, + v_tail_tip_wing_cross_section_movement, ], ) # Delete the extraneous wing cross section movement objects, as these are now # contained within the wing movement object. This is unnecessary, but it can make @@ -307,10 +246,8 @@ # Now define the airplane's movement object. In addition to their wing's and wing # cross sections' relative movements, airplane's reference positions can move as well. -airplane_movement = ps.movement.AirplaneMovement( - # Define the base airplane object. - base_airplane=example_airplane, - # Add the list of wing movement objects. +airplane_movement = ps.movement.AirplaneMovement(# Define the base airplane object. + base_airplane=example_airplane, # Add the list of wing movement objects. wing_movements=[main_wing_movement, v_tail_movement], # Define the amplitude of the reference position's change in x position. This # value is in meters. This is set to 0.0 meters, which is the default value. @@ -338,8 +275,7 @@ z_ref_period=0.0, # Define the time step spacing of the reference position's change in z position. # This is "sine" by default. The options are "sine" and "uniform". - z_ref_spacing="sine", -) + z_ref_spacing="sine", ) # Delete the extraneous wing movement objects, as these are now contained within the # airplane movement object. @@ -364,8 +300,7 @@ # Define the kinematic viscosity of the air in meters squared per second. This # defaults to 15.06e-6 meters squared per second, which corresponds to an air # temperature of 20 degrees Celsius. - nu=15.06e-6, -) + nu=15.06e-6, ) # Define the operating point's movement. The operating point's velocity can change # with respect to time. @@ -380,15 +315,12 @@ velocity_period=0.0, # Define the time step spacing of the velocity's change in time. This is "sine" # by default. The options are "sine" and "uniform". - velocity_spacing="sine", -) + velocity_spacing="sine", ) # Define the movement object. This contains the airplane movement and the operating # point movement. -movement = ps.movement.Movement( - # Add the airplane movement. - airplane_movements=[airplane_movement], - # Add the operating point movement. +movement = ps.movement.Movement(# Add the airplane movement. + airplane_movements=[airplane_movement], # Add the operating point movement. operating_point_movement=operating_point_movement, # Leave the number of time steps and the length of each time step unspecified. # The solver will automatically set the length of the time steps so that the wake @@ -397,9 +329,7 @@ # the number of steps will be set such that the wake extends ten chord lengths # back from the main wing. If the geometry isn't static, the number of steps will # be set such that three periods of the slowest movement oscillation complete. - num_steps=None, - delta_time=None, -) + num_steps=None, delta_time=None, ) # Delete the extraneous airplane and operating point movement objects, as these are # now contained within the movement object. @@ -407,17 +337,15 @@ del operating_point_movement # Define the unsteady example problem. -example_problem = ps.problems.UnsteadyProblem( - movement=movement, -) +example_problem = ps.problems.UnsteadyProblem(movement=movement, ) # Define a new solver. The available solver objects are the steady horseshoe vortex # lattice method solver, the steady ring vortex lattice method solver, and the # unsteady ring vortex lattice method solver. -example_solver = ps.unsteady_ring_vortex_lattice_method.UnsteadyRingVortexLatticeMethodSolver( +example_solver = (ps.unsteady_ring_vortex_lattice_method +.UnsteadyRingVortexLatticeMethodSolver( # Solvers just take in one attribute: the problem they are going to solve. - unsteady_problem=example_problem, -) + unsteady_problem=example_problem, )) # Delete the extraneous pointer to the problem as it is now contained within the # solver. @@ -430,13 +358,11 @@ # "Debug", "Info", "Warning", "Error", "Critical". The default value is "Warning". logging_level="Warning", # Use a prescribed wake model. This is faster, but may be slightly less accurate. - prescribed_wake=True, -) + prescribed_wake=True, ) # Call the software's draw function on the solver. Press "q" to close the plotter # after it draws the output. -ps.output.draw( - # Set the solver to the one we just ran. +ps.output.draw(# Set the solver to the one we just ran. solver=example_solver, # Tell the draw function to color the aircraft's wing panels with the local lift # coefficient. The valid arguments for this parameter are None, "induced drag", @@ -450,14 +376,12 @@ show_wake_vortices=False, # Tell the draw function to not save the drawing as an image file. This way, # the drawing will still be displayed but not saved. This value defaults to False. - save=False, -) + save=False, ) # Call the software's animate function on the solver. This produces a GIF of the wake # being shed. The GIF is saved in the same directory as this script. Press "q", # after orienting the view, to begin the animation. -ps.output.animate( - # Set the unsteady solver to the one we just ran. +ps.output.animate(# Set the unsteady solver to the one we just ran. unsteady_solver=example_solver, # Tell the animate function to color the aircraft's wing panels with the local # lift coefficient. The valid arguments for this parameter are None, "induced drag", @@ -469,22 +393,18 @@ # Tell the animate function to not save the animation as file. This way, # the animation will still be displayed but not saved. This value defaults to # False. - save=False, -) + save=False, ) # Call the software's plotting function on the solver. This produces graphs of the # output forces and moments with respect to time. -ps.output.plot_results_versus_time( - # Set the unsteady solver to the one we just ran. +ps.output.plot_results_versus_time(# Set the unsteady solver to the one we just ran. unsteady_solver=example_solver, # Set the show attribute to True, which is the default value. With this set to # show, some IDEs (such as PyCharm in "Scientific Mode") will display the plots # in a sidebar. Other IDEs may not display the plots, in which case you should # set the save attribute to True, and open the files after they've been saved to # the current directory. - show=True, - save=False, -) + show=True, save=False, ) # Compare the output you see with the expected outputs saved in the "docs/examples # expected output" directory. diff --git a/examples/unsteady_ring_vortex_lattice_method_solver_variable.py b/examples/unsteady_ring_vortex_lattice_method_solver_variable.py index 834f1073..f103ef46 100644 --- a/examples/unsteady_ring_vortex_lattice_method_solver_variable.py +++ b/examples/unsteady_ring_vortex_lattice_method_solver_variable.py @@ -18,30 +18,22 @@ # program is in SI units. Note: these values are relative to the global # coordinate system fixed front left corner of the first airplane's first wing's # root wing cross section. - x_ref=0.0, - y_ref=0.0, - z_ref=0.0, + x_ref=0.0, y_ref=0.0, z_ref=0.0, # Give the reference dimensions of this aircraft. "s_ref" is the reference area # in meters squared, "b_ref" is the reference span in meters, and "c_ref" is the # reference chord in meters. I set these values to None, which is their default, # so that they will be populated by the first wing object's calculated # characteristics. Note that the reference area used in this program is the # wetted area of the wing's mean-camberline surface. - s_ref=None, - b_ref=None, - c_ref=None, - # All airplane objects have a list of wings. - wings=[ - # Create the first wing object in this airplane. + s_ref=None, b_ref=None, c_ref=None, # All airplane objects have a list of wings. + wings=[# Create the first wing object in this airplane. ps.geometry.Wing( # Give the wing a name, this defaults to "Untitled Wing". name="Main Wing", # Define the location of the leading edge of the wing relative to the # global coordinate system fixed front left corner of the first # airplane's first wing's root wing cross section. These values all # default to 0.0 meters. - x_le=0.0, - y_le=0.0, - z_le=0.0, + x_le=0.0, y_le=0.0, z_le=0.0, # Declare that this wing is symmetric. This means that the geometry will # be reflected across plane of this wing's root wing cross section. Note # that the geometry coordinates are defined as such: If you were riding @@ -57,20 +49,16 @@ # in the chordwise direction, near the leading and trailing edges. The # other option is "uniform". I set this value to "uniform" here as it # increase the accuracy of unsteady solvers. - num_chordwise_panels=6, - chordwise_spacing="uniform", + num_chordwise_panels=6, chordwise_spacing="uniform", # Every wing has a list of wing cross sections. In order for the geometry # output to be sensible, each wing must have at least two wing cross # sections. - wing_cross_sections=[ - # Create a new wing cross section object. + wing_cross_sections=[# Create a new wing cross section object. ps.geometry.WingCrossSection( # Define the location of the leading edge of the wing cross # section relative to the wing's leading edge. These values all # default to 0.0 meters. - x_le=0.0, - y_le=0.0, - z_le=0.0, + x_le=0.0, y_le=0.0, z_le=0.0, # Define the twist of the wing cross section in degrees. This is # equivalent to incidence angle of cross section. The twist is # about the leading edge. Note that the twist is only stable up @@ -99,12 +87,10 @@ # which makes the panels relatively finer, in the spanwise # direction, near the cross section ends. The other option is # "uniform". - num_spanwise_panels=8, - spanwise_spacing="cosine", + num_spanwise_panels=8, spanwise_spacing="cosine", # Set the chord of this cross section to be 1.75 meters. This # value defaults to 1.0 meter. - chord=1.75, - # Every wing cross section has an airfoil object. + chord=1.75, # Every wing cross section has an airfoil object. airfoil=ps.geometry.Airfoil( # Give the airfoil a name. This defaults to "Untitled # Airfoil". This name should correspond to a name in the @@ -127,59 +113,24 @@ repanel=True, # This is number of points to use if repaneling the airfoil. # It is ignored if the repanel is False. The default is 400. - n_points_per_side=400, - ), - ), + n_points_per_side=400, ), ), # Define the next wing cross section. From here on out, # the declarations will not be as commented as the previous. See the # above comments if you have questions. - ps.geometry.WingCrossSection( - x_le=0.75, - y_le=6.0, - z_le=1.0, - chord=1.5, - twist=5.0, - # Give this wing cross section an airfoil. - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - ), - ], - ), + ps.geometry.WingCrossSection(x_le=0.75, y_le=6.0, z_le=1.0, chord=1.5, + twist=5.0, # Give this wing cross section an airfoil. + airfoil=ps.geometry.Airfoil(name="naca2412", ), ), ], ), # Define the next wing. - ps.geometry.Wing( - name="V-Tail", - x_le=6.75, - z_le=0.25, - num_chordwise_panels=6, - chordwise_spacing="uniform", - symmetric=True, + ps.geometry.Wing(name="V-Tail", x_le=6.75, z_le=0.25, num_chordwise_panels=6, + chordwise_spacing="uniform", symmetric=True, # Define this wing's root wing cross section. - wing_cross_sections=[ - ps.geometry.WingCrossSection( - chord=1.5, - # Give the root wing cross section an airfoil. - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - twist=-5.0, - ), + wing_cross_sections=[ps.geometry.WingCrossSection(chord=1.5, + # Give the root wing cross section an airfoil. + airfoil=ps.geometry.Airfoil(name="naca0012", ), twist=-5.0, ), # Define the wing's tip wing cross section. - ps.geometry.WingCrossSection( - x_le=0.5, - y_le=2.0, - z_le=1.0, - chord=1.0, - twist=-5.0, - # Give the tip wing cross section an airfoil. - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ], - ), - ], -) + ps.geometry.WingCrossSection(x_le=0.5, y_le=2.0, z_le=1.0, chord=1.0, + twist=-5.0, # Give the tip wing cross section an airfoil. + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), ], ), ], ) # Now define the main wing's root wing cross section's movement. Cross sections can # move in three ways: sweeping, pitching, and heaving. Sweeping is defined as the @@ -219,44 +170,32 @@ heaving_period=0.0, # Define the time step spacing of the heaving. This is "sine" by default. The # options are "sine" and "uniform". - heaving_spacing="sine", -) + heaving_spacing="sine", ) # Define the main wing's tip wing cross section's movement. main_wing_tip_wing_cross_section_movement = ps.movement.WingCrossSectionMovement( base_wing_cross_section=example_airplane.wings[0].wing_cross_sections[1], - sweeping_amplitude=30.0, - sweeping_period=1.0, - sweeping_spacing="sine", - pitching_amplitude=15.0, - pitching_period=1.0, - pitching_spacing="sine", - heaving_amplitude=0.0, - heaving_period=0.0, - heaving_spacing="sine", -) + sweeping_amplitude=30.0, sweeping_period=1.0, sweeping_spacing="sine", + pitching_amplitude=15.0, pitching_period=1.0, pitching_spacing="sine", + heaving_amplitude=0.0, heaving_period=0.0, heaving_spacing="sine", ) # Define the v-tail's root wing cross section's movement. This wing will be static, # so the movement attributes can be excluded, and the default values will suffice. v_tail_root_wing_cross_section_movement = ps.movement.WingCrossSectionMovement( - base_wing_cross_section=example_airplane.wings[1].wing_cross_sections[0], -) + base_wing_cross_section=example_airplane.wings[1].wing_cross_sections[0], ) # Define the v-tail's root wing cross section's movement. This wing will be static, # so the movement attributes can be excluded, and the default values will suffice. v_tail_tip_wing_cross_section_movement = ps.movement.WingCrossSectionMovement( - base_wing_cross_section=example_airplane.wings[1].wing_cross_sections[1], -) + base_wing_cross_section=example_airplane.wings[1].wing_cross_sections[1], ) # Now define the main wing's movement. In addition to their wing cross sections' # relative movements, wings' leading edge positions can move as well. main_wing_movement = ps.movement.WingMovement( # Define the base wing object. base_wing=example_airplane.wings[0], # Add the list of wing cross section movement objects. - wing_cross_sections_movements=[ - main_wing_root_wing_cross_section_movement, - main_wing_tip_wing_cross_section_movement, - ], + wing_cross_sections_movements=[main_wing_root_wing_cross_section_movement, + main_wing_tip_wing_cross_section_movement, ], # Define the amplitude of the leading edge's change in x position. This value is # in meters. This is set to 0.0 meters, which is the default value. x_le_amplitude=0.0, @@ -283,8 +222,7 @@ z_le_period=0.0, # Define the time step spacing of the leading edge's change in z position. This # is "sine" by default. The options are "sine" and "uniform". - z_le_spacing="sine", -) + z_le_spacing="sine", ) # Delete the extraneous wing cross section movement objects, as these are now # contained within the wing movement object. This is unnecessary, but it can make @@ -293,15 +231,11 @@ del main_wing_tip_wing_cross_section_movement # Make the v-tail's wing movement object. -v_tail_movement = ps.movement.WingMovement( - # Define the base wing object. +v_tail_movement = ps.movement.WingMovement(# Define the base wing object. base_wing=example_airplane.wings[1], # Add the list of wing cross section movement objects. - wing_cross_sections_movements=[ - v_tail_root_wing_cross_section_movement, - v_tail_tip_wing_cross_section_movement, - ], -) + wing_cross_sections_movements=[v_tail_root_wing_cross_section_movement, + v_tail_tip_wing_cross_section_movement, ], ) # Delete the extraneous wing cross section movement objects, as these are now # contained within the wing movement object. This is unnecessary, but it can make @@ -311,10 +245,8 @@ # Now define the airplane's movement object. In addition to their wing's and wing # cross sections' relative movements, airplane's reference positions can move as well. -airplane_movement = ps.movement.AirplaneMovement( - # Define the base airplane object. - base_airplane=example_airplane, - # Add the list of wing movement objects. +airplane_movement = ps.movement.AirplaneMovement(# Define the base airplane object. + base_airplane=example_airplane, # Add the list of wing movement objects. wing_movements=[main_wing_movement, v_tail_movement], # Define the amplitude of the reference position's change in x position. This # value is in meters. This is set to 0.0 meters, which is the default value. @@ -342,8 +274,7 @@ z_ref_period=0.0, # Define the time step spacing of the reference position's change in z position. # This is "sine" by default. The options are "sine" and "uniform". - z_ref_spacing="sine", -) + z_ref_spacing="sine", ) # Delete the extraneous wing movement objects, as these are now contained within the # airplane movement object. @@ -368,8 +299,7 @@ # Define the kinematic viscosity of the air in meters squared per second. This # defaults to 15.06e-6 meters squared per second, which corresponds to an air # temperature of 20 degrees Celsius. - nu=15.06e-6, -) + nu=15.06e-6, ) # Define the operating point's movement. The operating point's velocity can change # with respect to time. @@ -384,15 +314,12 @@ velocity_period=0.0, # Define the time step spacing of the velocity's change in time. This is "sine" # by default. The options are "sine" and "uniform". - velocity_spacing="sine", -) + velocity_spacing="sine", ) # Define the movement object. This contains the airplane movement and the operating # point movement. -movement = ps.movement.Movement( - # Add the airplane movement. - airplane_movements=[airplane_movement], - # Add the operating point movement. +movement = ps.movement.Movement(# Add the airplane movement. + airplane_movements=[airplane_movement], # Add the operating point movement. operating_point_movement=operating_point_movement, # Leave the number of time steps and the length of each time step unspecified. # The solver will automatically set the length of the time steps so that the wake @@ -401,9 +328,7 @@ # the number of steps will be set such that the wake extends ten chord lengths # back from the main wing. If the geometry isn't static, the number of steps will # be set such that three periods of the slowest movement oscillation complete. - num_steps=None, - delta_time=None, -) + num_steps=None, delta_time=None, ) # Delete the extraneous airplane and operating point movement objects, as these are # now contained within the movement object. @@ -411,17 +336,15 @@ del operating_point_movement # Define the unsteady example problem. -example_problem = ps.problems.UnsteadyProblem( - movement=movement, -) +example_problem = ps.problems.UnsteadyProblem(movement=movement, ) # Define a new solver. The available solver objects are the steady horseshoe vortex # lattice method solver, the steady ring vortex lattice method solver, and the # unsteady ring vortex lattice method solver. -example_solver = ps.unsteady_ring_vortex_lattice_method.UnsteadyRingVortexLatticeMethodSolver( +example_solver = (ps.unsteady_ring_vortex_lattice_method +.UnsteadyRingVortexLatticeMethodSolver( # Solvers just take in one attribute: the problem they are going to solve. - unsteady_problem=example_problem, -) + unsteady_problem=example_problem, )) # Delete the extraneous pointer to the problem as it is now contained within the # solver. @@ -434,14 +357,12 @@ # "Debug", "Info", "Warning", "Error", "Critical". The default value is "Warning". logging_level="Warning", # Use a prescribed wake model. This is faster, but may be slightly less accurate. - prescribed_wake=True, -) + prescribed_wake=True, ) # Call the software's animate function on the solver. This produces a GIF of the wake # being shed. The GIF is saved in the same directory as this script. Press "q", # after orienting the view, to begin the animation. -ps.output.animate( - # Set the unsteady solver to the one we just ran. +ps.output.animate(# Set the unsteady solver to the one we just ran. unsteady_solver=example_solver, # Tell the animate function to color the aircraft's wing panels with the local # lift coefficient. The valid arguments for this parameter are None, "induced drag", @@ -453,8 +374,7 @@ # Tell the animate function to not save the animation as file. This way, # the animation will still be displayed but not saved. This value defaults to # False. - save=False, -) + save=False, ) # Compare the output you see with the expected outputs saved in the "docs/examples # expected output" directory. diff --git a/examples/unsteady_ring_vortex_lattice_method_solver_variable_formation.py b/examples/unsteady_ring_vortex_lattice_method_solver_variable_formation.py index d4d94560..01ec8f12 100644 --- a/examples/unsteady_ring_vortex_lattice_method_solver_variable_formation.py +++ b/examples/unsteady_ring_vortex_lattice_method_solver_variable_formation.py @@ -10,250 +10,127 @@ y_spacing = 13 # Create the lead airplane object. -lead_airplane = ps.geometry.Airplane( - name="Lead Airplane", +lead_airplane = ps.geometry.Airplane(name="Lead Airplane", # Specify the location of the lead airplane's center of gravity. This is the # point around about which the solver will calculate the moments on the airplane. # These three values default to 0.0 meters. Note: these values are relative to # the global coordinate system fixed front left corner of the first airplane's # first wing's root wing cross section. - x_ref=0.0, - y_ref=0.0, - z_ref=0.0, - wings=[ - ps.geometry.Wing( - name="Main Wing", - # Define the location of the leading edge of the wing relative to the - # global coordinate system fixed front left corner of the first - # airplane's first wing's root wing cross section. - x_le=0.0, - y_le=0.0, - # Declare that this wing is symmetric. This means that the geometry will - # be reflected across plane of this wing's root wing cross section. Note - # that the geometry coordinates are defined as such: If you were riding - # in the airplane, the positive x direction would point behind you, - # the positive y direction would point out of your right wing, and the - # positive z direction would point upwards, out of your chair. These - # directions form a right-handed coordinate system. The default value of - # "symmetric" is false. - symmetric=True, - # Define the chordwise spacing of the wing panels to be "uniform" as this - # increase the accuracy of unsteady solvers. - chordwise_spacing="uniform", - num_chordwise_panels=4, - wing_cross_sections=[ - ps.geometry.WingCrossSection( - # Define the location of the leading edge of the wing cross - # section relative to the wing's leading edge. These values all - # default to 0.0 meters. - x_le=0.0, - y_le=0.0, - # Assign the twist of this wing cross section. Note: when - # assigning angles of attack to multiple airplanes, it is better - # to set the operating point's angle of attack to zero, and then - # use offset the twist values of all the wing cross sections to - # simulate each aircraft having an angle of attack. - twist=5.0, - chord=1.75, - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ps.geometry.WingCrossSection( - x_le=0.75, - y_le=6.0, - chord=1.5, - twist=5.0, - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ], - ), - ], -) + x_ref=0.0, y_ref=0.0, z_ref=0.0, wings=[ps.geometry.Wing(name="Main Wing", + # Define the location of the leading edge of the wing relative to the + # global coordinate system fixed front left corner of the first + # airplane's first wing's root wing cross section. + x_le=0.0, y_le=0.0, + # Declare that this wing is symmetric. This means that the geometry will + # be reflected across plane of this wing's root wing cross section. Note + # that the geometry coordinates are defined as such: If you were riding + # in the airplane, the positive x direction would point behind you, + # the positive y direction would point out of your right wing, and the + # positive z direction would point upwards, out of your chair. These + # directions form a right-handed coordinate system. The default value of + # "symmetric" is false. + symmetric=True, + # Define the chordwise spacing of the wing panels to be "uniform" as this + # increase the accuracy of unsteady solvers. + chordwise_spacing="uniform", num_chordwise_panels=4, wing_cross_sections=[ + ps.geometry.WingCrossSection( + # Define the location of the leading edge of the wing cross + # section relative to the wing's leading edge. These values all + # default to 0.0 meters. + x_le=0.0, y_le=0.0, + # Assign the twist of this wing cross section. Note: when + # assigning angles of attack to multiple airplanes, it is better + # to set the operating point's angle of attack to zero, and then + # use offset the twist values of all the wing cross sections to + # simulate each aircraft having an angle of attack. + twist=5.0, chord=1.75, + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), + ps.geometry.WingCrossSection(x_le=0.75, y_le=6.0, chord=1.5, twist=5.0, + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), ], ), ], ) # Now define the lead airplane's movement object. -lead_airplane_movement = ps.movement.AirplaneMovement( - base_airplane=lead_airplane, - wing_movements=[ - # Define the main wing's movement. - ps.movement.WingMovement( - base_wing=lead_airplane.wings[0], +lead_airplane_movement = ps.movement.AirplaneMovement(base_airplane=lead_airplane, + wing_movements=[# Define the main wing's movement. + ps.movement.WingMovement(base_wing=lead_airplane.wings[0], # Add the list of wing cross section movement objects. wing_cross_sections_movements=[ # Define the root wing cross section's movement object. ps.movement.WingCrossSectionMovement( base_wing_cross_section=lead_airplane.wings[0].wing_cross_sections[ - 0 - ], - ), - # Define the tip wing cross section's movement object. + 0], ), # Define the tip wing cross section's movement object. ps.movement.WingCrossSectionMovement( base_wing_cross_section=lead_airplane.wings[0].wing_cross_sections[ - 1 - ], - sweeping_amplitude=15.0, - sweeping_period=1.5, - sweeping_spacing="sine", - ), - ], - ), - ], -) + 1], sweeping_amplitude=15.0, sweeping_period=1.5, + sweeping_spacing="sine", ), ], ), ], ) # Create the trailing right airplane object. -right_airplane = ps.geometry.Airplane( - name="Right Airplane", +right_airplane = ps.geometry.Airplane(name="Right Airplane", # Specify the location of the right airplane's center of gravity. This is the # point around about which the solver will calculate the moments on the airplane. # These three values default to 0.0 meters. Note: these values are relative to # the global coordinate system fixed front left corner of the first airplane's # first wing's root wing cross section. - x_ref=x_spacing, - y_ref=y_spacing, - z_ref=0.0, - wings=[ - ps.geometry.Wing( - name="Main Wing", + x_ref=x_spacing, y_ref=y_spacing, z_ref=0.0, wings=[ + ps.geometry.Wing(name="Main Wing", # Define the location of the leading edge of the wing relative to the # global coordinate system fixed front left corner of the first # airplane's first wing's root wing cross section. - x_le=x_spacing, - y_le=y_spacing, - symmetric=True, - chordwise_spacing="uniform", - num_chordwise_panels=4, - wing_cross_sections=[ - ps.geometry.WingCrossSection( - twist=5.0, - chord=1.75, - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ps.geometry.WingCrossSection( - x_le=0.75, - y_le=6.0, - chord=1.5, - twist=5.0, - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ], - ), - ], -) + x_le=x_spacing, y_le=y_spacing, symmetric=True, chordwise_spacing="uniform", + num_chordwise_panels=4, wing_cross_sections=[ + ps.geometry.WingCrossSection(twist=5.0, chord=1.75, + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), + ps.geometry.WingCrossSection(x_le=0.75, y_le=6.0, chord=1.5, twist=5.0, + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), ], ), ], ) # Now define the trailing right airplane's movement object. -right_airplane_movement = ps.movement.AirplaneMovement( - base_airplane=right_airplane, - wing_movements=[ - ps.movement.WingMovement( - base_wing=right_airplane.wings[0], - wing_cross_sections_movements=[ - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=right_airplane.wings[0].wing_cross_sections[ - 0 - ], - ), - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=right_airplane.wings[0].wing_cross_sections[ - 1 - ], - sweeping_amplitude=15.0, - sweeping_period=1.5, - sweeping_spacing="sine", - ), - ], - ), - ], -) +right_airplane_movement = ps.movement.AirplaneMovement(base_airplane=right_airplane, + wing_movements=[ps.movement.WingMovement(base_wing=right_airplane.wings[0], + wing_cross_sections_movements=[ps.movement.WingCrossSectionMovement( + base_wing_cross_section=right_airplane.wings[0].wing_cross_sections[0], ), + ps.movement.WingCrossSectionMovement( + base_wing_cross_section=right_airplane.wings[0].wing_cross_sections[1], + sweeping_amplitude=15.0, sweeping_period=1.5, + sweeping_spacing="sine", ), ], ), ], ) # Create the trailing left airplane object. -left_airplane = ps.geometry.Airplane( - name="Left Airplane", +left_airplane = ps.geometry.Airplane(name="Left Airplane", # Specify the location of the left airplane's center of gravity. This is the # point around about which the solver will calculate the moments on the airplane. # These three values default to 0.0 meters. Note: these values are relative to # the global coordinate system fixed front left corner of the first airplane's # first wing's root wing cross section. - x_ref=x_spacing, - y_ref=-y_spacing, - z_ref=0.0, - wings=[ - ps.geometry.Wing( - name="Main Wing", + x_ref=x_spacing, y_ref=-y_spacing, z_ref=0.0, wings=[ + ps.geometry.Wing(name="Main Wing", # Define the location of the leading edge of the wing relative to the # global coordinate system fixed front left corner of the first # airplane's first wing's root wing cross section. - x_le=x_spacing, - y_le=-y_spacing, - symmetric=True, - chordwise_spacing="uniform", - num_chordwise_panels=4, - wing_cross_sections=[ - ps.geometry.WingCrossSection( - twist=5.0, - chord=1.75, - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ps.geometry.WingCrossSection( - x_le=0.75, - y_le=6.0, - chord=1.5, - twist=5.0, - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ], - ), - ], -) + x_le=x_spacing, y_le=-y_spacing, symmetric=True, + chordwise_spacing="uniform", num_chordwise_panels=4, wing_cross_sections=[ + ps.geometry.WingCrossSection(twist=5.0, chord=1.75, + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), + ps.geometry.WingCrossSection(x_le=0.75, y_le=6.0, chord=1.5, twist=5.0, + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), ], ), ], ) # Now define the trailing left airplane's movement object. -left_airplane_movement = ps.movement.AirplaneMovement( - base_airplane=left_airplane, - wing_movements=[ - ps.movement.WingMovement( - base_wing=left_airplane.wings[0], - wing_cross_sections_movements=[ - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=left_airplane.wings[0].wing_cross_sections[ - 0 - ], - ), - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=left_airplane.wings[0].wing_cross_sections[ - 1 - ], - sweeping_amplitude=15.0, - sweeping_period=1.5, - sweeping_spacing="sine", - ), - ], - ), - ], -) +left_airplane_movement = ps.movement.AirplaneMovement(base_airplane=left_airplane, + wing_movements=[ps.movement.WingMovement(base_wing=left_airplane.wings[0], + wing_cross_sections_movements=[ps.movement.WingCrossSectionMovement( + base_wing_cross_section=left_airplane.wings[0].wing_cross_sections[0], ), + ps.movement.WingCrossSectionMovement( + base_wing_cross_section=left_airplane.wings[0].wing_cross_sections[1], + sweeping_amplitude=15.0, sweeping_period=1.5, + sweeping_spacing="sine", ), ], ), ], ) # Define a new operating point object. This defines the state at which all the # airplanes objects are operating. Note: when assigning angles of attack to multiple # airplanes, it is better to set the operating point's angle of attack to zero, # and then use offset the twist values of all the wing cross sections to simulate # each aircraft having an angle of attack. -operating_point = ps.operating_point.OperatingPoint( - velocity=10.0, - alpha=0.0, -) +operating_point = ps.operating_point.OperatingPoint(velocity=10.0, alpha=0.0, ) # Define the operating point's movement. operating_point_movement = ps.movement.OperatingPointMovement( - base_operating_point=operating_point, -) + base_operating_point=operating_point, ) # Delete the extraneous airplane and operating point objects, as these are now # contained within their respective movement objects. @@ -265,14 +142,9 @@ # Define the movement object. This contains each airplane's movement and the operating # point movement. movement = ps.movement.Movement( - airplane_movements=[ - lead_airplane_movement, - right_airplane_movement, - left_airplane_movement, - ], - operating_point_movement=operating_point_movement, - num_cycles=2, -) + airplane_movements=[lead_airplane_movement, right_airplane_movement, + left_airplane_movement, ], operating_point_movement=operating_point_movement, + num_cycles=2, ) # Delete the extraneous airplane and operating point movement objects, as these are # now contained within the movement object. @@ -282,39 +154,30 @@ del operating_point_movement # Define the unsteady example problem. -problem = ps.problems.UnsteadyProblem( - movement=movement, -) +problem = ps.problems.UnsteadyProblem(movement=movement, ) # Define a new solver. The available solver objects are the steady horseshoe vortex # lattice method solver, the steady ring vortex lattice method solver, and the # unsteady ring vortex lattice method solver. solver = ps.unsteady_ring_vortex_lattice_method.UnsteadyRingVortexLatticeMethodSolver( # Solvers just take in one attribute: the problem they are going to solve. - unsteady_problem=problem, -) + unsteady_problem=problem, ) # Delete the extraneous pointer to the problem as it is now contained within the # solver. del problem # Run the example solver. -solver.run( - prescribed_wake=False, -) +solver.run(prescribed_wake=False, ) # Call the software's animate function on the solver. This produces a WebP animation # of the wake being shed. The animation is saved in the same directory as this # script. Press "q", after orienting the view, to begin the animation. -ps.output.animate( - unsteady_solver=solver, - scalar_type="lift", - show_wake_vortices=True, +ps.output.animate(unsteady_solver=solver, scalar_type="lift", show_wake_vortices=True, # Tell the animate function to not save the animation as file. This way, # the animation will still be displayed but not saved. This value defaults to # false. - save=True, -) + save=True, ) # Compare the output you see with the expected outputs saved in the "docs/examples # expected output" directory. diff --git a/examples/unsteady_static_convergence_example.py b/examples/unsteady_static_convergence_example.py index 0524e291..1c1a9a6f 100644 --- a/examples/unsteady_static_convergence_example.py +++ b/examples/unsteady_static_convergence_example.py @@ -6,64 +6,30 @@ # Create an airplane and airplane movement object. Read through the unsteady solver # examples for more details on creating this object. -airplane = ps.geometry.Airplane( - wings=[ - ps.geometry.Wing( - symmetric=True, - chordwise_spacing="uniform", - wing_cross_sections=[ - ps.geometry.WingCrossSection( - x_le=0.0, - y_le=0.0, - z_le=0.0, - chord=1.0, - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - spanwise_spacing="uniform", - ), - ps.geometry.WingCrossSection( - x_le=0.0, - y_le=3.0, - z_le=0.0, - chord=1.0, - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - ), - ], - ), - ], -) -airplane_movement = ps.movement.AirplaneMovement( - base_airplane=airplane, - wing_movements=[ - ps.movement.WingMovement( - base_wing=airplane.wings[0], - wing_cross_sections_movements=[ - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=airplane.wings[0].wing_cross_sections[0], - ), - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=airplane.wings[0].wing_cross_sections[1], - ), - ], - ) - ], -) +airplane = ps.geometry.Airplane(wings=[ + ps.geometry.Wing(symmetric=True, chordwise_spacing="uniform", wing_cross_sections=[ + ps.geometry.WingCrossSection(x_le=0.0, y_le=0.0, z_le=0.0, chord=1.0, + airfoil=ps.geometry.Airfoil(name="naca2412", ), + spanwise_spacing="uniform", ), + ps.geometry.WingCrossSection(x_le=0.0, y_le=3.0, z_le=0.0, chord=1.0, + airfoil=ps.geometry.Airfoil(name="naca2412", ), ), ], ), ], ) +airplane_movement = ps.movement.AirplaneMovement(base_airplane=airplane, + wing_movements=[ps.movement.WingMovement(base_wing=airplane.wings[0], + wing_cross_sections_movements=[ps.movement.WingCrossSectionMovement( + base_wing_cross_section=airplane.wings[0].wing_cross_sections[0], ), + ps.movement.WingCrossSectionMovement( + base_wing_cross_section=airplane.wings[0].wing_cross_sections[ + 1], ), ], )], ) # Create an operating point and an operating point movement object. operating_point = ps.operating_point.OperatingPoint() operating_point_movement = ps.movement.OperatingPointMovement( - base_operating_point=operating_point -) + base_operating_point=operating_point) # Create a movement object from the airplane movement and operating point movement # objects. -movement = ps.movement.Movement( - airplane_movements=[airplane_movement], - operating_point_movement=operating_point_movement, -) +movement = ps.movement.Movement(airplane_movements=[airplane_movement], + operating_point_movement=operating_point_movement, ) del airplane_movement del operating_point_movement @@ -80,15 +46,9 @@ # absolute percent error), it will return the parameters it found to result in a # converged solution. See the analyze_unsteady_convergence function docstring for # more details. The progress and results are displayed to the console. -ps.convergence.analyze_unsteady_convergence( - ref_problem=problem, - prescribed_wake=True, - free_wake=True, - num_chords_bounds=(5, 8), - panel_aspect_ratio_bounds=(2, 1), - num_chordwise_panels_bounds=(3, 6), - convergence_criteria=1.0, -) +ps.convergence.analyze_unsteady_convergence(ref_problem=problem, prescribed_wake=True, + free_wake=True, num_chords_bounds=(5, 8), panel_aspect_ratio_bounds=(2, 1), + num_chordwise_panels_bounds=(3, 6), convergence_criteria=1.0, ) # Check the console that the convergence analysis found that the solution converged # with the following parameters: diff --git a/examples/unsteady_variable_convergence_example.py b/examples/unsteady_variable_convergence_example.py index 1b0ba048..7f51e7fa 100644 --- a/examples/unsteady_variable_convergence_example.py +++ b/examples/unsteady_variable_convergence_example.py @@ -6,66 +6,30 @@ # Create an airplane and airplane movement object. Read through the unsteady solver # examples for more details on creating this object. -airplane = ps.geometry.Airplane( - wings=[ - ps.geometry.Wing( - symmetric=True, - chordwise_spacing="uniform", - wing_cross_sections=[ - ps.geometry.WingCrossSection( - x_le=0.0, - y_le=0.0, - z_le=0.0, - chord=1.0, - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - spanwise_spacing="uniform", - ), - ps.geometry.WingCrossSection( - x_le=0.0, - y_le=3.0, - z_le=0.0, - chord=1.0, - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - ), - ], - ), - ], -) -airplane_movement = ps.movement.AirplaneMovement( - base_airplane=airplane, - wing_movements=[ - ps.movement.WingMovement( - base_wing=airplane.wings[0], - wing_cross_sections_movements=[ - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=airplane.wings[0].wing_cross_sections[0], - ), - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=airplane.wings[0].wing_cross_sections[1], - sweeping_amplitude=15.0, - sweeping_period=1.0, - ), - ], - ) - ], -) +airplane = ps.geometry.Airplane(wings=[ + ps.geometry.Wing(symmetric=True, chordwise_spacing="uniform", wing_cross_sections=[ + ps.geometry.WingCrossSection(x_le=0.0, y_le=0.0, z_le=0.0, chord=1.0, + airfoil=ps.geometry.Airfoil(name="naca2412", ), + spanwise_spacing="uniform", ), + ps.geometry.WingCrossSection(x_le=0.0, y_le=3.0, z_le=0.0, chord=1.0, + airfoil=ps.geometry.Airfoil(name="naca2412", ), ), ], ), ], ) +airplane_movement = ps.movement.AirplaneMovement(base_airplane=airplane, + wing_movements=[ps.movement.WingMovement(base_wing=airplane.wings[0], + wing_cross_sections_movements=[ps.movement.WingCrossSectionMovement( + base_wing_cross_section=airplane.wings[0].wing_cross_sections[0], ), + ps.movement.WingCrossSectionMovement( + base_wing_cross_section=airplane.wings[0].wing_cross_sections[1], + sweeping_amplitude=15.0, sweeping_period=1.0, ), ], )], ) # Create an operating point and an operating point movement object. operating_point = ps.operating_point.OperatingPoint() operating_point_movement = ps.movement.OperatingPointMovement( - base_operating_point=operating_point -) + base_operating_point=operating_point) # Create a movement object from the airplane movement and operating point movement # objects. -movement = ps.movement.Movement( - airplane_movements=[airplane_movement], - operating_point_movement=operating_point_movement, -) +movement = ps.movement.Movement(airplane_movements=[airplane_movement], + operating_point_movement=operating_point_movement, ) del airplane_movement del operating_point_movement @@ -82,15 +46,9 @@ # absolute percent error), it will return the parameters it found to result in a # converged solution. See the analyze_unsteady_convergence function docstring for # more details. The progress and results are displayed to the console. -ps.convergence.analyze_unsteady_convergence( - ref_problem=problem, - prescribed_wake=True, - free_wake=True, - num_cycles_bounds=(1, 4), - panel_aspect_ratio_bounds=(2, 1), - num_chordwise_panels_bounds=(4, 7), - convergence_criteria=1.0, -) +ps.convergence.analyze_unsteady_convergence(ref_problem=problem, prescribed_wake=True, + free_wake=True, num_cycles_bounds=(1, 4), panel_aspect_ratio_bounds=(2, 1), + num_chordwise_panels_bounds=(4, 7), convergence_criteria=1.0, ) # Check the console that the convergence analysis found that the solution converged # with the following parameters: diff --git a/formation flight/formation_flight.py b/formation flight/formation_flight.py index efa7a205..c5cb6ee4 100644 --- a/formation flight/formation_flight.py +++ b/formation flight/formation_flight.py @@ -28,13 +28,9 @@ root_to_mid_chord = root_chord mid_to_tip_chord = (root_chord + tip_chord) / 2 -this_operating_point = ps.operating_point.OperatingPoint( - velocity=speed, - alpha=0.0, -) +this_operating_point = ps.operating_point.OperatingPoint(velocity=speed, alpha=0.0, ) this_operating_point_movement = ps.movement.OperatingPointMovement( - base_operating_point=this_operating_point, -) + base_operating_point=this_operating_point, ) del this_operating_point print("Prescribed Wake:", prescribed_wake) @@ -45,8 +41,7 @@ mid_to_tip_panel_chord = mid_to_tip_chord / num_chord root_to_mid_num_span = round( - root_to_mid_span / (aspect_ratio * root_to_mid_panel_chord) -) + root_to_mid_span / (aspect_ratio * root_to_mid_panel_chord)) mid_to_tip_num_span = round(mid_to_tip_span / (aspect_ratio * mid_to_tip_panel_chord)) these_airplane_movements = [] @@ -70,126 +65,66 @@ offset = row - 1 - this_airplane = ps.geometry.Airplane( - name=this_name, - x_ref=offset * x_spacing, - y_ref=offset_sign * offset * y_spacing, - wings=[ - ps.geometry.Wing( - name="Main Wing", - symmetric=True, - chordwise_spacing="uniform", - x_le=offset * x_spacing, - y_le=offset_sign * offset * y_spacing, - num_chordwise_panels=num_chord, + this_airplane = ps.geometry.Airplane(name=this_name, x_ref=offset * x_spacing, + y_ref=offset_sign * offset * y_spacing, wings=[ + ps.geometry.Wing(name="Main Wing", symmetric=True, + chordwise_spacing="uniform", x_le=offset * x_spacing, + y_le=offset_sign * offset * y_spacing, num_chordwise_panels=num_chord, wing_cross_sections=[ - ps.geometry.WingCrossSection( - twist=alpha, - chord=root_chord, + ps.geometry.WingCrossSection(twist=alpha, chord=root_chord, airfoil=ps.geometry.Airfoil(name="naca0012"), num_spanwise_panels=root_to_mid_num_span, - spanwise_spacing="cosine", - ), - ps.geometry.WingCrossSection( - twist=alpha, - y_le=root_to_mid_span, - chord=root_chord, - airfoil=ps.geometry.Airfoil(name="naca0012"), + spanwise_spacing="cosine", ), + ps.geometry.WingCrossSection(twist=alpha, y_le=root_to_mid_span, + chord=root_chord, airfoil=ps.geometry.Airfoil(name="naca0012"), num_spanwise_panels=mid_to_tip_num_span, - spanwise_spacing="cosine", - ), - ps.geometry.WingCrossSection( - twist=alpha, - y_le=root_to_mid_span + mid_to_tip_span, - chord=tip_chord, - airfoil=ps.geometry.Airfoil(name="naca0012"), - ), - ], - ), - ], - ) - - this_airplane_movement = ps.movement.AirplaneMovement( - base_airplane=this_airplane, - wing_movements=[ - ps.movement.WingMovement( - base_wing=this_airplane.wings[0], - wing_cross_sections_movements=[ - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=this_airplane.wings[ - 0 - ].wing_cross_sections[0], - ), - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=this_airplane.wings[ - 0 - ].wing_cross_sections[1], - sweeping_amplitude=flapping_amplitude, - sweeping_period=period, - sweeping_spacing="sine", - ), - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=this_airplane.wings[ - 0 - ].wing_cross_sections[2], - sweeping_amplitude=flapping_amplitude, - sweeping_period=period, - sweeping_spacing="sine", - ), - ], - ) - ], - ) + spanwise_spacing="cosine", ), + ps.geometry.WingCrossSection(twist=alpha, + y_le=root_to_mid_span + mid_to_tip_span, chord=tip_chord, + airfoil=ps.geometry.Airfoil(name="naca0012"), ), ], ), ], ) + + this_airplane_movement = ps.movement.AirplaneMovement(base_airplane=this_airplane, + wing_movements=[ps.movement.WingMovement(base_wing=this_airplane.wings[0], + wing_cross_sections_movements=[ps.movement.WingCrossSectionMovement( + base_wing_cross_section=this_airplane.wings[0].wing_cross_sections[ + 0], ), ps.movement.WingCrossSectionMovement( + base_wing_cross_section=this_airplane.wings[0].wing_cross_sections[1], + sweeping_amplitude=flapping_amplitude, sweeping_period=period, + sweeping_spacing="sine", ), ps.movement.WingCrossSectionMovement( + base_wing_cross_section=this_airplane.wings[0].wing_cross_sections[2], + sweeping_amplitude=flapping_amplitude, sweeping_period=period, + sweeping_spacing="sine", ), ], )], ) these_airplane_movements.append(this_airplane_movement) del this_airplane del this_airplane_movement -this_movement = ps.movement.Movement( - airplane_movements=these_airplane_movements, - operating_point_movement=this_operating_point_movement, - num_steps=None, - num_cycles=num_flaps, - delta_time=None, -) +this_movement = ps.movement.Movement(airplane_movements=these_airplane_movements, + operating_point_movement=this_operating_point_movement, num_steps=None, + num_cycles=num_flaps, delta_time=None, ) del these_airplane_movements -this_problem = ps.problems.UnsteadyProblem( - movement=this_movement, - only_final_results=False, -) +this_problem = ps.problems.UnsteadyProblem(movement=this_movement, + only_final_results=False, ) del this_movement this_solver = ( ps.unsteady_ring_vortex_lattice_method.UnsteadyRingVortexLatticeMethodSolver( - unsteady_problem=this_problem, - ) -) + unsteady_problem=this_problem, )) del this_problem -this_solver.run( - prescribed_wake=prescribed_wake, - calculate_streamlines=False, -) +this_solver.run(prescribed_wake=prescribed_wake, calculate_streamlines=False, ) ps.output.print_unsteady_results(unsteady_solver=this_solver) ps.output.plot_results_versus_time(unsteady_solver=this_solver, save=True) -ps.output.draw( - solver=this_solver, - scalar_type="lift", - show_wake_vortices=True, - save=True, -) - -ps.output.animate( - unsteady_solver=this_solver, - scalar_type="lift", - show_wake_vortices=True, - save=True, -) +ps.output.draw(solver=this_solver, scalar_type="lift", show_wake_vortices=True, + save=True, ) + +ps.output.animate(unsteady_solver=this_solver, scalar_type="lift", + show_wake_vortices=True, save=True, ) diff --git a/formation flight/formation_flight_convergence.py b/formation flight/formation_flight_convergence.py index c22a93ef..7693ef4d 100644 --- a/formation flight/formation_flight_convergence.py +++ b/formation flight/formation_flight_convergence.py @@ -43,11 +43,9 @@ num_chord_list = [i for i in range(min_num_chord, max_num_chord + 1)] rms_lifts = np.zeros( - (len(wake_state_list), len(num_flaps_list), len(num_chord_list), num_airplanes) -) + (len(wake_state_list), len(num_flaps_list), len(num_chord_list), num_airplanes)) rms_drags = np.zeros( - (len(wake_state_list), len(num_flaps_list), len(num_chord_list), num_airplanes) -) + (len(wake_state_list), len(num_flaps_list), len(num_chord_list), num_airplanes)) iter_times = np.zeros((len(wake_state_list), len(num_flaps_list), len(num_chord_list))) iteration = 0 @@ -67,13 +65,9 @@ wake_saturated = None this_solver = None -this_operating_point = ps.operating_point.OperatingPoint( - velocity=speed, - alpha=0.0, -) +this_operating_point = ps.operating_point.OperatingPoint(velocity=speed, alpha=0.0, ) this_operating_point_movement = ps.movement.OperatingPointMovement( - base_operating_point=this_operating_point, -) + base_operating_point=this_operating_point, ) del this_operating_point for wake_state_id, prescribed_wake in enumerate(wake_state_list): @@ -90,11 +84,9 @@ mid_to_tip_panel_chord = mid_to_tip_chord / num_chord root_to_mid_num_span = round( - root_to_mid_span / (aspect_ratio * root_to_mid_panel_chord) - ) + root_to_mid_span / (aspect_ratio * root_to_mid_panel_chord)) mid_to_tip_num_span = round( - mid_to_tip_span / (aspect_ratio * mid_to_tip_panel_chord) - ) + mid_to_tip_span / (aspect_ratio * mid_to_tip_panel_chord)) these_airplane_movements = [] row = None @@ -117,76 +109,44 @@ offset = row - 1 - this_airplane = ps.geometry.Airplane( - name=this_name, - x_ref=offset * x_spacing, - y_ref=offset_sign * offset * y_spacing, - wings=[ - ps.geometry.Wing( - name="Main Wing", - symmetric=True, - chordwise_spacing="uniform", - x_le=offset * x_spacing, - y_le=offset_sign * offset * y_spacing, - num_chordwise_panels=num_chord, - wing_cross_sections=[ - ps.geometry.WingCrossSection( - twist=alpha, - chord=root_chord, - airfoil=ps.geometry.Airfoil(name="naca0012"), - num_spanwise_panels=root_to_mid_num_span, - spanwise_spacing="cosine", - ), - ps.geometry.WingCrossSection( - twist=alpha, - y_le=root_to_mid_span, - chord=root_chord, - airfoil=ps.geometry.Airfoil(name="naca0012"), - num_spanwise_panels=mid_to_tip_num_span, - spanwise_spacing="cosine", - ), - ps.geometry.WingCrossSection( - twist=alpha, - y_le=root_to_mid_span + mid_to_tip_span, - chord=tip_chord, - airfoil=ps.geometry.Airfoil(name="naca0012"), - ), - ], - ), - ], - ) + this_airplane = ps.geometry.Airplane(name=this_name, + x_ref=offset * x_spacing, y_ref=offset_sign * offset * y_spacing, + wings=[ps.geometry.Wing(name="Main Wing", symmetric=True, + chordwise_spacing="uniform", x_le=offset * x_spacing, + y_le=offset_sign * offset * y_spacing, + num_chordwise_panels=num_chord, wing_cross_sections=[ + ps.geometry.WingCrossSection(twist=alpha, chord=root_chord, + airfoil=ps.geometry.Airfoil(name="naca0012"), + num_spanwise_panels=root_to_mid_num_span, + spanwise_spacing="cosine", ), + ps.geometry.WingCrossSection(twist=alpha, + y_le=root_to_mid_span, chord=root_chord, + airfoil=ps.geometry.Airfoil(name="naca0012"), + num_spanwise_panels=mid_to_tip_num_span, + spanwise_spacing="cosine", ), + ps.geometry.WingCrossSection(twist=alpha, + y_le=root_to_mid_span + mid_to_tip_span, + chord=tip_chord, airfoil=ps.geometry.Airfoil( + name="naca0012"), ), ], ), ], ) this_airplane_movement = ps.movement.AirplaneMovement( - base_airplane=this_airplane, - wing_movements=[ - ps.movement.WingMovement( - base_wing=this_airplane.wings[0], + base_airplane=this_airplane, wing_movements=[ + ps.movement.WingMovement(base_wing=this_airplane.wings[0], wing_cross_sections_movements=[ ps.movement.WingCrossSectionMovement( - base_wing_cross_section=this_airplane.wings[ - 0 - ].wing_cross_sections[0], - ), + base_wing_cross_section= + this_airplane.wings[0].wing_cross_sections[0], ), ps.movement.WingCrossSectionMovement( - base_wing_cross_section=this_airplane.wings[ - 0 - ].wing_cross_sections[1], + base_wing_cross_section= + this_airplane.wings[0].wing_cross_sections[1], sweeping_amplitude=flapping_amplitude, - sweeping_period=period, - sweeping_spacing="sine", - ), + sweeping_period=period, sweeping_spacing="sine", ), ps.movement.WingCrossSectionMovement( - base_wing_cross_section=this_airplane.wings[ - 0 - ].wing_cross_sections[2], + base_wing_cross_section= + this_airplane.wings[0].wing_cross_sections[2], sweeping_amplitude=flapping_amplitude, sweeping_period=period, - sweeping_spacing="sine", - ), - ], - ) - ], - ) + sweeping_spacing="sine", ), ], )], ) these_airplane_movements.append(this_airplane_movement) @@ -195,34 +155,26 @@ this_movement = ps.movement.Movement( airplane_movements=these_airplane_movements, - operating_point_movement=this_operating_point_movement, - num_steps=None, - num_cycles=num_flaps, - delta_time=None, - ) + operating_point_movement=this_operating_point_movement, num_steps=None, + num_cycles=num_flaps, delta_time=None, ) del these_airplane_movements - this_problem = ps.problems.UnsteadyProblem( - movement=this_movement, - only_final_results=True, - ) + this_problem = ps.problems.UnsteadyProblem(movement=this_movement, + only_final_results=True, ) del this_movement - this_solver = ps.unsteady_ring_vortex_lattice_method.UnsteadyRingVortexLatticeMethodSolver( - unsteady_problem=this_problem, - ) + this_solver = ( + ps.unsteady_ring_vortex_lattice_method.UnsteadyRingVortexLatticeMethodSolver( + unsteady_problem=this_problem, )) del this_problem iter_start = time.time() - this_solver.run( - logging_level="Critical", - prescribed_wake=prescribed_wake, - calculate_streamlines=False, - ) + this_solver.run(logging_level="Critical", prescribed_wake=prescribed_wake, + calculate_streamlines=False, ) iter_stop = time.time() iter_time = round((iter_stop - iter_start), 2) @@ -241,11 +193,9 @@ airplanes = this_solver.steady_problems[step].airplanes for airplane_id, airplane in enumerate(airplanes): total_forces[airplane_id, :, results_step] = ( - airplane.total_near_field_force_wind_axes - ) + airplane.total_near_field_force_wind_axes) total_moments[airplane_id, :, results_step] = ( - airplane.total_near_field_moment_wind_axes - ) + airplane.total_near_field_moment_wind_axes) results_step += 1 these_s_drags = total_forces[:, 0, :] ** 2 @@ -254,8 +204,8 @@ these_ms_drags = np.mean(these_s_drags, axis=-1) these_ms_lifts = np.mean(these_s_lifts, axis=-1) - these_rms_drags = these_ms_drags**0.5 - these_rms_lifts = these_ms_lifts**0.5 + these_rms_drags = these_ms_drags ** 0.5 + these_rms_lifts = these_ms_lifts ** 0.5 rms_drags[wake_state_id, num_flaps_id, num_chord_id, :] = these_rms_drags rms_lifts[wake_state_id, num_flaps_id, num_chord_id, :] = these_rms_lifts @@ -265,80 +215,56 @@ max_chord_rmspc = np.inf if wake_state_id > 0: - last_wake_rms_lifts = rms_lifts[ - wake_state_id - 1, num_flaps_id, num_chord_id, : - ] - last_wake_rms_drags = rms_drags[ - wake_state_id - 1, num_flaps_id, num_chord_id, : - ] + last_wake_rms_lifts = rms_lifts[wake_state_id - 1, num_flaps_id, + num_chord_id, :] + last_wake_rms_drags = rms_drags[wake_state_id - 1, num_flaps_id, + num_chord_id, :] wake_lift_rmspcs = 100 * np.abs( - (these_rms_lifts - last_wake_rms_lifts) / last_wake_rms_lifts - ) + (these_rms_lifts - last_wake_rms_lifts) / last_wake_rms_lifts) wake_drag_rmspcs = 100 * np.abs( - (these_rms_drags - last_wake_rms_drags) / last_wake_rms_drags - ) + (these_rms_drags - last_wake_rms_drags) / last_wake_rms_drags) max_wake_lift_rmspc = np.max(wake_lift_rmspcs) max_wake_drag_rmspc = np.max(wake_drag_rmspcs) max_wake_rmspc = max(max_wake_lift_rmspc, max_wake_drag_rmspc) - print( - "\t\t\t\tMax Wake RMSPC: ", - round(max_wake_rmspc, 2), - "%", - sep="", - ) + print("\t\t\t\tMax Wake RMSPC: ", round(max_wake_rmspc, 2), "%", + sep="", ) else: print("\t\t\t\tMax Wake RMSPC:", max_wake_rmspc) if num_flaps_id > 0: - last_flap_rms_lifts = rms_lifts[ - wake_state_id, num_flaps_id - 1, num_chord_id, : - ] - last_flap_rms_drags = rms_drags[ - wake_state_id, num_flaps_id - 1, num_chord_id, : - ] + last_flap_rms_lifts = rms_lifts[wake_state_id, num_flaps_id - 1, + num_chord_id, :] + last_flap_rms_drags = rms_drags[wake_state_id, num_flaps_id - 1, + num_chord_id, :] flap_lift_rmspcs = 100 * np.abs( - (these_rms_lifts - last_flap_rms_lifts) / last_flap_rms_lifts - ) + (these_rms_lifts - last_flap_rms_lifts) / last_flap_rms_lifts) flap_drag_rmspcs = 100 * np.abs( - (these_rms_drags - last_flap_rms_drags) / last_flap_rms_drags - ) + (these_rms_drags - last_flap_rms_drags) / last_flap_rms_drags) max_flap_lift_rmspc = np.max(flap_lift_rmspcs) max_flap_drag_rmspc = np.max(flap_drag_rmspcs) max_flap_rmspc = max(max_flap_lift_rmspc, max_flap_drag_rmspc) - print( - "\t\t\t\tMax Flap RMSPC: ", - round(max_flap_rmspc, 2), - "%", - sep="", - ) + print("\t\t\t\tMax Flap RMSPC: ", round(max_flap_rmspc, 2), "%", + sep="", ) else: print("\t\t\t\tMax Flap RMSPC:", max_flap_rmspc) if num_chord_id > 0: - last_chord_rms_lifts = rms_lifts[ - wake_state_id, num_flaps_id, num_chord_id - 1, : - ] - last_chord_rms_drags = rms_drags[ - wake_state_id, num_flaps_id, num_chord_id - 1, : - ] + last_chord_rms_lifts = rms_lifts[wake_state_id, num_flaps_id, + num_chord_id - 1, :] + last_chord_rms_drags = rms_drags[wake_state_id, num_flaps_id, + num_chord_id - 1, :] chord_lift_rmspcs = 100 * np.abs( - (these_rms_lifts - last_chord_rms_lifts) / last_chord_rms_lifts - ) + (these_rms_lifts - last_chord_rms_lifts) / last_chord_rms_lifts) chord_drag_rmspcs = 100 * np.abs( - (these_rms_drags - last_chord_rms_drags) / last_chord_rms_drags - ) + (these_rms_drags - last_chord_rms_drags) / last_chord_rms_drags) max_chord_lift_rmspc = np.max(chord_lift_rmspcs) max_chord_drag_rmspc = np.max(chord_drag_rmspcs) max_chord_rmspc = max(max_chord_lift_rmspc, max_chord_drag_rmspc) - print( - "\t\t\t\tMax Chord RMSPC: ", - round(max_chord_rmspc, 2), - "%", - sep="", - ) + print("\t\t\t\tMax Chord RMSPC: ", round(max_chord_rmspc, 2), "%", + sep="", ) else: print("\t\t\t\tMax Chord RMSPC:", max_chord_rmspc) @@ -404,10 +330,7 @@ converged_num_flaps = num_flaps_list[converged_num_flaps_id] converged_num_chord = num_chord_list[converged_num_chord_id] this_iter_time = iter_times[ - converged_wake_state_id, - converged_num_flaps_id, - converged_num_chord_id, - ] + converged_wake_state_id, converged_num_flaps_id, converged_num_chord_id,] plot_wake_state_id = converged_wake_state_id plot_num_flaps_id = converged_num_flaps_id @@ -433,33 +356,15 @@ else: continue - these_rms_lifts = rms_lifts[ - plot_wake_state_id, - plot_num_flaps_id, - : (plot_num_chord_id + 2), - airplane_id, - ] - these_rms_drags = rms_drags[ - plot_wake_state_id, - plot_num_flaps_id, - : (plot_num_chord_id + 2), - airplane_id, - ] - - lift_axes.plot( - num_chord_list[: (plot_num_chord_id + 2)], - these_rms_lifts, - label="Row " + str(row), - marker="o", - linestyle="--", - ) - drag_axes.plot( - num_chord_list[: (plot_num_chord_id + 2)], - these_rms_drags, - label="Row " + str(row), - marker="o", - linestyle="--", - ) + these_rms_lifts = rms_lifts[plot_wake_state_id, plot_num_flaps_id, + : (plot_num_chord_id + 2), airplane_id, ] + these_rms_drags = rms_drags[plot_wake_state_id, plot_num_flaps_id, + : (plot_num_chord_id + 2), airplane_id, ] + + lift_axes.plot(num_chord_list[: (plot_num_chord_id + 2)], these_rms_lifts, + label="Row " + str(row), marker="o", linestyle="--", ) + drag_axes.plot(num_chord_list[: (plot_num_chord_id + 2)], these_rms_drags, + label="Row " + str(row), marker="o", linestyle="--", ) lift_axes.set_xlabel("Number of Chordwise Panels") drag_axes.set_xlabel("Number of Chordwise Panels") @@ -495,33 +400,15 @@ else: continue - these_rms_lifts = rms_lifts[ - plot_wake_state_id, - : (plot_num_flaps_id + 2), - plot_num_chord_id, - airplane_id, - ] - these_rms_drags = rms_drags[ - plot_wake_state_id, - : (plot_num_flaps_id + 2), - plot_num_chord_id, - airplane_id, - ] - - lift_axes.plot( - num_flaps_list[: (plot_num_flaps_id + 2)], - these_rms_lifts, - label="Row " + str(row), - marker="o", - linestyle="--", - ) - drag_axes.plot( - num_flaps_list[: (plot_num_flaps_id + 2)], - these_rms_drags, - label="Row " + str(row), - marker="o", - linestyle="--", - ) + these_rms_lifts = rms_lifts[plot_wake_state_id, : (plot_num_flaps_id + 2), + plot_num_chord_id, airplane_id, ] + these_rms_drags = rms_drags[plot_wake_state_id, : (plot_num_flaps_id + 2), + plot_num_chord_id, airplane_id, ] + + lift_axes.plot(num_flaps_list[: (plot_num_flaps_id + 2)], these_rms_lifts, + label="Row " + str(row), marker="o", linestyle="--", ) + drag_axes.plot(num_flaps_list[: (plot_num_flaps_id + 2)], these_rms_drags, + label="Row " + str(row), marker="o", linestyle="--", ) lift_axes.set_xlabel("Number of Flap Cycles") drag_axes.set_xlabel("Number of Flap Cycles") diff --git a/main.py b/main.py index c8f74bec..d787a600 100644 --- a/main.py +++ b/main.py @@ -48,7 +48,6 @@ def __init__(self): self.displayText = "" def exampleMenu(self, ex_num): - files = [] for i, filename in enumerate(os.listdir("examples")): f = "examples." + filename diff --git a/pterasoftware/aerodynamics.py b/pterasoftware/aerodynamics.py index fac44c8c..7ebc3372 100644 --- a/pterasoftware/aerodynamics.py +++ b/pterasoftware/aerodynamics.py @@ -48,14 +48,13 @@ from . import functions - # Set the value of Squire's parameter that will be used by the induced velocity # functions. Squire's parameter relates to the size of the vortex cores and the rate # at which they grow. The value of this parameter is slightly controversial. It # dramatically affects the stability of the result. I'm using this value, as cited # for use in flapping-wing vehicles in "Role of Filament Strain in the Free-Vortex # Modeling of Rotor Wakes" (Ananthan and Leishman, 2004). It is unitless. -squire = 10**-4 +squire = 10 ** -4 # Set the value of Lamb's constant that will be used by the induced velocity # functions. Lamb's constant relates to the size of the vortex cores and the rate at @@ -118,14 +117,8 @@ class HorseshoeVortex: This class is not meant to be subclassed. """ - def __init__( - self, - finite_leg_origin, - finite_leg_termination, - strength, - infinite_leg_direction, - infinite_leg_length, - ): + def __init__(self, finite_leg_origin, finite_leg_termination, strength, + infinite_leg_direction, infinite_leg_length, ): """This is the initialization method. :param finite_leg_origin: 1D array @@ -151,28 +144,18 @@ def __init__( self.infinite_leg_direction = infinite_leg_direction self.infinite_leg_length = infinite_leg_length self.right_leg_origin = ( - self.finite_leg_origin + infinite_leg_direction * infinite_leg_length - ) + self.finite_leg_origin + infinite_leg_direction * infinite_leg_length) self.left_leg_termination = ( - self.finite_leg_termination + infinite_leg_direction * infinite_leg_length - ) + self.finite_leg_termination + infinite_leg_direction * + infinite_leg_length) # Initialize a line vortex to represent the horseshoe's finite leg. - self.right_leg = LineVortex( - origin=self.right_leg_origin, - termination=self.finite_leg_origin, - strength=self.strength, - ) - self.finite_leg = LineVortex( - origin=self.finite_leg_origin, - termination=self.finite_leg_termination, - strength=self.strength, - ) - self.left_leg = LineVortex( - origin=self.finite_leg_termination, - termination=self.left_leg_termination, - strength=self.strength, - ) + self.right_leg = LineVortex(origin=self.right_leg_origin, + termination=self.finite_leg_origin, strength=self.strength, ) + self.finite_leg = LineVortex(origin=self.finite_leg_origin, + termination=self.finite_leg_termination, strength=self.strength, ) + self.left_leg = LineVortex(origin=self.finite_leg_termination, + termination=self.left_leg_termination, strength=self.strength, ) def update_strength(self, strength): """This method updates the strength of this horseshoe vortex object, and the @@ -206,14 +189,8 @@ class RingVortex: This class is not meant to be subclassed. """ - def __init__( - self, - front_left_vertex, - front_right_vertex, - back_left_vertex, - back_right_vertex, - strength, - ): + def __init__(self, front_left_vertex, front_right_vertex, back_left_vertex, + back_right_vertex, strength, ): """This is the initialization method. :param front_left_vertex: 1D array @@ -238,34 +215,18 @@ def __init__( self.strength = strength # Initialize the line vortices that make up the ring vortex. - self.front_leg = LineVortex( - origin=self.front_right_vertex, - termination=self.front_left_vertex, - strength=self.strength, - ) - self.left_leg = LineVortex( - origin=self.front_left_vertex, - termination=self.back_left_vertex, - strength=self.strength, - ) - self.back_leg = LineVortex( - origin=self.back_left_vertex, - termination=self.back_right_vertex, - strength=self.strength, - ) - self.right_leg = LineVortex( - origin=self.back_right_vertex, - termination=self.front_right_vertex, - strength=self.strength, - ) + self.front_leg = LineVortex(origin=self.front_right_vertex, + termination=self.front_left_vertex, strength=self.strength, ) + self.left_leg = LineVortex(origin=self.front_left_vertex, + termination=self.back_left_vertex, strength=self.strength, ) + self.back_leg = LineVortex(origin=self.back_left_vertex, + termination=self.back_right_vertex, strength=self.strength, ) + self.right_leg = LineVortex(origin=self.back_right_vertex, + termination=self.front_right_vertex, strength=self.strength, ) # Initialize a variable to hold the centroid of the ring vortex. - self.center = functions.numba_centroid_of_quadrilateral( - self.front_left_vertex, - self.front_right_vertex, - self.back_left_vertex, - self.back_right_vertex, - ) + self.center = functions.numba_centroid_of_quadrilateral(self.front_left_vertex, + self.front_right_vertex, self.back_left_vertex, self.back_right_vertex, ) # Initialize a variable to hold the age of the ring vortex in seconds. self.age = 0 @@ -285,9 +246,8 @@ def update_strength(self, strength): self.left_leg.strength = strength self.back_leg.strength = strength - def update_position( - self, front_left_vertex, front_right_vertex, back_left_vertex, back_right_vertex - ): + def update_position(self, front_left_vertex, front_right_vertex, back_left_vertex, + back_right_vertex): """This method updates the position of the ring vortex, and the positions of all its attributes. @@ -311,47 +271,24 @@ def update_position( self.back_right_vertex = back_right_vertex # Initialize the line vortices that make up the ring vortex. - self.front_leg = LineVortex( - origin=self.front_right_vertex, - termination=self.front_left_vertex, - strength=self.strength, - ) - self.left_leg = LineVortex( - origin=self.front_left_vertex, - termination=self.back_left_vertex, - strength=self.strength, - ) - self.back_leg = LineVortex( - origin=self.back_left_vertex, - termination=self.back_right_vertex, - strength=self.strength, - ) - self.right_leg = LineVortex( - origin=self.back_right_vertex, - termination=self.front_right_vertex, - strength=self.strength, - ) + self.front_leg = LineVortex(origin=self.front_right_vertex, + termination=self.front_left_vertex, strength=self.strength, ) + self.left_leg = LineVortex(origin=self.front_left_vertex, + termination=self.back_left_vertex, strength=self.strength, ) + self.back_leg = LineVortex(origin=self.back_left_vertex, + termination=self.back_right_vertex, strength=self.strength, ) + self.right_leg = LineVortex(origin=self.back_right_vertex, + termination=self.front_right_vertex, strength=self.strength, ) # Initialize a variable to hold the centroid of the ring vortex. - self.center = functions.numba_centroid_of_quadrilateral( - self.front_left_vertex, - self.front_right_vertex, - self.back_left_vertex, - self.back_right_vertex, - ) + self.center = functions.numba_centroid_of_quadrilateral(self.front_left_vertex, + self.front_right_vertex, self.back_left_vertex, self.back_right_vertex, ) @njit(cache=True, fastmath=False) -def collapsed_velocities_from_horseshoe_vortices( - points, - back_right_vortex_vertices, - front_right_vortex_vertices, - front_left_vortex_vertices, - back_left_vortex_vertices, - strengths, - ages=None, - nu=0.0, -): +def collapsed_velocities_from_horseshoe_vortices(points, back_right_vortex_vertices, + front_right_vortex_vertices, front_left_vortex_vertices, + back_left_vortex_vertices, strengths, ages=None, nu=0.0, ): """This function takes in a group of points, and the attributes of a group of horseshoe vortices. At every point, it finds the cumulative induced velocity due to all the horseshoe vortices. @@ -400,42 +337,24 @@ def collapsed_velocities_from_horseshoe_vortices( velocity at each of the N points due to all the horseshoe vortices. The units are meters per second. """ - origins_list = [ - back_right_vortex_vertices, - front_right_vortex_vertices, - front_left_vortex_vertices, - ] - terminations_list = [ - front_right_vortex_vertices, - front_left_vortex_vertices, - back_left_vortex_vertices, - ] + origins_list = [back_right_vortex_vertices, front_right_vortex_vertices, + front_left_vortex_vertices, ] + terminations_list = [front_right_vortex_vertices, front_left_vortex_vertices, + back_left_vortex_vertices, ] induced_velocities = np.zeros((points.shape[0], 3)) # Get the velocity induced by each leg of the ring vortex. for i in range(3): - induced_velocities += collapsed_velocities_from_line_vortices( - points=points, - origins=origins_list[i], - terminations=terminations_list[i], - strengths=strengths, - ages=ages, - nu=nu, - ) + induced_velocities += collapsed_velocities_from_line_vortices(points=points, + origins=origins_list[i], terminations=terminations_list[i], + strengths=strengths, ages=ages, nu=nu, ) return induced_velocities @njit(cache=True, fastmath=False) -def expanded_velocities_from_horseshoe_vortices( - points, - back_right_vortex_vertices, - front_right_vortex_vertices, - front_left_vortex_vertices, - back_left_vortex_vertices, - strengths, - ages=None, - nu=0.0, -): +def expanded_velocities_from_horseshoe_vortices(points, back_right_vortex_vertices, + front_right_vortex_vertices, front_left_vortex_vertices, + back_left_vortex_vertices, strengths, ages=None, nu=0.0, ): """This function takes in a group of points, and the attributes of a group of horseshoe vortices. At every point, it finds the induced velocity due to each horseshoe vortex. @@ -484,42 +403,24 @@ def expanded_velocities_from_horseshoe_vortices( the velocity induced at one point by one of the horseshoe vortices. The units are meters per second. """ - origins_list = [ - back_right_vortex_vertices, - front_right_vortex_vertices, - front_left_vortex_vertices, - ] - terminations_list = [ - front_right_vortex_vertices, - front_left_vortex_vertices, - back_left_vortex_vertices, - ] + origins_list = [back_right_vortex_vertices, front_right_vortex_vertices, + front_left_vortex_vertices, ] + terminations_list = [front_right_vortex_vertices, front_left_vortex_vertices, + back_left_vortex_vertices, ] induced_velocities = np.zeros((points.shape[0], strengths.shape[0], 3)) # Get the velocity induced by each leg of the ring vortex. for i in range(3): - induced_velocities += expanded_velocities_from_line_vortices( - points=points, - origins=origins_list[i], - terminations=terminations_list[i], - strengths=strengths, - ages=ages, - nu=nu, - ) + induced_velocities += expanded_velocities_from_line_vortices(points=points, + origins=origins_list[i], terminations=terminations_list[i], + strengths=strengths, ages=ages, nu=nu, ) return induced_velocities @njit(cache=True, fastmath=False) -def collapsed_velocities_from_ring_vortices( - points, - back_right_vortex_vertices, - front_right_vortex_vertices, - front_left_vortex_vertices, - back_left_vortex_vertices, - strengths, - ages=None, - nu=0.0, -): +def collapsed_velocities_from_ring_vortices(points, back_right_vortex_vertices, + front_right_vortex_vertices, front_left_vortex_vertices, + back_left_vortex_vertices, strengths, ages=None, nu=0.0, ): """This function takes in a group of points, and the attributes of a group of ring vortices. At every point, it finds the cumulative induced velocity due to all the ring vortices. @@ -568,44 +469,25 @@ def collapsed_velocities_from_ring_vortices( velocity at each of the N points due to all the ring vortices. The units are meters per second. """ - origins_list = [ - back_right_vortex_vertices, - front_right_vortex_vertices, - front_left_vortex_vertices, - back_left_vortex_vertices, - ] - terminations_list = [ - front_right_vortex_vertices, - front_left_vortex_vertices, - back_left_vortex_vertices, - back_right_vortex_vertices, - ] + origins_list = [back_right_vortex_vertices, front_right_vortex_vertices, + front_left_vortex_vertices, back_left_vortex_vertices, ] + terminations_list = [front_right_vortex_vertices, front_left_vortex_vertices, + back_left_vortex_vertices, back_right_vortex_vertices, ] induced_velocities = np.zeros((points.shape[0], 3)) # Get the velocity induced by each leg of the ring vortex. for i in range(4): - induced_velocities += collapsed_velocities_from_line_vortices( - points=points, - origins=origins_list[i], - terminations=terminations_list[i], - strengths=strengths, - ages=ages, - nu=nu, - ) + induced_velocities += collapsed_velocities_from_line_vortices(points=points, + origins=origins_list[i], terminations=terminations_list[i], + strengths=strengths, ages=ages, nu=nu, ) return induced_velocities @njit(cache=True, fastmath=False) -def collapsed_velocities_from_ring_vortices_chordwise_segments( - points, - back_right_vortex_vertices, - front_right_vortex_vertices, - front_left_vortex_vertices, - back_left_vortex_vertices, - strengths, - ages=None, - nu=0.0, -): +def collapsed_velocities_from_ring_vortices_chordwise_segments(points, + back_right_vortex_vertices, front_right_vortex_vertices, + front_left_vortex_vertices, back_left_vortex_vertices, strengths, ages=None, + nu=0.0, ): """This function takes in a group of points, and the attributes of a group of ring vortices. At every point, it finds the cumulative induced velocity due to all the ring vortices' chordwise segments. @@ -654,40 +536,22 @@ def collapsed_velocities_from_ring_vortices_chordwise_segments( velocity at each of the N points due to all the ring vortices' spanwise segments. The units are meters per second. """ - origins_list = [ - back_right_vortex_vertices, - front_left_vortex_vertices, - ] - terminations_list = [ - front_right_vortex_vertices, - back_left_vortex_vertices, - ] + origins_list = [back_right_vortex_vertices, front_left_vortex_vertices, ] + terminations_list = [front_right_vortex_vertices, back_left_vortex_vertices, ] induced_velocities = np.zeros((points.shape[0], 3)) # Get the velocity induced by each leg of the ring vortex. for i in range(2): - induced_velocities += collapsed_velocities_from_line_vortices( - points=points, - origins=origins_list[i], - terminations=terminations_list[i], - strengths=strengths, - ages=ages, - nu=nu, - ) + induced_velocities += collapsed_velocities_from_line_vortices(points=points, + origins=origins_list[i], terminations=terminations_list[i], + strengths=strengths, ages=ages, nu=nu, ) return induced_velocities @njit(cache=True, fastmath=False) -def expanded_velocities_from_ring_vortices( - points, - back_right_vortex_vertices, - front_right_vortex_vertices, - front_left_vortex_vertices, - back_left_vortex_vertices, - strengths, - ages=None, - nu=0.0, -): +def expanded_velocities_from_ring_vortices(points, back_right_vortex_vertices, + front_right_vortex_vertices, front_left_vortex_vertices, + back_left_vortex_vertices, strengths, ages=None, nu=0.0, ): """This function takes in a group of points, and the attributes of a group of ring vortices. At every point, it finds the induced velocity due to each ring vortex. @@ -736,42 +600,23 @@ def expanded_velocities_from_ring_vortices( the velocity induced at one point by one of the ring vortices. The units are meters per second. """ - origins_list = [ - back_right_vortex_vertices, - front_right_vortex_vertices, - front_left_vortex_vertices, - back_left_vortex_vertices, - ] - terminations_list = [ - front_right_vortex_vertices, - front_left_vortex_vertices, - back_left_vortex_vertices, - back_right_vortex_vertices, - ] + origins_list = [back_right_vortex_vertices, front_right_vortex_vertices, + front_left_vortex_vertices, back_left_vortex_vertices, ] + terminations_list = [front_right_vortex_vertices, front_left_vortex_vertices, + back_left_vortex_vertices, back_right_vortex_vertices, ] induced_velocities = np.zeros((points.shape[0], strengths.shape[0], 3)) # Get the velocity induced by each leg of the ring vortex. for i in range(4): - induced_velocities += expanded_velocities_from_line_vortices( - points=points, - origins=origins_list[i], - terminations=terminations_list[i], - strengths=strengths, - ages=ages, - nu=nu, - ) + induced_velocities += expanded_velocities_from_line_vortices(points=points, + origins=origins_list[i], terminations=terminations_list[i], + strengths=strengths, ages=ages, nu=nu, ) return induced_velocities @njit(cache=True, fastmath=False) -def collapsed_velocities_from_line_vortices( - points, - origins, - terminations, - strengths, - ages=None, - nu=0.0, -): +def collapsed_velocities_from_line_vortices(points, origins, terminations, strengths, + ages=None, nu=0.0, ): """This function takes in a group of points, and the attributes of a group of line vortices. At every point, it finds the cumulative induced velocity due to all the line vortices. @@ -847,10 +692,10 @@ def collapsed_velocities_from_line_vortices( r_0_z = termination[2] - origin[2] # Find the r_0 vector's length. - r_0 = math.sqrt(r_0_x**2 + r_0_y**2 + r_0_z**2) + r_0 = math.sqrt(r_0_x ** 2 + r_0_y ** 2 + r_0_z ** 2) c_1 = strength / (4 * math.pi) - c_2 = r_0**2 * r_c**2 + c_2 = r_0 ** 2 * r_c ** 2 for point_id in range(num_points): point = points[point_id] @@ -871,9 +716,9 @@ def collapsed_velocities_from_line_vortices( r_3_z = r_1_x * r_2_y - r_1_y * r_2_x # Find the r_1, r_2, and r_3 vectors' lengths. - r_1 = math.sqrt(r_1_x**2 + r_1_y**2 + r_1_z**2) - r_2 = math.sqrt(r_2_x**2 + r_2_y**2 + r_2_z**2) - r_3 = math.sqrt(r_3_x**2 + r_3_y**2 + r_3_z**2) + r_1 = math.sqrt(r_1_x ** 2 + r_1_y ** 2 + r_1_z ** 2) + r_2 = math.sqrt(r_2_x ** 2 + r_2_y ** 2 + r_2_z ** 2) + r_3 = math.sqrt(r_3_x ** 2 + r_3_y ** 2 + r_3_z ** 2) c_3 = r_1_x * r_2_x + r_1_y * r_2_y + r_1_z * r_2_z @@ -881,12 +726,11 @@ def collapsed_velocities_from_line_vortices( # within machine epsilon), there is a removable discontinuity. In this # case, continue to the next point because there is no velocity induced # by the current vortex at this point. - if r_1 < eps or r_2 < eps or r_3**2 < eps: + if r_1 < eps or r_2 < eps or r_3 ** 2 < eps: continue else: - c_4 = ( - c_1 * (r_1 + r_2) * (r_1 * r_2 - c_3) / (r_1 * r_2 * (r_3**2 + c_2)) - ) + c_4 = (c_1 * (r_1 + r_2) * (r_1 * r_2 - c_3) / ( + r_1 * r_2 * (r_3 ** 2 + c_2))) velocities[point_id, 0] += c_4 * r_3_x velocities[point_id, 1] += c_4 * r_3_y velocities[point_id, 2] += c_4 * r_3_z @@ -895,14 +739,8 @@ def collapsed_velocities_from_line_vortices( @njit(cache=True, fastmath=False) -def expanded_velocities_from_line_vortices( - points, - origins, - terminations, - strengths, - ages=None, - nu=0.0, -): +def expanded_velocities_from_line_vortices(points, origins, terminations, strengths, + ages=None, nu=0.0, ): """This function takes in a group of points, and the attributes of a group of line vortices. At every point, it finds the induced velocity due to each line vortex. @@ -978,10 +816,10 @@ def expanded_velocities_from_line_vortices( r_0_z = termination[2] - origin[2] # Find the r_0 vector's length. - r_0 = math.sqrt(r_0_x**2 + r_0_y**2 + r_0_z**2) + r_0 = math.sqrt(r_0_x ** 2 + r_0_y ** 2 + r_0_z ** 2) c_1 = strength / (4 * math.pi) - c_2 = r_0**2 * r_c**2 + c_2 = r_0 ** 2 * r_c ** 2 for point_id in range(num_points): point = points[point_id] @@ -1002,9 +840,9 @@ def expanded_velocities_from_line_vortices( r_3_z = r_1_x * r_2_y - r_1_y * r_2_x # Find the r_1, r_2, and r_3 vectors' lengths. - r_1 = math.sqrt(r_1_x**2 + r_1_y**2 + r_1_z**2) - r_2 = math.sqrt(r_2_x**2 + r_2_y**2 + r_2_z**2) - r_3 = math.sqrt(r_3_x**2 + r_3_y**2 + r_3_z**2) + r_1 = math.sqrt(r_1_x ** 2 + r_1_y ** 2 + r_1_z ** 2) + r_2 = math.sqrt(r_2_x ** 2 + r_2_y ** 2 + r_2_z ** 2) + r_3 = math.sqrt(r_3_x ** 2 + r_3_y ** 2 + r_3_z ** 2) c_3 = r_1_x * r_2_x + r_1_y * r_2_y + r_1_z * r_2_z @@ -1012,12 +850,11 @@ def expanded_velocities_from_line_vortices( # within machine epsilon), there is a removable discontinuity. In this # case, set the velocity components to their true values, which are 0.0 # meters per second. - if r_1 < eps or r_2 < eps or r_3**2 < eps: + if r_1 < eps or r_2 < eps or r_3 ** 2 < eps: continue else: - c_4 = ( - c_1 * (r_1 + r_2) * (r_1 * r_2 - c_3) / (r_1 * r_2 * (r_3**2 + c_2)) - ) + c_4 = (c_1 * (r_1 + r_2) * (r_1 * r_2 - c_3) / ( + r_1 * r_2 * (r_3 ** 2 + c_2))) velocities[point_id, vortex_id, 0] = c_4 * r_3_x velocities[point_id, vortex_id, 1] = c_4 * r_3_y velocities[point_id, vortex_id, 2] = c_4 * r_3_z diff --git a/pterasoftware/convergence.py b/pterasoftware/convergence.py index 9b5d04ff..385aef0d 100644 --- a/pterasoftware/convergence.py +++ b/pterasoftware/convergence.py @@ -256,8 +256,7 @@ def analyze_steady_convergence( # this airplane's mesh. s_ref=None, c_ref=None, - b_ref=None, - # This value changes. + b_ref=None, # This value changes. wings=these_wings, ) ) @@ -899,8 +898,7 @@ def analyze_unsteady_convergence( # with this airplane's mesh. s_ref=None, c_ref=None, - b_ref=None, - # This value changes. + b_ref=None, # This value changes. wings=these_base_wings, ) diff --git a/pterasoftware/functions.py b/pterasoftware/functions.py index aeac2179..8226387d 100644 --- a/pterasoftware/functions.py +++ b/pterasoftware/functions.py @@ -291,7 +291,6 @@ def process_steady_solver_forces( # Iterate through this solver's panels. for panel_num, panel in enumerate(steady_solver.panels): - # Get this panel's near field forces and moments in geometry axes and wind axes. this_force_geometry_axes = near_field_forces_geometry_axes[panel_num, :] this_moment_geometry_axes = near_field_moments_geometry_axes[panel_num, :] @@ -337,7 +336,6 @@ def process_steady_solver_forces( # Iterate through the airplanes and calculate each one's coefficients. for airplane in steady_solver.airplanes: - # Calculate this airplane's force coefficients. induced_drag_coefficient = ( -airplane.total_near_field_force_wind_axes[0] @@ -424,7 +422,6 @@ def process_unsteady_solver_forces( # Iterate through this solver's panels. for panel_num, panel in enumerate(unsteady_solver.panels): - # Get this panel's near field forces and moments in geometry axes and wind # axes. this_force_geometry_axes = near_field_forces_geometry_axes[panel_num, :] @@ -471,7 +468,6 @@ def process_unsteady_solver_forces( # Iterate through the airplanes and calculate each one's coefficients. for airplane in unsteady_solver.current_airplanes: - # Calculate this airplane's force coefficients. induced_drag_coefficient = ( -airplane.total_near_field_force_wind_axes[0] diff --git a/pterasoftware/geometry.py b/pterasoftware/geometry.py index c4e72698..fd5e2c97 100644 --- a/pterasoftware/geometry.py +++ b/pterasoftware/geometry.py @@ -46,18 +46,8 @@ class Airplane: This class is not meant to be subclassed. """ - def __init__( - self, - name="Untitled", - x_ref=0.0, - y_ref=0.0, - z_ref=0.0, - weight=0.0, - wings=None, - s_ref=None, - c_ref=None, - b_ref=None, - ): + def __init__(self, name="Untitled", x_ref=0.0, y_ref=0.0, z_ref=0.0, weight=0.0, + wings=None, s_ref=None, c_ref=None, b_ref=None, ): """This is the initialization method. :param name: str, optional @@ -185,17 +175,9 @@ class Wing: This class is not meant to be subclassed. """ - def __init__( - self, - name="Untitled Wing", - x_le=0.0, - y_le=0.0, - z_le=0.0, - wing_cross_sections=None, - symmetric=False, - num_chordwise_panels=8, - chordwise_spacing="cosine", - ): + def __init__(self, name="Untitled Wing", x_le=0.0, y_le=0.0, z_le=0.0, + wing_cross_sections=None, symmetric=False, num_chordwise_panels=8, + chordwise_spacing="cosine", ): """This is the initialization method. :param name: str, optional @@ -289,11 +271,9 @@ def projected_area(self): # Iterate through the wing cross sections and add the area of their # corresponding wing sections to the total projected area. for wing_cross_section_id, wing_cross_section in enumerate( - self.wing_cross_sections[:-1] - ): + self.wing_cross_sections[:-1]): next_wing_cross_section = self.wing_cross_sections[ - wing_cross_section_id + 1 - ] + wing_cross_section_id + 1] span = abs(next_wing_cross_section.y_le - wing_cross_section.y_le) @@ -379,11 +359,9 @@ def mean_aerodynamic_chord(self): # Iterate through the wing cross sections to add the contribution of their # corresponding wing section to the piecewise integral. for wing_cross_section_id, wing_cross_section in enumerate( - self.wing_cross_sections[:-1] - ): + self.wing_cross_sections[:-1]): next_wing_cross_section = self.wing_cross_sections[ - wing_cross_section_id + 1 - ] + wing_cross_section_id + 1] root_chord = wing_cross_section.chord tip_chord = next_wing_cross_section.chord @@ -393,11 +371,8 @@ def mean_aerodynamic_chord(self): # projected on to the body-frame's XY plane). For a trapezoid, # the integral from the cited equation can be shown to evaluate to the # following. - integral += ( - section_length - * (root_chord**2 + root_chord * tip_chord + tip_chord**2) - / 3 - ) + integral += (section_length * ( + root_chord ** 2 + root_chord * tip_chord + tip_chord ** 2) / 3) # Multiply the integral's value by the coefficients from the cited equation. if self.symmetric: @@ -424,20 +399,10 @@ class WingCrossSection: This class is not meant to be subclassed. """ - def __init__( - self, - x_le=0.0, - y_le=0.0, - z_le=0.0, - chord=1.0, - twist=0.0, - airfoil=None, - control_surface_type="symmetric", - control_surface_hinge_point=0.75, - control_surface_deflection=0.0, - num_spanwise_panels=8, - spanwise_spacing="cosine", - ): + def __init__(self, x_le=0.0, y_le=0.0, z_le=0.0, chord=1.0, twist=0.0, airfoil=None, + control_surface_type="symmetric", control_surface_hinge_point=0.75, + control_surface_deflection=0.0, num_spanwise_panels=8, + spanwise_spacing="cosine", ): """This is the initialization method. :param x_le: float, optional @@ -520,9 +485,8 @@ def trailing_edge(self): """ # Find the rotation matrix given the cross section's twist. - rotation_matrix = functions.angle_axis_rotation_matrix( - self.twist * np.pi / 180, np.array([0, 1, 0]) - ) + rotation_matrix = functions.angle_axis_rotation_matrix(self.twist * np.pi / 180, + np.array([0, 1, 0])) # Use the rotation matrix and the leading edge coordinates to calculate the # trailing edge coordinates. @@ -576,13 +540,8 @@ class Airfoil: This class is not meant to be subclassed. """ - def __init__( - self, - name="Untitled Airfoil", - coordinates=None, - repanel=True, - n_points_per_side=400, - ): + def __init__(self, name="Untitled Airfoil", coordinates=None, repanel=True, + n_points_per_side=400, ): """This is the initialization method. :param name: str, optional @@ -639,7 +598,8 @@ def populate_coordinates(self): 4-series airfoil, or loaded from the airfoil database (a folder named "airfoils" in this directory, that contains a library of dat files for airfoil coordinates). NACA 4-series airfoil generation is an adaptation of: - https://en.wikipedia.org/wiki/NACA_airfoil#Equation_for_a_cambered_4-digit_NACA_airfoil. + https://en.wikipedia.org/wiki/NACA_airfoil#Equation_for_a_cambered_4 + -digit_NACA_airfoil. :return: None """ @@ -663,55 +623,29 @@ def populate_coordinates(self): # Make uncambered coordinates and generate cosine-spaced points. x_t = functions.cosspace(0, 1, n_points_per_side) - y_t = ( - 5 - * thickness - * ( - +0.2969 * np.power(x_t, 0.5) - - 0.1260 * x_t - - 0.3516 * np.power(x_t, 2) - + 0.2843 * np.power(x_t, 3) - - 0.1015 * np.power(x_t, 4) - ) - ) + y_t = (5 * thickness * (+0.2969 * np.power(x_t, + 0.5) - 0.1260 * x_t - 0.3516 * np.power( + x_t, 2) + 0.2843 * np.power(x_t, 3) - 0.1015 * np.power(x_t, + 4))) # Prevent divide by zero errors for airfoils like the NACA 0012. if camber_loc == 0: camber_loc = 0.5 # Get the camber. - y_c_piece1 = ( - max_camber - / camber_loc**2 - * ( - 2 * camber_loc * x_t[x_t <= camber_loc] - - x_t[x_t <= camber_loc] ** 2 - ) - ) - y_c_piece2 = ( - max_camber - / (1 - camber_loc) ** 2 - * ( - (1 - 2 * camber_loc) - + 2 * camber_loc * x_t[x_t > camber_loc] - - x_t[x_t > camber_loc] ** 2 - ) - ) + y_c_piece1 = (max_camber / camber_loc ** 2 * ( + 2 * camber_loc * x_t[x_t <= camber_loc] - x_t[ + x_t <= camber_loc] ** 2)) + y_c_piece2 = (max_camber / (1 - camber_loc) ** 2 * ( + (1 - 2 * camber_loc) + 2 * camber_loc * x_t[ + x_t > camber_loc] - x_t[x_t > camber_loc] ** 2)) y_c = np.hstack((y_c_piece1, y_c_piece2)) # Get camber slope. - first_piece_slope = ( - 2 - * max_camber - / camber_loc**2 - * (camber_loc - x_t[x_t <= camber_loc]) - ) - second_piece_slope = ( - 2 - * max_camber - / (1 - camber_loc) ** 2 - * (camber_loc - x_t[x_t > camber_loc]) - ) + first_piece_slope = (2 * max_camber / camber_loc ** 2 * ( + camber_loc - x_t[x_t <= camber_loc])) + second_piece_slope = (2 * max_camber / (1 - camber_loc) ** 2 * ( + camber_loc - x_t[x_t > camber_loc])) slope = np.hstack((first_piece_slope, second_piece_slope)) theta = np.arctan(slope) @@ -740,25 +674,21 @@ def populate_coordinates(self): try: # Import the airfoils package as "airfoils". - airfoils = importlib.import_module( - name=".airfoils", - package="pterasoftware", - ) + airfoils = importlib.import_module(name=".airfoils", + package="pterasoftware", ) # Read the text from the airfoil file. raw_text = importlib.resources.read_text(airfoils, name + ".dat") # Trim the text at the return characters. - trimmed_text = raw_text[raw_text.find("\n") :] + trimmed_text = raw_text[raw_text.find("\n"):] # Input the coordinates into a 1D array. coordinates_1d = np.fromstring(trimmed_text, sep="\n") # Check to make sure the number of elements in the array is even. - assert len(coordinates_1d) % 2 == 0, ( - "File was found in airfoil database, " - "but it could not be read correctly." - ) + assert len(coordinates_1d) % 2 == 0, ("File was found in airfoil database, " + "but it could not be read correctly.") # Reshape the 1D coordinates array into an N x 2 array, where N is the # number of rows. @@ -834,7 +764,7 @@ def lower_coordinates(self): """ # Find the lower coordinates. - lower_coordinates = self.coordinates[self.leading_edge_index() :, :] + lower_coordinates = self.coordinates[self.leading_edge_index():, :] # Return the lower coordinates. return lower_coordinates @@ -875,32 +805,27 @@ def get_downsampled_mcl(self, mcl_fractions): # Find the distances between points along the mean camber line, assuming # linear interpolation. mcl_distances_between_points = np.sqrt( - np.power(mcl[:-1, 0] - mcl[1:, 0], 2) - + np.power(mcl[:-1, 1] - mcl[1:, 1], 2) - ) + np.power(mcl[:-1, 0] - mcl[1:, 0], 2) + np.power(mcl[:-1, 1] - mcl[1:, 1], + 2)) # Create a horizontal 1D array that contains the distance along the mean # camber line of each point. mcl_distances_cumulative = np.hstack( - (0, np.cumsum(mcl_distances_between_points)) - ) + (0, np.cumsum(mcl_distances_between_points))) # Normalize the 1D array so that it ranges from 0 to 1. mcl_distances_cumulative_normalized = ( - mcl_distances_cumulative / mcl_distances_cumulative[-1] - ) + mcl_distances_cumulative / mcl_distances_cumulative[-1]) # Linearly interpolate to find the x coordinates of the mean camber line at # the given mean camber line fractions. - mcl_downsampled_x = np.interp( - x=mcl_fractions, xp=mcl_distances_cumulative_normalized, fp=mcl[:, 0] - ) + mcl_downsampled_x = np.interp(x=mcl_fractions, + xp=mcl_distances_cumulative_normalized, fp=mcl[:, 0]) # Linearly interpolate to find the y coordinates of the mean camber line at # the given mean camber line fractions. - mcl_downsampled_y = np.interp( - x=mcl_fractions, xp=mcl_distances_cumulative_normalized, fp=mcl[:, 1] - ) + mcl_downsampled_y = np.interp(x=mcl_fractions, + xp=mcl_distances_cumulative_normalized, fp=mcl[:, 1]) # Combine the x and y coordinates of the downsampled mean camber line. mcl_downsampled = np.column_stack((mcl_downsampled_x, mcl_downsampled_y)) @@ -922,12 +847,8 @@ def get_camber_at_chord_fraction(self, chord_fraction): # Create a function that interpolates between the x and y coordinates of the # mean camber line. - camber_function = sp_interp.interp1d( - x=self.mcl_coordinates[:, 0], - y=self.mcl_coordinates[:, 1], - copy=False, - fill_value="extrapolate", - ) + camber_function = sp_interp.interp1d(x=self.mcl_coordinates[:, 0], + y=self.mcl_coordinates[:, 1], copy=False, fill_value="extrapolate", ) # Find the value of the camber (the y coordinate) of the airfoil at the # requested chord fraction. @@ -962,23 +883,16 @@ def repanel_current_airfoil(self, n_points_per_side=100): # surfaces as a function of the chord fractions upper_func = sp_interp.PchipInterpolator( x=np.flip(upper_original_coordinates[:, 0]), - y=np.flip(upper_original_coordinates[:, 1]), - ) - lower_func = sp_interp.PchipInterpolator( - x=lower_original_coordinates[:, 0], y=lower_original_coordinates[:, 1] - ) + y=np.flip(upper_original_coordinates[:, 1]), ) + lower_func = sp_interp.PchipInterpolator(x=lower_original_coordinates[:, 0], + y=lower_original_coordinates[:, 1]) # Find the x and y coordinates of the upper and lower surfaces at each of the # cosine-spaced x values. x_coordinates = np.hstack( - (np.flip(cosine_spaced_x_values), cosine_spaced_x_values[1:]) - ) - y_coordinates = np.hstack( - ( - upper_func(np.flip(cosine_spaced_x_values)), - lower_func(cosine_spaced_x_values[1:]), - ) - ) + (np.flip(cosine_spaced_x_values), cosine_spaced_x_values[1:])) + y_coordinates = np.hstack((upper_func(np.flip(cosine_spaced_x_values)), + lower_func(cosine_spaced_x_values[1:]),)) # Stack the coordinates together and return them. coordinates = np.column_stack((x_coordinates, y_coordinates)) @@ -1010,8 +924,7 @@ def add_control_surface(self, deflection=0.0, hinge_point=0.75): # Find y coordinate at the hinge point x coordinate and make it a vector. hinge_point = np.array( - (hinge_point, self.get_camber_at_chord_fraction(hinge_point)) - ) + (hinge_point, self.get_camber_at_chord_fraction(hinge_point))) # Split the airfoil into the sections before and after the hinge. split_index = np.where(self.mcl_coordinates[:, 0] > hinge_point[0])[0][0] @@ -1022,31 +935,23 @@ def add_control_surface(self, deflection=0.0, hinge_point=0.75): # Rotate the mean camber line coordinates and upper minus mean camber line # vectors. - new_mcl_coordinates_after = ( - np.transpose( - rotation_matrix @ np.transpose(mcl_coordinates_after - hinge_point) - ) - + hinge_point - ) + new_mcl_coordinates_after = (np.transpose(rotation_matrix @ np.transpose( + mcl_coordinates_after - hinge_point)) + hinge_point) new_upper_minus_mcl_after = np.transpose( - rotation_matrix @ np.transpose(upper_minus_mcl_after) - ) + rotation_matrix @ np.transpose(upper_minus_mcl_after)) # Assemble the new, flapped airfoil. new_mcl_coordinates = np.vstack( - (mcl_coordinates_before, new_mcl_coordinates_after) - ) + (mcl_coordinates_before, new_mcl_coordinates_after)) new_upper_minus_mcl = np.vstack( - (upper_minus_mcl_before, new_upper_minus_mcl_after) - ) + (upper_minus_mcl_before, new_upper_minus_mcl_after)) upper_coordinates = np.flipud(new_mcl_coordinates + new_upper_minus_mcl) lower_coordinates = new_mcl_coordinates - new_upper_minus_mcl coordinates = np.vstack((upper_coordinates, lower_coordinates[1:, :])) # Initialize the new, flapped airfoil and return it. - flapped_airfoil = Airfoil( - name=self.name + " flapped", coordinates=coordinates, repanel=False - ) + flapped_airfoil = Airfoil(name=self.name + " flapped", coordinates=coordinates, + repanel=False) return flapped_airfoil def draw(self): diff --git a/pterasoftware/models/steady_horseshoe_vortex_lattice_method_solver.py b/pterasoftware/models/steady_horseshoe_vortex_lattice_method_solver.py index bdb8c680..8decbfd6 100644 --- a/pterasoftware/models/steady_horseshoe_vortex_lattice_method_solver.py +++ b/pterasoftware/models/steady_horseshoe_vortex_lattice_method_solver.py @@ -6,113 +6,47 @@ def __init__(self): var = "Variables" def runSolver(self): - example_airplane = ps.geometry.Airplane( - name="Example Airplane", - x_ref=0.0, - y_ref=0.0, - z_ref=0.0, - s_ref=None, - b_ref=None, - c_ref=None, - wings=[ - ps.geometry.Wing( - name="Main Wing", - x_le=0.0, - y_le=0.0, - z_le=0.0, - symmetric=True, - num_chordwise_panels=8, - chordwise_spacing="cosine", + example_airplane = ps.geometry.Airplane(name="Example Airplane", x_ref=0.0, + y_ref=0.0, z_ref=0.0, s_ref=None, b_ref=None, c_ref=None, wings=[ + ps.geometry.Wing(name="Main Wing", x_le=0.0, y_le=0.0, z_le=0.0, + symmetric=True, num_chordwise_panels=8, chordwise_spacing="cosine", wing_cross_sections=[ - ps.geometry.WingCrossSection( - x_le=0.0, - y_le=0.0, - z_le=0.0, - twist=0.0, - control_surface_type="symmetric", + ps.geometry.WingCrossSection(x_le=0.0, y_le=0.0, z_le=0.0, + twist=0.0, control_surface_type="symmetric", control_surface_hinge_point=0.75, - control_surface_deflection=0.0, - num_spanwise_panels=8, - spanwise_spacing="cosine", - chord=1.75, - airfoil=ps.geometry.Airfoil( - name="naca2412", - coordinates=None, - repanel=True, - n_points_per_side=400, - ), - ), - ps.geometry.WingCrossSection( - x_le=0.75, - y_le=6.0, - z_le=1.0, - chord=1.5, - twist=5.0, - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - ), - ], - ), - ps.geometry.Wing( - name="V-Tail", - x_le=6.75, - z_le=0.25, - symmetric=True, - wing_cross_sections=[ - ps.geometry.WingCrossSection( - chord=1.5, - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - twist=-5.0, - ), - ps.geometry.WingCrossSection( - x_le=0.5, - y_le=2.0, - z_le=1.0, - chord=1.0, - twist=-5.0, - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ], - ), - ], - ) - - example_operating_point = ps.operating_point.OperatingPoint( - density=1.225, - beta=0.0, - velocity=10.0, - alpha=1.0, - ) - - example_problem = ps.problems.SteadyProblem( - airplanes=[example_airplane], - operating_point=example_operating_point, - ) + control_surface_deflection=0.0, num_spanwise_panels=8, + spanwise_spacing="cosine", chord=1.75, + airfoil=ps.geometry.Airfoil(name="naca2412", + coordinates=None, repanel=True, + n_points_per_side=400, ), ), + ps.geometry.WingCrossSection(x_le=0.75, y_le=6.0, z_le=1.0, + chord=1.5, twist=5.0, + airfoil=ps.geometry.Airfoil(name="naca2412", ), ), ], ), + ps.geometry.Wing(name="V-Tail", x_le=6.75, z_le=0.25, symmetric=True, + wing_cross_sections=[ps.geometry.WingCrossSection(chord=1.5, + airfoil=ps.geometry.Airfoil(name="naca0012", ), twist=-5.0, ), + ps.geometry.WingCrossSection(x_le=0.5, y_le=2.0, z_le=1.0, + chord=1.0, twist=-5.0, airfoil=ps.geometry.Airfoil( + name="naca0012", ), ), ], ), ], ) + + example_operating_point = ps.operating_point.OperatingPoint(density=1.225, + beta=0.0, velocity=10.0, alpha=1.0, ) + + example_problem = ps.problems.SteadyProblem(airplanes=[example_airplane], + operating_point=example_operating_point, ) del example_airplane del example_operating_point - example_solver = ps.steady_horseshoe_vortex_lattice_method.SteadyHorseshoeVortexLatticeMethodSolver( - steady_problem=example_problem - ) + example_solver = (ps.steady_horseshoe_vortex_lattice_method + .SteadyHorseshoeVortexLatticeMethodSolver( + steady_problem=example_problem)) del example_problem - example_solver.run( - logging_level="Warning", - ) + example_solver.run(logging_level="Warning", ) ps.output.print_steady_results(steady_solver=example_solver) - ps.output.draw( - solver=example_solver, - scalar_type="lift", - show_streamlines=True, - show_wake_vortices=False, - save=False, - ) + ps.output.draw(solver=example_solver, scalar_type="lift", show_streamlines=True, + show_wake_vortices=False, save=False, ) diff --git a/pterasoftware/models/steady_ring_vortex_lattice_method_solver.py b/pterasoftware/models/steady_ring_vortex_lattice_method_solver.py index 926b07fa..f2ac67b2 100644 --- a/pterasoftware/models/steady_ring_vortex_lattice_method_solver.py +++ b/pterasoftware/models/steady_ring_vortex_lattice_method_solver.py @@ -18,187 +18,122 @@ # program is in SI units. Note: these values are relative to the global # coordinate system fixed front left corner of the first airplane's first wing's # root wing cross section. - x_ref=0.0, - y_ref=0.0, - z_ref=0.0, + x_ref=0.0, y_ref=0.0, z_ref=0.0, # Give the reference dimensions of this aircraft. "s_ref" is the reference area # in meters squared, "b_ref" is the reference span in meters, and "c_ref" is the # reference chord in meters. I set these values to None, which is their default, # so that they will be populated by the first wing object's calculated # characteristics. Note that the reference area used in this program is the # wetted area of the wing's mean-camberline surface. - s_ref=None, - b_ref=None, - c_ref=None, - wings=[ - ps.geometry.Wing( - name="Main Wing", - # Define the location of the leading edge of the wing relative to the - # global coordinate system fixed front left corner of the first - # airplane's first wing's root wing cross section. These values all + s_ref=None, b_ref=None, c_ref=None, wings=[ps.geometry.Wing(name="Main Wing", + # Define the location of the leading edge of the wing relative to the + # global coordinate system fixed front left corner of the first + # airplane's first wing's root wing cross section. These values all + # default to 0.0 meters. + x_le=0.0, y_le=0.0, z_le=0.0, + # Declare that this wing is symmetric. This means that the geometry will + # be reflected across plane of this wing's root wing cross section. Note + # that the geometry coordinates are defined as such: If you were riding + # in the airplane, the positive x direction would point behind you, + # the positive y direction would point out of your right wing, and the + # positive z direction would point upwards, out of your chair. These + # directions form a right-handed coordinate system. The default value of + # "symmetric" is false. + symmetric=True, + # Define the number of chordwise panels on the wing, and the spacing + # between them. The number of chordwise panels defaults to 8 panels. The + # spacing defaults to "cosine", which makes the panels relatively finer, + # in the chordwise direction, near the leading and trailing edges. The + # other option is "uniform". + num_chordwise_panels=8, chordwise_spacing="cosine", + # Every wing has a list of wing cross sections. In order for the geometry + # output to be sensible, each wing must have at least two wing cross + # sections. + wing_cross_sections=[ps.geometry.WingCrossSection( + # Define the location of the leading edge of the wing cross + # section relative to the wing's leading edge. These values all # default to 0.0 meters. - x_le=0.0, - y_le=0.0, - z_le=0.0, - # Declare that this wing is symmetric. This means that the geometry will - # be reflected across plane of this wing's root wing cross section. Note - # that the geometry coordinates are defined as such: If you were riding - # in the airplane, the positive x direction would point behind you, - # the positive y direction would point out of your right wing, and the - # positive z direction would point upwards, out of your chair. These - # directions form a right-handed coordinate system. The default value of - # "symmetric" is false. - symmetric=True, - # Define the number of chordwise panels on the wing, and the spacing - # between them. The number of chordwise panels defaults to 8 panels. The - # spacing defaults to "cosine", which makes the panels relatively finer, - # in the chordwise direction, near the leading and trailing edges. The - # other option is "uniform". - num_chordwise_panels=8, - chordwise_spacing="cosine", - # Every wing has a list of wing cross sections. In order for the geometry - # output to be sensible, each wing must have at least two wing cross - # sections. - wing_cross_sections=[ - ps.geometry.WingCrossSection( - # Define the location of the leading edge of the wing cross - # section relative to the wing's leading edge. These values all - # default to 0.0 meters. - x_le=0.0, - y_le=0.0, - z_le=0.0, - # Define the twist of the wing cross section in degrees. This is - # equivalent to incidence angle of cross section. The twist is - # about the leading edge. Note that the twist is only stable up - # to 45.0 degrees. Values above that produce unexpected results. - # This will be fixed in a future release. The default value is - # 0.0 degrees. Positive twist corresponds to positive rotation - # about the y axis, as defined by the right-hand rule. - twist=0.0, - # Define the type of control surface. The options are "symmetric" - # and "asymmetric". This is only applicable if your wing is also - # symmetric. If so, symmetric control surfaces will deflect in - # the same direction, like flaps, while asymmetric control - # surfaces will deflect in opposite directions, like ailerons. - # The default value is "symmetric". - control_surface_type="asymmetric", - # Define the point on the airfoil where the control surface - # hinges. This is expressed as a faction of the chord length, - # back from the leading edge. The default value is 0.75. - control_surface_hinge_point=0.75, - # Define the deflection of the control surface in degrees. The - # default is 0.0 degrees. We'll set it to 10.0 degrees to show an - # example of an aileron deflection. - control_surface_deflection=10.0, - # Define the number of spanwise panels on the wing cross section, - # and the spacing between them. The number of spanwise panels - # defaults to 8 panels. The spacing defaults to "cosine", - # which makes the panels relatively finer, in the spanwise - # direction, near the cross section ends. The other option is - # "uniform". - num_spanwise_panels=8, - spanwise_spacing="cosine", - # Set the chord of this cross section to be 1.75 meters. This - # value defaults to 1.0 meter. - chord=1.5, - airfoil=ps.geometry.Airfoil( - # Give the airfoil a name. This defaults to "Untitled - # Airfoil". This name should correspond to a name in the - # airfoil directory or a NACA four series airfoil, unless you - # are passing in your own coordinates. - name="naca2412", - # If you wish to pass in coordinates, set this to a N x 2 - # array of the airfoil's coordinates, where N is the number - # of coordinates. Treat this as an immutable, don't edit - # directly after initialization. If you wish to load - # coordinates from the airfoil directory, leave this as None. - # The default is None. Make sure that any airfoil coordinates - # used range in x from 0 to 1. - coordinates=None, - # This is the variable that determines whether or not you - # would like to repanel the airfoil coordinates. This applies - # to coordinates passed in by the user or to the directory - # coordinates. It is highly recommended to set this to True. - # The default is True. - repanel=True, - # This is number of points to use if repaneling the airfoil. - # It is ignored if the repanel is False. The default is 400. - n_points_per_side=400, - ), - ), - # Define the next wing cross section. From here on out, - # the declarations will not be as commented as the previous. See the - # above comments if you have questions. - ps.geometry.WingCrossSection( - x_le=1.5, - y_le=6.0, - z_le=0.5, - chord=0.75, - control_surface_type="asymmetric", - control_surface_hinge_point=0.75, - control_surface_deflection=10.0, - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - ), - ], - ), + x_le=0.0, y_le=0.0, z_le=0.0, + # Define the twist of the wing cross section in degrees. This is + # equivalent to incidence angle of cross section. The twist is + # about the leading edge. Note that the twist is only stable up + # to 45.0 degrees. Values above that produce unexpected results. + # This will be fixed in a future release. The default value is + # 0.0 degrees. Positive twist corresponds to positive rotation + # about the y axis, as defined by the right-hand rule. + twist=0.0, # Define the type of control surface. The options are "symmetric" + # and "asymmetric". This is only applicable if your wing is also + # symmetric. If so, symmetric control surfaces will deflect in + # the same direction, like flaps, while asymmetric control + # surfaces will deflect in opposite directions, like ailerons. + # The default value is "symmetric". + control_surface_type="asymmetric", + # Define the point on the airfoil where the control surface + # hinges. This is expressed as a faction of the chord length, + # back from the leading edge. The default value is 0.75. + control_surface_hinge_point=0.75, + # Define the deflection of the control surface in degrees. The + # default is 0.0 degrees. We'll set it to 10.0 degrees to show an + # example of an aileron deflection. + control_surface_deflection=10.0, + # Define the number of spanwise panels on the wing cross section, + # and the spacing between them. The number of spanwise panels + # defaults to 8 panels. The spacing defaults to "cosine", + # which makes the panels relatively finer, in the spanwise + # direction, near the cross section ends. The other option is + # "uniform". + num_spanwise_panels=8, spanwise_spacing="cosine", + # Set the chord of this cross section to be 1.75 meters. This + # value defaults to 1.0 meter. + chord=1.5, airfoil=ps.geometry.Airfoil( + # Give the airfoil a name. This defaults to "Untitled + # Airfoil". This name should correspond to a name in the + # airfoil directory or a NACA four series airfoil, unless you + # are passing in your own coordinates. + name="naca2412", + # If you wish to pass in coordinates, set this to a N x 2 + # array of the airfoil's coordinates, where N is the number + # of coordinates. Treat this as an immutable, don't edit + # directly after initialization. If you wish to load + # coordinates from the airfoil directory, leave this as None. + # The default is None. Make sure that any airfoil coordinates + # used range in x from 0 to 1. + coordinates=None, + # This is the variable that determines whether or not you + # would like to repanel the airfoil coordinates. This applies + # to coordinates passed in by the user or to the directory + # coordinates. It is highly recommended to set this to True. + # The default is True. + repanel=True, + # This is number of points to use if repaneling the airfoil. + # It is ignored if the repanel is False. The default is 400. + n_points_per_side=400, ), ), + # Define the next wing cross section. From here on out, + # the declarations will not be as commented as the previous. See the + # above comments if you have questions. + ps.geometry.WingCrossSection(x_le=1.5, y_le=6.0, z_le=0.5, chord=0.75, + control_surface_type="asymmetric", control_surface_hinge_point=0.75, + control_surface_deflection=10.0, + airfoil=ps.geometry.Airfoil(name="naca2412", ), ), ], ), # Define the next wing. - ps.geometry.Wing( - name="Horizontal Stabilizer", - x_le=6.75, - z_le=0.25, - symmetric=True, - wing_cross_sections=[ - ps.geometry.WingCrossSection( - chord=1.5, - # Give the root wing cross section an airfoil. - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - twist=-5.0, - ), + ps.geometry.Wing(name="Horizontal Stabilizer", x_le=6.75, z_le=0.25, + symmetric=True, wing_cross_sections=[ps.geometry.WingCrossSection(chord=1.5, + # Give the root wing cross section an airfoil. + airfoil=ps.geometry.Airfoil(name="naca0012", ), twist=-5.0, ), # Define the wing's tip wing cross section. - ps.geometry.WingCrossSection( - x_le=0.5, - y_le=2.0, - chord=1.0, - twist=-5.0, + ps.geometry.WingCrossSection(x_le=0.5, y_le=2.0, chord=1.0, twist=-5.0, # Give the tip wing cross section an airfoil. - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ], - ), + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), ], ), # Define the next wing. - ps.geometry.Wing( - name="Vertical Stabilizer", - x_le=6.75, - z_le=0.5, - symmetric=False, - wing_cross_sections=[ - ps.geometry.WingCrossSection( - chord=1.5, + ps.geometry.Wing(name="Vertical Stabilizer", x_le=6.75, z_le=0.5, + symmetric=False, wing_cross_sections=[ + ps.geometry.WingCrossSection(chord=1.5, # Give the root wing cross section an airfoil. - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), # Define the wing's tip wing cross section. - ps.geometry.WingCrossSection( - x_le=0.5, - z_le=2.0, - chord=1.0, + ps.geometry.WingCrossSection(x_le=0.5, z_le=2.0, chord=1.0, # Give the tip wing cross section an airfoil. - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ], - ), - ], -) + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), ], ), ], ) # Define a new operating point object. This defines the state at which the airplane # object is operating. @@ -214,8 +149,7 @@ velocity=10.0, # Define the angle of attack the airplane is experiencing. This defaults to 5.0 # degrees. - alpha=1.0, -) + alpha=1.0, ) # Define a new steady problem. A steady problem contains an airplane object and an # operating point object. @@ -223,8 +157,7 @@ # Set this steady problem's list of airplane objects to be the one we just created. airplanes=[example_airplane], # Set this steady problem's operating point object ot be the one we just created. - operating_point=example_operating_point, -) + operating_point=example_operating_point, ) # Now, the airplane and operating point object exist within the steady problem # object. I like to delete the external pointers to these objects to ease debugging. @@ -234,10 +167,10 @@ # Define a new solver. The available solver objects are the steady horseshoe vortex # lattice method solver, the steady ring vortex lattice method solver, and the # unsteady ring vortex lattice method solver. -example_solver = ps.steady_ring_vortex_lattice_method.SteadyRingVortexLatticeMethodSolver( +example_solver = (ps.steady_ring_vortex_lattice_method +.SteadyRingVortexLatticeMethodSolver( # Solvers just take in one attribute: the problem they are going to solve. - steady_problem=example_problem -) + steady_problem=example_problem)) # Delete the extraneous pointer to the problem as it is now contained within the # solver. Again, this is unnecessary, I just like to do this to ease debugging. @@ -248,15 +181,13 @@ # This parameter determines the detail of information that the solver's logger # will output while running. The options are, in order of detail and severity, # "Debug", "Info", "Warning", "Error", "Critical". The default value is "Warning". - logging_level="Warning", -) + logging_level="Warning", ) # Call this function from the output module to print the results. ps.output.print_steady_results(steady_solver=example_solver) # Call the software's draw function on the solver. -ps.output.draw( - solver=example_solver, +ps.output.draw(solver=example_solver, # Tell the draw function to color the aircraft's wing panels with the local # lift coefficient. The valid arguments for this parameter are None, "induced drag", # "side force", or "lift". @@ -269,8 +200,7 @@ show_wake_vortices=False, # The the draw function to not save the drawing as an image file. This way, # the drawing will still be displayed but not saved. This value defaults to false. - save=False, -) + save=False, ) # Compare the output you see with the expected outputs saved in the "docs/examples # expected output" directory. diff --git a/pterasoftware/models/unsteady_ring_vortex_lattice_method_solver_static.py b/pterasoftware/models/unsteady_ring_vortex_lattice_method_solver_static.py index 4c38290b..39c5d655 100644 --- a/pterasoftware/models/unsteady_ring_vortex_lattice_method_solver_static.py +++ b/pterasoftware/models/unsteady_ring_vortex_lattice_method_solver_static.py @@ -18,31 +18,22 @@ # program is in SI units. Note: these values are relative to the global # coordinate system fixed front left corner of the first airplane's first wing's # root wing cross section. - x_ref=0.0, - y_ref=0.0, - z_ref=0.0, + x_ref=0.0, y_ref=0.0, z_ref=0.0, # Give the reference dimensions of this aircraft. "s_ref" is the reference area # in meters squared, "b_ref" is the reference span in meters, and "c_ref" is the # reference chord in meters. I set these values to None, which is their default, # so that they will be populated by the first wing object's calculated # characteristics. Note that the reference area used in this program is the # wetted area of the wing's mean-camberline surface. - s_ref=None, - b_ref=None, - c_ref=None, - # All airplane objects have a list of wings. - wings=[ - # Create the first wing object in this airplane. - ps.geometry.Wing( - # Give the wing a name, this defaults to "Untitled Wing". + s_ref=None, b_ref=None, c_ref=None, # All airplane objects have a list of wings. + wings=[# Create the first wing object in this airplane. + ps.geometry.Wing(# Give the wing a name, this defaults to "Untitled Wing". name="Main Wing", # Define the location of the leading edge of the wing relative to the # global coordinate system fixed front left corner of the first # airplane's first wing's root wing cross section. These values all # default to 0.0 meters. - x_le=0.0, - y_le=0.0, - z_le=0.0, + x_le=0.0, y_le=0.0, z_le=0.0, # Declare that this wing is symmetric. This means that the geometry will # be reflected across plane of this wing's root wing cross section. Note # that the geometry coordinates are defined as such: If you were riding @@ -58,20 +49,16 @@ # in the chordwise direction, near the leading and trailing edges. The # other option is "uniform". I set this value to "uniform" here as it # increase the accuracy of unsteady solvers. - num_chordwise_panels=6, - chordwise_spacing="uniform", + num_chordwise_panels=6, chordwise_spacing="uniform", # Every wing has a list of wing cross sections. In order for the geometry # output to be sensible, each wing must have at least two wing cross # sections. - wing_cross_sections=[ - # Create a new wing cross section object. + wing_cross_sections=[# Create a new wing cross section object. ps.geometry.WingCrossSection( # Define the location of the leading edge of the wing cross # section relative to the wing's leading edge. These values all # default to 0.0 meters. - x_le=0.0, - y_le=0.0, - z_le=0.0, + x_le=0.0, y_le=0.0, z_le=0.0, # Define the twist of the wing cross section in degrees. This is # equivalent to incidence angle of cross section. The twist is # about the leading edge. Note that the twist is only stable up @@ -100,12 +87,10 @@ # which makes the panels relatively finer, in the spanwise # direction, near the cross section ends. The other option is # "uniform". - num_spanwise_panels=8, - spanwise_spacing="cosine", + num_spanwise_panels=8, spanwise_spacing="cosine", # Set the chord of this cross section to be 1.75 meters. This # value defaults to 1.0 meter. - chord=1.75, - # Every wing cross section has an airfoil object. + chord=1.75, # Every wing cross section has an airfoil object. airfoil=ps.geometry.Airfoil( # Give the airfoil a name. This defaults to "Untitled # Airfoil". This name should correspond to a name in the @@ -128,59 +113,24 @@ repanel=True, # This is number of points to use if repaneling the airfoil. # It is ignored if the repanel is False. The default is 400. - n_points_per_side=400, - ), - ), + n_points_per_side=400, ), ), # Define the next wing cross section. From here on out, # the declarations will not be as commented as the previous. See the # above comments if you have questions. - ps.geometry.WingCrossSection( - x_le=0.75, - y_le=6.0, - z_le=1.0, - chord=1.5, - twist=5.0, - # Give this wing cross section an airfoil. - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - ), - ], - ), + ps.geometry.WingCrossSection(x_le=0.75, y_le=6.0, z_le=1.0, chord=1.5, + twist=5.0, # Give this wing cross section an airfoil. + airfoil=ps.geometry.Airfoil(name="naca2412", ), ), ], ), # Define the next wing. - ps.geometry.Wing( - name="V-Tail", - x_le=6.75, - z_le=0.25, - num_chordwise_panels=6, - chordwise_spacing="uniform", - symmetric=True, + ps.geometry.Wing(name="V-Tail", x_le=6.75, z_le=0.25, num_chordwise_panels=6, + chordwise_spacing="uniform", symmetric=True, # Define this wing's root wing cross section. - wing_cross_sections=[ - ps.geometry.WingCrossSection( - chord=1.5, - # Give the root wing cross section an airfoil. - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - twist=-5.0, - ), + wing_cross_sections=[ps.geometry.WingCrossSection(chord=1.5, + # Give the root wing cross section an airfoil. + airfoil=ps.geometry.Airfoil(name="naca0012", ), twist=-5.0, ), # Define the wing's tip wing cross section. - ps.geometry.WingCrossSection( - x_le=0.5, - y_le=2.0, - z_le=1.0, - chord=1.0, - twist=-5.0, - # Give the tip wing cross section an airfoil. - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ], - ), - ], -) + ps.geometry.WingCrossSection(x_le=0.5, y_le=2.0, z_le=1.0, chord=1.0, + twist=-5.0, # Give the tip wing cross section an airfoil. + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), ], ), ], ) # Now define the main wing's root wing cross section's movement. Cross sections can # move in three ways: sweeping, pitching, and heaving. Sweeping is defined as the @@ -220,39 +170,33 @@ heaving_period=0.0, # Define the time step spacing of the heaving. This is "sine" by default. The # options are "sine" and "uniform". - heaving_spacing="sine", -) + heaving_spacing="sine", ) # Define the main wing's tip wing cross section's movement. As the example has static # geometry, the movement attributes can be excluded, and the default values will # suffice. main_wing_tip_wing_cross_section_movement = ps.movement.WingCrossSectionMovement( - base_wing_cross_section=example_airplane.wings[0].wing_cross_sections[1], -) + base_wing_cross_section=example_airplane.wings[0].wing_cross_sections[1], ) # Define the v-tail's root wing cross section's movement. As the example has static # geometry, the movement attributes can be excluded, and the default values will # suffice. v_tail_root_wing_cross_section_movement = ps.movement.WingCrossSectionMovement( - base_wing_cross_section=example_airplane.wings[1].wing_cross_sections[0], -) + base_wing_cross_section=example_airplane.wings[1].wing_cross_sections[0], ) # Define the v-tail's tip wing cross section's movement. As the example has static # geometry, the movement attributes can be excluded, and the default values will # suffice. v_tail_tip_wing_cross_section_movement = ps.movement.WingCrossSectionMovement( - base_wing_cross_section=example_airplane.wings[1].wing_cross_sections[1], -) + base_wing_cross_section=example_airplane.wings[1].wing_cross_sections[1], ) # Now define the main wing's movement. In addition to their wing cross sections' # relative movements, wings' leading edge positions can move as well. main_wing_movement = ps.movement.WingMovement( # Define the base wing object. base_wing=example_airplane.wings[0], # Add the list of wing cross section movement objects. - wing_cross_sections_movements=[ - main_wing_root_wing_cross_section_movement, - main_wing_tip_wing_cross_section_movement, - ], + wing_cross_sections_movements=[main_wing_root_wing_cross_section_movement, + main_wing_tip_wing_cross_section_movement, ], # Define the amplitude of the leading edge's change in x position. This value is # in meters. This is set to 0.0 meters, which is the default value. x_le_amplitude=0.0, @@ -279,8 +223,7 @@ z_le_period=0.0, # Define the time step spacing of the leading edge's change in z position. This # is "sine" by default. The options are "sine" and "uniform". - z_le_spacing="sine", -) + z_le_spacing="sine", ) # Delete the extraneous wing cross section movement objects, as these are now # contained within the wing movement object. This is unnecessary, but it can make @@ -289,15 +232,11 @@ del main_wing_tip_wing_cross_section_movement # Make the v-tail's wing movement object. -v_tail_movement = ps.movement.WingMovement( - # Define the base wing object. +v_tail_movement = ps.movement.WingMovement(# Define the base wing object. base_wing=example_airplane.wings[1], # Add the list of wing cross section movement objects. - wing_cross_sections_movements=[ - v_tail_root_wing_cross_section_movement, - v_tail_tip_wing_cross_section_movement, - ], -) + wing_cross_sections_movements=[v_tail_root_wing_cross_section_movement, + v_tail_tip_wing_cross_section_movement, ], ) # Delete the extraneous wing cross section movement objects, as these are now # contained within the wing movement object. This is unnecessary, but it can make @@ -307,10 +246,8 @@ # Now define the airplane's movement object. In addition to their wing's and wing # cross sections' relative movements, airplane's reference positions can move as well. -airplane_movement = ps.movement.AirplaneMovement( - # Define the base airplane object. - base_airplane=example_airplane, - # Add the list of wing movement objects. +airplane_movement = ps.movement.AirplaneMovement(# Define the base airplane object. + base_airplane=example_airplane, # Add the list of wing movement objects. wing_movements=[main_wing_movement, v_tail_movement], # Define the amplitude of the reference position's change in x position. This # value is in meters. This is set to 0.0 meters, which is the default value. @@ -338,8 +275,7 @@ z_ref_period=0.0, # Define the time step spacing of the reference position's change in z position. # This is "sine" by default. The options are "sine" and "uniform". - z_ref_spacing="sine", -) + z_ref_spacing="sine", ) # Delete the extraneous wing movement objects, as these are now contained within the # airplane movement object. @@ -364,8 +300,7 @@ # Define the kinematic viscosity of the air in meters squared per second. This # defaults to 15.06e-6 meters squared per second, which corresponds to an air # temperature of 20 degrees Celsius. - nu=15.06e-6, -) + nu=15.06e-6, ) # Define the operating point's movement. The operating point's velocity can change # with respect to time. @@ -380,15 +315,12 @@ velocity_period=0.0, # Define the time step spacing of the velocity's change in time. This is "sine" # by default. The options are "sine" and "uniform". - velocity_spacing="sine", -) + velocity_spacing="sine", ) # Define the movement object. This contains the airplane movement and the operating # point movement. -movement = ps.movement.Movement( - # Add the airplane movement. - airplane_movements=[airplane_movement], - # Add the operating point movement. +movement = ps.movement.Movement(# Add the airplane movement. + airplane_movements=[airplane_movement], # Add the operating point movement. operating_point_movement=operating_point_movement, # Leave the number of time steps and the length of each time step unspecified. # The solver will automatically set the length of the time steps so that the wake @@ -398,9 +330,7 @@ # lengths back from the main wing. If the geometry isn't static, the number of # steps will be set such that three periods of the slowest movement oscillation # complete. - num_steps=None, - delta_time=None, -) + num_steps=None, delta_time=None, ) # Delete the extraneous airplane and operating point movement objects, as these are # now contained within the movement object. @@ -408,17 +338,15 @@ del operating_point_movement # Define the unsteady example problem. -example_problem = ps.problems.UnsteadyProblem( - movement=movement, -) +example_problem = ps.problems.UnsteadyProblem(movement=movement, ) # Define a new solver. The available solver objects are the steady horseshoe vortex # lattice method solver, the steady ring vortex lattice method solver, and the # unsteady ring vortex lattice method solver. -example_solver = ps.unsteady_ring_vortex_lattice_method.UnsteadyRingVortexLatticeMethodSolver( +example_solver = (ps.unsteady_ring_vortex_lattice_method +.UnsteadyRingVortexLatticeMethodSolver( # Solvers just take in one attribute: the problem they are going to solve. - unsteady_problem=example_problem, -) + unsteady_problem=example_problem, )) # Delete the extraneous pointer to the problem as it is now contained within the # solver. @@ -431,13 +359,11 @@ # "Debug", "Info", "Warning", "Error", "Critical". The default value is "Warning". logging_level="Warning", # Use a prescribed wake model. This is faster, but may be slightly less accurate. - prescribed_wake=True, -) + prescribed_wake=True, ) # Call the software's draw function on the solver. Press "q" to close the plotter # after it draws the output. -ps.output.draw( - # Set the solver to the one we just ran. +ps.output.draw(# Set the solver to the one we just ran. solver=example_solver, # Tell the draw function to color the aircraft's wing panels with the local lift # coefficient. The valid arguments for this parameter are None, "induced drag", @@ -451,14 +377,12 @@ show_wake_vortices=False, # The the draw function to not save the drawing as an image file. This way, # the drawing will still be displayed but not saved. This value defaults to false. - save=False, -) + save=False, ) # Call the software's animate function on the solver. This produces a GIF of the wake # being shed. The GIF is saved in the same directory as this script. Press "q", # after orienting the view, to begin the animation. -ps.output.animate( - # Set the unsteady solver to the one we just ran. +ps.output.animate(# Set the unsteady solver to the one we just ran. unsteady_solver=example_solver, # Tell the animate function to color the aircraft's wing panels with the local # lift coefficient. The valid arguments for this parameter are None, "induced drag", @@ -470,22 +394,18 @@ # The the animate function to not save the animation as file. This way, # the animation will still be displayed but not saved. This value defaults to # false. - save=False, -) + save=False, ) # Call the software's plotting function on the solver. This produces graphs of the # output forces and moments with respect to time. -ps.output.plot_results_versus_time( - # Set the unsteady solver to the one we just ran. +ps.output.plot_results_versus_time(# Set the unsteady solver to the one we just ran. unsteady_solver=example_solver, # Set the show attribute to True, which is the default value. With this set to # show, some IDEs (such as PyCharm in "Scientific Mode") will display the plots # in a sidebar. Other IDEs may not display the plots, in which case you should # set the save attribute to True, and open the files after they've been saved to # the current directory. - show=True, - save=False, -) + show=True, save=False, ) # Compare the output you see with the expected outputs saved in the "docs/examples # expected output" directory. diff --git a/pterasoftware/models/unsteady_ring_vortex_lattice_method_solver_variable.py b/pterasoftware/models/unsteady_ring_vortex_lattice_method_solver_variable.py index ba91eda7..7bcba6e4 100644 --- a/pterasoftware/models/unsteady_ring_vortex_lattice_method_solver_variable.py +++ b/pterasoftware/models/unsteady_ring_vortex_lattice_method_solver_variable.py @@ -18,30 +18,22 @@ # program is in SI units. Note: these values are relative to the global # coordinate system fixed front left corner of the first airplane's first wing's # root wing cross section. - x_ref=0.0, - y_ref=0.0, - z_ref=0.0, + x_ref=0.0, y_ref=0.0, z_ref=0.0, # Give the reference dimensions of this aircraft. "s_ref" is the reference area # in meters squared, "b_ref" is the reference span in meters, and "c_ref" is the # reference chord in meters. I set these values to None, which is their default, # so that they will be populated by the first wing object's calculated # characteristics. Note that the reference area used in this program is the # wetted area of the wing's mean-camberline surface. - s_ref=None, - b_ref=None, - c_ref=None, - # All airplane objects have a list of wings. - wings=[ - # Create the first wing object in this airplane. + s_ref=None, b_ref=None, c_ref=None, # All airplane objects have a list of wings. + wings=[# Create the first wing object in this airplane. ps.geometry.Wing( # Give the wing a name, this defaults to "Untitled Wing". name="Main Wing", # Define the location of the leading edge of the wing relative to the # global coordinate system fixed front left corner of the first # airplane's first wing's root wing cross section. These values all # default to 0.0 meters. - x_le=0.0, - y_le=0.0, - z_le=0.0, + x_le=0.0, y_le=0.0, z_le=0.0, # Declare that this wing is symmetric. This means that the geometry will # be reflected across plane of this wing's root wing cross section. Note # that the geometry coordinates are defined as such: If you were riding @@ -57,20 +49,16 @@ # in the chordwise direction, near the leading and trailing edges. The # other option is "uniform". I set this value to "uniform" here as it # increase the accuracy of unsteady solvers. - num_chordwise_panels=6, - chordwise_spacing="uniform", + num_chordwise_panels=6, chordwise_spacing="uniform", # Every wing has a list of wing cross sections. In order for the geometry # output to be sensible, each wing must have at least two wing cross # sections. - wing_cross_sections=[ - # Create a new wing cross section object. + wing_cross_sections=[# Create a new wing cross section object. ps.geometry.WingCrossSection( # Define the location of the leading edge of the wing cross # section relative to the wing's leading edge. These values all # default to 0.0 meters. - x_le=0.0, - y_le=0.0, - z_le=0.0, + x_le=0.0, y_le=0.0, z_le=0.0, # Define the twist of the wing cross section in degrees. This is # equivalent to incidence angle of cross section. The twist is # about the leading edge. Note that the twist is only stable up @@ -99,12 +87,10 @@ # which makes the panels relatively finer, in the spanwise # direction, near the cross section ends. The other option is # "uniform". - num_spanwise_panels=8, - spanwise_spacing="cosine", + num_spanwise_panels=8, spanwise_spacing="cosine", # Set the chord of this cross section to be 1.75 meters. This # value defaults to 1.0 meter. - chord=1.75, - # Every wing cross section has an airfoil object. + chord=1.75, # Every wing cross section has an airfoil object. airfoil=ps.geometry.Airfoil( # Give the airfoil a name. This defaults to "Untitled # Airfoil". This name should correspond to a name in the @@ -127,59 +113,24 @@ repanel=True, # This is number of points to use if repaneling the airfoil. # It is ignored if the repanel is False. The default is 400. - n_points_per_side=400, - ), - ), + n_points_per_side=400, ), ), # Define the next wing cross section. From here on out, # the declarations will not be as commented as the previous. See the # above comments if you have questions. - ps.geometry.WingCrossSection( - x_le=0.75, - y_le=6.0, - z_le=1.0, - chord=1.5, - twist=5.0, - # Give this wing cross section an airfoil. - airfoil=ps.geometry.Airfoil( - name="naca2412", - ), - ), - ], - ), + ps.geometry.WingCrossSection(x_le=0.75, y_le=6.0, z_le=1.0, chord=1.5, + twist=5.0, # Give this wing cross section an airfoil. + airfoil=ps.geometry.Airfoil(name="naca2412", ), ), ], ), # Define the next wing. - ps.geometry.Wing( - name="V-Tail", - x_le=6.75, - z_le=0.25, - num_chordwise_panels=6, - chordwise_spacing="uniform", - symmetric=True, + ps.geometry.Wing(name="V-Tail", x_le=6.75, z_le=0.25, num_chordwise_panels=6, + chordwise_spacing="uniform", symmetric=True, # Define this wing's root wing cross section. - wing_cross_sections=[ - ps.geometry.WingCrossSection( - chord=1.5, - # Give the root wing cross section an airfoil. - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - twist=-5.0, - ), + wing_cross_sections=[ps.geometry.WingCrossSection(chord=1.5, + # Give the root wing cross section an airfoil. + airfoil=ps.geometry.Airfoil(name="naca0012", ), twist=-5.0, ), # Define the wing's tip wing cross section. - ps.geometry.WingCrossSection( - x_le=0.5, - y_le=2.0, - z_le=1.0, - chord=1.0, - twist=-5.0, - # Give the tip wing cross section an airfoil. - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ], - ), - ], -) + ps.geometry.WingCrossSection(x_le=0.5, y_le=2.0, z_le=1.0, chord=1.0, + twist=-5.0, # Give the tip wing cross section an airfoil. + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), ], ), ], ) # Now define the main wing's root wing cross section's movement. Cross sections can # move in three ways: sweeping, pitching, and heaving. Sweeping is defined as the @@ -219,44 +170,32 @@ heaving_period=0.0, # Define the time step spacing of the heaving. This is "sine" by default. The # options are "sine" and "uniform". - heaving_spacing="sine", -) + heaving_spacing="sine", ) # Define the main wing's tip wing cross section's movement. main_wing_tip_wing_cross_section_movement = ps.movement.WingCrossSectionMovement( base_wing_cross_section=example_airplane.wings[0].wing_cross_sections[1], - sweeping_amplitude=30.0, - sweeping_period=1.0, - sweeping_spacing="sine", - pitching_amplitude=15.0, - pitching_period=1.0, - pitching_spacing="sine", - heaving_amplitude=0.0, - heaving_period=0.0, - heaving_spacing="sine", -) + sweeping_amplitude=30.0, sweeping_period=1.0, sweeping_spacing="sine", + pitching_amplitude=15.0, pitching_period=1.0, pitching_spacing="sine", + heaving_amplitude=0.0, heaving_period=0.0, heaving_spacing="sine", ) # Define the v-tail's root wing cross section's movement. This wing will be static, # so the movement attributes can be excluded, and the default values will suffice. v_tail_root_wing_cross_section_movement = ps.movement.WingCrossSectionMovement( - base_wing_cross_section=example_airplane.wings[1].wing_cross_sections[0], -) + base_wing_cross_section=example_airplane.wings[1].wing_cross_sections[0], ) # Define the v-tail's root wing cross section's movement. This wing will be static, # so the movement attributes can be excluded, and the default values will suffice. v_tail_tip_wing_cross_section_movement = ps.movement.WingCrossSectionMovement( - base_wing_cross_section=example_airplane.wings[1].wing_cross_sections[1], -) + base_wing_cross_section=example_airplane.wings[1].wing_cross_sections[1], ) # Now define the main wing's movement. In addition to their wing cross sections' # relative movements, wings' leading edge positions can move as well. main_wing_movement = ps.movement.WingMovement( # Define the base wing object. base_wing=example_airplane.wings[0], # Add the list of wing cross section movement objects. - wing_cross_sections_movements=[ - main_wing_root_wing_cross_section_movement, - main_wing_tip_wing_cross_section_movement, - ], + wing_cross_sections_movements=[main_wing_root_wing_cross_section_movement, + main_wing_tip_wing_cross_section_movement, ], # Define the amplitude of the leading edge's change in x position. This value is # in meters. This is set to 0.0 meters, which is the default value. x_le_amplitude=0.0, @@ -283,8 +222,7 @@ z_le_period=0.0, # Define the time step spacing of the leading edge's change in z position. This # is "sine" by default. The options are "sine" and "uniform". - z_le_spacing="sine", -) + z_le_spacing="sine", ) # Delete the extraneous wing cross section movement objects, as these are now # contained within the wing movement object. This is unnecessary, but it can make @@ -293,15 +231,11 @@ del main_wing_tip_wing_cross_section_movement # Make the v-tail's wing movement object. -v_tail_movement = ps.movement.WingMovement( - # Define the base wing object. +v_tail_movement = ps.movement.WingMovement(# Define the base wing object. base_wing=example_airplane.wings[1], # Add the list of wing cross section movement objects. - wing_cross_sections_movements=[ - v_tail_root_wing_cross_section_movement, - v_tail_tip_wing_cross_section_movement, - ], -) + wing_cross_sections_movements=[v_tail_root_wing_cross_section_movement, + v_tail_tip_wing_cross_section_movement, ], ) # Delete the extraneous wing cross section movement objects, as these are now # contained within the wing movement object. This is unnecessary, but it can make @@ -311,10 +245,8 @@ # Now define the airplane's movement object. In addition to their wing's and wing # cross sections' relative movements, airplane's reference positions can move as well. -airplane_movement = ps.movement.AirplaneMovement( - # Define the base airplane object. - base_airplane=example_airplane, - # Add the list of wing movement objects. +airplane_movement = ps.movement.AirplaneMovement(# Define the base airplane object. + base_airplane=example_airplane, # Add the list of wing movement objects. wing_movements=[main_wing_movement, v_tail_movement], # Define the amplitude of the reference position's change in x position. This # value is in meters. This is set to 0.0 meters, which is the default value. @@ -342,8 +274,7 @@ z_ref_period=0.0, # Define the time step spacing of the reference position's change in z position. # This is "sine" by default. The options are "sine" and "uniform". - z_ref_spacing="sine", -) + z_ref_spacing="sine", ) # Delete the extraneous wing movement objects, as these are now contained within the # airplane movement object. @@ -368,8 +299,7 @@ # Define the kinematic viscosity of the air in meters squared per second. This # defaults to 15.06e-6 meters squared per second, which corresponds to an air # temperature of 20 degrees Celsius. - nu=15.06e-6, -) + nu=15.06e-6, ) # Define the operating point's movement. The operating point's velocity can change # with respect to time. @@ -384,15 +314,12 @@ velocity_period=0.0, # Define the time step spacing of the velocity's change in time. This is "sine" # by default. The options are "sine" and "uniform". - velocity_spacing="sine", -) + velocity_spacing="sine", ) # Define the movement object. This contains the airplane movement and the operating # point movement. -movement = ps.movement.Movement( - # Add the airplane movement. - airplane_movements=[airplane_movement], - # Add the operating point movement. +movement = ps.movement.Movement(# Add the airplane movement. + airplane_movements=[airplane_movement], # Add the operating point movement. operating_point_movement=operating_point_movement, # Leave the number of time steps and the length of each time step unspecified. # The solver will automatically set the length of the time steps so that the wake @@ -402,9 +329,7 @@ # lengths back from the main wing. If the geometry isn't static, the number of # steps will be set such that three periods of the slowest movement oscillation # complete. - num_steps=None, - delta_time=None, -) + num_steps=None, delta_time=None, ) # Delete the extraneous airplane and operating point movement objects, as these are # now contained within the movement object. @@ -412,17 +337,15 @@ del operating_point_movement # Define the unsteady example problem. -example_problem = ps.problems.UnsteadyProblem( - movement=movement, -) +example_problem = ps.problems.UnsteadyProblem(movement=movement, ) # Define a new solver. The available solver objects are the steady horseshoe vortex # lattice method solver, the steady ring vortex lattice method solver, and the # unsteady ring vortex lattice method solver. -example_solver = ps.unsteady_ring_vortex_lattice_method.UnsteadyRingVortexLatticeMethodSolver( +example_solver = (ps.unsteady_ring_vortex_lattice_method +.UnsteadyRingVortexLatticeMethodSolver( # Solvers just take in one attribute: the problem they are going to solve. - unsteady_problem=example_problem, -) + unsteady_problem=example_problem, )) # Delete the extraneous pointer to the problem as it is now contained within the # solver. @@ -435,14 +358,12 @@ # "Debug", "Info", "Warning", "Error", "Critical". The default value is "Warning". logging_level="Warning", # Use a prescribed wake model. This is faster, but may be slightly less accurate. - prescribed_wake=True, -) + prescribed_wake=True, ) # Call the software's animate function on the solver. This produces a GIF of the wake # being shed. The GIF is saved in the same directory as this script. Press "q", # after orienting the view, to begin the animation. -ps.output.animate( - # Set the unsteady solver to the one we just ran. +ps.output.animate(# Set the unsteady solver to the one we just ran. unsteady_solver=example_solver, # Tell the animate function to color the aircraft's wing panels with the local # lift coefficient. The valid arguments for this parameter are None, "induced drag", @@ -454,8 +375,7 @@ # The the animate function to not save the animation as file. This way, # the animation will still be displayed but not saved. This value defaults to # false. - save=False, -) + save=False, ) # Compare the output you see with the expected outputs saved in the "docs/examples # expected output" directory. diff --git a/pterasoftware/models/unsteady_ring_vortex_lattice_method_solver_variable_formation.py b/pterasoftware/models/unsteady_ring_vortex_lattice_method_solver_variable_formation.py index 30bf280f..5a0639da 100644 --- a/pterasoftware/models/unsteady_ring_vortex_lattice_method_solver_variable_formation.py +++ b/pterasoftware/models/unsteady_ring_vortex_lattice_method_solver_variable_formation.py @@ -16,250 +16,138 @@ def runSolver(self): y_spacing = 13 # Create the lead airplane object. - lead_airplane = ps.geometry.Airplane( - name="Lead Airplane", + lead_airplane = ps.geometry.Airplane(name="Lead Airplane", # Specify the location of the lead airplane's center of gravity. This is the - # point around about which the solver will calculate the moments on the airplane. + # point around about which the solver will calculate the moments on the + # airplane. # These three values default to 0.0 meters. Note: these values are relative to # the global coordinate system fixed front left corner of the first airplane's # first wing's root wing cross section. - x_ref=0.0, - y_ref=0.0, - z_ref=0.0, - wings=[ - ps.geometry.Wing( - name="Main Wing", - # Define the location of the leading edge of the wing relative to the - # global coordinate system fixed front left corner of the first - # airplane's first wing's root wing cross section. - x_le=0.0, - y_le=0.0, - # Declare that this wing is symmetric. This means that the geometry will - # be reflected across plane of this wing's root wing cross section. Note - # that the geometry coordinates are defined as such: If you were riding - # in the airplane, the positive x direction would point behind you, - # the positive y direction would point out of your right wing, and the - # positive z direction would point upwards, out of your chair. These - # directions form a right-handed coordinate system. The default value of - # "symmetric" is false. - symmetric=True, - # Define the chordwise spacing of the wing panels to be "uniform" as this - # increase the accuracy of unsteady solvers. - chordwise_spacing="uniform", - num_chordwise_panels=4, - wing_cross_sections=[ - ps.geometry.WingCrossSection( - # Define the location of the leading edge of the wing cross - # section relative to the wing's leading edge. These values all - # default to 0.0 meters. - x_le=0.0, - y_le=0.0, - # Assign the twist of this wing cross section. Note: when - # assigning angles of attack to multiple airplanes, it is better - # to set the operating point's angle of attack to zero, and then - # use offset the twist values of all the wing cross sections to - # simulate each aircraft having an angle of attack. - twist=5.0, - chord=1.75, - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ps.geometry.WingCrossSection( - x_le=0.75, - y_le=6.0, - chord=1.5, - twist=5.0, - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ], - ), - ], - ) + x_ref=0.0, y_ref=0.0, z_ref=0.0, wings=[ps.geometry.Wing(name="Main Wing", + # Define the location of the leading edge of the wing relative to the + # global coordinate system fixed front left corner of the first + # airplane's first wing's root wing cross section. + x_le=0.0, y_le=0.0, + # Declare that this wing is symmetric. This means that the geometry will + # be reflected across plane of this wing's root wing cross section. Note + # that the geometry coordinates are defined as such: If you were riding + # in the airplane, the positive x direction would point behind you, + # the positive y direction would point out of your right wing, and the + # positive z direction would point upwards, out of your chair. These + # directions form a right-handed coordinate system. The default value of + # "symmetric" is false. + symmetric=True, + # Define the chordwise spacing of the wing panels to be "uniform" as this + # increase the accuracy of unsteady solvers. + chordwise_spacing="uniform", num_chordwise_panels=4, + wing_cross_sections=[ps.geometry.WingCrossSection( + # Define the location of the leading edge of the wing cross + # section relative to the wing's leading edge. These values all + # default to 0.0 meters. + x_le=0.0, y_le=0.0, + # Assign the twist of this wing cross section. Note: when + # assigning angles of attack to multiple airplanes, it is better + # to set the operating point's angle of attack to zero, and then + # use offset the twist values of all the wing cross sections to + # simulate each aircraft having an angle of attack. + twist=5.0, chord=1.75, + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), + ps.geometry.WingCrossSection(x_le=0.75, y_le=6.0, chord=1.5, + twist=5.0, + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), ], ), ], ) # Now define the lead airplane's movement object. lead_airplane_movement = ps.movement.AirplaneMovement( base_airplane=lead_airplane, - wing_movements=[ - # Define the main wing's movement. - ps.movement.WingMovement( - base_wing=lead_airplane.wings[0], + wing_movements=[# Define the main wing's movement. + ps.movement.WingMovement(base_wing=lead_airplane.wings[0], # Add the list of wing cross section movement objects. wing_cross_sections_movements=[ # Define the root wing cross section's movement object. - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=lead_airplane.wings[ - 0 - ].wing_cross_sections[0], - ), + ps.movement.WingCrossSectionMovement(base_wing_cross_section= + lead_airplane.wings[0].wing_cross_sections[0], ), # Define the tip wing cross section's movement object. - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=lead_airplane.wings[ - 0 - ].wing_cross_sections[1], - sweeping_amplitude=15.0, - sweeping_period=1.5, - sweeping_spacing="sine", - ), - ], - ), - ], - ) + ps.movement.WingCrossSectionMovement(base_wing_cross_section= + lead_airplane.wings[0].wing_cross_sections[1], + sweeping_amplitude=15.0, sweeping_period=1.5, + sweeping_spacing="sine", ), ], ), ], ) # Create the trailing right airplane object. - right_airplane = ps.geometry.Airplane( - name="Right Airplane", + right_airplane = ps.geometry.Airplane(name="Right Airplane", # Specify the location of the right airplane's center of gravity. This is the # point around about which the solver will calculate the moments on the airplane. # These three values default to 0.0 meters. Note: these values are relative to # the global coordinate system fixed front left corner of the first airplane's # first wing's root wing cross section. - x_ref=x_spacing, - y_ref=y_spacing, - z_ref=0.0, - wings=[ - ps.geometry.Wing( - name="Main Wing", + x_ref=x_spacing, y_ref=y_spacing, z_ref=0.0, wings=[ + ps.geometry.Wing(name="Main Wing", # Define the location of the leading edge of the wing relative to the # global coordinate system fixed front left corner of the first # airplane's first wing's root wing cross section. - x_le=x_spacing, - y_le=y_spacing, - symmetric=True, - chordwise_spacing="uniform", - num_chordwise_panels=4, + x_le=x_spacing, y_le=y_spacing, symmetric=True, + chordwise_spacing="uniform", num_chordwise_panels=4, wing_cross_sections=[ - ps.geometry.WingCrossSection( - twist=5.0, - chord=1.75, - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ps.geometry.WingCrossSection( - x_le=0.75, - y_le=6.0, - chord=1.5, - twist=5.0, - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ], - ), - ], - ) + ps.geometry.WingCrossSection(twist=5.0, chord=1.75, + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), + ps.geometry.WingCrossSection(x_le=0.75, y_le=6.0, chord=1.5, + twist=5.0, airfoil=ps.geometry.Airfoil( + name="naca0012", ), ), ], ), ], ) # Now define the trailing right airplane's movement object. right_airplane_movement = ps.movement.AirplaneMovement( - base_airplane=right_airplane, - wing_movements=[ - ps.movement.WingMovement( - base_wing=right_airplane.wings[0], - wing_cross_sections_movements=[ - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=right_airplane.wings[ - 0 - ].wing_cross_sections[0], - ), - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=right_airplane.wings[ - 0 - ].wing_cross_sections[1], - sweeping_amplitude=15.0, - sweeping_period=1.5, - sweeping_spacing="sine", - ), - ], - ), - ], - ) + base_airplane=right_airplane, wing_movements=[ + ps.movement.WingMovement(base_wing=right_airplane.wings[0], + wing_cross_sections_movements=[ps.movement.WingCrossSectionMovement( + base_wing_cross_section= + right_airplane.wings[0].wing_cross_sections[0], ), + ps.movement.WingCrossSectionMovement(base_wing_cross_section= + right_airplane.wings[0].wing_cross_sections[1], + sweeping_amplitude=15.0, sweeping_period=1.5, + sweeping_spacing="sine", ), ], ), ], ) # Create the trailing left airplane object. - left_airplane = ps.geometry.Airplane( - name="Left Airplane", + left_airplane = ps.geometry.Airplane(name="Left Airplane", # Specify the location of the left airplane's center of gravity. This is the # point around about which the solver will calculate the moments on the airplane. # These three values default to 0.0 meters. Note: these values are relative to # the global coordinate system fixed front left corner of the first airplane's # first wing's root wing cross section. - x_ref=x_spacing, - y_ref=-y_spacing, - z_ref=0.0, - wings=[ - ps.geometry.Wing( - name="Main Wing", + x_ref=x_spacing, y_ref=-y_spacing, z_ref=0.0, wings=[ + ps.geometry.Wing(name="Main Wing", # Define the location of the leading edge of the wing relative to the # global coordinate system fixed front left corner of the first # airplane's first wing's root wing cross section. - x_le=x_spacing, - y_le=-y_spacing, - symmetric=True, - chordwise_spacing="uniform", - num_chordwise_panels=4, + x_le=x_spacing, y_le=-y_spacing, symmetric=True, + chordwise_spacing="uniform", num_chordwise_panels=4, wing_cross_sections=[ - ps.geometry.WingCrossSection( - twist=5.0, - chord=1.75, - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ps.geometry.WingCrossSection( - x_le=0.75, - y_le=6.0, - chord=1.5, - twist=5.0, - airfoil=ps.geometry.Airfoil( - name="naca0012", - ), - ), - ], - ), - ], - ) + ps.geometry.WingCrossSection(twist=5.0, chord=1.75, + airfoil=ps.geometry.Airfoil(name="naca0012", ), ), + ps.geometry.WingCrossSection(x_le=0.75, y_le=6.0, chord=1.5, + twist=5.0, airfoil=ps.geometry.Airfoil( + name="naca0012", ), ), ], ), ], ) # Now define the trailing left airplane's movement object. left_airplane_movement = ps.movement.AirplaneMovement( - base_airplane=left_airplane, - wing_movements=[ - ps.movement.WingMovement( - base_wing=left_airplane.wings[0], - wing_cross_sections_movements=[ - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=left_airplane.wings[ - 0 - ].wing_cross_sections[0], - ), - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=left_airplane.wings[ - 0 - ].wing_cross_sections[1], - sweeping_amplitude=15.0, - sweeping_period=1.5, - sweeping_spacing="sine", - ), - ], - ), - ], - ) + base_airplane=left_airplane, wing_movements=[ + ps.movement.WingMovement(base_wing=left_airplane.wings[0], + wing_cross_sections_movements=[ps.movement.WingCrossSectionMovement( + base_wing_cross_section= + left_airplane.wings[0].wing_cross_sections[0], ), + ps.movement.WingCrossSectionMovement(base_wing_cross_section= + left_airplane.wings[0].wing_cross_sections[1], + sweeping_amplitude=15.0, sweeping_period=1.5, + sweeping_spacing="sine", ), ], ), ], ) # Define a new operating point object. This defines the state at which all the # airplanes objects are operating. Note: when assigning angles of attack to multiple # airplanes, it is better to set the operating point's angle of attack to zero, # and then use offset the twist values of all the wing cross sections to simulate # each aircraft having an angle of attack. - operating_point = ps.operating_point.OperatingPoint( - velocity=10.0, - alpha=0.0, - ) + operating_point = ps.operating_point.OperatingPoint(velocity=10.0, alpha=0.0, ) # Define the operating point's movement. operating_point_movement = ps.movement.OperatingPointMovement( - base_operating_point=operating_point, - ) + base_operating_point=operating_point, ) # Delete the extraneous airplane and operating point objects, as these are now # contained within their respective movement objects. @@ -271,14 +159,9 @@ def runSolver(self): # Define the movement object. This contains each airplane's movement and the operating # point movement. movement = ps.movement.Movement( - airplane_movements=[ - lead_airplane_movement, - right_airplane_movement, - left_airplane_movement, - ], - operating_point_movement=operating_point_movement, - num_cycles=2, - ) + airplane_movements=[lead_airplane_movement, right_airplane_movement, + left_airplane_movement, ], + operating_point_movement=operating_point_movement, num_cycles=2, ) # Delete the extraneous airplane and operating point movement objects, as these are # now contained within the movement object. @@ -288,39 +171,30 @@ def runSolver(self): del operating_point_movement # Define the unsteady example problem. - problem = ps.problems.UnsteadyProblem( - movement=movement, - ) + problem = ps.problems.UnsteadyProblem(movement=movement, ) # Define a new solver. The available solver objects are the steady horseshoe vortex # lattice method solver, the steady ring vortex lattice method solver, and the # unsteady ring vortex lattice method solver. solver = ps.unsteady_ring_vortex_lattice_method.UnsteadyRingVortexLatticeMethodSolver( # Solvers just take in one attribute: the problem they are going to solve. - unsteady_problem=problem, - ) + unsteady_problem=problem, ) # Delete the extraneous pointer to the problem as it is now contained within the # solver. del problem # Run the example solver. - solver.run( - prescribed_wake=False, - ) + solver.run(prescribed_wake=False, ) # Call the software's animate function on the solver. This produces a GIF of the wake # being shed. The GIF is saved in the same directory as this script. Press "q", # after orienting the view, to begin the animation. - ps.output.animate( - unsteady_solver=solver, - scalar_type="lift", + ps.output.animate(unsteady_solver=solver, scalar_type="lift", show_wake_vortices=True, # The the animate function to not save the animation as file. This way, # the animation will still be displayed but not saved. This value defaults to # false. - save=False, - ) + save=False, ) - # Compare the output you see with the expected outputs saved in the "docs/examples - # expected output" directory. + # Compare the output you see with the expected outputs saved in the "docs/examples # expected output" directory. diff --git a/pterasoftware/movement.py b/pterasoftware/movement.py index 8ba9b6e6..d0043939 100644 --- a/pterasoftware/movement.py +++ b/pterasoftware/movement.py @@ -57,15 +57,8 @@ class Movement: This class is not meant to be subclassed. """ - def __init__( - self, - airplane_movements, - operating_point_movement, - num_steps=None, - num_cycles=None, - num_chords=None, - delta_time=None, - ): + def __init__(self, airplane_movements, operating_point_movement, num_steps=None, + num_cycles=None, num_chords=None, delta_time=None, ): """This is the initialization method. :param airplane_movements: list of AirplaneMovement objects @@ -112,8 +105,7 @@ def __init__( if num_steps is not None or self.get_max_period() == 0: raise Exception( "Only specify the number of cycles if you haven't specified the " - "number of steps and the movement isn't static!" - ) + "number of steps and the movement isn't static!") self.num_cycles = num_cycles else: self.num_cycles = None @@ -124,8 +116,7 @@ def __init__( if num_steps is not None or self.get_max_period() != 0: raise Exception( "Only specify the number of chords if you haven't specified the " - "number of steps and the movement is static!" - ) + "number of steps and the movement is static!") self.num_chords = num_chords else: self.num_chords = None @@ -135,17 +126,15 @@ def __init__( if delta_time is None: delta_times = [] for airplane_movement in self.airplane_movements: - # For a given airplane object, the ideal time step length is that # which sheds ring vortices off the main wing that have roughly the # same chord length as the panels on the main wing. This is based on # the base airplane's reference chord length, its main wing's number # of chordwise panels, and its base operating point's velocity. - delta_times.append( - airplane_movement.base_airplane.c_ref - / airplane_movement.base_airplane.wings[0].num_chordwise_panels - / operating_point_movement.base_operating_point.velocity - ) + delta_times.append(airplane_movement.base_airplane.c_ref / + airplane_movement.base_airplane.wings[ + 0].num_chordwise_panels / + operating_point_movement.base_operating_point.velocity) # Set the delta time to be the average of the airplanes' ideal delta times. delta_time = sum(delta_times) / len(delta_times) @@ -173,9 +162,7 @@ def __init__( # that the wake extends back by some number of reference chord lengths. wake_length = self.num_chords * max_c_ref panel_length = ( - delta_time - * self.operating_point_movement.base_operating_point.velocity - ) + delta_time * self.operating_point_movement.base_operating_point.velocity) num_steps = math.ceil(wake_length / panel_length) else: @@ -197,16 +184,13 @@ def __init__( self.airplanes = [] for airplane_movement in self.airplane_movements: self.airplanes.append( - airplane_movement.generate_airplanes( - num_steps=self.num_steps, delta_time=self.delta_time - ) - ) + airplane_movement.generate_airplanes(num_steps=self.num_steps, + delta_time=self.delta_time)) # Generate a lists of operating point objects that are the steps through the # movement of this problem's operating point. self.operating_points = operating_point_movement.generate_operating_points( - num_steps=self.num_steps, delta_time=self.delta_time - ) + num_steps=self.num_steps, delta_time=self.delta_time) def get_max_period(self): """This method returns the longest period of this movement object's sub- @@ -224,10 +208,8 @@ def get_max_period(self): # The global max period is the maximum of the max airplane period and the max # operating point period. - return max( - max_airplane_period, - self.operating_point_movement.get_max_period(), - ) + return max(max_airplane_period, + self.operating_point_movement.get_max_period(), ) class AirplaneMovement: @@ -247,20 +229,10 @@ class AirplaneMovement: This class is not meant to be subclassed. """ - def __init__( - self, - base_airplane, - wing_movements, - x_ref_amplitude=0.0, - x_ref_period=0.0, - x_ref_spacing="sine", - y_ref_amplitude=0.0, - y_ref_period=0.0, - y_ref_spacing="sine", - z_ref_amplitude=0.0, - z_ref_period=0.0, - z_ref_spacing="sine", - ): + def __init__(self, base_airplane, wing_movements, x_ref_amplitude=0.0, + x_ref_period=0.0, x_ref_spacing="sine", y_ref_amplitude=0.0, + y_ref_period=0.0, y_ref_spacing="sine", z_ref_amplitude=0.0, + z_ref_period=0.0, z_ref_spacing="sine", ): """This is the initialization method. :param base_airplane: Airplane @@ -334,23 +306,15 @@ def generate_airplanes(self, num_steps=10, delta_time=0.1): if self.x_ref_spacing == "sine": # Create an array of points with a sinusoidal spacing. - x_ref_list = oscillating_sinspace( - amplitude=self.x_ref_amplitude, - period=self.x_ref_period, - base_value=self.x_ref_base, - num_steps=num_steps, - delta_time=delta_time, - ) + x_ref_list = oscillating_sinspace(amplitude=self.x_ref_amplitude, + period=self.x_ref_period, base_value=self.x_ref_base, + num_steps=num_steps, delta_time=delta_time, ) elif self.x_ref_spacing == "uniform": # Create an array of points with a uniform spacing. - x_ref_list = oscillating_linspace( - amplitude=self.x_ref_amplitude, - period=self.x_ref_period, - base_value=self.x_ref_base, - num_steps=num_steps, - delta_time=delta_time, - ) + x_ref_list = oscillating_linspace(amplitude=self.x_ref_amplitude, + period=self.x_ref_period, base_value=self.x_ref_base, + num_steps=num_steps, delta_time=delta_time, ) else: # Throw an exception if the spacing value is not "sine" or "uniform". @@ -360,23 +324,15 @@ def generate_airplanes(self, num_steps=10, delta_time=0.1): if self.y_ref_spacing == "sine": # Create an array of points with a sinusoidal spacing. - y_ref_list = oscillating_sinspace( - amplitude=self.y_ref_amplitude, - period=self.y_ref_period, - base_value=self.y_ref_base, - num_steps=num_steps, - delta_time=delta_time, - ) + y_ref_list = oscillating_sinspace(amplitude=self.y_ref_amplitude, + period=self.y_ref_period, base_value=self.y_ref_base, + num_steps=num_steps, delta_time=delta_time, ) elif self.y_ref_spacing == "uniform": # Create an array of points with a uniform spacing. - y_ref_list = oscillating_linspace( - amplitude=self.y_ref_amplitude, - period=self.y_ref_period, - base_value=self.y_ref_base, - num_steps=num_steps, - delta_time=delta_time, - ) + y_ref_list = oscillating_linspace(amplitude=self.y_ref_amplitude, + period=self.y_ref_period, base_value=self.y_ref_base, + num_steps=num_steps, delta_time=delta_time, ) else: # Throw an exception if the spacing value is not "sine" or "uniform". @@ -386,23 +342,15 @@ def generate_airplanes(self, num_steps=10, delta_time=0.1): if self.z_ref_spacing == "sine": # Create an array of points with a sinusoidal spacing. - z_ref_list = oscillating_sinspace( - amplitude=self.z_ref_amplitude, - period=self.z_ref_period, - base_value=self.z_ref_base, - num_steps=num_steps, - delta_time=delta_time, - ) + z_ref_list = oscillating_sinspace(amplitude=self.z_ref_amplitude, + period=self.z_ref_period, base_value=self.z_ref_base, + num_steps=num_steps, delta_time=delta_time, ) elif self.z_ref_spacing == "uniform": # Create an array of points with a uniform spacing. - z_ref_list = oscillating_linspace( - amplitude=self.z_ref_amplitude, - period=self.z_ref_period, - base_value=self.z_ref_base, - num_steps=num_steps, - delta_time=delta_time, - ) + z_ref_list = oscillating_linspace(amplitude=self.z_ref_amplitude, + period=self.z_ref_period, base_value=self.z_ref_base, + num_steps=num_steps, delta_time=delta_time, ) else: # Throw an exception if the spacing value is not "sine" or "uniform". @@ -414,11 +362,10 @@ def generate_airplanes(self, num_steps=10, delta_time=0.1): # Iterate through the wing movement locations. for wing_movement_location, wing_movement in enumerate(self.wing_movements): - # Generate this wing's vector of other wing's based on its movement. this_wings_list_of_wings = np.array( - wing_movement.generate_wings(num_steps=num_steps, delta_time=delta_time) - ) + wing_movement.generate_wings(num_steps=num_steps, + delta_time=delta_time)) # Add this vector the airplane's array of wing objects. wings[wing_movement_location, :] = this_wings_list_of_wings @@ -438,9 +385,8 @@ def generate_airplanes(self, num_steps=10, delta_time=0.1): these_wings = wings[:, step] # Make a new airplane object for this time step. - this_airplane = geometry.Airplane( - name=name, x_ref=x_ref, y_ref=y_ref, z_ref=z_ref, wings=these_wings - ) + this_airplane = geometry.Airplane(name=name, x_ref=x_ref, y_ref=y_ref, + z_ref=z_ref, wings=these_wings) # Add this new object to the list of airplanes. airplanes.append(this_airplane) @@ -461,12 +407,8 @@ def get_max_period(self): wing_movement_max_periods.append(wing_movement.get_max_period()) max_wing_movement_period = max(wing_movement_max_periods) - max_period = max( - max_wing_movement_period, - self.x_ref_period, - self.y_ref_period, - self.z_ref_period, - ) + max_period = max(max_wing_movement_period, self.x_ref_period, self.y_ref_period, + self.z_ref_period, ) return max_period @@ -488,20 +430,10 @@ class WingMovement: This class is not meant to be subclassed. """ - def __init__( - self, - base_wing, - wing_cross_sections_movements, - x_le_amplitude=0.0, - x_le_period=0.0, - x_le_spacing="sine", - y_le_amplitude=0.0, - y_le_period=0.0, - y_le_spacing="sine", - z_le_amplitude=0.0, - z_le_period=0.0, - z_le_spacing="sine", - ): + def __init__(self, base_wing, wing_cross_sections_movements, x_le_amplitude=0.0, + x_le_period=0.0, x_le_spacing="sine", y_le_amplitude=0.0, y_le_period=0.0, + y_le_spacing="sine", z_le_amplitude=0.0, z_le_period=0.0, + z_le_spacing="sine", ): """This is the initialization method. :param base_wing: Wing @@ -570,23 +502,15 @@ def generate_wings(self, num_steps=10, delta_time=0.1): if self.x_le_spacing == "sine": # Create an array of points with a sinusoidal spacing. - x_le_list = oscillating_sinspace( - amplitude=self.x_le_amplitude, - period=self.x_le_period, - base_value=self.x_le_base, - num_steps=num_steps, - delta_time=delta_time, - ) + x_le_list = oscillating_sinspace(amplitude=self.x_le_amplitude, + period=self.x_le_period, base_value=self.x_le_base, num_steps=num_steps, + delta_time=delta_time, ) elif self.x_le_spacing == "uniform": # Create an array of points with a uniform spacing. - x_le_list = oscillating_linspace( - amplitude=self.x_le_amplitude, - period=self.x_le_period, - base_value=self.x_le_base, - num_steps=num_steps, - delta_time=delta_time, - ) + x_le_list = oscillating_linspace(amplitude=self.x_le_amplitude, + period=self.x_le_period, base_value=self.x_le_base, num_steps=num_steps, + delta_time=delta_time, ) else: # Throw an exception if the spacing value is not "sine" or "uniform". @@ -596,23 +520,15 @@ def generate_wings(self, num_steps=10, delta_time=0.1): if self.y_le_spacing == "sine": # Create an array of points with a sinusoidal spacing. - y_le_list = oscillating_sinspace( - amplitude=self.y_le_amplitude, - period=self.y_le_period, - base_value=self.y_le_base, - num_steps=num_steps, - delta_time=delta_time, - ) + y_le_list = oscillating_sinspace(amplitude=self.y_le_amplitude, + period=self.y_le_period, base_value=self.y_le_base, num_steps=num_steps, + delta_time=delta_time, ) elif self.y_le_spacing == "uniform": # Create an array of points with a uniform spacing. - y_le_list = oscillating_linspace( - amplitude=self.y_le_amplitude, - period=self.y_le_period, - base_value=self.y_le_base, - num_steps=num_steps, - delta_time=delta_time, - ) + y_le_list = oscillating_linspace(amplitude=self.y_le_amplitude, + period=self.y_le_period, base_value=self.y_le_base, num_steps=num_steps, + delta_time=delta_time, ) else: # Throw an exception if the spacing value is not "sine" or "uniform". @@ -622,23 +538,15 @@ def generate_wings(self, num_steps=10, delta_time=0.1): if self.z_le_spacing == "sine": # Create an array of points with a sinusoidal spacing. - z_le_list = oscillating_sinspace( - amplitude=self.z_le_amplitude, - period=self.z_le_period, - base_value=self.z_le_base, - num_steps=num_steps, - delta_time=delta_time, - ) + z_le_list = oscillating_sinspace(amplitude=self.z_le_amplitude, + period=self.z_le_period, base_value=self.z_le_base, num_steps=num_steps, + delta_time=delta_time, ) elif self.z_le_spacing == "uniform": # Create an array of points with a uniform spacing. - z_le_list = oscillating_linspace( - amplitude=self.z_le_amplitude, - period=self.z_le_period, - base_value=self.z_le_base, - num_steps=num_steps, - delta_time=delta_time, - ) + z_le_list = oscillating_linspace(amplitude=self.z_le_amplitude, + period=self.z_le_period, base_value=self.z_le_base, num_steps=num_steps, + delta_time=delta_time, ) else: # Throw an exception if the spacing value is not "sine" or "uniform". @@ -647,18 +555,16 @@ def generate_wings(self, num_steps=10, delta_time=0.1): # Create an empty array that will hold each of the wing's wing cross # section's vector of other wing cross section's based its movement. wing_cross_sections = np.empty( - (len(self.wing_cross_section_movements), num_steps), dtype=object - ) + (len(self.wing_cross_section_movements), num_steps), dtype=object) # Initialize a variable to hold the inner wing cross section's list of wing # cross sections for each time step. last_wing_cross_section_time_histories = None # Iterate through the wing cross section movement locations. - for ( - wing_cross_section_movement_location, - wing_cross_section_movement, - ) in enumerate(self.wing_cross_section_movements): + for (wing_cross_section_movement_location, + wing_cross_section_movement,) in enumerate( + self.wing_cross_section_movements): wing_is_vertical = False # Check if this is this wing's root cross section. @@ -666,17 +572,13 @@ def generate_wings(self, num_steps=10, delta_time=0.1): # Get the root cross section's sweeping and heaving attributes. first_wing_cross_section_movement_sweeping_amplitude = ( - wing_cross_section_movement.sweeping_amplitude - ) + wing_cross_section_movement.sweeping_amplitude) first_wing_cross_section_movement_sweeping_period = ( - wing_cross_section_movement.sweeping_period - ) + wing_cross_section_movement.sweeping_period) first_wing_cross_section_movement_heaving_amplitude = ( - wing_cross_section_movement.heaving_amplitude - ) + wing_cross_section_movement.heaving_amplitude) first_wing_cross_section_movement_heaving_period = ( - wing_cross_section_movement.heaving_period - ) + wing_cross_section_movement.heaving_period) # Check that the root cross section is not sweeping or heaving. assert first_wing_cross_section_movement_sweeping_amplitude == 0 @@ -696,8 +598,7 @@ def generate_wings(self, num_steps=10, delta_time=0.1): else: this_base_wing_cross_section = ( - wing_cross_section_movement.base_wing_cross_section - ) + wing_cross_section_movement.base_wing_cross_section) this_x_le = this_base_wing_cross_section.x_le this_y_le = this_base_wing_cross_section.y_le @@ -718,32 +619,22 @@ def generate_wings(self, num_steps=10, delta_time=0.1): # Find the span between this wing cross section and the inner wing # cross section. - wing_cross_section_span = np.sqrt( - (this_x_le - last_x_les[0]) ** 2 - + (this_y_le - last_y_les[0]) ** 2 - + (this_z_le - last_z_les[0]) ** 2 - ) + wing_cross_section_span = np.sqrt((this_x_le - last_x_les[0]) ** 2 + ( + this_y_le - last_y_les[0]) ** 2 + (this_z_le - last_z_les[ + 0]) ** 2) if this_y_le != last_y_les[0]: # Find the base sweep angle of this wing cross section compared # to the inner wing cross section at the first time step. - base_wing_cross_section_sweep = ( - np.arctan( - (this_z_le - last_z_les[0]) / (this_y_le - last_y_les[0]) - ) - * 180 - / np.pi - ) + base_wing_cross_section_sweep = (np.arctan( + (this_z_le - last_z_les[0]) / ( + this_y_le - last_y_les[0])) * 180 / np.pi) # Find the base heave angle of this wing cross section compared # to the inner wing cross section at the first time step. - base_wing_cross_section_heave = ( - np.arctan( - (this_x_le - last_x_les[0]) / (this_y_le - last_y_les[0]) - ) - * 180 - / np.pi - ) + base_wing_cross_section_heave = (np.arctan( + (this_x_le - last_x_les[0]) / ( + this_y_le - last_y_les[0])) * 180 / np.pi) else: base_wing_cross_section_sweep = 0.0 base_wing_cross_section_heave = 0.0 @@ -753,28 +644,21 @@ def generate_wings(self, num_steps=10, delta_time=0.1): # each time step based on its movement. this_wing_cross_sections_list_of_wing_cross_sections = np.array( wing_cross_section_movement.generate_wing_cross_sections( - num_steps=num_steps, - delta_time=delta_time, + num_steps=num_steps, delta_time=delta_time, cross_section_span=wing_cross_section_span, cross_section_sweep=base_wing_cross_section_sweep, cross_section_heave=base_wing_cross_section_heave, - last_x_les=last_x_les, - last_y_les=last_y_les, - last_z_les=last_z_les, - wing_is_vertical=wing_is_vertical, - ) - ) + last_x_les=last_x_les, last_y_les=last_y_les, last_z_les=last_z_les, + wing_is_vertical=wing_is_vertical, )) # Add this vector the wing's array of wing cross section objects. wing_cross_sections[wing_cross_section_movement_location, :] = ( - this_wing_cross_sections_list_of_wing_cross_sections - ) + this_wing_cross_sections_list_of_wing_cross_sections) # Update the inner wing cross section's list of wing cross sections for # each time step. last_wing_cross_section_time_histories = ( - this_wing_cross_sections_list_of_wing_cross_sections - ) + this_wing_cross_sections_list_of_wing_cross_sections) # Create an empty list of wings. wings = [] @@ -787,7 +671,6 @@ def generate_wings(self, num_steps=10, delta_time=0.1): # Iterate through the time steps. for step in range(num_steps): - # Get the reference position at this time step. x_le = x_le_list[step] y_le = y_le_list[step] @@ -795,16 +678,10 @@ def generate_wings(self, num_steps=10, delta_time=0.1): cross_sections = wing_cross_sections[:, step] # Make a new wing object for this time step. - this_wing = geometry.Wing( - name=name, - x_le=x_le, - y_le=y_le, - z_le=z_le, - wing_cross_sections=cross_sections, - symmetric=symmetric, + this_wing = geometry.Wing(name=name, x_le=x_le, y_le=y_le, z_le=z_le, + wing_cross_sections=cross_sections, symmetric=symmetric, num_chordwise_panels=num_chordwise_panels, - chordwise_spacing=chordwise_spacing, - ) + chordwise_spacing=chordwise_spacing, ) # Add this new object to the list of wings. wings.append(this_wing) @@ -823,18 +700,12 @@ def get_max_period(self): wing_cross_section_movement_max_periods = [] for wing_cross_section_movement in self.wing_cross_section_movements: wing_cross_section_movement_max_periods.append( - wing_cross_section_movement.get_max_period() - ) + wing_cross_section_movement.get_max_period()) max_wing_cross_section_movement_period = max( - wing_cross_section_movement_max_periods - ) + wing_cross_section_movement_max_periods) - max_period = max( - max_wing_cross_section_movement_period, - self.x_le_period, - self.y_le_period, - self.z_le_period, - ) + max_period = max(max_wing_cross_section_movement_period, self.x_le_period, + self.y_le_period, self.z_le_period, ) return max_period @@ -857,22 +728,11 @@ class WingCrossSectionMovement: This class is not meant to be subclassed. """ - def __init__( - self, - base_wing_cross_section, - sweeping_amplitude=0.0, - sweeping_period=0.0, - sweeping_spacing="sine", - custom_sweep_function=None, - pitching_amplitude=0.0, - pitching_period=0.0, - pitching_spacing="sine", - custom_pitch_function=None, - heaving_amplitude=0.0, - heaving_period=0.0, - heaving_spacing="sine", - custom_heave_function=None, - ): + def __init__(self, base_wing_cross_section, sweeping_amplitude=0.0, + sweeping_period=0.0, sweeping_spacing="sine", custom_sweep_function=None, + pitching_amplitude=0.0, pitching_period=0.0, pitching_spacing="sine", + custom_pitch_function=None, heaving_amplitude=0.0, heaving_period=0.0, + heaving_spacing="sine", custom_heave_function=None, ): """This is the initialization method. :param base_wing_cross_section: WingCrossSection @@ -967,21 +827,11 @@ def __init__( self.z_le_base = self.base_wing_cross_section.z_le self.twist_base = self.base_wing_cross_section.twist self.control_surface_deflection_base = ( - self.base_wing_cross_section.control_surface_deflection - ) - - def generate_wing_cross_sections( - self, - num_steps=10, - delta_time=0.1, - last_x_les=None, - last_y_les=None, - last_z_les=None, - wing_is_vertical=False, - cross_section_span=0.0, - cross_section_sweep=0.0, - cross_section_heave=0.0, - ): + self.base_wing_cross_section.control_surface_deflection) + + def generate_wing_cross_sections(self, num_steps=10, delta_time=0.1, + last_x_les=None, last_y_les=None, last_z_les=None, wing_is_vertical=False, + cross_section_span=0.0, cross_section_sweep=0.0, cross_section_heave=0.0, ): """This method creates the wing cross section objects at each time current_step, and groups them into a list. @@ -1029,41 +879,28 @@ def generate_wing_cross_sections( if self.sweeping_spacing == "sine": # Create an array of points with a sinusoidal spacing. - sweeping_list = oscillating_sinspace( - amplitude=self.sweeping_amplitude, - period=self.sweeping_period, - base_value=cross_section_sweep, - num_steps=num_steps, - delta_time=delta_time, - ) + sweeping_list = oscillating_sinspace(amplitude=self.sweeping_amplitude, + period=self.sweeping_period, base_value=cross_section_sweep, + num_steps=num_steps, delta_time=delta_time, ) elif self.sweeping_spacing == "uniform": # Create an array of points with a uniform spacing. - sweeping_list = oscillating_linspace( - amplitude=self.sweeping_amplitude, - period=self.sweeping_period, - base_value=cross_section_sweep, - num_steps=num_steps, - delta_time=delta_time, - ) + sweeping_list = oscillating_linspace(amplitude=self.sweeping_amplitude, + period=self.sweeping_period, base_value=cross_section_sweep, + num_steps=num_steps, delta_time=delta_time, ) elif self.sweeping_spacing == "custom": # Raise an exception if the user did not declare a custom sweep function. if self.custom_sweep_function is None: raise Exception( "You can't declare custom sweep spacing without providing a " - "custom sweep function." - ) + "custom sweep function.") # Create an array of points with a uniform spacing. - sweeping_list = oscillating_customspace( - amplitude=self.sweeping_amplitude, - period=self.sweeping_period, - base_value=cross_section_sweep, - num_steps=num_steps, - delta_time=delta_time, - custom_function=self.custom_sweep_function, - ) + sweeping_list = oscillating_customspace(amplitude=self.sweeping_amplitude, + period=self.sweeping_period, base_value=cross_section_sweep, + num_steps=num_steps, delta_time=delta_time, + custom_function=self.custom_sweep_function, ) else: # Throw an exception if the spacing value is not "sine" or "uniform". @@ -1073,41 +910,28 @@ def generate_wing_cross_sections( if self.pitching_spacing == "sine": # Create an array of points with a sinusoidal spacing. - pitching_list = oscillating_sinspace( - amplitude=self.pitching_amplitude, - period=self.pitching_period, - base_value=self.pitching_base, - num_steps=num_steps, - delta_time=delta_time, - ) + pitching_list = oscillating_sinspace(amplitude=self.pitching_amplitude, + period=self.pitching_period, base_value=self.pitching_base, + num_steps=num_steps, delta_time=delta_time, ) elif self.pitching_spacing == "uniform": # Create an array of points with a uniform spacing. - pitching_list = oscillating_linspace( - amplitude=self.pitching_amplitude, - period=self.pitching_period, - base_value=self.pitching_base, - num_steps=num_steps, - delta_time=delta_time, - ) + pitching_list = oscillating_linspace(amplitude=self.pitching_amplitude, + period=self.pitching_period, base_value=self.pitching_base, + num_steps=num_steps, delta_time=delta_time, ) elif self.pitching_spacing == "custom": # Raise an exception if the user did not declare a custom pitch function. if self.custom_pitch_function is None: raise Exception( "You can't declare custom pitch spacing without providing a " - "custom pitch function." - ) + "custom pitch function.") # Create an array of points with a uniform spacing. - pitching_list = oscillating_customspace( - amplitude=self.pitching_amplitude, - period=self.pitching_period, - base_value=self.pitching_base, - num_steps=num_steps, - delta_time=delta_time, - custom_function=self.custom_pitch_function, - ) + pitching_list = oscillating_customspace(amplitude=self.pitching_amplitude, + period=self.pitching_period, base_value=self.pitching_base, + num_steps=num_steps, delta_time=delta_time, + custom_function=self.custom_pitch_function, ) else: # Throw an exception if the spacing value is not "sine" or "uniform". @@ -1117,41 +941,28 @@ def generate_wing_cross_sections( if self.heaving_spacing == "sine": # Create an array of points with a sinusoidal spacing. - heaving_list = oscillating_sinspace( - amplitude=self.heaving_amplitude, - period=self.heaving_period, - base_value=cross_section_heave, - num_steps=num_steps, - delta_time=delta_time, - ) + heaving_list = oscillating_sinspace(amplitude=self.heaving_amplitude, + period=self.heaving_period, base_value=cross_section_heave, + num_steps=num_steps, delta_time=delta_time, ) elif self.heaving_spacing == "uniform": # Create an array of points with a uniform spacing. - heaving_list = oscillating_linspace( - amplitude=self.heaving_amplitude, - period=self.heaving_period, - base_value=cross_section_heave, - num_steps=num_steps, - delta_time=delta_time, - ) + heaving_list = oscillating_linspace(amplitude=self.heaving_amplitude, + period=self.heaving_period, base_value=cross_section_heave, + num_steps=num_steps, delta_time=delta_time, ) elif self.heaving_spacing == "custom": # Raise an exception if the user did not declare a custom heave function. if self.custom_heave_function is None: raise Exception( "You can't declare custom heave spacing without providing a " - "custom heave function." - ) + "custom heave function.") # Create an array of points with custom spacing. - heaving_list = oscillating_customspace( - amplitude=self.heaving_amplitude, - period=self.heaving_period, - base_value=cross_section_heave, - num_steps=num_steps, - delta_time=delta_time, - custom_function=self.custom_heave_function, - ) + heaving_list = oscillating_customspace(amplitude=self.heaving_amplitude, + period=self.heaving_period, base_value=cross_section_heave, + num_steps=num_steps, delta_time=delta_time, + custom_function=self.custom_heave_function, ) else: # Throw an exception if the spacing value is not "sine" or "uniform". @@ -1170,14 +981,11 @@ def generate_wing_cross_sections( # convert the lists of sweep, pitch, and heave values to radians before # passing them into numpy's trigonometry functions. x_le_list = last_x_les + cross_section_span * np.cos( - sweeping_list * np.pi / 180 - ) * np.sin(heaving_list * np.pi / 180) + sweeping_list * np.pi / 180) * np.sin(heaving_list * np.pi / 180) y_le_list = last_y_les + cross_section_span * np.cos( - sweeping_list * np.pi / 180 - ) * np.cos(heaving_list * np.pi / 180) + sweeping_list * np.pi / 180) * np.cos(heaving_list * np.pi / 180) z_le_list = last_z_les + cross_section_span * np.sin( - sweeping_list * np.pi / 180 - ) + sweeping_list * np.pi / 180) twist_list = pitching_list # Create an empty list of wing cross sections. @@ -1189,8 +997,7 @@ def generate_wing_cross_sections( control_surface_deflection = self.control_surface_deflection_base control_surface_type = self.base_wing_cross_section.control_surface_type control_surface_hinge_point = ( - self.base_wing_cross_section.control_surface_hinge_point - ) + self.base_wing_cross_section.control_surface_hinge_point) num_spanwise_panels = self.base_wing_cross_section.num_spanwise_panels spanwise_spacing = self.base_wing_cross_section.spanwise_spacing @@ -1203,19 +1010,13 @@ def generate_wing_cross_sections( twist = twist_list[step] # Make a new wing cross section object for this time step. - this_wing_cross_section = geometry.WingCrossSection( - x_le=x_le, - y_le=y_le, - z_le=z_le, - chord=chord, - twist=twist, - airfoil=airfoil, + this_wing_cross_section = geometry.WingCrossSection(x_le=x_le, y_le=y_le, + z_le=z_le, chord=chord, twist=twist, airfoil=airfoil, control_surface_type=control_surface_type, control_surface_hinge_point=control_surface_hinge_point, control_surface_deflection=control_surface_deflection, num_spanwise_panels=num_spanwise_panels, - spanwise_spacing=spanwise_spacing, - ) + spanwise_spacing=spanwise_spacing, ) # Add this new object to the list of wing cross sections. wing_cross_sections.append(this_wing_cross_section) @@ -1230,9 +1031,8 @@ def get_max_period(self): The longest period in seconds. """ - max_period = max( - self.sweeping_period, self.pitching_period, self.heaving_period - ) + max_period = max(self.sweeping_period, self.pitching_period, + self.heaving_period) return max_period @@ -1255,13 +1055,8 @@ class OperatingPointMovement: This class is not meant to be subclassed. """ - def __init__( - self, - base_operating_point, - velocity_amplitude=0.0, - velocity_period=0.0, - velocity_spacing="sine", - ): + def __init__(self, base_operating_point, velocity_amplitude=0.0, + velocity_period=0.0, velocity_spacing="sine", ): """This is the initialization method. :param base_operating_point: OperatingPoint @@ -1303,23 +1098,15 @@ def generate_operating_points(self, num_steps=10, delta_time=0.1): if self.velocity_spacing == "sine": # Create an array of points with a sinusoidal spacing. - velocity_list = oscillating_sinspace( - amplitude=self.velocity_amplitude, - period=self.velocity_period, - base_value=self.velocity_base, - num_steps=num_steps, - delta_time=delta_time, - ) + velocity_list = oscillating_sinspace(amplitude=self.velocity_amplitude, + period=self.velocity_period, base_value=self.velocity_base, + num_steps=num_steps, delta_time=delta_time, ) elif self.velocity_spacing == "uniform": # Create an array of points with a uniform spacing. - velocity_list = oscillating_linspace( - amplitude=self.velocity_amplitude, - period=self.velocity_period, - base_value=self.velocity_base, - num_steps=num_steps, - delta_time=delta_time, - ) + velocity_list = oscillating_linspace(amplitude=self.velocity_amplitude, + period=self.velocity_period, base_value=self.velocity_base, + num_steps=num_steps, delta_time=delta_time, ) else: # Throw an exception if the spacing value is not "sine" or "uniform". @@ -1339,9 +1126,8 @@ def generate_operating_points(self, num_steps=10, delta_time=0.1): velocity = velocity_list[step] # Make a new operating point object for this time step. - this_operating_point = operating_point.OperatingPoint( - density=density, velocity=velocity, alpha=alpha, beta=beta - ) + this_operating_point = operating_point.OperatingPoint(density=density, + velocity=velocity, alpha=alpha, beta=beta) # Add this new object to the list of operating points. operating_points.append(this_operating_point) @@ -1437,9 +1223,8 @@ def oscillating_linspace(amplitude, period, base_value, num_steps, delta_time): return a * signal.sawtooth((b * times + h), 0.5) + k -def oscillating_customspace( - amplitude, period, base_value, num_steps, delta_time, custom_function -): +def oscillating_customspace(amplitude, period, base_value, num_steps, delta_time, + custom_function): """This function returns a 1D array of values that are calculated by inputting a vector of linearly spaced time steps into a custom function. diff --git a/pterasoftware/operating_point.py b/pterasoftware/operating_point.py index ab588815..9c8da28a 100644 --- a/pterasoftware/operating_point.py +++ b/pterasoftware/operating_point.py @@ -43,15 +43,8 @@ class OperatingPoint: This class is not meant to be subclassed. """ - def __init__( - self, - density=1.225, - velocity=10.0, - alpha=5.0, - beta=0.0, - external_thrust=0.0, - nu=15.06e-6, - ): + def __init__(self, density=1.225, velocity=10.0, alpha=5.0, beta=0.0, + external_thrust=0.0, nu=15.06e-6, ): """This is the initialization method. :param density: float, optional @@ -95,7 +88,7 @@ def calculate_dynamic_pressure(self): """ # Calculate and return the freestream dynamic pressure - dynamic_pressure = 0.5 * self.density * self.velocity**2 + dynamic_pressure = 0.5 * self.density * self.velocity ** 2 return dynamic_pressure def calculate_rotation_matrix_wind_to_geometry(self): @@ -113,28 +106,19 @@ def calculate_rotation_matrix_wind_to_geometry(self): eye = np.eye(3) alpha_rotation = np.array( - [[cos_alpha, 0, -sin_alpha], [0, 1, 0], [sin_alpha, 0, cos_alpha]] - ) + [[cos_alpha, 0, -sin_alpha], [0, 1, 0], [sin_alpha, 0, cos_alpha]]) beta_rotation = np.array( - [[cos_beta, -sin_beta, 0], [sin_beta, cos_beta, 0], [0, 0, 1]] - ) + [[cos_beta, -sin_beta, 0], [sin_beta, cos_beta, 0], [0, 0, 1]]) # Flip the axes because in geometry axes x is downstream by convention, # while in wind axes x is upstream by convention. Same with z being up/down # respectively. - axes_flip = np.array( - [ - [-1, 0, 0], - [0, 1, 0], - [0, 0, -1], - ] - ) + axes_flip = np.array([[-1, 0, 0], [0, 1, 0], [0, 0, -1], ]) # Calculate and return the rotation matrix to convert wind axes to geometry # axes. rotation_matrix_wind_axes_to_geometry_axes = ( - axes_flip @ alpha_rotation @ beta_rotation @ eye - ) + axes_flip @ alpha_rotation @ beta_rotation @ eye) return rotation_matrix_wind_axes_to_geometry_axes def calculate_freestream_direction_geometry_axes(self): @@ -147,9 +131,8 @@ def calculate_freestream_direction_geometry_axes(self): velocity_direction_wind_axes = np.array([-1, 0, 0]) velocity_direction_geometry_axes = ( - self.calculate_rotation_matrix_wind_to_geometry() - @ velocity_direction_wind_axes - ) + self.calculate_rotation_matrix_wind_to_geometry() @ + velocity_direction_wind_axes) return velocity_direction_geometry_axes def calculate_freestream_velocity_geometry_axes(self): @@ -161,6 +144,5 @@ def calculate_freestream_velocity_geometry_axes(self): """ freestream_velocity_geometry_axes = ( - self.calculate_freestream_direction_geometry_axes() * self.velocity - ) + self.calculate_freestream_direction_geometry_axes() * self.velocity) return freestream_velocity_geometry_axes diff --git a/pterasoftware/output.py b/pterasoftware/output.py index 0ad42110..c140e431 100644 --- a/pterasoftware/output.py +++ b/pterasoftware/output.py @@ -58,28 +58,9 @@ # For the figure lines, use the "Prism" qualitative color map from # carto.com/carto-colors. -prism = [ - "#5F4690", - "#1D6996", - "#38A6A5", - "#0F8554", - "#73AF48", - "#EDAD08", - "#E17C05", - "#CC503E", - "#94346E", - "#6F4070", - "#994E95", - "#666666", -] -[ - drag_color, - side_color, - lift_color, - roll_color, - pitch_color, - yaw_color, -] = prism[3:9] +prism = ["#5F4690", "#1D6996", "#38A6A5", "#0F8554", "#73AF48", "#EDAD08", "#E17C05", + "#CC503E", "#94346E", "#6F4070", "#994E95", "#666666", ] +[drag_color, side_color, lift_color, roll_color, pitch_color, yaw_color, ] = prism[3:9] # Set constants for the color maps, scalar bars, and text boxes. color_map_num_sig = 3 @@ -102,13 +83,8 @@ marker_spacing = 1.0 / num_markers -def draw( - solver, - scalar_type=None, - show_streamlines=False, - show_wake_vortices=False, - save=False, -): +def draw(solver, scalar_type=None, show_streamlines=False, show_wake_vortices=False, + save=False, ): """Draw the geometry of the airplanes in a solver object. Citation: @@ -141,10 +117,9 @@ def draw( plotter = pv.Plotter(window_size=window_size, lighting=None) # Get the solver's geometry. - if isinstance( - solver, - unsteady_ring_vortex_lattice_method.UnsteadyRingVortexLatticeMethodSolver, - ): + if isinstance(solver, + unsteady_ring_vortex_lattice_method + .UnsteadyRingVortexLatticeMethodSolver, ): draw_step = solver.num_steps - 1 airplanes = solver.steady_problems[draw_step].airplanes @@ -152,22 +127,15 @@ def draw( # plot them. if show_wake_vortices: wake_ring_vortex_surfaces = get_wake_ring_vortex_surfaces(solver, draw_step) - plotter.add_mesh( - wake_ring_vortex_surfaces, - show_edges=True, - smooth_shading=False, - color=wake_vortex_color, - ) + plotter.add_mesh(wake_ring_vortex_surfaces, show_edges=True, + smooth_shading=False, color=wake_vortex_color, ) else: airplanes = solver.airplanes # Check if the user wants to show scalars on the wing panels. show_scalars = False if ( - scalar_type == "induced drag" - or scalar_type == "side force" - or scalar_type == "lift" - ): + scalar_type == "induced drag" or scalar_type == "side force" or scalar_type == "lift"): show_scalars = True # Get the panel surfaces. @@ -188,35 +156,20 @@ def draw( color_map = sequential_color_map c_min = max( np.mean(these_scalars) - color_map_num_sig * np.std(these_scalars), - np.min(these_scalars), - ) + np.min(these_scalars), ) c_max = min( np.mean(these_scalars) + color_map_num_sig * np.std(these_scalars), - np.max(these_scalars), - ) + np.max(these_scalars), ) else: color_map = diverging_color_map c_min = -color_map_num_sig * np.std(these_scalars) c_max = color_map_num_sig * np.std(these_scalars) - plot_scalars( - plotter, - these_scalars, - scalar_type, - min_scalar, - max_scalar, - color_map, - c_min, - c_max, - panel_surfaces, - ) + plot_scalars(plotter, these_scalars, scalar_type, min_scalar, max_scalar, + color_map, c_min, c_max, panel_surfaces, ) else: - plotter.add_mesh( - panel_surfaces, - show_edges=True, - color=panel_color, - smooth_shading=False, - ) + plotter.add_mesh(panel_surfaces, show_edges=True, color=panel_color, + smooth_shading=False, ) # Check if the user wants to plot streamlines. if show_streamlines: @@ -238,52 +191,30 @@ def draw( last_point = streamline_point_column[point_index - 1, :] # Add a line to make this segment of the streamline. - plotter.add_mesh( - pv.Line( - last_point, - point, - ), - show_edges=True, - color=streamline_color, - line_width=2, - smooth_shading=False, - ) + plotter.add_mesh(pv.Line(last_point, point, ), show_edges=True, + color=streamline_color, line_width=2, smooth_shading=False, ) # Set the plotter's background color and camera position. Then show the plotter # so the user can adjust the camera position and window. When the user closes the # window, the plotter object won't be closed so that it can be saved as an image # if the user wants. plotter.set_background(color=plotter_background_color) - plotter.show( - cpos=(-1, -1, 1), - full_screen=False, - auto_close=False, - ) + plotter.show(cpos=(-1, -1, 1), full_screen=False, auto_close=False, ) # If the user wants to save the image, take a screenshot, convert it into an # image object, and save it as a WebP. if save: image = webp.Image.fromarray( - plotter.screenshot( - filename=None, - transparent_background=True, - return_img=True, - ) - ) - webp.save_image( - img=image, file_path="Draw.webp", lossless=False, quality=quality - ) + plotter.screenshot(filename=None, transparent_background=True, + return_img=True, )) + webp.save_image(img=image, file_path="Draw.webp", lossless=False, + quality=quality) # Close all the plotters. pv.close_all() -def animate( - unsteady_solver, - scalar_type=None, - show_wake_vortices=False, - save=False, -): +def animate(unsteady_solver, scalar_type=None, show_wake_vortices=False, save=False, ): """Create an animation of a solver's geometries. :param unsteady_solver: UnsteadyRingVortexLatticeMethodSolver @@ -331,21 +262,14 @@ def animate( if save: # Add text to the animation that display's its speed relative to the true # speed. - plotter.add_text( - text="Speed: " + str(round(100 * speed)) + "%", - position=text_speed_position, - font_size=text_font_size, - viewport=True, - color=text_color, - ) + plotter.add_text(text="Speed: " + str(round(100 * speed)) + "%", + position=text_speed_position, font_size=text_font_size, viewport=True, + color=text_color, ) # Check if the user wants to show scalars on the wing panels. show_scalars = False if ( - scalar_type == "induced drag" - or scalar_type == "side force" - or scalar_type == "lift" - ): + scalar_type == "induced drag" or scalar_type == "side force" or scalar_type == "lift"): show_scalars = True # Initialize variables to hold the problems' scalars and their attributes. @@ -367,14 +291,10 @@ def animate( # have different signs (diverging color map). if np.sign(np.min(all_scalars)) == np.sign(np.max(all_scalars)): color_map = sequential_color_map - c_min = max( - np.mean(all_scalars) - color_map_num_sig * np.std(all_scalars), - np.min(all_scalars), - ) - c_max = min( - np.mean(all_scalars) + color_map_num_sig * np.std(all_scalars), - np.max(all_scalars), - ) + c_min = max(np.mean(all_scalars) - color_map_num_sig * np.std(all_scalars), + np.min(all_scalars), ) + c_max = min(np.mean(all_scalars) + color_map_num_sig * np.std(all_scalars), + np.max(all_scalars), ) else: color_map = diverging_color_map c_min = -color_map_num_sig * np.std(all_scalars) @@ -391,51 +311,27 @@ def animate( if show_scalars and first_results_step == 0: these_scalars = get_scalars(step_airplanes[0], scalar_type) - plot_scalars( - plotter, - these_scalars, - scalar_type, - min_scalar, - max_scalar, - color_map, - c_min, - c_max, - panel_surfaces, - ) + plot_scalars(plotter, these_scalars, scalar_type, min_scalar, max_scalar, + color_map, c_min, c_max, panel_surfaces, ) else: - plotter.add_mesh( - panel_surfaces, - show_edges=True, - color=panel_color, - smooth_shading=False, - ) + plotter.add_mesh(panel_surfaces, show_edges=True, color=panel_color, + smooth_shading=False, ) # Set the plotter background color and show the plotter. plotter.set_background(color=plotter_background_color) # Print a message to the console on how to set up the window. print( - 'Orient the view, then press "q" to close the window and produce the animation.' - ) + 'Orient the view, then press "q" to close the window and produce the animation.') # Show the plotter so the user can set up the camera. Then, they will close the # window, but the plotter object will stay open off-screen. - plotter.show( - title="Rendering speed not to scale.", - cpos=(-1, -1, 1), - full_screen=False, - auto_close=False, - ) + plotter.show(title="Rendering speed not to scale.", cpos=(-1, -1, 1), + full_screen=False, auto_close=False, ) # Start a list which will hold a WebP image of each frame. - images = [ - webp.Image.fromarray( - plotter.screenshot( - transparent_background=True, - return_img=True, - ) - ) - ] + images = [webp.Image.fromarray( + plotter.screenshot(transparent_background=True, return_img=True, ))] # Initialize a variable to keep track of which step we are on. current_step = 1 @@ -450,26 +346,17 @@ def animate( panel_surfaces = get_panel_surfaces(airplanes) if save: - plotter.add_text( - text="Speed: " + str(round(100 * speed)) + "%", - position=text_speed_position, - font_size=text_font_size, - viewport=True, - color=text_color, - ) + plotter.add_text(text="Speed: " + str(round(100 * speed)) + "%", + position=text_speed_position, font_size=text_font_size, viewport=True, + color=text_color, ) # If the user wants to show the wake ring vortices, then get their surfaces # and plot them. if show_wake_vortices: - wake_ring_vortex_surfaces = get_wake_ring_vortex_surfaces( - unsteady_solver, current_step - ) - plotter.add_mesh( - wake_ring_vortex_surfaces, - show_edges=True, - smooth_shading=False, - color=wake_vortex_color, - ) + wake_ring_vortex_surfaces = get_wake_ring_vortex_surfaces(unsteady_solver, + current_step) + plotter.add_mesh(wake_ring_vortex_surfaces, show_edges=True, + smooth_shading=False, color=wake_vortex_color, ) # Check if the user wants to plot scalars and this step is equal to or # greater than the first step with calculated results. If so, add the panel @@ -478,37 +365,18 @@ def animate( these_scalars = get_scalars(airplanes, scalar_type) - plot_scalars( - plotter, - these_scalars, - scalar_type, - min_scalar, - max_scalar, - color_map, - c_min, - c_max, - panel_surfaces, - ) + plot_scalars(plotter, these_scalars, scalar_type, min_scalar, max_scalar, + color_map, c_min, c_max, panel_surfaces, ) else: - plotter.add_mesh( - panel_surfaces, - show_edges=True, - color=panel_color, - smooth_shading=False, - ) + plotter.add_mesh(panel_surfaces, show_edges=True, color=panel_color, + smooth_shading=False, ) # Append a WebP image of this frame to the list of frame images if the user # wants to save an animation. if save: - images.append( - webp.Image.fromarray( - plotter.screenshot( - filename=None, - transparent_background=True, - return_img=True, - ) - ) - ) + images.append(webp.Image.fromarray( + plotter.screenshot(filename=None, transparent_background=True, + return_img=True, ))) # Increment the step number tracker. current_step += 1 @@ -516,9 +384,8 @@ def animate( # If the user wants to save the file, save the list of images as an animated WebP. if save: # Convert the list of WebP images to a WebP animation. - webp.save_images( - images, "Animate.webp", fps=actual_fps, lossless=False, quality=quality - ) + webp.save_images(images, "Animate.webp", fps=actual_fps, lossless=False, + quality=quality) # Close all the plotters. pv.close_all() @@ -554,27 +421,19 @@ def plot_results_versus_time(unsteady_solver, show=True, save=False): # Create a 1D array with the time at each time step where results have been # calculated. - times = np.linspace( - first_results_time_step_time, - final_time_step_time, - num_steps_to_average, - endpoint=True, - ) + times = np.linspace(first_results_time_step_time, final_time_step_time, + num_steps_to_average, endpoint=True, ) # Initialize matrices to hold the forces, moments, and coefficients at each time # step which has results. total_near_field_force_wind_axes = np.zeros( - (num_airplanes, 3, num_steps_to_average) - ) + (num_airplanes, 3, num_steps_to_average)) total_near_field_force_coefficients_wind_axes = np.zeros( - (num_airplanes, 3, num_steps_to_average) - ) + (num_airplanes, 3, num_steps_to_average)) total_near_field_moment_wind_axes = np.zeros( - (num_airplanes, 3, num_steps_to_average) - ) + (num_airplanes, 3, num_steps_to_average)) total_near_field_moment_coefficients_wind_axes = np.zeros( - (num_airplanes, 3, num_steps_to_average) - ) + (num_airplanes, 3, num_steps_to_average)) # Initialize a variable to track position in the results arrays. results_step = 0 @@ -588,17 +447,13 @@ def plot_results_versus_time(unsteady_solver, show=True, save=False): # Iterate through this step's airplanes. for airplane_id, airplane in enumerate(airplanes): total_near_field_force_wind_axes[airplane_id, :, results_step] = ( - airplane.total_near_field_force_wind_axes - ) - total_near_field_force_coefficients_wind_axes[ - airplane_id, :, results_step - ] = airplane.total_near_field_force_coefficients_wind_axes + airplane.total_near_field_force_wind_axes) + total_near_field_force_coefficients_wind_axes[airplane_id, :, + results_step] = airplane.total_near_field_force_coefficients_wind_axes total_near_field_moment_wind_axes[airplane_id, :, results_step] = ( - airplane.total_near_field_moment_wind_axes - ) - total_near_field_moment_coefficients_wind_axes[ - airplane_id, :, results_step - ] = airplane.total_near_field_moment_coefficients_wind_axes + airplane.total_near_field_moment_wind_axes) + total_near_field_moment_coefficients_wind_axes[airplane_id, :, + results_step] = airplane.total_near_field_moment_coefficients_wind_axes results_step += 1 @@ -660,114 +515,60 @@ def plot_results_versus_time(unsteady_solver, show=True, save=False): moment_coefficients_axes.set_facecolor(figure_background_color) # Populate the plots. - force_axes.plot( - times, - total_near_field_force_wind_axes[airplane_id, 0], - label="Induced Drag", - color=drag_color, - marker=".", + force_axes.plot(times, total_near_field_force_wind_axes[airplane_id, 0], + label="Induced Drag", color=drag_color, marker=".", markevery=(marker_spacing * 0 / 3, marker_spacing), - markersize=marker_size, - ) - force_axes.plot( - times, - total_near_field_force_wind_axes[airplane_id, 1], - label="Side Force", - color=side_color, - marker=".", + markersize=marker_size, ) + force_axes.plot(times, total_near_field_force_wind_axes[airplane_id, 1], + label="Side Force", color=side_color, marker=".", markevery=(marker_spacing * 1 / 3, marker_spacing), - markersize=marker_size, - ) - force_axes.plot( - times, - total_near_field_force_wind_axes[airplane_id, 2], - label="Lift", - color=lift_color, - marker=".", + markersize=marker_size, ) + force_axes.plot(times, total_near_field_force_wind_axes[airplane_id, 2], + label="Lift", color=lift_color, marker=".", markevery=(marker_spacing * 2 / 3, marker_spacing), - markersize=marker_size, - ) - force_coefficients_axes.plot( - times, + markersize=marker_size, ) + force_coefficients_axes.plot(times, total_near_field_force_coefficients_wind_axes[airplane_id, 0], - label="Induced Drag", - color=drag_color, - marker=".", + label="Induced Drag", color=drag_color, marker=".", markevery=(marker_spacing * 0 / 3, marker_spacing), - markersize=marker_size, - ) - force_coefficients_axes.plot( - times, + markersize=marker_size, ) + force_coefficients_axes.plot(times, total_near_field_force_coefficients_wind_axes[airplane_id, 1], - label="Side Force", - color=side_color, - marker=".", + label="Side Force", color=side_color, marker=".", markevery=(marker_spacing * 1 / 3, marker_spacing), - markersize=marker_size, - ) - force_coefficients_axes.plot( - times, - total_near_field_force_coefficients_wind_axes[airplane_id, 2], - label="Lift", - color=lift_color, - marker=".", + markersize=marker_size, ) + force_coefficients_axes.plot(times, + total_near_field_force_coefficients_wind_axes[airplane_id, 2], label="Lift", + color=lift_color, marker=".", markevery=(marker_spacing * 2 / 3, marker_spacing), - markersize=marker_size, - ) - moment_axes.plot( - times, - total_near_field_moment_wind_axes[airplane_id, 0], - label="Roll", - color=roll_color, - marker=".", + markersize=marker_size, ) + moment_axes.plot(times, total_near_field_moment_wind_axes[airplane_id, 0], + label="Roll", color=roll_color, marker=".", markevery=(marker_spacing * 0 / 3, marker_spacing), - markersize=marker_size, - ) - moment_axes.plot( - times, - total_near_field_moment_wind_axes[airplane_id, 1], - label="Pitch", - color=pitch_color, - marker=".", + markersize=marker_size, ) + moment_axes.plot(times, total_near_field_moment_wind_axes[airplane_id, 1], + label="Pitch", color=pitch_color, marker=".", markevery=(marker_spacing * 1 / 3, marker_spacing), - markersize=marker_size, - ) - moment_axes.plot( - times, - total_near_field_moment_wind_axes[airplane_id, 2], - label="Yaw", - color=yaw_color, - marker=".", + markersize=marker_size, ) + moment_axes.plot(times, total_near_field_moment_wind_axes[airplane_id, 2], + label="Yaw", color=yaw_color, marker=".", markevery=(marker_spacing * 2 / 3, marker_spacing), - markersize=marker_size, - ) - moment_coefficients_axes.plot( - times, + markersize=marker_size, ) + moment_coefficients_axes.plot(times, total_near_field_moment_coefficients_wind_axes[airplane_id, 0], - label="Roll", - color=roll_color, - marker=".", + label="Roll", color=roll_color, marker=".", markevery=(marker_spacing * 0 / 3, marker_spacing), - markersize=marker_size, - ) - moment_coefficients_axes.plot( - times, + markersize=marker_size, ) + moment_coefficients_axes.plot(times, total_near_field_moment_coefficients_wind_axes[airplane_id, 1], - label="Pitch", - color=pitch_color, - marker=".", + label="Pitch", color=pitch_color, marker=".", markevery=(marker_spacing * 1 / 3, marker_spacing), - markersize=marker_size, - ) - moment_coefficients_axes.plot( - times, - total_near_field_moment_coefficients_wind_axes[airplane_id, 2], - label="Yaw", - color=yaw_color, - marker=".", + markersize=marker_size, ) + moment_coefficients_axes.plot(times, + total_near_field_moment_coefficients_wind_axes[airplane_id, 2], label="Yaw", + color=yaw_color, marker=".", markevery=(marker_spacing * 2 / 3, marker_spacing), - markersize=marker_size, - ) + markersize=marker_size, ) # Find and format this airplane's name for use in the plot titles. airplane_name = unsteady_solver.steady_problems[0].airplanes[airplane_id].name @@ -791,45 +592,23 @@ def plot_results_versus_time(unsteady_solver, show=True, save=False): moment_coefficients_axes.set_title(moment_coefficient_title, color=text_color) # Format the plots' legends. - force_axes.legend( - facecolor=figure_background_color, - edgecolor=figure_background_color, - labelcolor=text_color, - ) - force_coefficients_axes.legend( - facecolor=figure_background_color, - edgecolor=figure_background_color, - labelcolor=text_color, - ) - moment_axes.legend( - facecolor=figure_background_color, - edgecolor=figure_background_color, - labelcolor=text_color, - ) - moment_coefficients_axes.legend( - facecolor=figure_background_color, - edgecolor=figure_background_color, - labelcolor=text_color, - ) + force_axes.legend(facecolor=figure_background_color, + edgecolor=figure_background_color, labelcolor=text_color, ) + force_coefficients_axes.legend(facecolor=figure_background_color, + edgecolor=figure_background_color, labelcolor=text_color, ) + moment_axes.legend(facecolor=figure_background_color, + edgecolor=figure_background_color, labelcolor=text_color, ) + moment_coefficients_axes.legend(facecolor=figure_background_color, + edgecolor=figure_background_color, labelcolor=text_color, ) # Save the figures as PNGs if the user wants to do so. if save: - force_figure.savefig( - airplane_name + " Forces.png", - dpi=300, - ) - force_coefficients_figure.savefig( - airplane_name + " Force Coefficients.png", - dpi=300, - ) - moment_figure.savefig( - airplane_name + " Moments.png", - dpi=300, - ) + force_figure.savefig(airplane_name + " Forces.png", dpi=300, ) + force_coefficients_figure.savefig(airplane_name + " Force Coefficients.png", + dpi=300, ) + moment_figure.savefig(airplane_name + " Moments.png", dpi=300, ) moment_coefficients_figure.savefig( - airplane_name + " Moment Coefficients.png", - dpi=300, - ) + airplane_name + " Moment Coefficients.png", dpi=300, ) # If the user wants to show the plots, do so. if show: @@ -856,81 +635,45 @@ def print_steady_results(steady_solver): # Print out this airplane's forces. print("\tForces in Wind Axes:") - print( - "\t\tInduced Drag:\t\t\t", - np.round(airplane.total_near_field_force_wind_axes[0], 3), - " N", - sep="", - ) - print( - "\t\tSide Force:\t\t\t\t", - np.round(airplane.total_near_field_force_wind_axes[1], 3), - " N", - sep="", - ) - print( - "\t\tLift:\t\t\t\t\t", - np.round(airplane.total_near_field_force_wind_axes[2], 3), - " N", - sep="", - ) + print("\t\tInduced Drag:\t\t\t", + np.round(airplane.total_near_field_force_wind_axes[0], 3), " N", sep="", ) + print("\t\tSide Force:\t\t\t\t", + np.round(airplane.total_near_field_force_wind_axes[1], 3), " N", sep="", ) + print("\t\tLift:\t\t\t\t\t", + np.round(airplane.total_near_field_force_wind_axes[2], 3), " N", sep="", ) # Print out this airplane's moments. print("\n\tMoments in Wind Axes:") - print( - "\t\tRolling Moment:\t\t\t", - np.round(airplane.total_near_field_moment_wind_axes[0], 3), - " Nm", - sep="", - ) - print( - "\t\tPitching Moment:\t\t", - np.round(airplane.total_near_field_moment_wind_axes[1], 3), - " Nm", - sep="", - ) - print( - "\t\tYawing Moment:\t\t\t", - np.round(airplane.total_near_field_moment_wind_axes[2], 3), - " Nm", - sep="", - ) + print("\t\tRolling Moment:\t\t\t", + np.round(airplane.total_near_field_moment_wind_axes[0], 3), " Nm", sep="", ) + print("\t\tPitching Moment:\t\t", + np.round(airplane.total_near_field_moment_wind_axes[1], 3), " Nm", sep="", ) + print("\t\tYawing Moment:\t\t\t", + np.round(airplane.total_near_field_moment_wind_axes[2], 3), " Nm", sep="", ) # Print out this airplane's force coefficients. print("\n\tForce Coefficients in Wind Axes:") - print( - "\t\tCDi:\t\t\t\t\t", + print("\t\tCDi:\t\t\t\t\t", np.round(airplane.total_near_field_force_coefficients_wind_axes[0], 3), - sep="", - ) - print( - "\t\tCY:\t\t\t\t\t\t", + sep="", ) + print("\t\tCY:\t\t\t\t\t\t", np.round(airplane.total_near_field_force_coefficients_wind_axes[1], 3), - sep="", - ) - print( - "\t\tCL:\t\t\t\t\t\t", + sep="", ) + print("\t\tCL:\t\t\t\t\t\t", np.round(airplane.total_near_field_force_coefficients_wind_axes[2], 3), - sep="", - ) + sep="", ) # Print out this airplane's moment coefficients. print("\n\tMoment Coefficients in Wind Axes:") - print( - "\t\tCl:\t\t\t\t\t\t", + print("\t\tCl:\t\t\t\t\t\t", np.round(airplane.total_near_field_moment_coefficients_wind_axes[0], 3), - sep="", - ) - print( - "\t\tCm:\t\t\t\t\t\t", + sep="", ) + print("\t\tCm:\t\t\t\t\t\t", np.round(airplane.total_near_field_moment_coefficients_wind_axes[1], 3), - sep="", - ) - print( - "\t\tCn:\t\t\t\t\t\t", + sep="", ) + print("\t\tCn:\t\t\t\t\t\t", np.round(airplane.total_near_field_moment_coefficients_wind_axes[2], 3), - sep="", - ) + sep="", ) # If the results from more airplanes are going to be printed, print new line # to separate them. @@ -949,17 +692,14 @@ def print_unsteady_results(unsteady_solver): forces = unsteady_solver.unsteady_problem.final_mean_near_field_forces_wind_axes moments = unsteady_solver.unsteady_problem.final_mean_near_field_moments_wind_axes force_coefficients = ( - unsteady_solver.unsteady_problem.final_mean_near_field_force_coefficients_wind_axes - ) + unsteady_solver.unsteady_problem.final_mean_near_field_force_coefficients_wind_axes) moment_coefficients = ( - unsteady_solver.unsteady_problem.final_mean_near_field_moment_coefficients_wind_axes - ) + unsteady_solver.unsteady_problem.final_mean_near_field_moment_coefficients_wind_axes) # For each airplane object, calculate and print the average force, moment, # force coefficient, and moment coefficient values. for airplane_id, airplane in enumerate( - unsteady_solver.steady_problems[0].airplanes - ): + unsteady_solver.steady_problems[0].airplanes): these_forces = forces[airplane_id] these_moments = moments[airplane_id] these_force_coefficients = force_coefficients[airplane_id] @@ -983,81 +723,34 @@ def print_unsteady_results(unsteady_solver): # Print out this airplane's average forces. print("\tFinal Forces in Wind Axes:") - print( - "\t\tInduced Drag:\t\t\t", - np.round(this_induced_drag, 3), - " N", - sep="", - ) - print( - "\t\tSide Force:\t\t\t\t", - np.round(this_side_force, 3), - " N", - sep="", - ) - print( - "\t\tLift:\t\t\t\t\t", - np.round(this_lift, 3), - " N", - sep="", - ) + print("\t\tInduced Drag:\t\t\t", np.round(this_induced_drag, 3), " N", sep="", ) + print("\t\tSide Force:\t\t\t\t", np.round(this_side_force, 3), " N", sep="", ) + print("\t\tLift:\t\t\t\t\t", np.round(this_lift, 3), " N", sep="", ) # Print out this airplane's average moments. print("\n\tFinal Moments in Wind Axes:") - print( - "\t\tRolling Moment:\t\t\t", - np.round(this_rolling_moment, 3), - " Nm", - sep="", - ) - print( - "\t\tPitching Moment:\t\t", - np.round(this_pitching_moment, 3), - " Nm", - sep="", - ) - print( - "\t\tYawing Moment:\t\t\t", - np.round(this_yawing_moment, 3), - " Nm", - sep="", - ) + print("\t\tRolling Moment:\t\t\t", np.round(this_rolling_moment, 3), " Nm", + sep="", ) + print("\t\tPitching Moment:\t\t", np.round(this_pitching_moment, 3), " Nm", + sep="", ) + print("\t\tYawing Moment:\t\t\t", np.round(this_yawing_moment, 3), " Nm", + sep="", ) # Print out this airplane's average force coefficients. print("\n\tFinal Force Coefficients in Wind Axes:") - print( - "\t\tCDi:\t\t\t\t\t", - np.round(this_induced_drag_coefficient, 3), - sep="", - ) - print( - "\t\tCY:\t\t\t\t\t\t", - np.round(this_side_force_coefficient, 3), - sep="", - ) - print( - "\t\tCL:\t\t\t\t\t\t", - np.round(this_lift_coefficient, 3), - sep="", - ) + print("\t\tCDi:\t\t\t\t\t", np.round(this_induced_drag_coefficient, 3), + sep="", ) + print("\t\tCY:\t\t\t\t\t\t", np.round(this_side_force_coefficient, 3), sep="", ) + print("\t\tCL:\t\t\t\t\t\t", np.round(this_lift_coefficient, 3), sep="", ) # Print out this airplane's average moment coefficients. print("\n\tFinal Moment Coefficients in Wind Axes:") - print( - "\t\tCl:\t\t\t\t\t\t", - np.round(this_rolling_moment_coefficient, 3), - sep="", - ) - print( - "\t\tCm:\t\t\t\t\t\t", - np.round(this_pitching_moment_coefficient, 3), - sep="", - ) - print( - "\t\tCn:\t\t\t\t\t\t", - np.round(this_yawing_moment_coefficient, 3), - sep="", - ) + print("\t\tCl:\t\t\t\t\t\t", np.round(this_rolling_moment_coefficient, 3), + sep="", ) + print("\t\tCm:\t\t\t\t\t\t", np.round(this_pitching_moment_coefficient, 3), + sep="", ) + print("\t\tCn:\t\t\t\t\t\t", np.round(this_yawing_moment_coefficient, 3), + sep="", ) # If the results from more airplanes are going to be printed, print new line # to separate them. @@ -1065,9 +758,7 @@ def print_unsteady_results(unsteady_solver): print("") -def get_panel_surfaces( - airplanes, -): +def get_panel_surfaces(airplanes, ): """This function returns a PolyData representation of the wing panel surfaces associated with all the airplanes in a given list. @@ -1093,23 +784,12 @@ def get_panel_surfaces( for panel in panels: # Stack this panel's vertices and faces. Look through the PolyData # documentation for more details. - panel_vertices_to_add = np.vstack( - ( - panel.front_left_vertex, - panel.front_right_vertex, - panel.back_right_vertex, - panel.back_left_vertex, - ) - ) + panel_vertices_to_add = np.vstack(( + panel.front_left_vertex, panel.front_right_vertex, + panel.back_right_vertex, panel.back_left_vertex,)) panel_face_to_add = np.array( - [ - 4, - (panel_num * 4), - (panel_num * 4) + 1, - (panel_num * 4) + 2, - (panel_num * 4) + 3, - ] - ) + [4, (panel_num * 4), (panel_num * 4) + 1, (panel_num * 4) + 2, + (panel_num * 4) + 3, ]) # Stack this panel's vertices and faces with the array of all the # vertices and faces. @@ -1137,17 +817,13 @@ def get_wake_ring_vortex_surfaces(solver, step): """ num_wake_ring_vortices = solver.num_wake_ring_vortices_list[step] wake_ring_vortex_front_right_vertices = ( - solver.wake_ring_vortex_front_right_vertices_list[step] - ) + solver.wake_ring_vortex_front_right_vertices_list[step]) wake_ring_vortex_front_left_vertices = ( - solver.wake_ring_vortex_front_left_vertices_list[step] - ) + solver.wake_ring_vortex_front_left_vertices_list[step]) wake_ring_vortex_back_left_vertices = ( - solver.wake_ring_vortex_back_left_vertices_list[step] - ) + solver.wake_ring_vortex_back_left_vertices_list[step]) wake_ring_vortex_back_right_vertices = ( - solver.wake_ring_vortex_back_right_vertices_list[step] - ) + solver.wake_ring_vortex_back_right_vertices_list[step]) # Initialize empty arrays to hold each wake ring vortex's vertices and its face. wake_ring_vortex_vertices = np.zeros((0, 3), dtype=int) @@ -1155,43 +831,27 @@ def get_wake_ring_vortex_surfaces(solver, step): for wake_ring_vortex_num in range(num_wake_ring_vortices): this_front_right_vertex = wake_ring_vortex_front_right_vertices[ - wake_ring_vortex_num - ] + wake_ring_vortex_num] this_front_left_vertex = wake_ring_vortex_front_left_vertices[ - wake_ring_vortex_num - ] + wake_ring_vortex_num] this_back_left_vertex = wake_ring_vortex_back_left_vertices[ - wake_ring_vortex_num - ] + wake_ring_vortex_num] this_back_right_vertex = wake_ring_vortex_back_right_vertices[ - wake_ring_vortex_num - ] - - wake_ring_vortex_vertices_to_add = np.vstack( - ( - this_front_left_vertex, - this_front_right_vertex, - this_back_right_vertex, - this_back_left_vertex, - ) - ) + wake_ring_vortex_num] + + wake_ring_vortex_vertices_to_add = np.vstack(( + this_front_left_vertex, this_front_right_vertex, this_back_right_vertex, + this_back_left_vertex,)) wake_ring_vortex_face_to_add = np.array( - [ - 4, - (wake_ring_vortex_num * 4), - (wake_ring_vortex_num * 4) + 1, - (wake_ring_vortex_num * 4) + 2, - (wake_ring_vortex_num * 4) + 3, - ] - ) + [4, (wake_ring_vortex_num * 4), (wake_ring_vortex_num * 4) + 1, + (wake_ring_vortex_num * 4) + 2, + (wake_ring_vortex_num * 4) + 3, ]) # Stack this wake ring vortex's vertices and faces. wake_ring_vortex_vertices = np.vstack( - (wake_ring_vortex_vertices, wake_ring_vortex_vertices_to_add) - ) + (wake_ring_vortex_vertices, wake_ring_vortex_vertices_to_add)) wake_ring_vortex_faces = np.hstack( - (wake_ring_vortex_faces, wake_ring_vortex_face_to_add) - ) + (wake_ring_vortex_faces, wake_ring_vortex_face_to_add)) # Increment the wake ring vortex counter. wake_ring_vortex_num += 1 @@ -1200,10 +860,7 @@ def get_wake_ring_vortex_surfaces(solver, step): return pv.PolyData(wake_ring_vortex_vertices, wake_ring_vortex_faces) -def get_scalars( - airplanes, - scalar_type, -): +def get_scalars(airplanes, scalar_type, ): """This function gets the coefficient values from a problem's airplane objects, and puts them into a 1D array to be used as scalars for display by other output methods. @@ -1241,17 +898,8 @@ def get_scalars( return scalars -def plot_scalars( - plotter, - these_scalars, - scalar_type, - min_scalar, - max_scalar, - color_map, - c_min, - c_max, - panel_surfaces, -): +def plot_scalars(plotter, these_scalars, scalar_type, min_scalar, max_scalar, color_map, + c_min, c_max, panel_surfaces, ): """This function plots a scalar bars, the mesh panels with a particular set of scalars, and labels for the minimum and maximum scalar values. @@ -1266,38 +914,15 @@ def plot_scalars( :param panel_surfaces: :return: """ - scalar_bar_args = dict( - title=scalar_type.title() + " Coefficient", - title_font_size=bar_title_font_size, - label_font_size=bar_label_font_size, - width=bar_width, - position_x=bar_position_x, - position_y=bar_position_y, - n_labels=bar_n_labels, - fmt="%.2f", - color=text_color, - ) - plotter.add_mesh( - panel_surfaces, - show_edges=True, - cmap=color_map, - clim=[c_min, c_max], - scalars=these_scalars, - smooth_shading=False, - scalar_bar_args=scalar_bar_args, - ) - plotter.add_text( - text="Max: " + str(max_scalar), - position=text_max_position, - font_size=text_font_size, - viewport=True, - color=text_color, - ) - plotter.add_text( - text="Min: " + str(min_scalar), - position=text_min_position, - font_size=text_font_size, - viewport=True, - color=text_color, - ) + scalar_bar_args = dict(title=scalar_type.title() + " Coefficient", + title_font_size=bar_title_font_size, label_font_size=bar_label_font_size, + width=bar_width, position_x=bar_position_x, position_y=bar_position_y, + n_labels=bar_n_labels, fmt="%.2f", color=text_color, ) + plotter.add_mesh(panel_surfaces, show_edges=True, cmap=color_map, + clim=[c_min, c_max], scalars=these_scalars, smooth_shading=False, + scalar_bar_args=scalar_bar_args, ) + plotter.add_text(text="Max: " + str(max_scalar), position=text_max_position, + font_size=text_font_size, viewport=True, color=text_color, ) + plotter.add_text(text="Min: " + str(min_scalar), position=text_min_position, + font_size=text_font_size, viewport=True, color=text_color, ) plotter.update_scalars(these_scalars) diff --git a/pterasoftware/panel.py b/pterasoftware/panel.py index 59e9dc0f..b3ebf232 100644 --- a/pterasoftware/panel.py +++ b/pterasoftware/panel.py @@ -39,15 +39,8 @@ class Panel: This class is not meant to be subclassed. """ - def __init__( - self, - front_right_vertex, - front_left_vertex, - back_left_vertex, - back_right_vertex, - is_leading_edge, - is_trailing_edge, - ): + def __init__(self, front_right_vertex, front_left_vertex, back_left_vertex, + back_right_vertex, is_leading_edge, is_trailing_edge, ): """This is the initialization method. :param front_right_vertex: 1D array with three elements @@ -141,8 +134,7 @@ def collocation_point(self): # Find the vector between the points three quarters of the way down the left # and right legs of the panel. three_quarter_chord_vector = ( - left_three_quarter_chord_mark - right_three_quarter_chord_mark - ) + left_three_quarter_chord_mark - right_three_quarter_chord_mark) # Find the collocation point, which is halfway between the points three # quarters of the way down the left and right legs of the panel. Then @@ -222,12 +214,11 @@ def calculate_normalized_induced_velocity(self, point): if self.ring_vortex is not None: normalized_induced_velocity += ( - self.ring_vortex.calculate_normalized_induced_velocity(point=point) - ) + self.ring_vortex.calculate_normalized_induced_velocity(point=point)) if self.horseshoe_vortex is not None: normalized_induced_velocity += ( - self.horseshoe_vortex.calculate_normalized_induced_velocity(point=point) - ) + self.horseshoe_vortex.calculate_normalized_induced_velocity( + point=point)) return normalized_induced_velocity @@ -251,8 +242,7 @@ def calculate_induced_velocity(self, point): induced_velocity += self.ring_vortex.calculate_induced_velocity(point=point) if self.horseshoe_vortex is not None: induced_velocity += self.horseshoe_vortex.calculate_induced_velocity( - point=point - ) + point=point) return induced_velocity diff --git a/pterasoftware/problems.py b/pterasoftware/problems.py index e22758bb..779eb426 100644 --- a/pterasoftware/problems.py +++ b/pterasoftware/problems.py @@ -82,9 +82,8 @@ def __init__(self, movement, only_final_results=False): if self.max_period == 0: self.first_averaging_step = self.num_steps - 1 else: - self.first_averaging_step = max( - 0, math.floor(self.num_steps - (self.max_period / self.delta_time)) - ) + self.first_averaging_step = max(0, + math.floor(self.num_steps - (self.max_period / self.delta_time))) # If the user only wants to calculate forces and moments for the final cycle # (for cyclic motion) or for the final time step (for static movement) set @@ -135,9 +134,8 @@ def __init__(self, movement, only_final_results=False): this_operating_point = movement.operating_points[step_id] # Initialize the steady problem object at this time step. - this_steady_problem = SteadyProblem( - airplanes=these_airplanes, operating_point=this_operating_point - ) + this_steady_problem = SteadyProblem(airplanes=these_airplanes, + operating_point=this_operating_point) # Append this steady problem to the list of steady problems. self.steady_problems.append(this_steady_problem) diff --git a/pterasoftware/steady_horseshoe_vortex_lattice_method.py b/pterasoftware/steady_horseshoe_vortex_lattice_method.py index 3f58f53b..0df4ebf4 100644 --- a/pterasoftware/steady_horseshoe_vortex_lattice_method.py +++ b/pterasoftware/steady_horseshoe_vortex_lattice_method.py @@ -78,8 +78,7 @@ def __init__(self, steady_problem): # Initialize attributes to hold aerodynamic data that pertains to this problem. self.wing_wing_influences = np.zeros((self.num_panels, self.num_panels)) self.freestream_velocity = ( - self.operating_point.calculate_freestream_velocity_geometry_axes() - ) + self.operating_point.calculate_freestream_velocity_geometry_axes()) self.freestream_wing_influences = np.zeros(self.num_panels) self.vortex_strengths = np.zeros(self.num_panels) self.panel_normal_directions = np.zeros((self.num_panels, 3)) @@ -111,8 +110,7 @@ def run(self, logging_level="Warning"): """ # Configure the problem's logger. logging_level_value = functions.convert_logging_level_name_to_value( - logging_level - ) + logging_level) logging.basicConfig(level=logging_level_value) # Initialize this problem's panels to have vortices congruent with this @@ -158,8 +156,7 @@ def initialize_panel_vortices(self): """ # Find the freestream direction in geometry axes. freestream_direction = ( - self.operating_point.calculate_freestream_direction_geometry_axes() - ) + self.operating_point.calculate_freestream_direction_geometry_axes()) # Iterate through each airplane's wings. for airplane in self.airplanes: @@ -185,10 +182,8 @@ def initialize_panel_vortices(self): panel.horseshoe_vortex = aerodynamics.HorseshoeVortex( finite_leg_origin=front_right_vortex_vertex, finite_leg_termination=front_left_vortex_vertex, - strength=None, - infinite_leg_direction=freestream_direction, - infinite_leg_length=infinite_leg_length, - ) + strength=None, infinite_leg_direction=freestream_direction, + infinite_leg_length=infinite_leg_length, ) def collapse_geometry(self): """This method converts attributes of the problem's geometry into 1D arrays. @@ -214,46 +209,32 @@ def collapse_geometry(self): # attributes. self.panels[global_panel_position] = panel self.panel_normal_directions[global_panel_position, :] = ( - panel.unit_normal - ) + panel.unit_normal) self.panel_areas[global_panel_position] = panel.area self.panel_collocation_points[global_panel_position, :] = ( - panel.collocation_point - ) + panel.collocation_point) self.panel_back_right_vortex_vertices[global_panel_position, :] = ( - panel.horseshoe_vortex.right_leg.origin - ) + panel.horseshoe_vortex.right_leg.origin) self.panel_front_right_vortex_vertices[global_panel_position, :] = ( - panel.horseshoe_vortex.right_leg.termination - ) + panel.horseshoe_vortex.right_leg.termination) self.panel_front_left_vortex_vertices[global_panel_position, :] = ( - panel.horseshoe_vortex.left_leg.origin - ) + panel.horseshoe_vortex.left_leg.origin) self.panel_back_left_vortex_vertices[global_panel_position, :] = ( - panel.horseshoe_vortex.left_leg.termination - ) + panel.horseshoe_vortex.left_leg.termination) self.panel_bound_vortex_centers[global_panel_position, :] = ( - panel.horseshoe_vortex.finite_leg.center - ) + panel.horseshoe_vortex.finite_leg.center) self.panel_bound_vortex_vectors[global_panel_position, :] = ( - panel.horseshoe_vortex.finite_leg.vector - ) + panel.horseshoe_vortex.finite_leg.vector) self.panel_moment_references[global_panel_position, :] = ( - airplane.xyz_ref - ) + airplane.xyz_ref) # Check if this panel is on the trailing edge. if panel.is_trailing_edge: # If it is, calculate it's streamline seed point and add it # to the solver's array of seed points. - self.seed_points = np.vstack( - ( - self.seed_points, - panel.back_left_vertex - + 0.5 - * (panel.back_right_vertex - panel.back_left_vertex), - ) - ) + self.seed_points = np.vstack((self.seed_points, + panel.back_left_vertex + 0.5 * ( + panel.back_right_vertex - panel.back_left_vertex),)) # Increment the global panel position. global_panel_position += 1 @@ -272,17 +253,13 @@ def calculate_wing_wing_influences(self): front_right_vortex_vertices=self.panel_front_right_vortex_vertices, front_left_vortex_vertices=self.panel_front_left_vortex_vertices, back_left_vortex_vertices=self.panel_back_left_vortex_vertices, - strengths=np.ones(self.num_panels), - ) + strengths=np.ones(self.num_panels), ) # Take the batch dot product of the normalized velocities with each panel's # normal direction. This is now the problem's matrix of wing-wing influence # coefficients. - self.wing_wing_influences = np.einsum( - "...k,...k->...", - induced_velocities, - np.expand_dims(self.panel_normal_directions, axis=1), - ) + self.wing_wing_influences = np.einsum("...k,...k->...", induced_velocities, + np.expand_dims(self.panel_normal_directions, axis=1), ) def calculate_vortex_strengths(self): """Solve for each panel's vortex strengths. @@ -290,13 +267,11 @@ def calculate_vortex_strengths(self): :return: None """ # Solve for the strength of each panel's vortex. - self.vortex_strengths = np.linalg.solve( - self.wing_wing_influences, -self.freestream_wing_influences - ) + self.vortex_strengths = np.linalg.solve(self.wing_wing_influences, + -self.freestream_wing_influences) # Iterate through the panels and update their vortex strengths. for panel_num, panel in enumerate(self.panels): - # Update this panel's horseshoe vortex strength. panel.horseshoe_vortex.update_strength(self.vortex_strengths[panel_num]) @@ -328,8 +303,7 @@ def calculate_solution_velocity(self, points): front_right_vortex_vertices=self.panel_front_right_vortex_vertices, front_left_vortex_vertices=self.panel_front_left_vortex_vertices, back_left_vortex_vertices=self.panel_back_left_vortex_vertices, - strengths=self.vortex_strengths, - ) + strengths=self.vortex_strengths, ) return induced_velocities + self.freestream_velocity @@ -344,27 +318,21 @@ def calculate_near_field_forces_and_moments(self): """ # Calculate the total velocity at every panel's bound vortex center. total_velocities = self.calculate_solution_velocity( - points=self.panel_bound_vortex_centers - ) + points=self.panel_bound_vortex_centers) # Calculate the near field force, in geometry axes, on each panel's bound # vortex. near_field_forces_geometry_axes = ( - self.operating_point.density - * np.expand_dims(self.vortex_strengths, axis=1) - * np.cross(total_velocities, self.panel_bound_vortex_vectors, axis=-1) - ) + self.operating_point.density * np.expand_dims(self.vortex_strengths, + axis=1) * np.cross( + total_velocities, self.panel_bound_vortex_vectors, axis=-1)) # Calculate the near field moments, in geometry axes, on each panel's bound # vortex. near_field_moments_geometry_axes = np.cross( self.panel_bound_vortex_centers - self.panel_moment_references, - near_field_forces_geometry_axes, - axis=-1, - ) + near_field_forces_geometry_axes, axis=-1, ) - functions.process_steady_solver_forces( - steady_solver=self, + functions.process_steady_solver_forces(steady_solver=self, near_field_forces_geometry_axes=near_field_forces_geometry_axes, - near_field_moments_geometry_axes=near_field_moments_geometry_axes, - ) + near_field_moments_geometry_axes=near_field_moments_geometry_axes, ) diff --git a/pterasoftware/steady_ring_vortex_lattice_method.py b/pterasoftware/steady_ring_vortex_lattice_method.py index 2d1e86cd..ea03cf30 100644 --- a/pterasoftware/steady_ring_vortex_lattice_method.py +++ b/pterasoftware/steady_ring_vortex_lattice_method.py @@ -79,8 +79,7 @@ def __init__(self, steady_problem): # Initialize attributes to hold aerodynamic data that pertains to this problem. self.wing_wing_influences = np.zeros((self.num_panels, self.num_panels)) self.freestream_velocity = ( - self.operating_point.calculate_freestream_velocity_geometry_axes() - ) + self.operating_point.calculate_freestream_velocity_geometry_axes()) self.freestream_wing_influences = np.zeros(self.num_panels) self.vortex_strengths = np.ones(self.num_panels) self.panel_normal_directions = np.zeros((self.num_panels, 3)) @@ -133,8 +132,7 @@ def run(self, logging_level="Warning"): """ # Configure the problem's logger. logging_level_value = functions.convert_logging_level_name_to_value( - logging_level - ) + logging_level) logging.basicConfig(level=logging_level_value) # Initialize this problem's panels to have vortices congruent with this @@ -191,8 +189,7 @@ def initialize_panel_vortices(self): """ # Find the freestream direction in geometry axes. freestream_direction = ( - self.operating_point.calculate_freestream_direction_geometry_axes() - ) + self.operating_point.calculate_freestream_direction_geometry_axes()) # Iterate through each airplane's wings. for airplane in self.airplanes: @@ -219,21 +216,16 @@ def initialize_panel_vortices(self): # whether the panel is along the trailing edge or not. if not panel.is_trailing_edge: next_chordwise_panel = wing.panels[ - chordwise_position + 1, spanwise_position - ] + chordwise_position + 1, spanwise_position] back_left_vortex_vertex = ( - next_chordwise_panel.front_left_vortex_vertex - ) + next_chordwise_panel.front_left_vortex_vertex) back_right_vortex_vertex = ( - next_chordwise_panel.front_right_vortex_vertex - ) + next_chordwise_panel.front_right_vortex_vertex) else: back_left_vortex_vertex = front_left_vortex_vertex + ( - panel.back_left_vertex - panel.front_left_vertex - ) + panel.back_left_vertex - panel.front_left_vertex) back_right_vortex_vertex = front_right_vortex_vertex + ( - panel.back_right_vertex - panel.front_right_vertex - ) + panel.back_right_vertex - panel.front_right_vertex) # If the panel is along the trailing edge, initialize its # horseshoe vortex. @@ -242,17 +234,14 @@ def initialize_panel_vortices(self): finite_leg_termination=back_left_vortex_vertex, strength=None, infinite_leg_direction=freestream_direction, - infinite_leg_length=infinite_leg_length, - ) + infinite_leg_length=infinite_leg_length, ) # Initialize the panel's ring vortex. panel.ring_vortex = aerodynamics.RingVortex( front_right_vertex=front_right_vortex_vertex, front_left_vertex=front_left_vortex_vertex, back_left_vertex=back_left_vortex_vertex, - back_right_vertex=back_right_vortex_vertex, - strength=None, - ) + back_right_vertex=back_right_vortex_vertex, strength=None, ) def collapse_geometry(self): """This method converts attributes of the problem's geometry into 1D @@ -276,30 +265,23 @@ def collapse_geometry(self): # Update the solver's list of attributes with this panel's # attributes. - functions.update_ring_vortex_solvers_panel_attributes( - solver=self, - global_panel_position=global_panel_position, - panel=panel, - airplane=airplane, - ) + functions.update_ring_vortex_solvers_panel_attributes(solver=self, + global_panel_position=global_panel_position, panel=panel, + airplane=airplane, ) if panel.is_trailing_edge: - # Also, update the attribute lists horseshoe vortex # attributes at this position with this panel's horseshoe # vortex attributes self.horseshoe_vortex_back_right_vertex[ - global_panel_position - ] = panel.horseshoe_vortex.right_leg.origin + global_panel_position] = ( + panel.horseshoe_vortex.right_leg.origin) self.horseshoe_vortex_front_right_vertex[ - global_panel_position - ] = panel.horseshoe_vortex.right_leg.termination + global_panel_position] = panel.horseshoe_vortex.right_leg.termination self.horseshoe_vortex_front_left_vertex[ - global_panel_position - ] = panel.horseshoe_vortex.left_leg.origin + global_panel_position] = panel.horseshoe_vortex.left_leg.origin self.horseshoe_vortex_back_left_vertex[ - global_panel_position - ] = panel.horseshoe_vortex.left_leg.termination + global_panel_position] = panel.horseshoe_vortex.left_leg.termination # Set the horseshoe vortex strength at this position to 1.0. # This will be updated after the correct vortex strengths are @@ -325,8 +307,7 @@ def calculate_wing_wing_influences(self): front_right_vortex_vertices=self.panel_front_right_vortex_vertices, front_left_vortex_vertices=self.panel_front_left_vortex_vertices, back_left_vortex_vertices=self.panel_back_left_vortex_vertices, - strengths=self.vortex_strengths, - ) + strengths=self.vortex_strengths, ) # Find the matrix of normalized velocities induced at every panel's # collocation point by every panel's horseshoe vortex. The answer is @@ -342,9 +323,7 @@ def calculate_wing_wing_influences(self): front_right_vortex_vertices=self.horseshoe_vortex_front_right_vertex, front_left_vortex_vertices=self.horseshoe_vortex_front_left_vertex, back_left_vortex_vertices=self.horseshoe_vortex_back_left_vertex, - strengths=self.horseshoe_vortex_strengths, - ) - ) + strengths=self.horseshoe_vortex_strengths, )) # Calculate the total normalized influences, which is the sum of the # influences by the ring vortices and by the horseshoe vortices. @@ -353,11 +332,8 @@ def calculate_wing_wing_influences(self): # Take the batch dot product of the normalized velocities with each panel's # normal direction. This is now the problem's matrix of wing-wing influence # coefficients. - self.wing_wing_influences = np.einsum( - "...k,...k->...", - total_influences, - np.expand_dims(self.panel_normal_directions, axis=1), - ) + self.wing_wing_influences = np.einsum("...k,...k->...", total_influences, + np.expand_dims(self.panel_normal_directions, axis=1), ) def calculate_vortex_strengths(self): """Solve for each panel's vortex strengths. @@ -365,9 +341,8 @@ def calculate_vortex_strengths(self): :return: None """ # Solve for the strength of each panel's vortex. - self.vortex_strengths = np.linalg.solve( - self.wing_wing_influences, -self.freestream_wing_influences - ) + self.vortex_strengths = np.linalg.solve(self.wing_wing_influences, + -self.freestream_wing_influences) # Iterate through the panels and update their vortex strengths. for panel_num in range(self.panels.size): @@ -383,8 +358,7 @@ def calculate_vortex_strengths(self): # Update the panel's horseshoe vortex strength. panel.horseshoe_vortex.update_strength(self.vortex_strengths[panel_num]) self.horseshoe_vortex_strengths[panel_num] = self.vortex_strengths[ - panel_num - ] + panel_num] def calculate_solution_velocity(self, points): """This function takes in a group of points. At every point, it finds the @@ -416,8 +390,7 @@ def calculate_solution_velocity(self, points): front_right_vortex_vertices=self.panel_front_right_vortex_vertices, front_left_vortex_vertices=self.panel_front_left_vortex_vertices, back_left_vortex_vertices=self.panel_back_left_vortex_vertices, - strengths=self.vortex_strengths, - ) + strengths=self.vortex_strengths, ) # Find the matrix of velocities induced at every panel's collocation point by # every panel's horseshoe vortex. This can be called in a batch fashion, @@ -426,15 +399,12 @@ def calculate_solution_velocity(self, points): # zero, which eliminates their effects. The effect of every horseshoe vortex # on each point will be summed. horseshoe_vortex_influences = ( - aerodynamics.collapsed_velocities_from_horseshoe_vortices( - points=points, + aerodynamics.collapsed_velocities_from_horseshoe_vortices(points=points, back_right_vortex_vertices=self.horseshoe_vortex_back_right_vertex, front_right_vortex_vertices=self.horseshoe_vortex_front_right_vertex, front_left_vortex_vertices=self.horseshoe_vortex_front_left_vertex, back_left_vortex_vertices=self.horseshoe_vortex_back_left_vertex, - strengths=self.horseshoe_vortex_strengths, - ) - ) + strengths=self.horseshoe_vortex_strengths, )) # Find the total influence of the vortices, which is the sum of the influence # due to the ring vortices and the horseshoe vortices. @@ -483,25 +453,21 @@ def calculate_near_field_forces_and_moments(self): # Change the effective right vortex line strength from zero # to this panel's ring vortex's strength. effective_right_vortex_line_strengths[global_panel_position] = ( - self.vortex_strengths[global_panel_position] - ) + self.vortex_strengths[global_panel_position]) else: # Get the panel directly to the right of this panel. panel_to_right = wing.panels[ - panel.local_chordwise_position, - panel.local_spanwise_position + 1, - ] + panel.local_chordwise_position, panel.local_spanwise_position + 1,] # Change the effective right vortex line strength from zero # to the difference between this panel's ring vortex's # strength, and the ring vortex strength of the panel to the # right of it. effective_right_vortex_line_strengths[global_panel_position] = ( - self.vortex_strengths[global_panel_position] - - panel_to_right.ring_vortex.strength - ) + self.vortex_strengths[ + global_panel_position] - panel_to_right.ring_vortex.strength) # Check if this panel is on its wing's leading edge. if panel.is_leading_edge: @@ -509,24 +475,20 @@ def calculate_near_field_forces_and_moments(self): # Change the effective front vortex line strength from zero # to this panel's ring vortex's strength. effective_front_vortex_line_strengths[global_panel_position] = ( - self.vortex_strengths[global_panel_position] - ) + self.vortex_strengths[global_panel_position]) else: # Get the panel directly in front of this panel. panel_to_front = wing.panels[ - panel.local_chordwise_position - 1, - panel.local_spanwise_position, - ] + panel.local_chordwise_position - 1, panel.local_spanwise_position,] # Change the effective front vortex line strength from zero # to the difference between this panel's ring vortex's # strength, and the ring vortex strength of the panel in # front of it. effective_front_vortex_line_strengths[global_panel_position] = ( - self.vortex_strengths[global_panel_position] - - panel_to_front.ring_vortex.strength - ) + self.vortex_strengths[ + global_panel_position] - panel_to_front.ring_vortex.strength) # Check if this panel is on its wing's left edge. if panel.is_left_edge: @@ -534,23 +496,19 @@ def calculate_near_field_forces_and_moments(self): # Change the effective left vortex line strength from zero to # this panel's ring vortex's strength. effective_left_vortex_line_strengths[global_panel_position] = ( - self.vortex_strengths[global_panel_position] - ) + self.vortex_strengths[global_panel_position]) else: # Get the panel directly to the left of this panel. panel_to_left = wing.panels[ - panel.local_chordwise_position, - panel.local_spanwise_position - 1, - ] + panel.local_chordwise_position, panel.local_spanwise_position - 1,] # Change the effective left vortex line strength from zero to # the difference between this panel's ring vortex's strength, # and the ring vortex strength of the panel to the left of it. effective_left_vortex_line_strengths[global_panel_position] = ( - self.vortex_strengths[global_panel_position] - - panel_to_left.ring_vortex.strength - ) + self.vortex_strengths[ + global_panel_position] - panel_to_left.ring_vortex.strength) # Increment the global panel position. global_panel_position += 1 @@ -558,82 +516,53 @@ def calculate_near_field_forces_and_moments(self): # Calculate the solution velocities at the centers of the panel's front leg, # left leg, and right leg. velocities_at_ring_vortex_front_leg_centers = self.calculate_solution_velocity( - points=self.panel_front_vortex_centers - ) + points=self.panel_front_vortex_centers) velocities_at_ring_vortex_left_leg_centers = self.calculate_solution_velocity( - points=self.panel_left_vortex_centers - ) + points=self.panel_left_vortex_centers) velocities_at_ring_vortex_right_leg_centers = self.calculate_solution_velocity( - points=self.panel_right_vortex_centers - ) + points=self.panel_right_vortex_centers) # Using the effective line vortex strengths, and the Kutta-Joukowski theorem # to find the near field force in geometry axes on the front leg, left leg, # and right leg. near_field_forces_on_ring_vortex_right_legs_geometry_axes = ( - self.operating_point.density - * np.expand_dims(effective_right_vortex_line_strengths, axis=1) - * np.cross( - velocities_at_ring_vortex_right_leg_centers, - self.panel_right_vortex_vectors, - axis=-1, - ) - ) + self.operating_point.density * np.expand_dims( + effective_right_vortex_line_strengths, axis=1) * np.cross( + velocities_at_ring_vortex_right_leg_centers, + self.panel_right_vortex_vectors, axis=-1, )) near_field_forces_on_ring_vortex_front_legs_geometry_axes = ( - self.operating_point.density - * np.expand_dims(effective_front_vortex_line_strengths, axis=1) - * np.cross( - velocities_at_ring_vortex_front_leg_centers, - self.panel_front_vortex_vectors, - axis=-1, - ) - ) + self.operating_point.density * np.expand_dims( + effective_front_vortex_line_strengths, axis=1) * np.cross( + velocities_at_ring_vortex_front_leg_centers, + self.panel_front_vortex_vectors, axis=-1, )) near_field_forces_on_ring_vortex_left_legs_geometry_axes = ( - self.operating_point.density - * np.expand_dims(effective_left_vortex_line_strengths, axis=1) - * np.cross( - velocities_at_ring_vortex_left_leg_centers, - self.panel_left_vortex_vectors, - axis=-1, - ) - ) + self.operating_point.density * np.expand_dims( + effective_left_vortex_line_strengths, axis=1) * np.cross( + velocities_at_ring_vortex_left_leg_centers, self.panel_left_vortex_vectors, + axis=-1, )) # Sum the forces on the legs to calculate the total near field force, # in geometry axes, on each panel. near_field_forces_geometry_axes = ( - near_field_forces_on_ring_vortex_front_legs_geometry_axes - + near_field_forces_on_ring_vortex_left_legs_geometry_axes - + near_field_forces_on_ring_vortex_right_legs_geometry_axes - ) + near_field_forces_on_ring_vortex_front_legs_geometry_axes + near_field_forces_on_ring_vortex_left_legs_geometry_axes + near_field_forces_on_ring_vortex_right_legs_geometry_axes) # Find the near field moment in geometry axes on the front leg, left leg, # and right leg. near_field_moments_on_ring_vortex_front_legs_geometry_axes = np.cross( self.panel_front_vortex_centers - self.panel_moment_references, - near_field_forces_on_ring_vortex_front_legs_geometry_axes, - axis=-1, - ) + near_field_forces_on_ring_vortex_front_legs_geometry_axes, axis=-1, ) near_field_moments_on_ring_vortex_left_legs_geometry_axes = np.cross( self.panel_left_vortex_centers - self.panel_moment_references, - near_field_forces_on_ring_vortex_left_legs_geometry_axes, - axis=-1, - ) + near_field_forces_on_ring_vortex_left_legs_geometry_axes, axis=-1, ) near_field_moments_on_ring_vortex_right_legs_geometry_axes = np.cross( self.panel_right_vortex_centers - self.panel_moment_references, - near_field_forces_on_ring_vortex_right_legs_geometry_axes, - axis=-1, - ) + near_field_forces_on_ring_vortex_right_legs_geometry_axes, axis=-1, ) # Sum the moments on the legs to calculate the total near field moment, # in geometry axes, on each panel. near_field_moments_geometry_axes = ( - near_field_moments_on_ring_vortex_front_legs_geometry_axes - + near_field_moments_on_ring_vortex_left_legs_geometry_axes - + near_field_moments_on_ring_vortex_right_legs_geometry_axes - ) + near_field_moments_on_ring_vortex_front_legs_geometry_axes + near_field_moments_on_ring_vortex_left_legs_geometry_axes + near_field_moments_on_ring_vortex_right_legs_geometry_axes) - functions.process_steady_solver_forces( - steady_solver=self, + functions.process_steady_solver_forces(steady_solver=self, near_field_forces_geometry_axes=near_field_forces_geometry_axes, - near_field_moments_geometry_axes=near_field_moments_geometry_axes, - ) + near_field_moments_geometry_axes=near_field_moments_geometry_axes, ) diff --git a/pterasoftware/trim.py b/pterasoftware/trim.py index dd4d33b2..9d558f88 100644 --- a/pterasoftware/trim.py +++ b/pterasoftware/trim.py @@ -31,21 +31,13 @@ from . import movement from . import problems - trim_logger = logging.getLogger("trim") trim_logger.setLevel(logging.DEBUG) # ToDo: Document this function. -def analyze_steady_trim( - problem, - velocity_bounds, - alpha_bounds, - beta_bounds, - external_thrust_bounds, - objective_cut_off=0.01, - num_calls=100, -): +def analyze_steady_trim(problem, velocity_bounds, alpha_bounds, beta_bounds, + external_thrust_bounds, objective_cut_off=0.01, num_calls=100, ): """This function attempts to calculate a trim condition of a steady solver by varying the operating point's velocity, angle of attack, angle of sideslip, and external thrust until the net force and net moment on the aircraft are @@ -56,8 +48,7 @@ def analyze_steady_trim( """ if len(problem.airplanes) != 1: trim_logger.error( - "The problem objects for trim analyses must have only one airplane." - ) + "The problem objects for trim analyses must have only one airplane.") weight = problem.airplanes[0].weight base_velocity = problem.operating_point.velocity @@ -68,24 +59,18 @@ def analyze_steady_trim( if base_velocity < velocity_bounds[0] or base_velocity > velocity_bounds[1]: raise Exception( "The operating point's velocity must be within the specified velocity " - "bounds." - ) + "bounds.") if base_alpha < alpha_bounds[0] or base_alpha > alpha_bounds[1]: raise Exception( - "The operating point's alpha must be within the specified alpha bounds." - ) + "The operating point's alpha must be within the specified alpha bounds.") if base_beta < beta_bounds[0] or base_beta > beta_bounds[1]: raise Exception( - "The operating point's beta must be within the specified beta bounds." - ) - if ( - base_external_thrust < external_thrust_bounds[0] - or base_external_thrust > external_thrust_bounds[1] - ): + "The operating point's beta must be within the specified beta bounds.") + if (base_external_thrust < external_thrust_bounds[0] or base_external_thrust > + external_thrust_bounds[1]): raise Exception( "The operating point's external thrust must be within the specified " - "external thrust bounds." - ) + "external thrust bounds.") current_arguments = [np.nan, np.nan, np.nan, np.nan] @@ -111,21 +96,18 @@ def objective_function(arguments): external_forces = np.array([external_thrust, 0, weight]) external_force_coefficients = external_forces / dynamic_pressure / s_ref - solver = steady_horseshoe_vortex_lattice_method.SteadyHorseshoeVortexLatticeMethodSolver( - steady_problem=problem - ) + solver = (steady_horseshoe_vortex_lattice_method + .SteadyHorseshoeVortexLatticeMethodSolver( + steady_problem=problem)) solver.run() airplane = solver.airplanes[0] net_force_coefficient = np.linalg.norm( - airplane.total_near_field_force_coefficients_wind_axes - - external_force_coefficients - ) + airplane.total_near_field_force_coefficients_wind_axes - external_force_coefficients) net_moment_coefficient = np.linalg.norm( - airplane.total_near_field_moment_coefficients_wind_axes - ) + airplane.total_near_field_moment_coefficients_wind_axes) objective = (abs(net_force_coefficient) + abs(net_moment_coefficient)) / 2 @@ -136,15 +118,7 @@ def objective_function(arguments): o_str = str(round(objective, 3)) state_msg = ( - "State: velocity=" - + v_str - + ", alpha=" - + a_str - + ", beta=" - + b_str - + ", external thrust=" - + t_str - ) + "State: velocity=" + v_str + ", alpha=" + a_str + ", beta=" + b_str + ", external thrust=" + t_str) obj_msg = "Objective: " + o_str trim_logger.info(state_msg) @@ -159,58 +133,36 @@ def objective_function(arguments): return objective initial_guess = np.array( - [base_velocity, base_alpha, base_beta, base_external_thrust] - ) + [base_velocity, base_alpha, base_beta, base_external_thrust]) bounds = (velocity_bounds, alpha_bounds, beta_bounds, external_thrust_bounds) trim_logger.info("Starting local search.") try: - scipy.optimize.minimize( - fun=objective_function, - x0=initial_guess, - bounds=bounds, - method="L-BFGS-B", - options={"maxfun": num_calls, "eps": 0.01}, - ) + scipy.optimize.minimize(fun=objective_function, x0=initial_guess, bounds=bounds, + method="L-BFGS-B", options={"maxfun": num_calls, "eps": 0.01}, ) except StopIteration: trim_logger.info("Acceptable value reached with local search.") return current_arguments trim_logger.warning( - "No acceptable value reached with local search. Starting global search." - ) + "No acceptable value reached with local search. Starting global search.") try: - scipy.optimize.dual_annealing( - func=objective_function, - bounds=bounds, - x0=initial_guess, - maxfun=num_calls, - minimizer_kwargs={ - "method": "L-BFGS-B", - "options": {"maxfun": num_calls, "eps": 0.01}, - }, - ) + scipy.optimize.dual_annealing(func=objective_function, bounds=bounds, + x0=initial_guess, maxfun=num_calls, minimizer_kwargs={"method": "L-BFGS-B", + "options": {"maxfun": num_calls, "eps": 0.01}, }, ) except StopIteration: trim_logger.info("Acceptable global minima found.") return current_arguments trim_logger.critical( "No trim condition found. Try increasing the bounds and the maximum number of " - "iterations." - ) + "iterations.") return [np.nan, np.nan, np.nan, np.nan] # ToDo: Document this function. -def analyze_unsteady_trim( - airplane_movement, - operating_point, - velocity_bounds, - alpha_bounds, - beta_bounds, - objective_cut_off=0.01, - num_calls=100, -): +def analyze_unsteady_trim(airplane_movement, operating_point, velocity_bounds, + alpha_bounds, beta_bounds, objective_cut_off=0.01, num_calls=100, ): """This function attempts to calculate a trim condition of an unsteady solver by varying the operating point's velocity, angle of attack, angle of sideslip, and external thrust until the net cycle-averaged force and net cycle-averaged @@ -227,16 +179,13 @@ def analyze_unsteady_trim( if base_velocity < velocity_bounds[0] or base_velocity > velocity_bounds[1]: trim_logger.error( "The operating point's velocity must be within the specified velocity " - "bounds." - ) + "bounds.") if base_alpha < alpha_bounds[0] or base_alpha > alpha_bounds[1]: trim_logger.error( - "The operating point's alpha must be within the specified alpha bounds." - ) + "The operating point's alpha must be within the specified alpha bounds.") if base_beta < beta_bounds[0] or base_beta > beta_bounds[1]: trim_logger.error( - "The operating point's beta must be within the specified beta bounds." - ) + "The operating point's beta must be within the specified beta bounds.") current_arguments = [np.nan, np.nan, np.nan] @@ -263,36 +212,27 @@ def objective_function(arguments): external_force_coefficients = external_forces / dynamic_pressure / s_ref operating_point_movement = movement.OperatingPointMovement( - base_operating_point=operating_point - ) + base_operating_point=operating_point) - this_movement = movement.Movement( - airplane_movements=[airplane_movement], - operating_point_movement=operating_point_movement, - ) + this_movement = movement.Movement(airplane_movements=[airplane_movement], + operating_point_movement=operating_point_movement, ) - this_problem = problems.UnsteadyProblem( - movement=this_movement, only_final_results=True - ) + this_problem = problems.UnsteadyProblem(movement=this_movement, + only_final_results=True) this_solver = ( unsteady_ring_vortex_lattice_method.UnsteadyRingVortexLatticeMethodSolver( - unsteady_problem=this_problem - ) - ) + unsteady_problem=this_problem)) this_solver.run(logging_level="Critical") force_coefficients = ( - this_solver.unsteady_problem.final_total_near_field_force_coefficients_wind_axes - ) + this_solver.unsteady_problem.final_total_near_field_force_coefficients_wind_axes) moment_coefficients = ( - this_solver.unsteady_problem.final_total_near_field_moment_coefficients_wind_axes - ) + this_solver.unsteady_problem.final_total_near_field_moment_coefficients_wind_axes) net_force_coefficients = np.linalg.norm( - force_coefficients - external_force_coefficients - ) + force_coefficients - external_force_coefficients) net_moment_coefficients = np.linalg.norm(moment_coefficients) objective = (abs(net_force_coefficients) + abs(net_moment_coefficients)) / 2 @@ -321,37 +261,23 @@ def objective_function(arguments): trim_logger.info("Starting local search.") try: - scipy.optimize.minimize( - fun=objective_function, - x0=initial_guess, - bounds=bounds, - method="L-BFGS-B", - options={"maxfun": num_calls, "eps": 0.01}, - ) + scipy.optimize.minimize(fun=objective_function, x0=initial_guess, bounds=bounds, + method="L-BFGS-B", options={"maxfun": num_calls, "eps": 0.01}, ) except StopIteration: trim_logger.info("Acceptable value reached with local search.") return current_arguments trim_logger.warning( - "No acceptable value reached with local search. Starting global search." - ) + "No acceptable value reached with local search. Starting global search.") try: - scipy.optimize.dual_annealing( - func=objective_function, - bounds=bounds, - x0=initial_guess, - maxfun=num_calls, - minimizer_kwargs={ - "method": "L-BFGS-B", - "options": {"maxfun": num_calls, "eps": 0.01}, - }, - ) + scipy.optimize.dual_annealing(func=objective_function, bounds=bounds, + x0=initial_guess, maxfun=num_calls, minimizer_kwargs={"method": "L-BFGS-B", + "options": {"maxfun": num_calls, "eps": 0.01}, }, ) except StopIteration: trim_logger.info("Acceptable global minima found.") return current_arguments trim_logger.critical( "No trim condition found. Try increasing the bounds and the maximum number of " - "iterations." - ) + "iterations.") return [np.nan, np.nan, np.nan] diff --git a/pterasoftware/ui_resources/main_window.py b/pterasoftware/ui_resources/main_window.py index 653360a6..a8450c28 100644 --- a/pterasoftware/ui_resources/main_window.py +++ b/pterasoftware/ui_resources/main_window.py @@ -1208,7 +1208,8 @@ def retranslateUi(self, MainWindowDesign): self.Logo.setText( QCoreApplication.translate( "MainWindowDesign", - '

', + "

', None, ) ) diff --git a/pterasoftware/ui_resources/main_window.ui b/pterasoftware/ui_resources/main_window.ui index 8ce09d1a..5ada5e78 100644 --- a/pterasoftware/ui_resources/main_window.ui +++ b/pterasoftware/ui_resources/main_window.ui @@ -1,1362 +1,1788 @@ - MainWindowDesign - - - - 0 - 0 - 1145 - 880 - - - - - 0 - 0 - - - - - 1052 - 880 - - - - MainWindow - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - - - 0 - 0 - - - - -1 - - - 0 - - - - Aircraft Parameters - - - - - - - - - 0 - 0 - - - - - 12 - 75 - true - + MainWindowDesign + + + + 0 + 0 + 1145 + 880 + + + + + 0 + 0 + + + + + 1052 + 880 + + + + MainWindow + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + 0 + 0 + + + + -1 + + + 0 + + + + Aircraft Parameters + + + + + + + + + 0 + 0 + + + + + 12 + 75 + true + + + + Aircraft + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 10 + 75 + true + + + + Name + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 10 + 75 + true + + + + CofG Position + + + + + + + + 0 + 0 + + + + + 10 + + + + x + + + + + + + + 0 + 0 + + + + + 149 + 0 + + + + + + + + + 0 + 0 + + + + + 10 + + + + y + + + + + + + + 0 + 0 + + + + + 149 + 0 + + + + + + + + + 0 + 0 + + + + Z + + + + + + + + 0 + 0 + + + + + 149 + 0 + + + + + + + + Other Options go here + + + + Qt::AlignCenter + + + + + + + Save Aircraft + + + + + + + + + + + + + + + x + + + + + + + y + + + + + + + + 0 + 0 + + + + + 10 + 75 + true + + + + Leading Edge Location + + + + + + + + -33.000000000000000 + + + + 45.000000000000000 + + + + + + + + + + + 49.000000000000000 + + + + + + + + + 0 + 0 + + + + Number of Panels + + + + + + + + 49.000000000000000 + + + + + + + + Symmetric? + + + + + + + Panel Spacing Option + + + + + + + + + 0 + 0 + + + + + 10 + 75 + true + + + + Chord-wise Panels + + + + + + + + + Uniform + + + + + Cosine + + + + + Reverse Cosine + + + + + + + + + <html><head/><body><p>z</p></body></html> + + + + + + + -50.000000000000000 + + + + 44.000000000000000 + + + + + + + + + 0 + 0 + + + + + 12 + 75 + true + + + + Qt::PreventContextMenu + + + + QFrame::Sunken + + + 1 + + + Main Wing + + + Qt::AlignCenter + + + + + + + + + + + 0 + 0 + + + + 1 + + + + + 318 + 0 + + + + Wing Root Cross Section + + + + + + + + + + + + + 49.000000000000000 + + + + + + + + + -50.000000000000000 + + + + + 44.000000000000000 + + + + + + + + + + + + + 0 + + + 0 + + + + + + 0 + + 0 + + + + + Number + of Panels + + + + + + + + x + + + + + + + + + + 10 + + 75 + + true + + + + + Chord + and Control + Surface + + + + + + + + Chord + + + + + Qt::AlignCenter + + + + + + + + + + + Control + Surface + Deflection + + + + + Qt::AlignCenter + + + + + + + + y + + + + + + + + + + + + + 0 + + + 0 + + + + + + + 10 + + 75 + + true + + + + + + Span-wise + panels + + + + + + + + <html><head/><body><p>z</p></body></html> + + + + + + + Panel + Spacing + Option + + + + + + + + + -33.000000000000000 + + + + + 45.000000000000000 + + + + + + + + + + 0 + + + 0 + + + + + + + 10 + + 75 + + true + + + + + Leading + Edge + Location + + + + + + + + Control + Surface + Hinge Point + + + + + + + + + + + + 49.000000000000000 + + + + + + + + + + Uniform + + + + + + + Cosine + + + + + + + Reverse + Cosine + + + + + + + + + + Symmetric? + + + + + + + + + + 0 + + + 0 + + + + + + + 12 + + 75 + + true + + + + + + Qt::PreventContextMenu + + + + + QFrame::Sunken + + + + 1 + + + + Root + Cross + Section + + + + + Qt::AlignCenter + + + + + + + + + + + + + 0 + + + 0 + + + + + 0 + + + + + Aerofoil + + + + + Qt::AlignCenter + + + + + + + + + + + + Wing Tip Cross Section + + + + + + + + + + + + + + + + 49.000000000000000 + + + + + + + + y + + + + + + + + Panel + Spacing + Option + + + + + + + + + + + + 49.000000000000000 + + + + + + + + + + 0 + + + 0 + + + + + + 0 + + 0 + + + + + Number + of Panels + + + + + + + + + + Uniform + + + + + + + Cosine + + + + + + + Reverse + Cosine + + + + + + + + + + + 10 + + 75 + + true + + + + + Chord + and Control + Surface + + + + + + + + + + 0 + + + 0 + + + + + + + 12 + + 75 + + true + + + + + + Qt::PreventContextMenu + + + + + QFrame::Sunken + + + + 1 + + + + Root + Cross + Section + + + + + Qt::AlignCenter + + + + + + + + + + + Control + Surface + Hinge Point + + + + + + + + + -50.000000000000000 + + + + + 44.000000000000000 + + + + + + + + + + 0 + + + 0 + + + + + + + 10 + + 75 + + true + + + + + Leading + Edge + Location + + + + + + + + + Symmetric? + + + + + + + + x + + + + + + + + Chord + + + + + Qt::AlignCenter + + + + + + + + + + 0 + + + 0 + + + + + + + 10 + + 75 + + true + + + + + + Span-wise + Panels + + + + + + + + <html><head/><body><p>z</p></body></html> + + + + + + + + + + + -33.000000000000000 + + + + + 45.000000000000000 + + + + + + + + + + + Control + Surface + Deflection + + + + + Qt::AlignCenter + + + + + + + + + Aerofoil + + + + + Qt::AlignCenter + + + + + + + + + + + + + + + + Model + + + + + + Controller + + + + + + + + + Materials + + + + + Boundary Conditions + + + + + Solution + + + + + Visualiser Settings + + + + + 0 + 0 + 331 + 235 + + + + + + + + + + Move + + + + + + + 49.000000000000000 + + + + + + + Homing + + + + + + + -50.000000000000000 + + + 44.000000000000000 + + + + + + + -33.000000000000000 + + + 45.000000000000000 + + + + + + + + + + 49.000000000000000 + + + + + + + -45.000000000000000 + + + 0.000000000000000 + + + + + + + Injection + + + + + + + x + + + + + + + y + + + + + + + <html><head/><body><p>z</p></body></html> + + + + + + + <html><head/><body><p>β</p></body></html> + + + + + + + d + + + + + + + + + + + + false + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + <html><head/><body><p><img + src="docs/Black_Text_Logo.png"/></p></body></html> + + + + Qt::AlignCenter + + + + + + + + + + 0 + + + 24 + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Qt::NoContextMenu + + + Generate Results + + + + + + + Plot Visualisation + + + + + + + Terminal Output + + + + + + + + 5 + 0 + + + + + + + + + + + + + 0 + 0 + 1145 + 21 + + + + Help + + + + Examples + + + + + + + + + + + + + + + + + + + + + File + + + + + + + Aircraft + + + + + + + + + - Aircraft - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - - 10 - 75 - true - + About + + - Name - - - - - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - - 10 - 75 - true - + Manual + + - CofG Position - - - - - - - - 0 - 0 - - - - - 10 - + Quit + + - x - - - - - - - - 0 - 0 - - - - - 149 - 0 - + Import Data - - - - - - - 0 - 0 - + + + + Export Data - - - 10 - + + + + Preset Aircraft List + + - y + Example 1: Analyze steady trim example - - - - - - - 0 - 0 - + + + + Example 2: Analyze unsteady trim example - - - 149 - 0 - + + + + Example 3: Steady convergence example - - - - - - - 0 - 0 - + + + + Example 4: Steady horseshoe vortex lattice method solver + + + - Z + Example 5: Steady ring vortex lattice method solver static + - - - - - - - 0 - 0 - + + + + Example 6: Unsteady ring vortex lattice method solver static + - - - 149 - 0 - + + + + Example 7: Unsteady ring vortex lattice method solver variable + - - - - + + - Other Options go here + Example 8: Unsteady ring vortex lattice method solver variable + formation + - - Qt::AlignCenter + + + + Example 9: Unsteady static convergence example - - - - + + - Save Aircraft + Example 10: Unsteady variable convergence example - - - - - - - - - - - - - - x - - - - - - - y - - - - - - - - 0 - 0 - - - - - 10 - 75 - true - - - - Leading Edge Location - - - - - - - -33.000000000000000 - - - 45.000000000000000 - - - - - - - - - - 49.000000000000000 - - - - - - - - 0 - 0 - - - - Number of Panels - - - - - - - 49.000000000000000 - - - - - - - Symmetric? - - - - - - - Panel Spacing Option - - - - - - - - 0 - 0 - - - - - 10 - 75 - true - - - - Chord-wise Panels - - - - - - - - Uniform - - - - - Cosine - - - - - Reverse Cosine - - - - - - - - <html><head/><body><p>z</p></body></html> - - - - - - - -50.000000000000000 - - - 44.000000000000000 - - - - - - - - 0 - 0 - - - - - 12 - 75 - true - - - - Qt::PreventContextMenu - - - QFrame::Sunken - - - 1 - - - Main Wing - - - Qt::AlignCenter - - - - - - - - - - - 0 - 0 - - - - 1 - - - - - 318 - 0 - - - - Wing Root Cross Section - - - - - - - - - - - - 49.000000000000000 - - - - - - - -50.000000000000000 - - - 44.000000000000000 - - - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - Number of Panels - - - - - - - x - - - - - - - - 10 - 75 - true - - - - Chord and Control Surface - - - - - - - Chord - - - Qt::AlignCenter - - - - - - - - - - Control Surface Deflection - - - Qt::AlignCenter - - - - - - - y - - - - - - - - - - - 0 - 0 - - - - - 10 - 75 - true - - - - Span-wise panels - - - - - - - <html><head/><body><p>z</p></body></html> - - - - - - - Panel Spacing Option - - - - - - - -33.000000000000000 - - - 45.000000000000000 - - - - - - - - 0 - 0 - - - - - 10 - 75 - true - - - - Leading Edge Location - - - - - - - Control Surface Hinge Point - - - - - - - - - - 49.000000000000000 - - - - - - - - Uniform - - - - - Cosine - - - - - Reverse Cosine - - - - - - - - Symmetric? - - - - - - - - 0 - 0 - - - - - 12 - 75 - true - - - - Qt::PreventContextMenu - - - QFrame::Sunken - - - 1 - - - Root Cross Section - - - Qt::AlignCenter - - - - - - - - - - - 0 - 0 - - - - 0 - - - Aerofoil - - - Qt::AlignCenter - - - - - - - - - - - Wing Tip Cross Section - - - - - - - - - - - - - - - 49.000000000000000 - - - - - - - y - - - - - - - Panel Spacing Option - - - - - - - - - - 49.000000000000000 - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - Number of Panels - - - - - - - - Uniform - - - - - Cosine - - - - - Reverse Cosine - - - - - - - - - 10 - 75 - true - - - - Chord and Control Surface - - - - - - - - 0 - 0 - - - - - 12 - 75 - true - - - - Qt::PreventContextMenu - - - QFrame::Sunken - - - 1 - - - Root Cross Section - - - Qt::AlignCenter - - - - - - - - - - Control Surface Hinge Point - - - - - - - -50.000000000000000 - - - 44.000000000000000 - - - - - - - - 0 - 0 - - - - - 10 - 75 - true - - - - Leading Edge Location - - - - - - - Symmetric? - - - - - - - x - - - - - - - Chord - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - - 10 - 75 - true - - - - Span-wise Panels - - - - - - - <html><head/><body><p>z</p></body></html> - - - - - - - - - - -33.000000000000000 - - - 45.000000000000000 - - - - - - - - - - Control Surface Deflection - - - Qt::AlignCenter - - - - - - - Aerofoil - - - Qt::AlignCenter - - - - - - - - - - - - - - - Model - - - - - - Controller - - - - - - - - - Materials - - - - - Boundary Conditions - - - - - Solution - - - - - Visualiser Settings - - - - - 0 - 0 - 331 - 235 - - - - - - - - - - Move - - - - - - - 49.000000000000000 - - - - - - - Homing - - - - - - - -50.000000000000000 - - - 44.000000000000000 - - - - - - - -33.000000000000000 - - - 45.000000000000000 - - - - - - - - - - 49.000000000000000 - - - - - - - -45.000000000000000 - - - 0.000000000000000 - - - - - - - Injection - - - - - - - x - - - - - - - y - - - - - - - <html><head/><body><p>z</p></body></html> - - - - - - - <html><head/><body><p>β</p></body></html> - - - - - - - d - - - - - - - - - - - - false - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - <html><head/><body><p><img src="docs/Black_Text_Logo.png"/></p></body></html> - - - Qt::AlignCenter - - - - - - - - - - 0 - - - 24 - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - Qt::NoContextMenu - - - Generate Results - - - - - - - Plot Visualisation - - - - - - - Terminal Output - - - - - - - - 5 - 0 - - - - - - - - - - - - - 0 - 0 - 1145 - 21 - - - - - Help - - - - Examples - - - - - - - - - - - + - - - - - - - - - File - - - - - - - Aircraft - - - - - - - - - - - About - - - - - Manual - - - - - Quit - - - - - Import Data - - - - - Export Data - - - - - Preset Aircraft List - - - - - Example 1: Analyze steady trim example - - - - - Example 2: Analyze unsteady trim example - - - - - Example 3: Steady convergence example - - - - - Example 4: Steady horseshoe vortex lattice method solver - - - - - Example 5: Steady ring vortex lattice method solver static - - - - - Example 6: Unsteady ring vortex lattice method solver static - - - - - Example 7: Unsteady ring vortex lattice method solver variable - - - - - Example 8: Unsteady ring vortex lattice method solver variable formation - - - - - Example 9: Unsteady static convergence example - - - - - Example 10: Unsteady variable convergence example - - - - - - + + + diff --git a/pterasoftware/ui_resources/textdialog.py b/pterasoftware/ui_resources/textdialog.py index 32985163..b6c7315c 100644 --- a/pterasoftware/ui_resources/textdialog.py +++ b/pterasoftware/ui_resources/textdialog.py @@ -5,10 +5,8 @@ class Ui_TextAboutDialog(object): def setupUi(self, TextAboutDialog): TextAboutDialog.setObjectName("TextAboutDialog") TextAboutDialog.resize(1000, 900) - sizePolicy = QtWidgets.QSizePolicy( - QtWidgets.QSizePolicy.MinimumExpanding, - QtWidgets.QSizePolicy.MinimumExpanding, - ) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, + QtWidgets.QSizePolicy.MinimumExpanding, ) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(TextAboutDialog.sizePolicy().hasHeightForWidth()) @@ -19,10 +17,8 @@ def setupUi(self, TextAboutDialog): self.textEdit = QtWidgets.QTextEdit(TextAboutDialog) self.textEdit.setEnabled(True) self.textEdit.setGeometry(QtCore.QRect(0, 0, 1000, 900)) - sizePolicy = QtWidgets.QSizePolicy( - QtWidgets.QSizePolicy.MinimumExpanding, - QtWidgets.QSizePolicy.MinimumExpanding, - ) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, + QtWidgets.QSizePolicy.MinimumExpanding, ) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.textEdit.sizePolicy().hasHeightForWidth()) @@ -38,5 +34,4 @@ def setupUi(self, TextAboutDialog): def retranslateUi(self, TextAboutDialog): TextAboutDialog.setWindowTitle( - QtWidgets.QApplication.translate("TextAboutDialog", "Dialog", None, -1) - ) + QtWidgets.QApplication.translate("TextAboutDialog", "Dialog", None, -1)) diff --git a/pterasoftware/unsteady_ring_vortex_lattice_method.py b/pterasoftware/unsteady_ring_vortex_lattice_method.py index ab6250a6..5ae9fe27 100644 --- a/pterasoftware/unsteady_ring_vortex_lattice_method.py +++ b/pterasoftware/unsteady_ring_vortex_lattice_method.py @@ -177,12 +177,8 @@ def __init__(self, unsteady_problem): self.current_wake_ring_vortex_ages = None self.num_panels = None - def run( - self, - logging_level="Warning", - prescribed_wake=True, - calculate_streamlines=True, - ): + def run(self, logging_level="Warning", prescribed_wake=True, + calculate_streamlines=True, ): """This method runs the solver on the unsteady problem. :param logging_level: str, optional @@ -201,8 +197,7 @@ def run( """ # Configure the problem's logger. logging_level_value = functions.convert_logging_level_name_to_value( - logging_level - ) + logging_level) logging.basicConfig(level=logging_level_value) # The following loop iterates through the steps to populate currently empty @@ -233,34 +228,26 @@ def run( this_wake_ring_vortex_strengths = np.zeros(this_num_wake_ring_vortices) this_wake_ring_vortex_ages = np.zeros(this_num_wake_ring_vortices) this_wake_ring_vortex_front_right_vertices = np.zeros( - (this_num_wake_ring_vortices, 3) - ) + (this_num_wake_ring_vortices, 3)) this_wake_ring_vortex_front_left_vertices = np.zeros( - (this_num_wake_ring_vortices, 3) - ) + (this_num_wake_ring_vortices, 3)) this_wake_ring_vortex_back_left_vertices = np.zeros( - (this_num_wake_ring_vortices, 3) - ) + (this_num_wake_ring_vortices, 3)) this_wake_ring_vortex_back_right_vertices = np.zeros( - (this_num_wake_ring_vortices, 3) - ) + (this_num_wake_ring_vortices, 3)) # Append this step's arrays to the list of arrays. self.num_wake_ring_vortices_list.append(this_num_wake_ring_vortices) self.wake_ring_vortex_strengths_list.append(this_wake_ring_vortex_strengths) self.wake_ring_vortex_ages_list.append(this_wake_ring_vortex_ages) self.wake_ring_vortex_front_right_vertices_list.append( - this_wake_ring_vortex_front_right_vertices - ) + this_wake_ring_vortex_front_right_vertices) self.wake_ring_vortex_front_left_vertices_list.append( - this_wake_ring_vortex_front_left_vertices - ) + this_wake_ring_vortex_front_left_vertices) self.wake_ring_vortex_back_left_vertices_list.append( - this_wake_ring_vortex_back_left_vertices - ) + this_wake_ring_vortex_back_left_vertices) self.wake_ring_vortex_back_right_vertices_list.append( - this_wake_ring_vortex_back_right_vertices - ) + this_wake_ring_vortex_back_right_vertices) # The following loop attempts to predict how much time each step will take, # relative to the other steps. This data will be used to generate estimates @@ -301,16 +288,10 @@ def run( # Unless the logging level is at or above Warning, run the simulation with a # progress bar. - with tqdm( - total=approx_total_time, - unit="", - unit_scale=True, - ncols=100, - desc="Simulating", - disable=logging_level_value != logging.WARNING, - bar_format="{desc}:{percentage:3.0f}% |{bar}| Elapsed: {elapsed}, " - "Remaining: {remaining}", - ) as bar: + with tqdm(total=approx_total_time, unit="", unit_scale=True, ncols=100, + desc="Simulating", disable=logging_level_value != logging.WARNING, + bar_format="{desc}:{percentage:3.0f}% |{bar}| Elapsed: {elapsed}, " + "Remaining: {remaining}", ) as bar: # Initialize all the airplanes' panels' vortices. logging.info("Initializing all airplanes' panel vortices.") @@ -327,21 +308,14 @@ def run( # point. self.current_step = step self.current_airplanes = self.steady_problems[ - self.current_step - ].airplanes + self.current_step].airplanes self.current_operating_point = self.steady_problems[ - self.current_step - ].operating_point + self.current_step].operating_point self.current_freestream_velocity_geometry_axes = ( - self.current_operating_point.calculate_freestream_velocity_geometry_axes() - ) + self.current_operating_point.calculate_freestream_velocity_geometry_axes()) logging.info( - "Beginning time step " - + str(self.current_step) - + " out of " - + str(self.num_steps - 1) - + "." - ) + "Beginning time step " + str(self.current_step) + " out of " + str( + self.num_steps - 1) + ".") # Calculate the number of panels for this time step. self.num_panels = 0 @@ -351,11 +325,9 @@ def run( # Initialize attributes to hold aerodynamic data that pertains to this # problem. self.current_wing_wing_influences = np.zeros( - (self.num_panels, self.num_panels) - ) + (self.num_panels, self.num_panels)) self.current_freestream_velocity_geometry_axes = ( - self.current_operating_point.calculate_freestream_velocity_geometry_axes() - ) + self.current_operating_point.calculate_freestream_velocity_geometry_axes()) self.current_freestream_wing_influences = np.zeros(self.num_panels) self.current_wake_wing_influences = np.zeros(self.num_panels) self.current_vortex_strengths = np.ones(self.num_panels) @@ -393,17 +365,13 @@ def run( self.last_panel_collocation_points = np.zeros((self.num_panels, 3)) self.last_panel_vortex_strengths = np.zeros(self.num_panels) self.last_panel_back_right_vortex_vertices = np.zeros( - (self.num_panels, 3) - ) + (self.num_panels, 3)) self.last_panel_front_right_vortex_vertices = np.zeros( - (self.num_panels, 3) - ) + (self.num_panels, 3)) self.last_panel_front_left_vortex_vertices = np.zeros( - (self.num_panels, 3) - ) + (self.num_panels, 3)) self.last_panel_back_left_vortex_vertices = np.zeros( - (self.num_panels, 3) - ) + (self.num_panels, 3)) self.last_panel_right_vortex_centers = np.zeros((self.num_panels, 3)) self.last_panel_front_vortex_centers = np.zeros((self.num_panels, 3)) self.last_panel_left_vortex_centers = np.zeros((self.num_panels, 3)) @@ -412,23 +380,17 @@ def run( # Get the pre-allocated (but still all zero) arrays of wake # information that are associated with this time step. self.current_wake_ring_vortex_strengths = ( - self.wake_ring_vortex_strengths_list[step] - ) + self.wake_ring_vortex_strengths_list[step]) self.current_wake_ring_vortex_ages = self.wake_ring_vortex_ages_list[ - step - ] + step] self.current_wake_ring_vortex_front_right_vertices = ( - self.wake_ring_vortex_front_right_vertices_list[step] - ) + self.wake_ring_vortex_front_right_vertices_list[step]) self.current_wake_ring_vortex_front_left_vertices = ( - self.wake_ring_vortex_front_left_vertices_list[step] - ) + self.wake_ring_vortex_front_left_vertices_list[step]) self.current_wake_ring_vortex_back_left_vertices = ( - self.wake_ring_vortex_back_left_vertices_list[step] - ) + self.wake_ring_vortex_back_left_vertices_list[step]) self.current_wake_ring_vortex_back_right_vertices = ( - self.wake_ring_vortex_back_right_vertices_list[step] - ) + self.wake_ring_vortex_back_right_vertices_list[step]) # Collapse this problem's geometry matrices into 1D arrays of # attributes. @@ -494,8 +456,8 @@ def initialize_panel_vortices(self): # Get the freestream velocity at this time step's problem. this_freestream_velocity_geometry_axes = ( - steady_problem.operating_point.calculate_freestream_velocity_geometry_axes() - ) + steady_problem.operating_point + .calculate_freestream_velocity_geometry_axes()) # Iterate through this problem's airplanes' wings. for airplane in steady_problem.airplanes: @@ -517,14 +479,11 @@ def initialize_panel_vortices(self): # whether the panel is along the trailing edge or not. if not panel.is_trailing_edge: next_chordwise_panel = wing.panels[ - chordwise_position + 1, spanwise_position - ] + chordwise_position + 1, spanwise_position] back_left_vortex_vertex = ( - next_chordwise_panel.front_left_vortex_vertex - ) + next_chordwise_panel.front_left_vortex_vertex) back_right_vortex_vertex = ( - next_chordwise_panel.front_right_vortex_vertex - ) + next_chordwise_panel.front_right_vortex_vertex) else: # As these vertices are directly behind the trailing # edge, they are spaced back from their panel's @@ -534,23 +493,11 @@ def initialize_panel_vortices(self): # of "Modeling of aerodynamic forces in flapping # flight with the Unsteady Vortex Lattice Method" by # Thomas Lambert. - back_left_vortex_vertex = ( - front_left_vortex_vertex - + (panel.back_left_vertex - panel.front_left_vertex) - + this_freestream_velocity_geometry_axes - * self.delta_time - * 0.25 - ) + back_left_vortex_vertex = (front_left_vortex_vertex + ( + panel.back_left_vertex - panel.front_left_vertex) + this_freestream_velocity_geometry_axes * self.delta_time * 0.25) back_right_vortex_vertex = ( - front_right_vortex_vertex - + ( - panel.back_right_vertex - - panel.front_right_vertex - ) - + this_freestream_velocity_geometry_axes - * self.delta_time - * 0.25 - ) + front_right_vortex_vertex + ( + panel.back_right_vertex - panel.front_right_vertex) + this_freestream_velocity_geometry_axes * self.delta_time * 0.25) # Initialize the panel's ring vortex. panel.ring_vortex = aerodynamics.RingVortex( @@ -558,8 +505,7 @@ def initialize_panel_vortices(self): front_left_vertex=front_left_vortex_vertex, back_left_vertex=back_left_vortex_vertex, back_right_vertex=back_right_vortex_vertex, - strength=None, - ) + strength=None, ) def collapse_geometry(self): """This method converts attributes of the problem's geometry into 1D @@ -582,38 +528,32 @@ def collapse_geometry(self): # Iterate through the 1D array of this wing's panels. for panel in panels: - # Update the solver's list of attributes with this panel's # attributes. - functions.update_ring_vortex_solvers_panel_attributes( - solver=self, - global_panel_position=global_panel_position, - panel=panel, - airplane=airplane, - ) + functions.update_ring_vortex_solvers_panel_attributes(solver=self, + global_panel_position=global_panel_position, panel=panel, + airplane=airplane, ) # Increment the global panel position. global_panel_position += 1 for wake_ring_vortex in wake_ring_vortices: self.current_wake_ring_vortex_strengths[ - global_wake_ring_vortex_position - ] = wake_ring_vortex.strength + global_wake_ring_vortex_position] = wake_ring_vortex.strength self.current_wake_ring_vortex_ages[ - global_wake_ring_vortex_position - ] = wake_ring_vortex.age + global_wake_ring_vortex_position] = wake_ring_vortex.age self.current_wake_ring_vortex_front_right_vertices[ - global_wake_ring_vortex_position, : - ] = wake_ring_vortex.front_right_vertex + global_wake_ring_vortex_position, + :] = wake_ring_vortex.front_right_vertex self.current_wake_ring_vortex_front_left_vertices[ - global_wake_ring_vortex_position, : - ] = wake_ring_vortex.front_left_vertex + global_wake_ring_vortex_position, + :] = wake_ring_vortex.front_left_vertex self.current_wake_ring_vortex_back_left_vertices[ - global_wake_ring_vortex_position, : - ] = wake_ring_vortex.back_left_vertex + global_wake_ring_vortex_position, + :] = wake_ring_vortex.back_left_vertex self.current_wake_ring_vortex_back_right_vertices[ - global_wake_ring_vortex_position, : - ] = wake_ring_vortex.back_right_vertex + global_wake_ring_vortex_position, + :] = wake_ring_vortex.back_right_vertex global_wake_ring_vortex_position += 1 @@ -634,39 +574,29 @@ def collapse_geometry(self): # Iterate through the 1D array of this wing's panels. for panel in panels: - # Update the solver's list of attributes with this panel's # attributes. self.last_panel_collocation_points[global_panel_position, :] = ( - panel.collocation_point - ) + panel.collocation_point) self.last_panel_vortex_strengths[global_panel_position] = ( - panel.ring_vortex.strength - ) + panel.ring_vortex.strength) self.last_panel_back_right_vortex_vertices[ - global_panel_position, : - ] = panel.ring_vortex.right_leg.origin + global_panel_position, :] = panel.ring_vortex.right_leg.origin self.last_panel_front_right_vortex_vertices[ - global_panel_position, : - ] = panel.ring_vortex.right_leg.termination + global_panel_position, + :] = panel.ring_vortex.right_leg.termination self.last_panel_front_left_vortex_vertices[ - global_panel_position, : - ] = panel.ring_vortex.left_leg.origin - self.last_panel_back_left_vortex_vertices[ - global_panel_position, : - ] = panel.ring_vortex.left_leg.termination - self.last_panel_right_vortex_centers[ - global_panel_position, : - ] = panel.ring_vortex.right_leg.center - self.last_panel_front_vortex_centers[ - global_panel_position, : - ] = panel.ring_vortex.front_leg.center - self.last_panel_left_vortex_centers[ - global_panel_position, : - ] = panel.ring_vortex.left_leg.center - self.last_panel_back_vortex_centers[ - global_panel_position, : - ] = panel.ring_vortex.back_leg.center + global_panel_position, :] = panel.ring_vortex.left_leg.origin + self.last_panel_back_left_vortex_vertices[global_panel_position, + :] = panel.ring_vortex.left_leg.termination + self.last_panel_right_vortex_centers[global_panel_position, + :] = panel.ring_vortex.right_leg.center + self.last_panel_front_vortex_centers[global_panel_position, + :] = panel.ring_vortex.front_leg.center + self.last_panel_left_vortex_centers[global_panel_position, + :] = panel.ring_vortex.left_leg.center + self.last_panel_back_vortex_centers[global_panel_position, + :] = panel.ring_vortex.back_leg.center # Increment the global panel position. global_panel_position += 1 @@ -687,19 +617,14 @@ def calculate_wing_wing_influences(self): front_right_vortex_vertices=self.panel_front_right_vortex_vertices, front_left_vortex_vertices=self.panel_front_left_vortex_vertices, back_left_vortex_vertices=self.panel_back_left_vortex_vertices, - strengths=self.current_vortex_strengths, - ages=None, - nu=self.current_operating_point.nu, - ) + strengths=self.current_vortex_strengths, ages=None, + nu=self.current_operating_point.nu, ) # Take the batch dot product of the normalized velocities with each panel's # normal direction. This is now the problem's matrix of wing-wing influence # coefficients. - self.current_wing_wing_influences = np.einsum( - "...k,...k->...", - total_influences, - np.expand_dims(self.panel_normal_directions, axis=1), - ) + self.current_wing_wing_influences = np.einsum("...k,...k->...", + total_influences, np.expand_dims(self.panel_normal_directions, axis=1), ) def calculate_freestream_wing_influences(self): """This method finds the vector of freestream-wing influence coefficients @@ -712,30 +637,22 @@ def calculate_freestream_wing_influences(self): """ # Find the normal components of the freestream velocity on every panel by # taking a batch dot product. - freestream_influences = np.einsum( - "ij,j->i", - self.panel_normal_directions, - self.current_freestream_velocity_geometry_axes, - ) + freestream_influences = np.einsum("ij,j->i", self.panel_normal_directions, + self.current_freestream_velocity_geometry_axes, ) # Get the current flapping velocities at every collocation point. current_flapping_velocities_at_collocation_points = ( - self.calculate_current_flapping_velocities_at_collocation_points() - ) + self.calculate_current_flapping_velocities_at_collocation_points()) # Find the normal components of every panel's flapping velocities at their # collocation points by taking a batch dot product. - flapping_influences = np.einsum( - "ij,ij->i", - self.panel_normal_directions, - current_flapping_velocities_at_collocation_points, - ) + flapping_influences = np.einsum("ij,ij->i", self.panel_normal_directions, + current_flapping_velocities_at_collocation_points, ) # Calculate the total current freestream-wing influences by summing the # freestream influences and the flapping influences. self.current_freestream_wing_influences = ( - freestream_influences + flapping_influences - ) + freestream_influences + flapping_influences) def calculate_wake_wing_influences(self): """This method finds the vector of the wake-wing influences associated with @@ -761,14 +678,12 @@ def calculate_wake_wing_influences(self): back_left_vortex_vertices=self.current_wake_ring_vortex_back_left_vertices, strengths=self.current_wake_ring_vortex_strengths, ages=self.current_wake_ring_vortex_ages, - nu=self.current_operating_point.nu, - ) + nu=self.current_operating_point.nu, ) # Set the current wake-wing influences to the normal component of the # wake induced velocities at each panel. - self.current_wake_wing_influences = np.einsum( - "ij,ij->i", velocities_from_wake, self.panel_normal_directions - ) + self.current_wake_wing_influences = np.einsum("ij,ij->i", + velocities_from_wake, self.panel_normal_directions) else: @@ -784,9 +699,7 @@ def calculate_vortex_strengths(self): # Solve for the strength of each panel's vortex. self.current_vortex_strengths = np.linalg.solve( self.current_wing_wing_influences, - -self.current_wake_wing_influences - - self.current_freestream_wing_influences, - ) + -self.current_wake_wing_influences - self.current_freestream_wing_influences, ) # Iterate through the panels and update their vortex strengths. for panel_num in range(self.panels.size): @@ -828,10 +741,8 @@ def calculate_solution_velocity(self, points): front_right_vortex_vertices=self.panel_front_right_vortex_vertices, front_left_vortex_vertices=self.panel_front_left_vortex_vertices, back_left_vortex_vertices=self.panel_back_left_vortex_vertices, - strengths=self.current_vortex_strengths, - ages=None, - nu=self.current_operating_point.nu, - ) + strengths=self.current_vortex_strengths, ages=None, + nu=self.current_operating_point.nu, ) # Find the vector of velocities induced at every point by every wake ring # vortex. The effect of every wake ring vortex on each point will be summed. @@ -843,8 +754,7 @@ def calculate_solution_velocity(self, points): back_left_vortex_vertices=self.current_wake_ring_vortex_back_left_vertices, strengths=self.current_wake_ring_vortex_strengths, ages=self.current_wake_ring_vortex_ages, - nu=self.current_operating_point.nu, - ) + nu=self.current_operating_point.nu, ) # Find the total influence of the vortices, which is the sum of the influence # due to the bound ring vortices and the wake ring vortices. @@ -893,25 +803,21 @@ def calculate_near_field_forces_and_moments(self): # Change the effective right vortex line strength from zero # to this panel's ring vortex's strength. effective_right_vortex_line_strengths[global_panel_position] = ( - self.current_vortex_strengths[global_panel_position] - ) + self.current_vortex_strengths[global_panel_position]) else: # Get the panel directly to the right of this panel. panel_to_right = wing.panels[ - panel.local_chordwise_position, - panel.local_spanwise_position + 1, - ] + panel.local_chordwise_position, panel.local_spanwise_position + 1,] # Change the effective right vortex line strength from zero # to the difference between this panel's ring vortex's # strength, and the ring vortex strength of the panel to the # right of it. effective_right_vortex_line_strengths[global_panel_position] = ( - self.current_vortex_strengths[global_panel_position] - - panel_to_right.ring_vortex.strength - ) + self.current_vortex_strengths[ + global_panel_position] - panel_to_right.ring_vortex.strength) # Check if this panel is on its wing's leading edge. if panel.is_leading_edge: @@ -919,24 +825,20 @@ def calculate_near_field_forces_and_moments(self): # Change the effective front vortex line strength from zero # to this panel's ring vortex's strength. effective_front_vortex_line_strengths[global_panel_position] = ( - self.current_vortex_strengths[global_panel_position] - ) + self.current_vortex_strengths[global_panel_position]) else: # Get the panel directly in front of this panel. panel_to_front = wing.panels[ - panel.local_chordwise_position - 1, - panel.local_spanwise_position, - ] + panel.local_chordwise_position - 1, panel.local_spanwise_position,] # Change the effective front vortex line strength from zero # to the difference between this panel's ring vortex's # strength, and the ring vortex strength of the panel in # front of it. effective_front_vortex_line_strengths[global_panel_position] = ( - self.current_vortex_strengths[global_panel_position] - - panel_to_front.ring_vortex.strength - ) + self.current_vortex_strengths[ + global_panel_position] - panel_to_front.ring_vortex.strength) # Check if this panel is on its wing's left edge. if panel.is_left_edge: @@ -944,129 +846,91 @@ def calculate_near_field_forces_and_moments(self): # Change the effective left vortex line strength from zero to # this panel's ring vortex's strength. effective_left_vortex_line_strengths[global_panel_position] = ( - self.current_vortex_strengths[global_panel_position] - ) + self.current_vortex_strengths[global_panel_position]) else: # Get the panel directly to the left of this panel. panel_to_left = wing.panels[ - panel.local_chordwise_position, - panel.local_spanwise_position - 1, - ] + panel.local_chordwise_position, panel.local_spanwise_position - 1,] # Change the effective left vortex line strength from zero to # the difference between this panel's ring vortex's strength, # and the ring vortex strength of the panel to the left of it. effective_left_vortex_line_strengths[global_panel_position] = ( - self.current_vortex_strengths[global_panel_position] - - panel_to_left.ring_vortex.strength - ) + self.current_vortex_strengths[ + global_panel_position] - panel_to_left.ring_vortex.strength) # Increment the global panel position. global_panel_position += 1 # Calculate the solution velocities at the centers of the panel's front leg, # left leg, and right leg. - velocities_at_ring_vortex_front_leg_centers = ( - self.calculate_solution_velocity(points=self.panel_front_vortex_centers) - + self.calculate_current_flapping_velocities_at_front_leg_centers() - ) - velocities_at_ring_vortex_left_leg_centers = ( - self.calculate_solution_velocity(points=self.panel_left_vortex_centers) - + self.calculate_current_flapping_velocities_at_left_leg_centers() - ) - velocities_at_ring_vortex_right_leg_centers = ( - self.calculate_solution_velocity(points=self.panel_right_vortex_centers) - + self.calculate_current_flapping_velocities_at_right_leg_centers() - ) + velocities_at_ring_vortex_front_leg_centers = (self.calculate_solution_velocity( + points=self.panel_front_vortex_centers) + self.calculate_current_flapping_velocities_at_front_leg_centers()) + velocities_at_ring_vortex_left_leg_centers = (self.calculate_solution_velocity( + points=self.panel_left_vortex_centers) + self.calculate_current_flapping_velocities_at_left_leg_centers()) + velocities_at_ring_vortex_right_leg_centers = (self.calculate_solution_velocity( + points=self.panel_right_vortex_centers) + self.calculate_current_flapping_velocities_at_right_leg_centers()) # Using the effective line vortex strengths, and the Kutta-Joukowski theorem # to find the near field force in geometry axes on the front leg, left leg, # and right leg. Also calculate the unsteady component of the force on each # panel, which is derived from the unsteady Bernoulli equation. near_field_forces_on_ring_vortex_right_legs_geometry_axes = ( - self.current_operating_point.density - * np.expand_dims(effective_right_vortex_line_strengths, axis=1) - * functions.numba_1d_explicit_cross( - velocities_at_ring_vortex_right_leg_centers, - self.panel_right_vortex_vectors, - ) - ) + self.current_operating_point.density * np.expand_dims( + effective_right_vortex_line_strengths, + axis=1) * functions.numba_1d_explicit_cross( + velocities_at_ring_vortex_right_leg_centers, + self.panel_right_vortex_vectors, )) near_field_forces_on_ring_vortex_front_legs_geometry_axes = ( - self.current_operating_point.density - * np.expand_dims(effective_front_vortex_line_strengths, axis=1) - * functions.numba_1d_explicit_cross( - velocities_at_ring_vortex_front_leg_centers, - self.panel_front_vortex_vectors, - ) - ) + self.current_operating_point.density * np.expand_dims( + effective_front_vortex_line_strengths, + axis=1) * functions.numba_1d_explicit_cross( + velocities_at_ring_vortex_front_leg_centers, + self.panel_front_vortex_vectors, )) near_field_forces_on_ring_vortex_left_legs_geometry_axes = ( - self.current_operating_point.density - * np.expand_dims(effective_left_vortex_line_strengths, axis=1) - * functions.numba_1d_explicit_cross( - velocities_at_ring_vortex_left_leg_centers, - self.panel_left_vortex_vectors, - ) - ) + self.current_operating_point.density * np.expand_dims( + effective_left_vortex_line_strengths, + axis=1) * functions.numba_1d_explicit_cross( + velocities_at_ring_vortex_left_leg_centers, + self.panel_left_vortex_vectors, )) unsteady_near_field_forces_geometry_axes = ( - self.current_operating_point.density - * np.expand_dims( - (self.current_vortex_strengths - self.last_panel_vortex_strengths), - axis=1, - ) - * np.expand_dims(self.panel_areas, axis=1) - * self.panel_normal_directions - / self.delta_time - ) + self.current_operating_point.density * np.expand_dims( + (self.current_vortex_strengths - self.last_panel_vortex_strengths), + axis=1, ) * np.expand_dims(self.panel_areas, + axis=1) * self.panel_normal_directions / self.delta_time) # Sum the forces on the legs, and the unsteady force, to calculate the total # near field force, in geometry axes, on each panel. near_field_forces_geometry_axes = ( - near_field_forces_on_ring_vortex_front_legs_geometry_axes - + near_field_forces_on_ring_vortex_left_legs_geometry_axes - + near_field_forces_on_ring_vortex_right_legs_geometry_axes - + unsteady_near_field_forces_geometry_axes - ) + near_field_forces_on_ring_vortex_front_legs_geometry_axes + near_field_forces_on_ring_vortex_left_legs_geometry_axes + near_field_forces_on_ring_vortex_right_legs_geometry_axes + unsteady_near_field_forces_geometry_axes) # Find the near field moment in geometry axes on the front leg, left leg, # and right leg. Also find the moment on each panel due to the unsteady force. near_field_moments_on_ring_vortex_front_legs_geometry_axes = ( functions.numba_1d_explicit_cross( self.panel_front_vortex_centers - self.panel_moment_references, - near_field_forces_on_ring_vortex_front_legs_geometry_axes, - ) - ) + near_field_forces_on_ring_vortex_front_legs_geometry_axes, )) near_field_moments_on_ring_vortex_left_legs_geometry_axes = ( functions.numba_1d_explicit_cross( self.panel_left_vortex_centers - self.panel_moment_references, - near_field_forces_on_ring_vortex_left_legs_geometry_axes, - ) - ) + near_field_forces_on_ring_vortex_left_legs_geometry_axes, )) near_field_moments_on_ring_vortex_right_legs_geometry_axes = ( functions.numba_1d_explicit_cross( self.panel_right_vortex_centers - self.panel_moment_references, - near_field_forces_on_ring_vortex_right_legs_geometry_axes, - ) - ) + near_field_forces_on_ring_vortex_right_legs_geometry_axes, )) unsteady_near_field_moments_geometry_axes = functions.numba_1d_explicit_cross( self.panel_collocation_points - self.panel_moment_references, - unsteady_near_field_forces_geometry_axes, - ) + unsteady_near_field_forces_geometry_axes, ) # Sum the moments on the legs, and the unsteady moment, to calculate the # total near field moment, in geometry axes, on each panel. near_field_moments_geometry_axes = ( - near_field_moments_on_ring_vortex_front_legs_geometry_axes - + near_field_moments_on_ring_vortex_left_legs_geometry_axes - + near_field_moments_on_ring_vortex_right_legs_geometry_axes - + unsteady_near_field_moments_geometry_axes - ) - - functions.process_unsteady_solver_forces( - unsteady_solver=self, + near_field_moments_on_ring_vortex_front_legs_geometry_axes + near_field_moments_on_ring_vortex_left_legs_geometry_axes + near_field_moments_on_ring_vortex_right_legs_geometry_axes + unsteady_near_field_moments_geometry_axes) + + functions.process_unsteady_solver_forces(unsteady_solver=self, near_field_forces_geometry_axes=near_field_forces_geometry_axes, - near_field_moments_geometry_axes=near_field_moments_geometry_axes, - ) + near_field_moments_geometry_axes=near_field_moments_geometry_axes, ) def populate_next_airplanes_wake(self, prescribed_wake=True): """This method updates the next time step's airplanes' wakes. @@ -1079,8 +943,7 @@ def populate_next_airplanes_wake(self, prescribed_wake=True): """ # Populate the locations of the next step's airplanes' wake vortex vertices: self.populate_next_airplanes_wake_vortex_vertices( - prescribed_wake=prescribed_wake - ) + prescribed_wake=prescribed_wake) # Populate the locations of the next step's airplanes' wake vortices. self.populate_next_airplanes_wake_vortices() @@ -1132,50 +995,42 @@ def populate_next_airplanes_wake_vortex_vertices(self, prescribed_wake=True): # Initialize a matrix to hold the vertices of the new row of # wake ring vortices. first_row_of_wake_ring_vortex_vertices = np.zeros( - (1, num_spanwise_panels + 1, 3) - ) + (1, num_spanwise_panels + 1, 3)) # Iterate through the spanwise panel positions. for spanwise_position in range(num_spanwise_panels): # Get the next wing's panel object at this location. next_panel = next_wing.panels[ - chordwise_position, spanwise_position - ] + chordwise_position, spanwise_position] # The position of the next front left wake ring vortex # vertex is the next panel's ring vortex's back left # vertex. next_front_left_vertex = ( - next_panel.ring_vortex.back_left_vertex - ) + next_panel.ring_vortex.back_left_vertex) # Add this to the new row of wake ring vortex vertices. first_row_of_wake_ring_vortex_vertices[ - 0, spanwise_position - ] = next_front_left_vertex + 0, spanwise_position] = next_front_left_vertex # Check if this panel is on the right edge of the wing. if spanwise_position == (num_spanwise_panels - 1): - # The position of the next front right wake ring # vortex vertex is the next panel's ring vortex's # back right vertex. next_front_right_vertex = ( - next_panel.ring_vortex.back_right_vertex - ) + next_panel.ring_vortex.back_right_vertex) # Add this to the new row of wake ring vortex vertices. first_row_of_wake_ring_vortex_vertices[ - 0, spanwise_position + 1 - ] = next_front_right_vertex + 0, spanwise_position + 1] = next_front_right_vertex # Set the next wing's matrix of wake ring vortex vertices to # a copy of the row of new wake ring vortex vertices. This is # correct because this is the first time step. next_wing.wake_ring_vortex_vertices = np.copy( - first_row_of_wake_ring_vortex_vertices - ) + first_row_of_wake_ring_vortex_vertices) # Initialize variables to hold the number of spanwise vertices. num_spanwise_vertices = num_spanwise_panels + 1 @@ -1183,8 +1038,7 @@ def populate_next_airplanes_wake_vortex_vertices(self, prescribed_wake=True): # Initialize a new matrix to hold the second row of wake ring # vortex vertices. second_row_of_wake_ring_vortex_vertices = np.zeros( - (1, num_spanwise_panels + 1, 3) - ) + (1, num_spanwise_panels + 1, 3)) # Iterate through the spanwise vertex positions. for spanwise_vertex_position in range(num_spanwise_vertices): @@ -1192,45 +1046,34 @@ def populate_next_airplanes_wake_vortex_vertices(self, prescribed_wake=True): # Get the corresponding vertex from the first row. wake_ring_vortex_vertex = ( next_wing.wake_ring_vortex_vertices[ - 0, spanwise_vertex_position - ] - ) + 0, spanwise_vertex_position]) if prescribed_wake: # If the wake is prescribed, set the velocity at this # vertex to the freestream velocity. velocity_at_first_row_wake_ring_vortex_vertex = ( - self.current_freestream_velocity_geometry_axes - ) + self.current_freestream_velocity_geometry_axes) else: # If the wake is not prescribed, set the velocity at # this vertex to the solution velocity at this point. velocity_at_first_row_wake_ring_vortex_vertex = ( self.calculate_solution_velocity( - np.expand_dims(wake_ring_vortex_vertex, axis=0) - ) - ) + np.expand_dims(wake_ring_vortex_vertex, + axis=0))) # Update the second row with the interpolated position of # the first vertex. second_row_of_wake_ring_vortex_vertices[ - 0, spanwise_vertex_position - ] = ( - wake_ring_vortex_vertex - + velocity_at_first_row_wake_ring_vortex_vertex - * self.delta_time - ) + 0, spanwise_vertex_position] = ( + wake_ring_vortex_vertex + velocity_at_first_row_wake_ring_vortex_vertex * self.delta_time) # Update the wing's wake ring vortex vertex matrix by # vertically stacking the second row below it. - next_wing.wake_ring_vortex_vertices = np.vstack( - ( - next_wing.wake_ring_vortex_vertices, - second_row_of_wake_ring_vortex_vertices, - ) - ) + next_wing.wake_ring_vortex_vertices = np.vstack(( + next_wing.wake_ring_vortex_vertices, + second_row_of_wake_ring_vortex_vertices,)) # If this isn't the first step, then do this. else: @@ -1238,61 +1081,45 @@ def populate_next_airplanes_wake_vortex_vertices(self, prescribed_wake=True): # Set the next wing's wake ring vortex vertex matrix to a # copy of this wing's wake ring vortex vertex matrix. next_wing.wake_ring_vortex_vertices = np.copy( - this_wing.wake_ring_vortex_vertices - ) + this_wing.wake_ring_vortex_vertices) # Get the number of chordwise and spanwise vertices. num_chordwise_vertices = ( - next_wing.wake_ring_vortex_vertices.shape[0] - ) + next_wing.wake_ring_vortex_vertices.shape[0]) num_spanwise_vertices = ( - next_wing.wake_ring_vortex_vertices.shape[1] - ) + next_wing.wake_ring_vortex_vertices.shape[1]) # Iterate through the chordwise and spanwise vertex positions. for chordwise_vertex_position in range(num_chordwise_vertices): for spanwise_vertex_position in range( - num_spanwise_vertices - ): + num_spanwise_vertices): # Get the wake ring vortex vertex at this position. wake_ring_vortex_vertex = ( next_wing.wake_ring_vortex_vertices[ - chordwise_vertex_position, - spanwise_vertex_position, - ] - ) + chordwise_vertex_position, spanwise_vertex_position,]) if prescribed_wake: # If the wake is prescribed, set the velocity at # this vertex to the freestream velocity. velocity_at_first_row_wake_vortex_vertex = ( - self.current_freestream_velocity_geometry_axes - ) + self.current_freestream_velocity_geometry_axes) else: # If the wake is not prescribed, set the velocity # at this vertex to the solution velocity at this # point. velocity_at_first_row_wake_vortex_vertex = ( - np.squeeze( - self.calculate_solution_velocity( - np.expand_dims( - wake_ring_vortex_vertex, axis=0 - ) - ) - ) - ) + np.squeeze(self.calculate_solution_velocity( + np.expand_dims(wake_ring_vortex_vertex, + axis=0)))) # Update the vertex at this point with its # interpolated position. next_wing.wake_ring_vortex_vertices[ - chordwise_vertex_position, spanwise_vertex_position - ] += ( - velocity_at_first_row_wake_vortex_vertex - * self.delta_time - ) + chordwise_vertex_position, spanwise_vertex_position] += ( + velocity_at_first_row_wake_vortex_vertex * self.delta_time) # Set the chordwise position to the trailing edge. chordwise_position = this_wing.num_chordwise_panels - 1 @@ -1300,8 +1127,7 @@ def populate_next_airplanes_wake_vortex_vertices(self, prescribed_wake=True): # Initialize a new matrix to hold the new first row of wake # ring vortex vertices. first_row_of_wake_ring_vortex_vertices = np.zeros( - (1, this_wing.num_spanwise_panels + 1, 3) - ) + (1, this_wing.num_spanwise_panels + 1, 3)) # Iterate spanwise through the trailing edge panels. for spanwise_position in range(this_wing.num_spanwise_panels): @@ -1309,32 +1135,25 @@ def populate_next_airplanes_wake_vortex_vertices(self, prescribed_wake=True): # Get the panel object at this location on the next # airplane's wing object. next_panel = next_wing.panels[ - chordwise_position, spanwise_position - ] + chordwise_position, spanwise_position] # Add the panel object's back left ring vortex vertex to # the matrix of new wake ring vortex vertices. first_row_of_wake_ring_vortex_vertices[ - 0, spanwise_position - ] = next_panel.ring_vortex.back_left_vertex + 0, spanwise_position] = next_panel.ring_vortex.back_left_vertex if spanwise_position == (this_wing.num_spanwise_panels - 1): - # If the panel object is at the right edge of the # wing, add its back right ring vortex vertex to the # matrix of new wake ring vortex vertices. first_row_of_wake_ring_vortex_vertices[ - 0, spanwise_position + 1 - ] = next_panel.ring_vortex.back_right_vertex + 0, spanwise_position + 1] = next_panel.ring_vortex.back_right_vertex # Stack the new first row of wake ring vortex vertices above # the wing's matrix of wake ring vortex vertices. - next_wing.wake_ring_vortex_vertices = np.vstack( - ( - first_row_of_wake_ring_vortex_vertices, - next_wing.wake_ring_vortex_vertices, - ) - ) + next_wing.wake_ring_vortex_vertices = np.vstack(( + first_row_of_wake_ring_vortex_vertices, + next_wing.wake_ring_vortex_vertices,)) def populate_next_airplanes_wake_vortices(self): """This method populates the locations of the next airplanes' wake vortices. @@ -1355,39 +1174,32 @@ def populate_next_airplanes_wake_vortices(self): # Iterate through the copy of the current airplane's wing positions. for wing_id, this_wing in enumerate( - self.current_airplanes[airplane_id].wings - ): + self.current_airplanes[airplane_id].wings): next_wing = next_airplane.wings[wing_id] # Get the next wing's matrix of wake ring vortex vertices. next_wing_wake_ring_vortex_vertices = ( - next_wing.wake_ring_vortex_vertices - ) + next_wing.wake_ring_vortex_vertices) this_wing_wake_ring_vortices = ( - self.current_airplanes[airplane_id] - .wings[wing_id] - .wake_ring_vortices - ) + self.current_airplanes[airplane_id].wings[ + wing_id].wake_ring_vortices) # Find the number of chordwise and spanwise vertices in the next # wing's matrix of wake ring vortex vertices. num_chordwise_vertices = next_wing_wake_ring_vortex_vertices.shape[ - 0 - ] + 0] num_spanwise_vertices = next_wing_wake_ring_vortex_vertices.shape[1] # Initialize a new matrix to hold the new row of wake ring # vortices. new_row_of_wake_ring_vortices = np.empty( - (1, num_spanwise_vertices - 1), dtype=object - ) + (1, num_spanwise_vertices - 1), dtype=object) # Stack the new matrix on top of the copy of this wing's matrix # and assign it to the next wing. next_wing.wake_ring_vortices = np.vstack( - (new_row_of_wake_ring_vortices, this_wing_wake_ring_vortices) - ) + (new_row_of_wake_ring_vortices, this_wing_wake_ring_vortices)) # Iterate through the vertex positions. for chordwise_vertex_position in range(num_chordwise_vertices): @@ -1396,11 +1208,9 @@ def populate_next_airplanes_wake_vortices(self): # Set booleans to determine if this vertex is on the # right and/or trailing edge of the wake. has_right_vertex = ( - spanwise_vertex_position + 1 - ) < num_spanwise_vertices + spanwise_vertex_position + 1) < num_spanwise_vertices has_back_vertex = ( - chordwise_vertex_position + 1 - ) < num_chordwise_vertices + chordwise_vertex_position + 1) < num_chordwise_vertices if has_right_vertex and has_back_vertex: @@ -1409,72 +1219,51 @@ def populate_next_airplanes_wake_vortices(self): # be associated with the corresponding ring vortex at # this position. front_left_vertex = next_wing_wake_ring_vortex_vertices[ - chordwise_vertex_position, spanwise_vertex_position - ] + chordwise_vertex_position, spanwise_vertex_position] front_right_vertex = ( next_wing_wake_ring_vortex_vertices[ - chordwise_vertex_position, - spanwise_vertex_position + 1, - ] - ) + chordwise_vertex_position, spanwise_vertex_position + 1,]) back_left_vertex = next_wing_wake_ring_vortex_vertices[ - chordwise_vertex_position + 1, - spanwise_vertex_position, - ] + chordwise_vertex_position + 1, spanwise_vertex_position,] back_right_vertex = next_wing_wake_ring_vortex_vertices[ - chordwise_vertex_position + 1, - spanwise_vertex_position + 1, - ] + chordwise_vertex_position + 1, spanwise_vertex_position + 1,] if chordwise_vertex_position > 0: # If this isn't the front of the wake, update the # position of the ring vortex at this location. next_wing.wake_ring_vortices[ - chordwise_vertex_position, - spanwise_vertex_position, - ].update_position( + chordwise_vertex_position, spanwise_vertex_position,].update_position( front_left_vertex=front_left_vertex, front_right_vertex=front_right_vertex, back_left_vertex=back_left_vertex, - back_right_vertex=back_right_vertex, - ) + back_right_vertex=back_right_vertex, ) # Also, update the age of this ring vortex. if self.current_step == 0: next_wing.wake_ring_vortices[ - chordwise_vertex_position, - spanwise_vertex_position, - ].age = self.delta_time + chordwise_vertex_position, spanwise_vertex_position,].age = self.delta_time else: next_wing.wake_ring_vortices[ - chordwise_vertex_position, - spanwise_vertex_position, - ].age += self.delta_time + chordwise_vertex_position, spanwise_vertex_position,].age += self.delta_time if chordwise_vertex_position == 0: - # If this is the front of the wake, get the # vortex strength from the wing panel's ring # vortex direction in front of it. this_strength_copy = this_wing.panels[ - this_wing.num_chordwise_panels - 1, - spanwise_vertex_position, - ].ring_vortex.strength + this_wing.num_chordwise_panels - 1, spanwise_vertex_position,].ring_vortex.strength # Then, make a new ring vortex at this location, # with the panel's ring vortex's strength, # and add it to the matrix of ring vortices. next_wing.wake_ring_vortices[ - chordwise_vertex_position, - spanwise_vertex_position, - ] = aerodynamics.RingVortex( + chordwise_vertex_position, spanwise_vertex_position,] = aerodynamics.RingVortex( front_left_vertex=front_left_vertex, front_right_vertex=front_right_vertex, back_left_vertex=back_left_vertex, back_right_vertex=back_right_vertex, - strength=this_strength_copy, - ) + strength=this_strength_copy, ) def calculate_current_flapping_velocities_at_collocation_points(self): """This method finds the apparent flow velocity due to flapping at the @@ -1492,7 +1281,6 @@ def calculate_current_flapping_velocities_at_collocation_points(self): """ # Check if the current step is the first step. if self.current_step < 1: - # Set the flapping velocities to be zero for all points. Then, return the # flapping velocities. return np.zeros((self.num_panels, 3)) @@ -1521,7 +1309,6 @@ def calculate_current_flapping_velocities_at_right_leg_centers(self): """ # Check if the current step is the first step. if self.current_step < 1: - # Set the flapping velocities to be zero for all points. Then, return the # flapping velocities. return np.zeros((self.num_panels, 3)) @@ -1550,7 +1337,6 @@ def calculate_current_flapping_velocities_at_front_leg_centers(self): """ # Check if the current step is the first step. if self.current_step < 1: - # Set the flapping velocities to be zero for all points. Then, return the # flapping velocities. return np.zeros((self.num_panels, 3)) @@ -1579,7 +1365,6 @@ def calculate_current_flapping_velocities_at_left_leg_centers(self): """ # Check if the current step is the first step. if self.current_step < 1: - # Set the flapping velocities to be zero for all points. Then, return the # flapping velocities. return np.zeros((self.num_panels, 3)) @@ -1610,17 +1395,13 @@ def finalize_near_field_forces_and_moments(self): # Initialize matrices to hold the forces, moments, and coefficients at each of # the time steps that has results. total_near_field_forces_wind_axes = np.zeros( - (self.num_airplanes, 3, num_steps_to_average) - ) + (self.num_airplanes, 3, num_steps_to_average)) total_near_field_force_coefficients_wind_axes = np.zeros( - (self.num_airplanes, 3, num_steps_to_average) - ) + (self.num_airplanes, 3, num_steps_to_average)) total_near_field_moments_wind_axes = np.zeros( - (self.num_airplanes, 3, num_steps_to_average) - ) + (self.num_airplanes, 3, num_steps_to_average)) total_near_field_moment_coefficients_wind_axes = np.zeros( - (self.num_airplanes, 3, num_steps_to_average) - ) + (self.num_airplanes, 3, num_steps_to_average)) # Initialize a variable to track position in the results arrays. results_step = 0 @@ -1635,17 +1416,13 @@ def finalize_near_field_forces_and_moments(self): # Iterate through this step's airplanes. for airplane_id, airplane in enumerate(these_airplanes): total_near_field_forces_wind_axes[airplane_id, :, results_step] = ( - airplane.total_near_field_force_wind_axes - ) - total_near_field_force_coefficients_wind_axes[ - airplane_id, :, results_step - ] = airplane.total_near_field_force_coefficients_wind_axes + airplane.total_near_field_force_wind_axes) + total_near_field_force_coefficients_wind_axes[airplane_id, :, + results_step] = airplane.total_near_field_force_coefficients_wind_axes total_near_field_moments_wind_axes[airplane_id, :, results_step] = ( - airplane.total_near_field_moment_wind_axes - ) - total_near_field_moment_coefficients_wind_axes[ - airplane_id, :, results_step - ] = airplane.total_near_field_moment_coefficients_wind_axes + airplane.total_near_field_moment_wind_axes) + total_near_field_moment_coefficients_wind_axes[airplane_id, :, + results_step] = airplane.total_near_field_moment_coefficients_wind_axes results_step += 1 @@ -1655,82 +1432,51 @@ def finalize_near_field_forces_and_moments(self): if is_static: self.unsteady_problem.final_near_field_forces_wind_axes.append( - total_near_field_forces_wind_axes[airplane_id, :, -1] - ) + total_near_field_forces_wind_axes[airplane_id, :, -1]) self.unsteady_problem.final_near_field_force_coefficients_wind_axes.append( - total_near_field_force_coefficients_wind_axes[airplane_id, :, -1] - ) + total_near_field_force_coefficients_wind_axes[airplane_id, :, -1]) self.unsteady_problem.final_near_field_moments_wind_axes.append( - total_near_field_moments_wind_axes[airplane_id, :, -1] - ) + total_near_field_moments_wind_axes[airplane_id, :, -1]) self.unsteady_problem.final_near_field_moment_coefficients_wind_axes.append( - total_near_field_moment_coefficients_wind_axes[airplane_id, :, -1] - ) + total_near_field_moment_coefficients_wind_axes[airplane_id, :, -1]) else: - mean_forces = np.mean( - total_near_field_forces_wind_axes[airplane_id], axis=-1 - ) + mean_forces = np.mean(total_near_field_forces_wind_axes[airplane_id], + axis=-1) mean_force_coefficients = np.mean( - total_near_field_force_coefficients_wind_axes[airplane_id], axis=-1 - ) - mean_moments = np.mean( - total_near_field_moments_wind_axes[airplane_id], axis=-1 - ) + total_near_field_force_coefficients_wind_axes[airplane_id], axis=-1) + mean_moments = np.mean(total_near_field_moments_wind_axes[airplane_id], + axis=-1) mean_moment_coefficients = np.mean( - total_near_field_moment_coefficients_wind_axes[airplane_id], axis=-1 - ) + total_near_field_moment_coefficients_wind_axes[airplane_id], + axis=-1) rms_forces = np.sqrt( - np.mean( - np.square(total_near_field_forces_wind_axes[airplane_id]), - axis=-1, - ) - ) - rms_force_coefficients = np.sqrt( - np.mean( - np.square( - total_near_field_force_coefficients_wind_axes[airplane_id] - ), - axis=-1, - ) - ) + np.mean(np.square(total_near_field_forces_wind_axes[airplane_id]), + axis=-1, )) + rms_force_coefficients = np.sqrt(np.mean(np.square( + total_near_field_force_coefficients_wind_axes[airplane_id]), + axis=-1, )) rms_moments = np.sqrt( - np.mean( - np.square(total_near_field_moments_wind_axes[airplane_id]), - axis=-1, - ) - ) - rms_moment_coefficients = np.sqrt( - np.mean( - np.square( - total_near_field_moment_coefficients_wind_axes[airplane_id] - ), - axis=-1, - ) - ) + np.mean(np.square(total_near_field_moments_wind_axes[airplane_id]), + axis=-1, )) + rms_moment_coefficients = np.sqrt(np.mean(np.square( + total_near_field_moment_coefficients_wind_axes[airplane_id]), + axis=-1, )) self.unsteady_problem.final_mean_near_field_forces_wind_axes.append( - mean_forces - ) + mean_forces) self.unsteady_problem.final_mean_near_field_force_coefficients_wind_axes.append( - mean_force_coefficients - ) + mean_force_coefficients) self.unsteady_problem.final_mean_near_field_moments_wind_axes.append( - mean_moments - ) + mean_moments) self.unsteady_problem.final_mean_near_field_moment_coefficients_wind_axes.append( - mean_moment_coefficients - ) + mean_moment_coefficients) self.unsteady_problem.final_rms_near_field_forces_wind_axes.append( - rms_forces - ) + rms_forces) self.unsteady_problem.final_rms_near_field_force_coefficients_wind_axes.append( - rms_force_coefficients - ) + rms_force_coefficients) self.unsteady_problem.final_rms_near_field_moments_wind_axes.append( - rms_moments - ) + rms_moments) self.unsteady_problem.final_rms_near_field_moment_coefficients_wind_axes.append( - rms_moment_coefficients - ) + rms_moment_coefficients) diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py index a0b7b703..18ef002b 100644 --- a/tests/integration/__init__.py +++ b/tests/integration/__init__.py @@ -48,7 +48,10 @@ import tests.integration.test_steady_ring_vortex_lattice_method import tests.integration.test_steady_trim import tests.integration.test_unsteady_convergence -import tests.integration.test_unsteady_ring_vortex_lattice_method_multiple_wing_static_geometry -import tests.integration.test_unsteady_ring_vortex_lattice_method_multiple_wing_variable_geometry +import \ + (tests.integration + .test_unsteady_ring_vortex_lattice_method_multiple_wing_static_geometry) +import \ + tests.integration.test_unsteady_ring_vortex_lattice_method_multiple_wing_variable_geometry import tests.integration.test_unsteady_ring_vortex_lattice_method_static_geometry import tests.integration.test_unsteady_ring_vortex_lattice_method_variable_geometry diff --git a/tests/integration/fixtures/airplane_fixtures.py b/tests/integration/fixtures/airplane_fixtures.py index 2cb5c0d6..7d966530 100644 --- a/tests/integration/fixtures/airplane_fixtures.py +++ b/tests/integration/fixtures/airplane_fixtures.py @@ -35,25 +35,12 @@ def make_steady_validation_airplane(): This is the airplane fixture. """ # Create and return the airplane object. - steady_validation_airplane = ps.geometry.Airplane( - wings=[ - ps.geometry.Wing( - symmetric=True, - wing_cross_sections=[ - ps.geometry.WingCrossSection( - airfoil=ps.geometry.Airfoil(name="naca2412"), - ), - ps.geometry.WingCrossSection( - x_le=1.0, - y_le=5.0, - twist=5.0, - chord=0.75, - airfoil=ps.geometry.Airfoil(name="naca2412"), - ), - ], - ) - ], - ) + steady_validation_airplane = ps.geometry.Airplane(wings=[ + ps.geometry.Wing(symmetric=True, wing_cross_sections=[ + ps.geometry.WingCrossSection( + airfoil=ps.geometry.Airfoil(name="naca2412"), ), + ps.geometry.WingCrossSection(x_le=1.0, y_le=5.0, twist=5.0, chord=0.75, + airfoil=ps.geometry.Airfoil(name="naca2412"), ), ], )], ) return steady_validation_airplane @@ -65,109 +52,41 @@ def make_multiple_wing_steady_validation_airplane(): This is the airplane fixture. """ # Create and return the airplane object. - multiple_wing_steady_validation_airplane = ps.geometry.Airplane( - x_ref=0.0, - y_ref=0.0, - z_ref=0.0, - weight=1 * 9.81, - wings=[ - ps.geometry.Wing( - x_le=0.0, - y_le=0.0, - z_le=0.0, - wing_cross_sections=[ - ps.geometry.WingCrossSection( - x_le=0.0, - y_le=0.0, - z_le=0.0, - chord=1.0, - twist=0.0, - airfoil=ps.geometry.Airfoil( - name="naca23012", - coordinates=None, - repanel=True, - n_points_per_side=400, - ), - control_surface_type="symmetric", - control_surface_hinge_point=0.75, - control_surface_deflection=0.0, - num_spanwise_panels=8, - spanwise_spacing="uniform", - ), - ps.geometry.WingCrossSection( - x_le=1.0, - y_le=5.0, - z_le=0.0, - chord=0.75, - twist=0.0, - airfoil=ps.geometry.Airfoil( - name="naca23012", - coordinates=None, - repanel=True, - n_points_per_side=400, - ), - control_surface_type="symmetric", - control_surface_hinge_point=0.75, - control_surface_deflection=0.0, - num_spanwise_panels=8, - spanwise_spacing="uniform", - ), - ], - symmetric=True, - num_chordwise_panels=8, - chordwise_spacing="uniform", - ), - ps.geometry.Wing( - x_le=5.0, - y_le=0.0, - z_le=0.0, - wing_cross_sections=[ - ps.geometry.WingCrossSection( - x_le=0.0, - y_le=0.0, - z_le=0.0, - chord=1.00, - twist=-5.0, - airfoil=ps.geometry.Airfoil( - name="naca0010", - coordinates=None, - repanel=True, - n_points_per_side=400, - ), - control_surface_type="symmetric", - control_surface_hinge_point=0.75, - control_surface_deflection=0.0, - num_spanwise_panels=8, - spanwise_spacing="uniform", - ), - ps.geometry.WingCrossSection( - x_le=1.0, - y_le=1.0, - z_le=0.0, - chord=0.75, - twist=-5.0, - airfoil=ps.geometry.Airfoil( - name="naca0010", - coordinates=None, - repanel=True, - n_points_per_side=400, - ), - control_surface_type="symmetric", - control_surface_hinge_point=0.75, - control_surface_deflection=0.0, - num_spanwise_panels=8, - spanwise_spacing="uniform", - ), - ], - symmetric=True, - num_chordwise_panels=8, - chordwise_spacing="uniform", - ), - ], - s_ref=None, - c_ref=None, - b_ref=None, - ) + multiple_wing_steady_validation_airplane = ps.geometry.Airplane(x_ref=0.0, + y_ref=0.0, z_ref=0.0, weight=1 * 9.81, wings=[ + ps.geometry.Wing(x_le=0.0, y_le=0.0, z_le=0.0, wing_cross_sections=[ + ps.geometry.WingCrossSection(x_le=0.0, y_le=0.0, z_le=0.0, chord=1.0, + twist=0.0, + airfoil=ps.geometry.Airfoil(name="naca23012", coordinates=None, + repanel=True, n_points_per_side=400, ), + control_surface_type="symmetric", control_surface_hinge_point=0.75, + control_surface_deflection=0.0, num_spanwise_panels=8, + spanwise_spacing="uniform", ), + ps.geometry.WingCrossSection(x_le=1.0, y_le=5.0, z_le=0.0, chord=0.75, + twist=0.0, + airfoil=ps.geometry.Airfoil(name="naca23012", coordinates=None, + repanel=True, n_points_per_side=400, ), + control_surface_type="symmetric", control_surface_hinge_point=0.75, + control_surface_deflection=0.0, num_spanwise_panels=8, + spanwise_spacing="uniform", ), ], symmetric=True, + num_chordwise_panels=8, chordwise_spacing="uniform", ), + ps.geometry.Wing(x_le=5.0, y_le=0.0, z_le=0.0, wing_cross_sections=[ + ps.geometry.WingCrossSection(x_le=0.0, y_le=0.0, z_le=0.0, chord=1.00, + twist=-5.0, + airfoil=ps.geometry.Airfoil(name="naca0010", coordinates=None, + repanel=True, n_points_per_side=400, ), + control_surface_type="symmetric", control_surface_hinge_point=0.75, + control_surface_deflection=0.0, num_spanwise_panels=8, + spanwise_spacing="uniform", ), + ps.geometry.WingCrossSection(x_le=1.0, y_le=1.0, z_le=0.0, chord=0.75, + twist=-5.0, + airfoil=ps.geometry.Airfoil(name="naca0010", coordinates=None, + repanel=True, n_points_per_side=400, ), + control_surface_type="symmetric", control_surface_hinge_point=0.75, + control_surface_deflection=0.0, num_spanwise_panels=8, + spanwise_spacing="uniform", ), ], symmetric=True, + num_chordwise_panels=8, chordwise_spacing="uniform", ), ], s_ref=None, + c_ref=None, b_ref=None, ) return multiple_wing_steady_validation_airplane @@ -179,30 +98,14 @@ def make_asymmetric_unsteady_validation_airplane(): This is the airplane fixture. """ # Create and return the airplane object. - asymmetric_unsteady_validation_airplane = ps.geometry.Airplane( - y_ref=5.0, - wings=[ - ps.geometry.Wing( - num_chordwise_panels=8, - chordwise_spacing="uniform", - wing_cross_sections=[ - ps.geometry.WingCrossSection( - airfoil=ps.geometry.Airfoil(name="naca2412"), - num_spanwise_panels=16, - spanwise_spacing="cosine", - chord=1.0, - ), - ps.geometry.WingCrossSection( - y_le=10.0, - chord=1.0, - airfoil=ps.geometry.Airfoil(name="naca2412"), - num_spanwise_panels=16, - spanwise_spacing="cosine", - ), - ], - ) - ], - ) + asymmetric_unsteady_validation_airplane = ps.geometry.Airplane(y_ref=5.0, wings=[ + ps.geometry.Wing(num_chordwise_panels=8, chordwise_spacing="uniform", + wing_cross_sections=[ps.geometry.WingCrossSection( + airfoil=ps.geometry.Airfoil(name="naca2412"), num_spanwise_panels=16, + spanwise_spacing="cosine", chord=1.0, ), + ps.geometry.WingCrossSection(y_le=10.0, chord=1.0, + airfoil=ps.geometry.Airfoil(name="naca2412"), + num_spanwise_panels=16, spanwise_spacing="cosine", ), ], )], ) return asymmetric_unsteady_validation_airplane @@ -214,27 +117,14 @@ def make_symmetric_unsteady_validation_airplane(): This is the airplane fixture. """ # Create and return the airplane object. - symmetric_unsteady_validation_airplane = ps.geometry.Airplane( - wings=[ - ps.geometry.Wing( - symmetric=True, - chordwise_spacing="uniform", - wing_cross_sections=[ - ps.geometry.WingCrossSection( - airfoil=ps.geometry.Airfoil(name="naca2412"), - chord=2.0, - spanwise_spacing="cosine", - ), - ps.geometry.WingCrossSection( - y_le=5.0, - chord=2.0, - airfoil=ps.geometry.Airfoil(name="naca2412"), - spanwise_spacing="cosine", - ), - ], - ), - ], - ) + symmetric_unsteady_validation_airplane = ps.geometry.Airplane(wings=[ + ps.geometry.Wing(symmetric=True, chordwise_spacing="uniform", + wing_cross_sections=[ps.geometry.WingCrossSection( + airfoil=ps.geometry.Airfoil(name="naca2412"), chord=2.0, + spanwise_spacing="cosine", ), + ps.geometry.WingCrossSection(y_le=5.0, chord=2.0, + airfoil=ps.geometry.Airfoil(name="naca2412"), + spanwise_spacing="cosine", ), ], ), ], ) return symmetric_unsteady_validation_airplane @@ -246,69 +136,28 @@ def make_symmetric_multiple_wing_unsteady_validation_airplane(): This is the airplane fixture. """ # Create and return the airplane object. - symmetric_multiple_wing_steady_validation_airplane = ps.geometry.Airplane( - wings=[ - ps.geometry.Wing( - symmetric=True, - chordwise_spacing="uniform", - wing_cross_sections=[ - ps.geometry.WingCrossSection( - airfoil=ps.geometry.Airfoil(name="naca2412"), - chord=1.5, - spanwise_spacing="cosine", - ), - ps.geometry.WingCrossSection( - x_le=0.5, - y_le=5.0, - z_le=0.0, - chord=1.0, - airfoil=ps.geometry.Airfoil(name="naca2412"), - spanwise_spacing="cosine", - ), - ], - ), - ps.geometry.Wing( - symmetric=True, - z_le=1.75, - x_le=6.25, - chordwise_spacing="uniform", - wing_cross_sections=[ - ps.geometry.WingCrossSection( - airfoil=ps.geometry.Airfoil(name="naca0010"), - spanwise_spacing="cosine", - twist=-5.0, - chord=1.00, - ), - ps.geometry.WingCrossSection( - y_le=1.5, - twist=-5.0, - chord=0.75, - x_le=0.25, - airfoil=ps.geometry.Airfoil(name="naca0010"), - spanwise_spacing="cosine", - ), - ], - ), - ps.geometry.Wing( - symmetric=False, - z_le=0.125, - x_le=6.25, - chordwise_spacing="uniform", - wing_cross_sections=[ - ps.geometry.WingCrossSection( - airfoil=ps.geometry.Airfoil(name="naca0010"), - spanwise_spacing="cosine", - chord=1.0, - ), - ps.geometry.WingCrossSection( - z_le=1.5, - chord=0.75, - x_le=0.25, - airfoil=ps.geometry.Airfoil(name="naca0010"), - spanwise_spacing="cosine", - ), - ], - ), - ], - ) + symmetric_multiple_wing_steady_validation_airplane = ps.geometry.Airplane(wings=[ + ps.geometry.Wing(symmetric=True, chordwise_spacing="uniform", + wing_cross_sections=[ps.geometry.WingCrossSection( + airfoil=ps.geometry.Airfoil(name="naca2412"), chord=1.5, + spanwise_spacing="cosine", ), + ps.geometry.WingCrossSection(x_le=0.5, y_le=5.0, z_le=0.0, chord=1.0, + airfoil=ps.geometry.Airfoil(name="naca2412"), + spanwise_spacing="cosine", ), ], ), + ps.geometry.Wing(symmetric=True, z_le=1.75, x_le=6.25, + chordwise_spacing="uniform", wing_cross_sections=[ + ps.geometry.WingCrossSection( + airfoil=ps.geometry.Airfoil(name="naca0010"), + spanwise_spacing="cosine", twist=-5.0, chord=1.00, ), + ps.geometry.WingCrossSection(y_le=1.5, twist=-5.0, chord=0.75, + x_le=0.25, airfoil=ps.geometry.Airfoil(name="naca0010"), + spanwise_spacing="cosine", ), ], ), + ps.geometry.Wing(symmetric=False, z_le=0.125, x_le=6.25, + chordwise_spacing="uniform", wing_cross_sections=[ + ps.geometry.WingCrossSection( + airfoil=ps.geometry.Airfoil(name="naca0010"), + spanwise_spacing="cosine", chord=1.0, ), + ps.geometry.WingCrossSection(z_le=1.5, chord=0.75, x_le=0.25, + airfoil=ps.geometry.Airfoil(name="naca0010"), + spanwise_spacing="cosine", ), ], ), ], ) return symmetric_multiple_wing_steady_validation_airplane diff --git a/tests/integration/fixtures/movement_fixtures.py b/tests/integration/fixtures/movement_fixtures.py index d69598ab..4b733f35 100644 --- a/tests/integration/fixtures/movement_fixtures.py +++ b/tests/integration/fixtures/movement_fixtures.py @@ -34,40 +34,27 @@ def make_static_validation_movement(): # Construct an airplane object and an operating point object. unsteady_validation_airplane = ( - airplane_fixtures.make_asymmetric_unsteady_validation_airplane() - ) + airplane_fixtures.make_asymmetric_unsteady_validation_airplane()) unsteady_validation_operating_point = ( - operating_point_fixtures.make_validation_operating_point() - ) + operating_point_fixtures.make_validation_operating_point()) # Create a wing cross section movement object associated with this airplane's # root wing cross section. unsteady_validation_root_wing_cross_section_movement = ( - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=unsteady_validation_airplane.wings[ - 0 - ].wing_cross_sections[0] - ) - ) + ps.movement.WingCrossSectionMovement(base_wing_cross_section= + unsteady_validation_airplane.wings[0].wing_cross_sections[0])) # Create a wing cross section movement object associated with this airplane's tip # wing cross section. unsteady_validation_tip_wing_cross_section_movement = ( - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=unsteady_validation_airplane.wings[ - 0 - ].wing_cross_sections[1], - ) - ) + ps.movement.WingCrossSectionMovement(base_wing_cross_section= + unsteady_validation_airplane.wings[0].wing_cross_sections[1], )) # Create a wing movement object associated with this airplane's wing. unsteady_validation_wing_movement = ps.movement.WingMovement( - base_wing=unsteady_validation_airplane.wings[0], - wing_cross_sections_movements=[ + base_wing=unsteady_validation_airplane.wings[0], wing_cross_sections_movements=[ unsteady_validation_root_wing_cross_section_movement, - unsteady_validation_tip_wing_cross_section_movement, - ], - ) + unsteady_validation_tip_wing_cross_section_movement, ], ) # Delete the now extraneous constructing fixtures. del unsteady_validation_root_wing_cross_section_movement @@ -76,8 +63,7 @@ def make_static_validation_movement(): # Create an airplane movement object associated with this airplane. unsteady_validation_airplane_movement = ps.movement.AirplaneMovement( base_airplane=unsteady_validation_airplane, - wing_movements=[unsteady_validation_wing_movement], - ) + wing_movements=[unsteady_validation_wing_movement], ) # Delete the now extraneous constructing fixtures. del unsteady_validation_airplane @@ -85,8 +71,7 @@ def make_static_validation_movement(): # Create an operating point movement object associated with this operating point. unsteady_validation_operating_point_movement = ps.movement.OperatingPointMovement( - base_operating_point=unsteady_validation_operating_point - ) + base_operating_point=unsteady_validation_operating_point) # Delete the now extraneous constructing fixtures. del unsteady_validation_operating_point @@ -95,9 +80,7 @@ def make_static_validation_movement(): unsteady_validation_movement = ps.movement.Movement( airplane_movements=[unsteady_validation_airplane_movement], operating_point_movement=unsteady_validation_operating_point_movement, - num_steps=None, - delta_time=None, - ) + num_steps=None, delta_time=None, ) # Delete the now extraneous constructing fixtures. del unsteady_validation_airplane_movement @@ -116,49 +99,30 @@ def make_variable_validation_movement(): # Construct an airplane object and an operating point object. unsteady_validation_airplane = ( - airplane_fixtures.make_symmetric_unsteady_validation_airplane() - ) + airplane_fixtures.make_symmetric_unsteady_validation_airplane()) unsteady_validation_operating_point = ( - operating_point_fixtures.make_validation_operating_point() - ) + operating_point_fixtures.make_validation_operating_point()) # Create a wing cross section movement object associated with this airplane's # root wing cross section. unsteady_validation_root_wing_cross_section_movement = ( - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=unsteady_validation_airplane.wings[ - 0 - ].wing_cross_sections[0], - ) - ) + ps.movement.WingCrossSectionMovement(base_wing_cross_section= + unsteady_validation_airplane.wings[0].wing_cross_sections[0], )) # Create a wing cross section movement object associated with this airplane's tip # wing cross section. unsteady_validation_tip_wing_cross_section_movement = ( - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=unsteady_validation_airplane.wings[ - 0 - ].wing_cross_sections[1], - sweeping_amplitude=30.0, - sweeping_period=1.0, - sweeping_spacing="sine", - pitching_amplitude=30.0, - pitching_period=0.5, - pitching_spacing="sine", - heaving_amplitude=30.0, - heaving_period=0.5, - heaving_spacing="sine", - ) - ) + ps.movement.WingCrossSectionMovement(base_wing_cross_section= + unsteady_validation_airplane.wings[0].wing_cross_sections[1], + sweeping_amplitude=30.0, sweeping_period=1.0, sweeping_spacing="sine", + pitching_amplitude=30.0, pitching_period=0.5, pitching_spacing="sine", + heaving_amplitude=30.0, heaving_period=0.5, heaving_spacing="sine", )) # Create a wing movement object associated with this airplane's wing. unsteady_validation_wing_movement = ps.movement.WingMovement( - base_wing=unsteady_validation_airplane.wings[0], - wing_cross_sections_movements=[ + base_wing=unsteady_validation_airplane.wings[0], wing_cross_sections_movements=[ unsteady_validation_root_wing_cross_section_movement, - unsteady_validation_tip_wing_cross_section_movement, - ], - ) + unsteady_validation_tip_wing_cross_section_movement, ], ) # Delete the now extraneous constructing fixtures. del unsteady_validation_root_wing_cross_section_movement @@ -167,10 +131,7 @@ def make_variable_validation_movement(): # Create an airplane movement object associated with this airplane. unsteady_validation_airplane_movement = ps.movement.AirplaneMovement( base_airplane=unsteady_validation_airplane, - wing_movements=[ - unsteady_validation_wing_movement, - ], - ) + wing_movements=[unsteady_validation_wing_movement, ], ) # Delete the now extraneous constructing fixtures. del unsteady_validation_airplane @@ -178,8 +139,7 @@ def make_variable_validation_movement(): # Create an operating point movement object associated with this operating point. unsteady_validation_operating_point_movement = ps.movement.OperatingPointMovement( - base_operating_point=unsteady_validation_operating_point, - ) + base_operating_point=unsteady_validation_operating_point, ) # Delete the now extraneous constructing fixtures. del unsteady_validation_operating_point @@ -187,8 +147,7 @@ def make_variable_validation_movement(): # Create a movement object associated with this airplane and operating point. unsteady_validation_movement = ps.movement.Movement( airplane_movements=[unsteady_validation_airplane_movement], - operating_point_movement=unsteady_validation_operating_point_movement, - ) + operating_point_movement=unsteady_validation_operating_point_movement, ) # Delete the now extraneous constructing fixtures. del unsteady_validation_airplane_movement @@ -208,99 +167,64 @@ def make_multiple_wing_static_validation_movement(): # Construct an airplane object and an operating point object. unsteady_validation_airplane = ( - airplane_fixtures.make_symmetric_multiple_wing_unsteady_validation_airplane() - ) + airplane_fixtures.make_symmetric_multiple_wing_unsteady_validation_airplane()) unsteady_validation_operating_point = ( - operating_point_fixtures.make_validation_operating_point() - ) + operating_point_fixtures.make_validation_operating_point()) # Create a wing cross section movement object associated with this airplane's # main wing's root wing cross section. unsteady_validation_main_wing_root_cross_section_movement = ( - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=unsteady_validation_airplane.wings[ - 0 - ].wing_cross_sections[0] - ) - ) + ps.movement.WingCrossSectionMovement(base_wing_cross_section= + unsteady_validation_airplane.wings[0].wing_cross_sections[0])) # Create a wing cross section movement object associated with this airplane's # main wing's tip wing cross section. unsteady_validation_main_wing_tip_cross_section_movement = ( - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=unsteady_validation_airplane.wings[ - 0 - ].wing_cross_sections[1], - ) - ) + ps.movement.WingCrossSectionMovement(base_wing_cross_section= + unsteady_validation_airplane.wings[0].wing_cross_sections[1], )) # Create a wing cross section movement object associated with this airplane's # horizontal stabilizer's root wing cross section. unsteady_validation_hstab_root_cross_section_movement = ( - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=unsteady_validation_airplane.wings[ - 1 - ].wing_cross_sections[0] - ) - ) + ps.movement.WingCrossSectionMovement(base_wing_cross_section= + unsteady_validation_airplane.wings[1].wing_cross_sections[0])) # Create a wing cross section movement object associated with this airplane's # horizontal stabilizer's tip wing cross section. unsteady_validation_hstab_tip_cross_section_movement = ( - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=unsteady_validation_airplane.wings[ - 1 - ].wing_cross_sections[1], - ) - ) + ps.movement.WingCrossSectionMovement(base_wing_cross_section= + unsteady_validation_airplane.wings[1].wing_cross_sections[1], )) # Create a wing cross section movement object associated with this airplane's # vertical stabilizer's root wing cross section. unsteady_validation_vstab_root_cross_section_movement = ( - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=unsteady_validation_airplane.wings[ - 2 - ].wing_cross_sections[0] - ) - ) + ps.movement.WingCrossSectionMovement(base_wing_cross_section= + unsteady_validation_airplane.wings[2].wing_cross_sections[0])) # Create a wing cross section movement object associated with this airplane's # vertical stabilizer's tip wing cross section. unsteady_validation_vstab_tip_cross_section_movement = ( - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=unsteady_validation_airplane.wings[ - 2 - ].wing_cross_sections[1], - ) - ) + ps.movement.WingCrossSectionMovement(base_wing_cross_section= + unsteady_validation_airplane.wings[2].wing_cross_sections[1], )) # Create a wing movement object associated with this airplane's main wing. unsteady_validation_main_wing_movement = ps.movement.WingMovement( - base_wing=unsteady_validation_airplane.wings[0], - wing_cross_sections_movements=[ + base_wing=unsteady_validation_airplane.wings[0], wing_cross_sections_movements=[ unsteady_validation_main_wing_root_cross_section_movement, - unsteady_validation_main_wing_tip_cross_section_movement, - ], - ) + unsteady_validation_main_wing_tip_cross_section_movement, ], ) # Create a wing movement object associated with this airplane's horizontal # stabilizer. unsteady_validation_hstab_movement = ps.movement.WingMovement( - base_wing=unsteady_validation_airplane.wings[1], - wing_cross_sections_movements=[ + base_wing=unsteady_validation_airplane.wings[1], wing_cross_sections_movements=[ unsteady_validation_hstab_root_cross_section_movement, - unsteady_validation_hstab_tip_cross_section_movement, - ], - ) + unsteady_validation_hstab_tip_cross_section_movement, ], ) # Create a wing movement object associated with this airplane's vertical stabilizer. unsteady_validation_vstab_movement = ps.movement.WingMovement( - base_wing=unsteady_validation_airplane.wings[2], - wing_cross_sections_movements=[ + base_wing=unsteady_validation_airplane.wings[2], wing_cross_sections_movements=[ unsteady_validation_vstab_root_cross_section_movement, - unsteady_validation_vstab_tip_cross_section_movement, - ], - ) + unsteady_validation_vstab_tip_cross_section_movement, ], ) # Delete the now extraneous constructing fixtures. del unsteady_validation_main_wing_root_cross_section_movement @@ -313,12 +237,8 @@ def make_multiple_wing_static_validation_movement(): # Create an airplane movement object associated with this airplane. unsteady_validation_airplane_movement = ps.movement.AirplaneMovement( base_airplane=unsteady_validation_airplane, - wing_movements=[ - unsteady_validation_main_wing_movement, - unsteady_validation_hstab_movement, - unsteady_validation_vstab_movement, - ], - ) + wing_movements=[unsteady_validation_main_wing_movement, + unsteady_validation_hstab_movement, unsteady_validation_vstab_movement, ], ) # Delete the now extraneous constructing fixtures. del unsteady_validation_airplane @@ -328,8 +248,7 @@ def make_multiple_wing_static_validation_movement(): # Create an operating point movement object associated with this operating point. unsteady_validation_operating_point_movement = ps.movement.OperatingPointMovement( - base_operating_point=unsteady_validation_operating_point - ) + base_operating_point=unsteady_validation_operating_point) # Delete the now extraneous constructing fixtures. del unsteady_validation_operating_point @@ -338,9 +257,7 @@ def make_multiple_wing_static_validation_movement(): unsteady_validation_movement = ps.movement.Movement( airplane_movements=[unsteady_validation_airplane_movement], operating_point_movement=unsteady_validation_operating_point_movement, - num_steps=8, - delta_time=1 / 8 / 10, - ) + num_steps=8, delta_time=1 / 8 / 10, ) # Delete the now extraneous constructing fixtures. del unsteady_validation_airplane_movement @@ -360,108 +277,67 @@ def make_multiple_wing_variable_validation_movement(): # Construct an airplane object and an operating point object. unsteady_validation_airplane = ( - airplane_fixtures.make_symmetric_multiple_wing_unsteady_validation_airplane() - ) + airplane_fixtures.make_symmetric_multiple_wing_unsteady_validation_airplane()) unsteady_validation_operating_point = ( - operating_point_fixtures.make_validation_operating_point() - ) + operating_point_fixtures.make_validation_operating_point()) # Create a wing cross section movement object associated with this airplane's # main wing's root wing cross section. unsteady_validation_main_wing_root_cross_section_movement = ( - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=unsteady_validation_airplane.wings[ - 0 - ].wing_cross_sections[0], - ) - ) + ps.movement.WingCrossSectionMovement(base_wing_cross_section= + unsteady_validation_airplane.wings[0].wing_cross_sections[0], )) # Create a wing cross section movement object associated with this airplane's # main wing's tip wing cross section. unsteady_validation_main_wing_tip_cross_section_movement = ( - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=unsteady_validation_airplane.wings[ - 0 - ].wing_cross_sections[1], - sweeping_amplitude=30.0, - sweeping_period=1.0, - sweeping_spacing="sine", - heaving_amplitude=15.0, - heaving_period=1.0, - heaving_spacing="sine", - pitching_amplitude=15.0, - pitching_period=0.5, - pitching_spacing="sine", - ) - ) + ps.movement.WingCrossSectionMovement(base_wing_cross_section= + unsteady_validation_airplane.wings[0].wing_cross_sections[1], + sweeping_amplitude=30.0, sweeping_period=1.0, sweeping_spacing="sine", + heaving_amplitude=15.0, heaving_period=1.0, heaving_spacing="sine", + pitching_amplitude=15.0, pitching_period=0.5, pitching_spacing="sine", )) # Create a wing cross section movement object associated with this airplane's # horizontal stabilizer's root wing cross section. unsteady_validation_hstab_root_cross_section_movement = ( - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=unsteady_validation_airplane.wings[ - 1 - ].wing_cross_sections[0] - ) - ) + ps.movement.WingCrossSectionMovement(base_wing_cross_section= + unsteady_validation_airplane.wings[1].wing_cross_sections[0])) # Create a wing cross section movement object associated with this airplane's # horizontal stabilizer's tip wing cross section. unsteady_validation_hstab_tip_cross_section_movement = ( - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=unsteady_validation_airplane.wings[ - 1 - ].wing_cross_sections[1], - ) - ) + ps.movement.WingCrossSectionMovement(base_wing_cross_section= + unsteady_validation_airplane.wings[1].wing_cross_sections[1], )) # Create a wing cross section movement object associated with this airplane's # vertical stabilizer's root wing cross section. unsteady_validation_vstab_root_cross_section_movement = ( - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=unsteady_validation_airplane.wings[ - 2 - ].wing_cross_sections[0] - ) - ) + ps.movement.WingCrossSectionMovement(base_wing_cross_section= + unsteady_validation_airplane.wings[2].wing_cross_sections[0])) # Create a wing cross section movement object associated with this airplane's # vertical stabilizer's tip wing cross section. unsteady_validation_vstab_tip_cross_section_movement = ( - ps.movement.WingCrossSectionMovement( - base_wing_cross_section=unsteady_validation_airplane.wings[ - 2 - ].wing_cross_sections[1], - ) - ) + ps.movement.WingCrossSectionMovement(base_wing_cross_section= + unsteady_validation_airplane.wings[2].wing_cross_sections[1], )) # Create a wing movement object associated with this airplane's main wing. unsteady_validation_main_wing_movement = ps.movement.WingMovement( - base_wing=unsteady_validation_airplane.wings[0], - wing_cross_sections_movements=[ + base_wing=unsteady_validation_airplane.wings[0], wing_cross_sections_movements=[ unsteady_validation_main_wing_root_cross_section_movement, - unsteady_validation_main_wing_tip_cross_section_movement, - ], - ) + unsteady_validation_main_wing_tip_cross_section_movement, ], ) # Create a wing movement object associated with this airplane's horizontal # stabilizer. unsteady_validation_hstab_movement = ps.movement.WingMovement( - base_wing=unsteady_validation_airplane.wings[1], - wing_cross_sections_movements=[ + base_wing=unsteady_validation_airplane.wings[1], wing_cross_sections_movements=[ unsteady_validation_hstab_root_cross_section_movement, - unsteady_validation_hstab_tip_cross_section_movement, - ], - ) + unsteady_validation_hstab_tip_cross_section_movement, ], ) # Create a wing movement object associated with this airplane's vertical stabilizer. unsteady_validation_vstab_movement = ps.movement.WingMovement( - base_wing=unsteady_validation_airplane.wings[2], - wing_cross_sections_movements=[ + base_wing=unsteady_validation_airplane.wings[2], wing_cross_sections_movements=[ unsteady_validation_vstab_root_cross_section_movement, - unsteady_validation_vstab_tip_cross_section_movement, - ], - ) + unsteady_validation_vstab_tip_cross_section_movement, ], ) # Delete the now extraneous constructing fixtures. del unsteady_validation_main_wing_root_cross_section_movement @@ -474,12 +350,8 @@ def make_multiple_wing_variable_validation_movement(): # Create an airplane movement object associated with this airplane. unsteady_validation_airplane_movement = ps.movement.AirplaneMovement( base_airplane=unsteady_validation_airplane, - wing_movements=[ - unsteady_validation_main_wing_movement, - unsteady_validation_hstab_movement, - unsteady_validation_vstab_movement, - ], - ) + wing_movements=[unsteady_validation_main_wing_movement, + unsteady_validation_hstab_movement, unsteady_validation_vstab_movement, ], ) # Delete the now extraneous constructing fixtures. del unsteady_validation_airplane @@ -489,8 +361,7 @@ def make_multiple_wing_variable_validation_movement(): # Create an operating point movement object associated with this operating point. unsteady_validation_operating_point_movement = ps.movement.OperatingPointMovement( - base_operating_point=unsteady_validation_operating_point - ) + base_operating_point=unsteady_validation_operating_point) # Delete the now extraneous constructing fixtures. del unsteady_validation_operating_point @@ -499,9 +370,7 @@ def make_multiple_wing_variable_validation_movement(): unsteady_validation_movement = ps.movement.Movement( airplane_movements=[unsteady_validation_airplane_movement], operating_point_movement=unsteady_validation_operating_point_movement, - num_steps=20, - delta_time=1 / 8 / 10, - ) + num_steps=20, delta_time=1 / 8 / 10, ) # Delete the now extraneous constructing fixtures. del unsteady_validation_airplane_movement diff --git a/tests/integration/fixtures/problem_fixtures.py b/tests/integration/fixtures/problem_fixtures.py index acc1c361..c8e7f4b1 100644 --- a/tests/integration/fixtures/problem_fixtures.py +++ b/tests/integration/fixtures/problem_fixtures.py @@ -44,14 +44,12 @@ def make_steady_validation_problem(): # Create the constructing fixtures. steady_validation_airplane = airplane_fixtures.make_steady_validation_airplane() steady_validation_operating_point = ( - operating_point_fixtures.make_validation_operating_point() - ) + operating_point_fixtures.make_validation_operating_point()) # Create the problem fixture. steady_validation_problem = ps.problems.SteadyProblem( airplanes=[steady_validation_airplane], - operating_point=steady_validation_operating_point, - ) + operating_point=steady_validation_operating_point, ) # Delete the constructing fixtures. del steady_validation_airplane @@ -71,17 +69,14 @@ def make_steady_multiple_wing_validation_problem(): # Create the constructing fixtures. steady_validation_airplane = ( - airplane_fixtures.make_multiple_wing_steady_validation_airplane() - ) + airplane_fixtures.make_multiple_wing_steady_validation_airplane()) steady_validation_operating_point = ( - operating_point_fixtures.make_validation_operating_point() - ) + operating_point_fixtures.make_validation_operating_point()) # Create the problem fixture. steady_validation_problem = ps.problems.SteadyProblem( airplanes=[steady_validation_airplane], - operating_point=steady_validation_operating_point, - ) + operating_point=steady_validation_operating_point, ) # Delete the constructing fixtures. del steady_validation_airplane @@ -104,8 +99,7 @@ def make_unsteady_validation_problem_with_static_geometry(): # Create the problem fixture. unsteady_validation_problem = ps.problems.UnsteadyProblem( - movement=unsteady_validation_movement - ) + movement=unsteady_validation_movement) # Delete the constructing fixture. del unsteady_validation_movement @@ -127,8 +121,7 @@ def make_unsteady_validation_problem_with_variable_geometry(): # Create the problem fixture. unsteady_validation_problem = ps.problems.UnsteadyProblem( - movement=unsteady_validation_movement - ) + movement=unsteady_validation_movement) # Delete the constructing fixture. del unsteady_validation_movement @@ -147,13 +140,11 @@ def make_unsteady_validation_problem_with_multiple_wing_static_geometry(): # Create the constructing fixture. unsteady_validation_movement = ( - movement_fixtures.make_multiple_wing_static_validation_movement() - ) + movement_fixtures.make_multiple_wing_static_validation_movement()) # Create the problem fixture. unsteady_validation_problem = ps.problems.UnsteadyProblem( - movement=unsteady_validation_movement - ) + movement=unsteady_validation_movement) # Delete the constructing fixture. del unsteady_validation_movement @@ -172,13 +163,11 @@ def make_unsteady_validation_problem_with_multiple_wing_variable_geometry(): # Create the constructing fixture. unsteady_validation_movement = ( - movement_fixtures.make_multiple_wing_variable_validation_movement() - ) + movement_fixtures.make_multiple_wing_variable_validation_movement()) # Create the problem fixture. unsteady_validation_problem = ps.problems.UnsteadyProblem( - movement=unsteady_validation_movement - ) + movement=unsteady_validation_movement) # Delete the constructing fixture. del unsteady_validation_movement diff --git a/tests/integration/test_output.py b/tests/integration/test_output.py index 7e6854fc..91effbed 100644 --- a/tests/integration/test_output.py +++ b/tests/integration/test_output.py @@ -1,6 +1,7 @@ """This module is a testing case for the output module. -Note: Most of the tests in this case do not currently test the output against an expected output. Instead, +Note: Most of the tests in this case do not currently test the output against an +expected output. Instead, they test that the methods to create the output don't throw any errors. This module contains the following classes: @@ -48,8 +49,7 @@ def setUp(self): # Set up the constructing fixtures. self.unsteady_solver = ( - solver_fixtures.make_unsteady_ring_vortex_lattice_method_validation_solver_with_static_geometry() - ) + solver_fixtures.make_unsteady_ring_vortex_lattice_method_validation_solver_with_static_geometry()) def tearDown(self): """This method is automatically called before each testing method to tear down the fixtures. @@ -68,9 +68,8 @@ def test_plot_results_versus_time_does_not_throw(self): # Call the plot_results_versus_time method on the solver fixture. The show flag is set to False, # so the figures will not be displayed. - ps.output.plot_results_versus_time( - unsteady_solver=self.unsteady_solver, show=False - ) + ps.output.plot_results_versus_time(unsteady_solver=self.unsteady_solver, + show=False) def test_animate_does_not_throw(self): """This method tests that the animate method does not throw any errors. @@ -79,12 +78,8 @@ def test_animate_does_not_throw(self): """ # Call the animate function on the unsteady solver fixture. - ps.output.animate( - unsteady_solver=self.unsteady_solver, - scalar_type=None, - show_wake_vortices=False, - save=False, - ) + ps.output.animate(unsteady_solver=self.unsteady_solver, scalar_type=None, + show_wake_vortices=False, save=False, ) def test_draw_does_not_throw(self): """This method tests that the draw method does not throw any errors. @@ -93,9 +88,5 @@ def test_draw_does_not_throw(self): """ # Call the draw function on the unsteady solver fixture. - ps.output.draw( - solver=self.unsteady_solver, - scalar_type=None, - show_wake_vortices=False, - show_streamlines=False, - ) + ps.output.draw(solver=self.unsteady_solver, scalar_type=None, + show_wake_vortices=False, show_streamlines=False, ) diff --git a/tests/integration/test_steady_convergence.py b/tests/integration/test_steady_convergence.py index 36e52448..d61a6533 100644 --- a/tests/integration/test_steady_convergence.py +++ b/tests/integration/test_steady_convergence.py @@ -45,8 +45,7 @@ def setUp(self): # Create the steady problem. self.steady_validation_problem = ( - problem_fixtures.make_steady_validation_problem() - ) + problem_fixtures.make_steady_validation_problem()) def tearDown(self): """This method tears down the test. @@ -66,10 +65,8 @@ def test_steady_horseshoe_convergence(self): converged_parameters = ps.convergence.analyze_steady_convergence( ref_problem=self.steady_validation_problem, solver_type="steady horseshoe vortex lattice method", - panel_aspect_ratio_bounds=(4, 1), - num_chordwise_panels_bounds=(3, 10), - convergence_criteria=1.0, - ) + panel_aspect_ratio_bounds=(4, 1), num_chordwise_panels_bounds=(3, 10), + convergence_criteria=1.0, ) converged_panel_ar = converged_parameters[0] converged_num_chordwise = converged_parameters[1] @@ -90,10 +87,8 @@ def test_steady_ring_convergence(self): converged_parameters = ps.convergence.analyze_steady_convergence( ref_problem=self.steady_validation_problem, solver_type="steady ring vortex lattice method", - panel_aspect_ratio_bounds=(4, 1), - num_chordwise_panels_bounds=(3, 10), - convergence_criteria=1.0, - ) + panel_aspect_ratio_bounds=(4, 1), num_chordwise_panels_bounds=(3, 10), + convergence_criteria=1.0, ) converged_panel_ar = converged_parameters[0] converged_num_chordwise = converged_parameters[1] diff --git a/tests/integration/test_steady_horseshoe_vortex_lattice_method.py b/tests/integration/test_steady_horseshoe_vortex_lattice_method.py index 8bda01df..8059b203 100644 --- a/tests/integration/test_steady_horseshoe_vortex_lattice_method.py +++ b/tests/integration/test_steady_horseshoe_vortex_lattice_method.py @@ -60,11 +60,9 @@ def setUp(self): # Create the steady method solvers. self.steady_horseshoe_vortex_lattice_method_validation_solver = ( - solver_fixtures.make_steady_horseshoe_vortex_lattice_method_validation_solver() - ) + solver_fixtures.make_steady_horseshoe_vortex_lattice_method_validation_solver()) self.steady_multiple_wing_horseshoe_vortex_lattice_method_validation_solver = ( - solver_fixtures.make_steady_multiple_wing_horseshoe_vortex_lattice_method_validation_solver() - ) + solver_fixtures.make_steady_multiple_wing_horseshoe_vortex_lattice_method_validation_solver()) def tearDown(self): """This method tears down the test. @@ -87,25 +85,19 @@ def test_method(self): c_di_expected = 0.019 c_di_calculated = ( self.steady_horseshoe_vortex_lattice_method_validation_solver.airplanes[ - 0 - ].total_near_field_force_coefficients_wind_axes[0] - ) + 0].total_near_field_force_coefficients_wind_axes[0]) c_di_error = abs(c_di_calculated - c_di_expected) / c_di_expected c_l_expected = 0.790 c_l_calculated = ( self.steady_horseshoe_vortex_lattice_method_validation_solver.airplanes[ - 0 - ].total_near_field_force_coefficients_wind_axes[2] - ) + 0].total_near_field_force_coefficients_wind_axes[2]) c_l_error = abs(c_l_calculated - c_l_expected) / c_l_expected c_m_expected = -0.690 c_m_calculated = ( self.steady_horseshoe_vortex_lattice_method_validation_solver.airplanes[ - 0 - ].total_near_field_moment_coefficients_wind_axes[1] - ) + 0].total_near_field_moment_coefficients_wind_axes[1]) c_m_error = abs(c_m_calculated - c_m_expected) / c_m_expected # Set the allowable percent error. @@ -113,10 +105,7 @@ def test_method(self): ps.output.draw( solver=self.steady_horseshoe_vortex_lattice_method_validation_solver, - show_wake_vortices=False, - show_streamlines=True, - scalar_type="side force", - ) + show_wake_vortices=False, show_streamlines=True, scalar_type="side force", ) # Assert that the percent errors are less than the allowable error. self.assertTrue(abs(c_di_error) < allowable_error) @@ -130,31 +119,26 @@ def test_method_multiple_wings(self): """ # Run the solver. - self.steady_multiple_wing_horseshoe_vortex_lattice_method_validation_solver.run() + (self.steady_multiple_wing_horseshoe_vortex_lattice_method_validation_solver + .run()) # Calculate the percent errors of the output. c_di_expected = 0.007 - c_di_calculated = self.steady_multiple_wing_horseshoe_vortex_lattice_method_validation_solver.airplanes[ - 0 - ].total_near_field_force_coefficients_wind_axes[ - 0 - ] + c_di_calculated = \ + self.steady_multiple_wing_horseshoe_vortex_lattice_method_validation_solver.airplanes[ + 0].total_near_field_force_coefficients_wind_axes[0] c_di_error = abs(c_di_calculated - c_di_expected) / c_di_expected c_l_expected = 0.524 - c_l_calculated = self.steady_multiple_wing_horseshoe_vortex_lattice_method_validation_solver.airplanes[ - 0 - ].total_near_field_force_coefficients_wind_axes[ - 2 - ] + c_l_calculated = \ + self.steady_multiple_wing_horseshoe_vortex_lattice_method_validation_solver.airplanes[ + 0].total_near_field_force_coefficients_wind_axes[2] c_l_error = abs(c_l_calculated - c_l_expected) / c_l_expected c_m_expected = -0.350 - c_m_calculated = self.steady_multiple_wing_horseshoe_vortex_lattice_method_validation_solver.airplanes[ - 0 - ].total_near_field_moment_coefficients_wind_axes[ - 1 - ] + c_m_calculated = \ + self.steady_multiple_wing_horseshoe_vortex_lattice_method_validation_solver.airplanes[ + 0].total_near_field_moment_coefficients_wind_axes[1] c_m_error = abs(c_m_calculated - c_m_expected) / c_m_expected # Set the allowable percent error. @@ -162,10 +146,8 @@ def test_method_multiple_wings(self): ps.output.draw( solver=self.steady_multiple_wing_horseshoe_vortex_lattice_method_validation_solver, - scalar_type="induced drag", - show_streamlines=True, - show_wake_vortices=False, - ) + scalar_type="induced drag", show_streamlines=True, + show_wake_vortices=False, ) # Assert that the percent errors are less than the allowable error. self.assertTrue(abs(c_di_error) < allowable_error) diff --git a/tests/integration/test_steady_ring_vortex_lattice_method.py b/tests/integration/test_steady_ring_vortex_lattice_method.py index be7c5719..24b5b8a5 100644 --- a/tests/integration/test_steady_ring_vortex_lattice_method.py +++ b/tests/integration/test_steady_ring_vortex_lattice_method.py @@ -50,8 +50,7 @@ def setUp(self): # Create the steady method solver. self.steady_ring_vortex_lattice_method_validation_solver = ( - solver_fixtures.make_steady_ring_vortex_lattice_method_validation_solver() - ) + solver_fixtures.make_steady_ring_vortex_lattice_method_validation_solver()) def tearDown(self): """This method tears down the test. @@ -74,36 +73,26 @@ def test_method(self): c_di_expected = 0.019 c_di_calculated = ( self.steady_ring_vortex_lattice_method_validation_solver.airplanes[ - 0 - ].total_near_field_force_coefficients_wind_axes[0] - ) + 0].total_near_field_force_coefficients_wind_axes[0]) c_di_error = abs(c_di_calculated - c_di_expected) / c_di_expected c_l_expected = 0.788 c_l_calculated = ( self.steady_ring_vortex_lattice_method_validation_solver.airplanes[ - 0 - ].total_near_field_force_coefficients_wind_axes[2] - ) + 0].total_near_field_force_coefficients_wind_axes[2]) c_l_error = abs(c_l_calculated - c_l_expected) / c_l_expected c_m_expected = -0.687 c_m_calculated = ( self.steady_ring_vortex_lattice_method_validation_solver.airplanes[ - 0 - ].total_near_field_moment_coefficients_wind_axes[1] - ) + 0].total_near_field_moment_coefficients_wind_axes[1]) c_m_error = abs(c_m_calculated - c_m_expected) / c_m_expected # Set the allowable percent error. allowable_error = 0.10 - ps.output.draw( - solver=self.steady_ring_vortex_lattice_method_validation_solver, - show_wake_vortices=False, - show_streamlines=True, - scalar_type="lift", - ) + ps.output.draw(solver=self.steady_ring_vortex_lattice_method_validation_solver, + show_wake_vortices=False, show_streamlines=True, scalar_type="lift", ) # Assert that the percent errors are less than the allowable error. self.assertTrue(abs(c_di_error) < allowable_error) diff --git a/tests/integration/test_unsteady_ring_vortex_lattice_method_multiple_wing_static_geometry.py b/tests/integration/test_unsteady_ring_vortex_lattice_method_multiple_wing_static_geometry.py index bc3a34fc..a5dc6051 100644 --- a/tests/integration/test_unsteady_ring_vortex_lattice_method_multiple_wing_static_geometry.py +++ b/tests/integration/test_unsteady_ring_vortex_lattice_method_multiple_wing_static_geometry.py @@ -1,4 +1,5 @@ -"""This is a testing case for the unsteady ring vortex lattice method solver with static, multi-wing geometry. +"""This is a testing case for the unsteady ring vortex lattice method solver with +static, multi-wing geometry. Note: This case does not currently test the solver's output against an expected output. Instead, it just tests that the solver doesn't throw an error. @@ -47,8 +48,7 @@ def setUp(self): # Create the unsteady method solver. self.unsteady_ring_vortex_lattice_method_validation_solver = ( - solver_fixtures.make_unsteady_ring_vortex_lattice_method_validation_solver_with_multiple_wing_static_geometry() - ) + solver_fixtures.make_unsteady_ring_vortex_lattice_method_validation_solver_with_multiple_wing_static_geometry()) def tearDown(self): """This method tears down the test. @@ -66,12 +66,8 @@ def test_method_does_not_throw(self): # Run the solver. self.unsteady_ring_vortex_lattice_method_validation_solver.run( - prescribed_wake=True, - ) + prescribed_wake=True, ) ps.output.animate( unsteady_solver=self.unsteady_ring_vortex_lattice_method_validation_solver, - show_wake_vortices=True, - scalar_type="side force", - save=False, - ) + show_wake_vortices=True, scalar_type="side force", save=False, ) diff --git a/tests/integration/test_unsteady_ring_vortex_lattice_method_multiple_wing_variable_geometry.py b/tests/integration/test_unsteady_ring_vortex_lattice_method_multiple_wing_variable_geometry.py index adb26df9..3d89f3e0 100644 --- a/tests/integration/test_unsteady_ring_vortex_lattice_method_multiple_wing_variable_geometry.py +++ b/tests/integration/test_unsteady_ring_vortex_lattice_method_multiple_wing_variable_geometry.py @@ -1,4 +1,5 @@ -"""This is a testing case for the unsteady ring vortex lattice method solver with variable, multi-wing geometry. +"""This is a testing case for the unsteady ring vortex lattice method solver with +variable, multi-wing geometry. Note: This case does not currently test the solver's output against an expected output. Instead, it just tests that the solver doesn't throw an error. @@ -22,8 +23,7 @@ class TestUnsteadyRingVortexLatticeMethodMultipleWingVariableGeometry( - unittest.TestCase -): + unittest.TestCase): """This is a class for testing the unsteady ring vortex lattice method solver on multi-wing, variable geometry. @@ -50,8 +50,7 @@ def setUp(self): # Create the unsteady method solver. self.unsteady_ring_vortex_lattice_method_validation_solver = ( - solver_fixtures.make_unsteady_ring_vortex_lattice_method_validation_solver_with_multiple_wing_variable_geometry() - ) + solver_fixtures.make_unsteady_ring_vortex_lattice_method_validation_solver_with_multiple_wing_variable_geometry()) def tearDown(self): """This method tears down the test. @@ -69,12 +68,8 @@ def test_method_does_not_throw(self): # Run the solver. self.unsteady_ring_vortex_lattice_method_validation_solver.run( - prescribed_wake=True, - ) + prescribed_wake=True, ) ps.output.animate( unsteady_solver=self.unsteady_ring_vortex_lattice_method_validation_solver, - scalar_type="induced drag", - show_wake_vortices=True, - save=False, - ) + scalar_type="induced drag", show_wake_vortices=True, save=False, ) diff --git a/tests/integration/test_unsteady_ring_vortex_lattice_method_static_geometry.py b/tests/integration/test_unsteady_ring_vortex_lattice_method_static_geometry.py index 272959cc..9267877f 100644 --- a/tests/integration/test_unsteady_ring_vortex_lattice_method_static_geometry.py +++ b/tests/integration/test_unsteady_ring_vortex_lattice_method_static_geometry.py @@ -6,7 +6,8 @@ CDi: 0.011 Cm: -0.197 -Note: The expected output was created using XFLR5's inviscid VLM2 analysis type, which is a ring vortex lattice +Note: The expected output was created using XFLR5's inviscid VLM2 analysis type, +which is a ring vortex lattice method solver. The geometry in this case is static. Therefore, the results of this unsteady solver should converge to be close to XFLR5's static result. @@ -53,8 +54,7 @@ def setUp(self): # Create the unsteady method solver. self.unsteady_ring_vortex_lattice_method_validation_solver = ( - solver_fixtures.make_unsteady_ring_vortex_lattice_method_validation_solver_with_static_geometry() - ) + solver_fixtures.make_unsteady_ring_vortex_lattice_method_validation_solver_with_static_geometry()) def tearDown(self): """This method tears down the test. @@ -72,8 +72,7 @@ def test_method(self): # Run the solver. self.unsteady_ring_vortex_lattice_method_validation_solver.run( - prescribed_wake=True - ) + prescribed_wake=True) this_solver = self.unsteady_ring_vortex_lattice_method_validation_solver this_airplane = this_solver.current_airplanes[0] @@ -96,16 +95,11 @@ def test_method(self): ps.output.animate( unsteady_solver=self.unsteady_ring_vortex_lattice_method_validation_solver, - show_wake_vortices=True, - scalar_type="lift", - save=False, - ) + show_wake_vortices=True, scalar_type="lift", save=False, ) ps.output.plot_results_versus_time( unsteady_solver=self.unsteady_ring_vortex_lattice_method_validation_solver, - show=False, - save=False, - ) + show=False, save=False, ) # Assert that the percent errors are less than the allowable error. self.assertTrue(abs(c_di_error) < allowable_error) diff --git a/tests/integration/test_unsteady_ring_vortex_lattice_method_variable_geometry.py b/tests/integration/test_unsteady_ring_vortex_lattice_method_variable_geometry.py index 371962a1..09ae41b7 100644 --- a/tests/integration/test_unsteady_ring_vortex_lattice_method_variable_geometry.py +++ b/tests/integration/test_unsteady_ring_vortex_lattice_method_variable_geometry.py @@ -1,4 +1,5 @@ -"""This is a testing case for the unsteady ring vortex lattice method solver with variable geometry. +"""This is a testing case for the unsteady ring vortex lattice method solver with +variable geometry. Note: This case does not currently test the solver's output against an expected output. Instead, it just tests that the solver doesn't throw an error. @@ -47,8 +48,7 @@ def setUp(self): # Create the unsteady method solver. self.unsteady_ring_vortex_lattice_method_validation_solver = ( - solver_fixtures.make_unsteady_ring_vortex_lattice_method_validation_solver_with_variable_geometry() - ) + solver_fixtures.make_unsteady_ring_vortex_lattice_method_validation_solver_with_variable_geometry()) def tearDown(self): """This method tears down the test. @@ -65,18 +65,12 @@ def test_method_does_not_throw(self): """ # Run the unsteady solver. self.unsteady_ring_vortex_lattice_method_validation_solver.run( - prescribed_wake=True, - ) + prescribed_wake=True, ) ps.output.animate( unsteady_solver=self.unsteady_ring_vortex_lattice_method_validation_solver, - scalar_type="lift", - show_wake_vortices=True, - save=False, - ) + scalar_type="lift", show_wake_vortices=True, save=False, ) ps.output.plot_results_versus_time( unsteady_solver=self.unsteady_ring_vortex_lattice_method_validation_solver, - show=False, - save=False, - ) + show=False, save=False, ) diff --git a/tests/unit/fixtures/vortex_fixtures.py b/tests/unit/fixtures/vortex_fixtures.py index 364fa625..f37970ce 100644 --- a/tests/unit/fixtures/vortex_fixtures.py +++ b/tests/unit/fixtures/vortex_fixtures.py @@ -90,11 +90,8 @@ def make_line_vortex_fixture(): strength_fixture = make_strength_fixture() # Create the line vortex object. - line_vortex_fixture = ps.aerodynamics.LineVortex( - origin=origin_fixture, - termination=termination_fixture, - strength=strength_fixture, - ) + line_vortex_fixture = ps.aerodynamics.LineVortex(origin=origin_fixture, + termination=termination_fixture, strength=strength_fixture, ) # Delete the constructing fixtures. del origin_fixture @@ -142,12 +139,10 @@ def make_horseshoe_vortex_fixture(): # Create the horseshoe vortex object. horseshoe_vortex_fixture = ps.aerodynamics.HorseshoeVortex( - finite_leg_origin=origin_fixture, - finite_leg_termination=termination_fixture, + finite_leg_origin=origin_fixture, finite_leg_termination=termination_fixture, strength=strength_fixture, infinite_leg_direction=infinite_leg_direction_fixture, - infinite_leg_length=infinite_leg_length_fixture, - ) + infinite_leg_length=infinite_leg_length_fixture, ) # Delete the constructing fixtures. del origin_fixture @@ -222,9 +217,7 @@ def make_ring_vortex_fixture(): front_left_vertex=front_left_vertex_fixture, front_right_vertex=front_right_vertex_fixture, back_left_vertex=back_left_vertex_fixture, - back_right_vertex=back_right_vertex_fixture, - strength=strength_fixture, - ) + back_right_vertex=back_right_vertex_fixture, strength=strength_fixture, ) # Delete the constructing fixtures. del front_left_vertex_fixture diff --git a/tests/unit/test_horseshoe_vortex.py b/tests/unit/test_horseshoe_vortex.py index 97404454..1feb747d 100644 --- a/tests/unit/test_horseshoe_vortex.py +++ b/tests/unit/test_horseshoe_vortex.py @@ -49,21 +49,16 @@ def setUp(self): # Get the constructing fixtures. self.horseshoe_vortex_fixture = ( - tests.unit.fixtures.vortex_fixtures.make_horseshoe_vortex_fixture() - ) + tests.unit.fixtures.vortex_fixtures.make_horseshoe_vortex_fixture()) self.origin_fixture = tests.unit.fixtures.vortex_fixtures.make_origin_fixture() self.termination_fixture = ( - tests.unit.fixtures.vortex_fixtures.make_termination_fixture() - ) + tests.unit.fixtures.vortex_fixtures.make_termination_fixture()) self.strength_fixture = ( - tests.unit.fixtures.vortex_fixtures.make_strength_fixture() - ) + tests.unit.fixtures.vortex_fixtures.make_strength_fixture()) self.infinite_leg_direction_fixture = ( - tests.unit.fixtures.vortex_fixtures.make_infinite_leg_direction_fixture() - ) + tests.unit.fixtures.vortex_fixtures.make_infinite_leg_direction_fixture()) self.infinite_leg_length_fixture = ( - tests.unit.fixtures.vortex_fixtures.make_infinite_leg_length_fixture() - ) + tests.unit.fixtures.vortex_fixtures.make_infinite_leg_length_fixture()) def tearDown(self): """This method is automatically called before each testing method to tear @@ -87,68 +82,38 @@ def test_class(self): """ # Test that the objects are all the right type. - self.assertIsInstance( - self.horseshoe_vortex_fixture, - ps.aerodynamics.HorseshoeVortex, - ) - self.assertIsInstance( - self.horseshoe_vortex_fixture.finite_leg, - ps.aerodynamics.LineVortex, - ) - self.assertIsInstance( - self.horseshoe_vortex_fixture.left_leg, - ps.aerodynamics.LineVortex, - ) - self.assertIsInstance( - self.horseshoe_vortex_fixture.right_leg, - ps.aerodynamics.LineVortex, - ) + self.assertIsInstance(self.horseshoe_vortex_fixture, + ps.aerodynamics.HorseshoeVortex, ) + self.assertIsInstance(self.horseshoe_vortex_fixture.finite_leg, + ps.aerodynamics.LineVortex, ) + self.assertIsInstance(self.horseshoe_vortex_fixture.left_leg, + ps.aerodynamics.LineVortex, ) + self.assertIsInstance(self.horseshoe_vortex_fixture.right_leg, + ps.aerodynamics.LineVortex, ) # Test that the vortex objects' coordinates were correctly set. + self.assertTrue(np.allclose(self.horseshoe_vortex_fixture.finite_leg_origin, + self.origin_fixture)) self.assertTrue( - np.allclose( - self.horseshoe_vortex_fixture.finite_leg_origin, self.origin_fixture - ) - ) - self.assertTrue( - np.allclose( - self.horseshoe_vortex_fixture.finite_leg_termination, - self.termination_fixture, - ) - ) + np.allclose(self.horseshoe_vortex_fixture.finite_leg_termination, + self.termination_fixture, )) # Test that the horseshoe vortex object's strength was set correctly. self.assertEqual(self.horseshoe_vortex_fixture.strength, self.strength_fixture) # Test that other class attributes were correctly set. self.assertTrue( - np.allclose( - self.horseshoe_vortex_fixture.infinite_leg_direction, - self.infinite_leg_direction_fixture, - ) - ) - self.assertEqual( - self.horseshoe_vortex_fixture.infinite_leg_length, - self.infinite_leg_length_fixture, - ) + np.allclose(self.horseshoe_vortex_fixture.infinite_leg_direction, + self.infinite_leg_direction_fixture, )) + self.assertEqual(self.horseshoe_vortex_fixture.infinite_leg_length, + self.infinite_leg_length_fixture, ) # Test that the infinite legs' coordinates are correct. - self.assertTrue( - np.allclose( - self.horseshoe_vortex_fixture.right_leg_origin, - self.horseshoe_vortex_fixture.finite_leg_origin - + self.horseshoe_vortex_fixture.infinite_leg_direction - * self.horseshoe_vortex_fixture.infinite_leg_length, - ) - ) - self.assertTrue( - np.allclose( - self.horseshoe_vortex_fixture.left_leg_termination, - self.horseshoe_vortex_fixture.finite_leg_termination - + self.horseshoe_vortex_fixture.infinite_leg_direction - * self.horseshoe_vortex_fixture.infinite_leg_length, - ) - ) + self.assertTrue(np.allclose(self.horseshoe_vortex_fixture.right_leg_origin, + self.horseshoe_vortex_fixture.finite_leg_origin + + self.horseshoe_vortex_fixture.infinite_leg_direction * self.horseshoe_vortex_fixture.infinite_leg_length, )) + self.assertTrue(np.allclose(self.horseshoe_vortex_fixture.left_leg_termination, + self.horseshoe_vortex_fixture.finite_leg_termination + self.horseshoe_vortex_fixture.infinite_leg_direction * self.horseshoe_vortex_fixture.infinite_leg_length, )) def test_update_strength(self): """This method tests the update_strength method. @@ -165,15 +130,12 @@ def test_update_strength(self): # Test that all the strength's have been updated correctly. self.assertEqual(self.horseshoe_vortex_fixture.strength, new_strength_fixture) - self.assertEqual( - self.horseshoe_vortex_fixture.right_leg.strength, new_strength_fixture - ) - self.assertEqual( - self.horseshoe_vortex_fixture.finite_leg.strength, new_strength_fixture - ) - self.assertEqual( - self.horseshoe_vortex_fixture.left_leg.strength, new_strength_fixture - ) + self.assertEqual(self.horseshoe_vortex_fixture.right_leg.strength, + new_strength_fixture) + self.assertEqual(self.horseshoe_vortex_fixture.finite_leg.strength, + new_strength_fixture) + self.assertEqual(self.horseshoe_vortex_fixture.left_leg.strength, + new_strength_fixture) # Revert the change. self.horseshoe_vortex_fixture.update_strength(strength=old_strength_fixture) diff --git a/tests/unit/test_line_vortex.py b/tests/unit/test_line_vortex.py index 3e663e40..39883cb9 100644 --- a/tests/unit/test_line_vortex.py +++ b/tests/unit/test_line_vortex.py @@ -46,15 +46,12 @@ def setUp(self): # Create the constructing fixtures. self.line_vortex_fixture = ( - tests.unit.fixtures.vortex_fixtures.make_line_vortex_fixture() - ) + tests.unit.fixtures.vortex_fixtures.make_line_vortex_fixture()) self.origin_fixture = tests.unit.fixtures.vortex_fixtures.make_origin_fixture() self.termination_fixture = ( - tests.unit.fixtures.vortex_fixtures.make_termination_fixture() - ) + tests.unit.fixtures.vortex_fixtures.make_termination_fixture()) self.strength_fixture = ( - tests.unit.fixtures.vortex_fixtures.make_strength_fixture() - ) + tests.unit.fixtures.vortex_fixtures.make_strength_fixture()) def tearDown(self): """This method is automatically called before each testing method to tear @@ -80,19 +77,15 @@ def test_class(self): # Test that the vortex's coordinates were correctly set. self.assertTrue( - np.allclose(self.line_vortex_fixture.origin, self.origin_fixture) - ) + np.allclose(self.line_vortex_fixture.origin, self.origin_fixture)) self.assertTrue( - np.allclose(self.line_vortex_fixture.termination, self.termination_fixture) - ) + np.allclose(self.line_vortex_fixture.termination, self.termination_fixture)) # Test that the vortex's strength was correctly set. self.assertEqual(self.line_vortex_fixture.strength, self.strength_fixture) # Test that the vortex's center and vector were correctly calculated. self.assertTrue( - np.allclose(self.line_vortex_fixture.center, np.array([0.5, 0.5, 0.5])) - ) + np.allclose(self.line_vortex_fixture.center, np.array([0.5, 0.5, 0.5]))) self.assertTrue( - np.allclose(self.line_vortex_fixture.vector, np.array([1, 1, 1])) - ) + np.allclose(self.line_vortex_fixture.vector, np.array([1, 1, 1]))) diff --git a/tests/unit/test_ring_vortex.py b/tests/unit/test_ring_vortex.py index 84a0f1ba..1296fb03 100644 --- a/tests/unit/test_ring_vortex.py +++ b/tests/unit/test_ring_vortex.py @@ -50,23 +50,17 @@ def setUp(self): # Set up the constructing fixtures. self.ring_vortex_fixture = ( - tests.unit.fixtures.vortex_fixtures.make_ring_vortex_fixture() - ) + tests.unit.fixtures.vortex_fixtures.make_ring_vortex_fixture()) self.strength_fixture = ( - tests.unit.fixtures.vortex_fixtures.make_strength_fixture() - ) + tests.unit.fixtures.vortex_fixtures.make_strength_fixture()) self.front_left_vertex_fixture = ( - tests.unit.fixtures.vortex_fixtures.make_front_left_vertex_fixture() - ) + tests.unit.fixtures.vortex_fixtures.make_front_left_vertex_fixture()) self.front_right_vertex_fixture = ( - tests.unit.fixtures.vortex_fixtures.make_front_right_vertex_fixture() - ) + tests.unit.fixtures.vortex_fixtures.make_front_right_vertex_fixture()) self.back_left_vertex_fixture = ( - tests.unit.fixtures.vortex_fixtures.make_back_left_vertex_fixture() - ) + tests.unit.fixtures.vortex_fixtures.make_back_left_vertex_fixture()) self.back_right_vertex_fixture = ( - tests.unit.fixtures.vortex_fixtures.make_back_right_vertex_fixture() - ) + tests.unit.fixtures.vortex_fixtures.make_back_right_vertex_fixture()) def tearDown(self): """This method is automatically called before each testing method to tear @@ -91,83 +85,43 @@ def test_class(self): # Test that the objects are all the right type. self.assertIsInstance(self.ring_vortex_fixture, ps.aerodynamics.RingVortex) - self.assertIsInstance( - self.ring_vortex_fixture.front_leg, - ps.aerodynamics.LineVortex, - ) - self.assertIsInstance( - self.ring_vortex_fixture.left_leg, ps.aerodynamics.LineVortex - ) - self.assertIsInstance( - self.ring_vortex_fixture.back_leg, ps.aerodynamics.LineVortex - ) - self.assertIsInstance( - self.ring_vortex_fixture.right_leg, - ps.aerodynamics.LineVortex, - ) + self.assertIsInstance(self.ring_vortex_fixture.front_leg, + ps.aerodynamics.LineVortex, ) + self.assertIsInstance(self.ring_vortex_fixture.left_leg, + ps.aerodynamics.LineVortex) + self.assertIsInstance(self.ring_vortex_fixture.back_leg, + ps.aerodynamics.LineVortex) + self.assertIsInstance(self.ring_vortex_fixture.right_leg, + ps.aerodynamics.LineVortex, ) # Test that the vortex objects' coordinates were correctly set. - self.assertTrue( - np.allclose( - self.ring_vortex_fixture.front_leg.origin, - self.front_right_vertex_fixture, - ) - ) - self.assertTrue( - np.allclose( - self.ring_vortex_fixture.front_leg.termination, - self.front_left_vertex_fixture, - ) - ) - self.assertTrue( - np.allclose( - self.ring_vortex_fixture.left_leg.origin, self.front_left_vertex_fixture - ) - ) - self.assertTrue( - np.allclose( - self.ring_vortex_fixture.left_leg.termination, - self.back_left_vertex_fixture, - ) - ) - self.assertTrue( - np.allclose( - self.ring_vortex_fixture.back_leg.origin, self.back_left_vertex_fixture - ) - ) - self.assertTrue( - np.allclose( - self.ring_vortex_fixture.back_leg.termination, - self.back_right_vertex_fixture, - ) - ) - self.assertTrue( - np.allclose( - self.ring_vortex_fixture.right_leg.origin, - self.back_right_vertex_fixture, - ) - ) - self.assertTrue( - np.allclose( - self.ring_vortex_fixture.right_leg.termination, - self.front_right_vertex_fixture, - ) - ) + self.assertTrue(np.allclose(self.ring_vortex_fixture.front_leg.origin, + self.front_right_vertex_fixture, )) + self.assertTrue(np.allclose(self.ring_vortex_fixture.front_leg.termination, + self.front_left_vertex_fixture, )) + self.assertTrue(np.allclose(self.ring_vortex_fixture.left_leg.origin, + self.front_left_vertex_fixture)) + self.assertTrue(np.allclose(self.ring_vortex_fixture.left_leg.termination, + self.back_left_vertex_fixture, )) + self.assertTrue(np.allclose(self.ring_vortex_fixture.back_leg.origin, + self.back_left_vertex_fixture)) + self.assertTrue(np.allclose(self.ring_vortex_fixture.back_leg.termination, + self.back_right_vertex_fixture, )) + self.assertTrue(np.allclose(self.ring_vortex_fixture.right_leg.origin, + self.back_right_vertex_fixture, )) + self.assertTrue(np.allclose(self.ring_vortex_fixture.right_leg.termination, + self.front_right_vertex_fixture, )) # Test that the vortex objects' strengths were correctly set. self.assertEqual(self.ring_vortex_fixture.strength, self.strength_fixture) - self.assertEqual( - self.ring_vortex_fixture.front_leg.strength, self.strength_fixture - ) - self.assertEqual( - self.ring_vortex_fixture.left_leg.strength, self.strength_fixture - ) - self.assertEqual( - self.ring_vortex_fixture.back_leg.strength, self.strength_fixture - ) - self.assertEqual( - self.ring_vortex_fixture.right_leg.strength, self.strength_fixture - ) + self.assertEqual(self.ring_vortex_fixture.front_leg.strength, + self.strength_fixture) + self.assertEqual(self.ring_vortex_fixture.left_leg.strength, + self.strength_fixture) + self.assertEqual(self.ring_vortex_fixture.back_leg.strength, + self.strength_fixture) + self.assertEqual(self.ring_vortex_fixture.right_leg.strength, + self.strength_fixture) def test_update_strength(self): """This method tests the update_strength method. @@ -184,18 +138,14 @@ def test_update_strength(self): # Test that all the strength's have been updated correctly. self.assertEqual(self.ring_vortex_fixture.strength, new_strength_fixture) - self.assertEqual( - self.ring_vortex_fixture.front_leg.strength, new_strength_fixture - ) - self.assertEqual( - self.ring_vortex_fixture.left_leg.strength, new_strength_fixture - ) - self.assertEqual( - self.ring_vortex_fixture.back_leg.strength, new_strength_fixture - ) - self.assertEqual( - self.ring_vortex_fixture.right_leg.strength, new_strength_fixture - ) + self.assertEqual(self.ring_vortex_fixture.front_leg.strength, + new_strength_fixture) + self.assertEqual(self.ring_vortex_fixture.left_leg.strength, + new_strength_fixture) + self.assertEqual(self.ring_vortex_fixture.back_leg.strength, + new_strength_fixture) + self.assertEqual(self.ring_vortex_fixture.right_leg.strength, + new_strength_fixture) # Revert the change. self.ring_vortex_fixture.update_strength(strength=old_strength_fixture) @@ -215,102 +165,52 @@ def test_update_position(self): # Create fixtures to hold the soon-to-be new values of the ring vortex's # position. new_front_right_vertex_fixture = old_front_right_vertex_fixture + np.array( - [1, 0, 0] - ) + [1, 0, 0]) new_front_left_vertex_fixture = old_front_left_vertex_fixture + np.array( - [1, 0, 0] - ) + [1, 0, 0]) new_back_left_vertex_fixture = old_back_left_vertex_fixture + np.array( - [1, 0, 0] - ) + [1, 0, 0]) new_back_right_vertex_fixture = old_back_right_vertex_fixture + np.array( - [1, 0, 0] - ) + [1, 0, 0]) # Update the ring vortex fixture's position. self.ring_vortex_fixture.update_position( front_right_vertex=new_front_right_vertex_fixture, front_left_vertex=new_front_left_vertex_fixture, back_left_vertex=new_back_left_vertex_fixture, - back_right_vertex=new_back_right_vertex_fixture, - ) + back_right_vertex=new_back_right_vertex_fixture, ) # Test that the position of the ring vortex was correctly updated. - self.assertTrue( - np.allclose( - self.ring_vortex_fixture.front_right_vertex, - new_front_right_vertex_fixture, - ) - ) - self.assertTrue( - np.allclose( - self.ring_vortex_fixture.front_left_vertex, - new_front_left_vertex_fixture, - ) - ) - self.assertTrue( - np.allclose( - self.ring_vortex_fixture.back_left_vertex, new_back_left_vertex_fixture - ) - ) - self.assertTrue( - np.allclose( - self.ring_vortex_fixture.back_right_vertex, - new_back_right_vertex_fixture, - ) - ) + self.assertTrue(np.allclose(self.ring_vortex_fixture.front_right_vertex, + new_front_right_vertex_fixture, )) + self.assertTrue(np.allclose(self.ring_vortex_fixture.front_left_vertex, + new_front_left_vertex_fixture, )) + self.assertTrue(np.allclose(self.ring_vortex_fixture.back_left_vertex, + new_back_left_vertex_fixture)) + self.assertTrue(np.allclose(self.ring_vortex_fixture.back_right_vertex, + new_back_right_vertex_fixture, )) # Check that the positions of the child objects have been correctly updated. - self.assertTrue( - np.allclose( - self.ring_vortex_fixture.front_leg.origin, - new_front_right_vertex_fixture, - ) - ) - self.assertTrue( - np.allclose( - self.ring_vortex_fixture.front_leg.termination, - new_front_left_vertex_fixture, - ) - ) - self.assertTrue( - np.allclose( - self.ring_vortex_fixture.left_leg.origin, new_front_left_vertex_fixture - ) - ) - self.assertTrue( - np.allclose( - self.ring_vortex_fixture.left_leg.termination, - new_back_left_vertex_fixture, - ) - ) - self.assertTrue( - np.allclose( - self.ring_vortex_fixture.back_leg.origin, new_back_left_vertex_fixture - ) - ) - self.assertTrue( - np.allclose( - self.ring_vortex_fixture.back_leg.termination, - new_back_right_vertex_fixture, - ) - ) - self.assertTrue( - np.allclose( - self.ring_vortex_fixture.right_leg.origin, new_back_right_vertex_fixture - ) - ) - self.assertTrue( - np.allclose( - self.ring_vortex_fixture.right_leg.termination, - new_front_right_vertex_fixture, - ) - ) + self.assertTrue(np.allclose(self.ring_vortex_fixture.front_leg.origin, + new_front_right_vertex_fixture, )) + self.assertTrue(np.allclose(self.ring_vortex_fixture.front_leg.termination, + new_front_left_vertex_fixture, )) + self.assertTrue(np.allclose(self.ring_vortex_fixture.left_leg.origin, + new_front_left_vertex_fixture)) + self.assertTrue(np.allclose(self.ring_vortex_fixture.left_leg.termination, + new_back_left_vertex_fixture, )) + self.assertTrue(np.allclose(self.ring_vortex_fixture.back_leg.origin, + new_back_left_vertex_fixture)) + self.assertTrue(np.allclose(self.ring_vortex_fixture.back_leg.termination, + new_back_right_vertex_fixture, )) + self.assertTrue(np.allclose(self.ring_vortex_fixture.right_leg.origin, + new_back_right_vertex_fixture)) + self.assertTrue(np.allclose(self.ring_vortex_fixture.right_leg.termination, + new_front_right_vertex_fixture, )) # Revert the changes. self.ring_vortex_fixture.update_position( front_right_vertex=old_front_right_vertex_fixture, front_left_vertex=old_front_left_vertex_fixture, back_left_vertex=old_back_left_vertex_fixture, - back_right_vertex=old_back_right_vertex_fixture, - ) + back_right_vertex=old_back_right_vertex_fixture, )