├── .gitignore ├── .readthedocs.yaml ├── LICENSE ├── README.md ├── docs ├── Makefile ├── conf.py ├── index.rst ├── make.bat ├── modules.rst ├── openplaning.rst ├── readme.rst └── requirements.txt ├── openplaning ├── __init__.py ├── openplaning.py └── tables │ ├── Raw_0.2.csv │ ├── Raw_0.4.csv │ ├── Raw_0.6.csv │ ├── Raw_V_0.2.csv │ ├── Raw_V_0.4.csv │ ├── Raw_V_0.6.csv │ ├── V_0.2.csv │ └── V_0.4.csv └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | __pycache__ 3 | 4 | # C extensions 5 | *.so 6 | 7 | # Packages 8 | *.egg 9 | *.egg-info 10 | dist 11 | build 12 | eggs 13 | .eggs 14 | parts 15 | bin 16 | var 17 | sdist 18 | wheelhouse 19 | develop-eggs 20 | .installed.cfg 21 | lib 22 | lib64 23 | venv*/ 24 | pyvenv*/ 25 | pip-wheel-metadata/ 26 | 27 | # Installer logs 28 | pip-log.txt 29 | 30 | # Unit test / coverage reports 31 | .coverage 32 | .tox 33 | .coverage.* 34 | .pytest_cache/ 35 | nosetests.xml 36 | coverage.xml 37 | htmlcov 38 | 39 | # Translations 40 | *.mo 41 | 42 | # Buildout 43 | .mr.developer.cfg 44 | 45 | # IDE project files 46 | .project 47 | .pydevproject 48 | .idea 49 | .vscode 50 | *.iml 51 | *.komodoproject 52 | 53 | # Complexity 54 | output/*.html 55 | output/*/index.html 56 | 57 | # Sphinx 58 | docs/_build 59 | 60 | .DS_Store 61 | *~ 62 | .*.sw[po] 63 | .build 64 | .ve 65 | .env 66 | .cache 67 | .pytest 68 | .benchmarks 69 | .bootstrap 70 | .appveyor.token 71 | *.bak 72 | 73 | # Mypy Cache 74 | .mypy_cache/ 75 | 76 | # Jupyter 77 | .ipynb_checkpoints 78 | test.ipynb 79 | 80 | # Misc 81 | TODO.txt 82 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # Read the Docs configuration file for Sphinx projects 2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 3 | 4 | # Required 5 | version: 2 6 | 7 | # Set the OS, Python version and other tools you might need 8 | build: 9 | os: ubuntu-22.04 10 | tools: 11 | python: "3.12" 12 | # You can also specify other tool versions: 13 | # nodejs: "20" 14 | # rust: "1.70" 15 | # golang: "1.20" 16 | 17 | # Build documentation in the "docs/" directory with Sphinx 18 | sphinx: 19 | configuration: docs/conf.py 20 | # You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs 21 | # builder: "dirhtml" 22 | # Fail on all warnings to avoid broken references 23 | # fail_on_warning: true 24 | 25 | # Optionally build your docs in additional formats such as PDF and ePub 26 | # formats: 27 | # - pdf 28 | # - epub 29 | 30 | # Optional but recommended, declare the Python requirements required 31 | # to build your documentation 32 | # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html 33 | python: 34 | install: 35 | - requirements: docs/requirements.txt 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021, Esteban L. Castro-Feliciano 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenPlaning 2 | 3 | [![Documentation Status](https://readthedocs.org/projects/openplaning/badge/?version=latest)](https://openplaning.readthedocs.io/en/latest/?badge=latest) 4 | 5 | OpenPlaning is a Python library for the hydrodynamic evaluation of planing hulls based on the Savitsky empirical methods. 6 | 7 | ## Installation 8 | 9 | Use the package manager [pip](https://pip.pypa.io/en/stable/) to install openplaning. 10 | 11 | ```bash 12 | pip install openplaning 13 | ``` 14 | 15 | ## Examples 16 | 17 | You can run the example below, plus an optimization case study, online with Binder: 18 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/elcf/binder-openplaning/main?filepath=OpenPlaningExamples.ipynb) 19 | 20 | ```python 21 | from openplaning import PlaningBoat 22 | 23 | #Vessel particulars (from the Savitsky '76 example) 24 | speed = 13.07 #m/s 25 | weight = 827400 #N 26 | beam = 7.315 #m 27 | length = 24.38 #m, vessel LOA 28 | lcg = 10.67 #m, long. center of gravity 29 | vcg = beam/7 #m, vert. center of gravity 30 | r_g = 0.25*length #m, radius of gyration 31 | beta = 15 #deg, deadrise 32 | 33 | #Propulsion 34 | epsilon = 0 #deg, thrust angle w.r.t. keel 35 | vT = vcg #m, thrust vertical distance 36 | lT = lcg #m, thrust horizontal distance 37 | 38 | #Trim tab particulars 39 | sigma = 1.0 #flap span-hull beam ratio 40 | delta = 5 #deg, flap deflection 41 | Lf = 0.3048 #m, flap chord 42 | 43 | #Seaway 44 | H_sig = 1.402 #m, significant wave height 45 | 46 | #Additional options 47 | wetted_lengths_type = 3 #1 = Use Faltinsen 2005 wave rise approximation, 2 = Use Savitsky's '64 approach, 3 = Use Savitsky's '76 approach. Defaults to 1. 48 | roughness_penalty_type = 2 #1 = Use Mosaad's '86 regression, 2 = Use Townsin's '84 regression. Defaults to 1. 49 | 50 | #Create boat object 51 | boat = PlaningBoat(speed, weight, beam, lcg, vcg, r_g, beta, epsilon, vT, lT, length, H_sig, Lf=Lf, sigma=sigma, delta=delta, wetted_lengths_type=wetted_lengths_type, roughness_penalty_type=roughness_penalty_type) 52 | 53 | #Calculates the equilibrium trim and heave, and updates boat.tau and boat.z_wl 54 | boat.get_steady_trim() 55 | 56 | boat.print_description() 57 | ``` 58 | 59 | Output: 60 | ```plaintext 61 | ---VESSEL--- 62 | Speed 13.07 m/s 63 | V_k 25.40808 knot 64 | Fn (beam) 1.543154 65 | Fn (volume) 2.001392 66 | V_m 12.96912 m/s, mean bottom fluid speed 67 | Rn 2.550646e+08 based on V_m and mean wetted-length 68 | 69 | Weight 827400 N 70 | Mass 84371.75 kg 71 | Volume 82.24409 m³ 72 | Beam 7.315 m 73 | LCG 10.67 m from stern 74 | VCG 1.045 m from keel 75 | R_g 6.095 m 76 | Deadrise 15 deg 77 | 78 | LOA 24.38 m 79 | AHR 150 10⁻⁶m, average hull roughness 80 | 81 | ---ATTITUDE--- 82 | z_wl 0.1384811 m, vertical distance of center of gravity to the calm water line 83 | tau 2.878945 deg, trim angle 84 | η₃ 0 deg, additional heave 85 | η₅ 0 deg, additional trim 86 | 87 | ---PROPULSION--- 88 | Thrust angle 0 deg w.r.t. keel (CCW with body-fixed origin at 9 o'clock) 89 | LCT 10.67 m from stern, positive forward 90 | VCT 1.045 m from keel, positive up 91 | 92 | ---FLAP--- 93 | Chord 0.3048 m 94 | Span/Beam 1 95 | Angle 5 deg w.r.t. keel (CCW with body-fixed origin at 9 o'clock) 96 | 97 | ---AIR DRAG--- 98 | l_air 0 m, distance from stern to center of air pressure 99 | h_air 0 m, height from keel to top of square which bounds the air-drag-inducing shape 100 | b_air 0 m, transverse width of square which bounds the air-drag-inducing shape 101 | C_shape 0 area coefficient for air-drag-inducing shape. C_shape = 1 means the air drag reference area is h_air*b_air 102 | C_D 0.7 air drag coefficient 103 | 104 | ---ENVIRONMENT--- 105 | ρ 1025.87 kg/m³, water density 106 | ν 1.19e-06 m²/s, water kinematic viscosity 107 | ρ_air 1.225 kg/m³, air density 108 | g 9.8066 m/s², gravitational acceleration 109 | 110 | ---WETTED LENGTH OPTIONS--- 111 | wetted_lengths_type 3 (1 = Use Faltinsen 2005 wave rise approximation, 2 = Use Savitsky's '64 approach, 3 = Use Savitsky's '76 approach) 112 | z_max_type 1 (1 = Uses 3rd order polynomial fit (faster, recommended), 2 = Use cubic interpolation) 113 | 114 | ---RUNNING GEOMETRY--- 115 | L_K 28.69256 m, keel wetted length 116 | L_C 17.67617 m, chine wetted length 117 | L_C2 15.05145 m, side chine wetted length 118 | λ 3.199428 mean wetted-length to beam ratio (L_K+L_C)/(2*beam) 119 | x_s 11.0164 m, distance from keel/water-line intersection to start of wetted chine 120 | z_max 0.7704615 maximum pressure coordinate coefficient (z_max/Ut) 121 | alpha 18.36643 deg, spray line angle w.r.t. keel in plan view 122 | LCP 11.05546 m, longitudinal center of pressure from stern 123 | T 1.441111 m, draft of keel at transom 124 | wetted_bottom_area 175.5762 m², bottom wetted surface area 125 | 126 | ---ROUGHNESS DRAG PENALTY--- 127 | roughness_penalty_type 2 (1 = Use Mosaad's '86 regression, 2 = Use Townsin's '84 regression) 128 | ΔC_f 0.2485087 10⁻³ change in friction coefficient 129 | ΔL/ΔD None roughness induced change of hull lift to change of hull drag ratio 130 | ΔC_L 0 10⁻³ change in lift coefficient 131 | 132 | ---FORCES [F_x (N, +aft), F_z (N, +up), M_cg (N*m, +pitch up)]--- 133 | Hydrodynamic Force = 134 | [39245.86 780400.3 301189.8] 135 | 136 | Skin Friction = 137 | [31893.98 -1603.929 -18962.39] 138 | 139 | Roughness Lift Change = 140 | [0 0 0] 141 | 142 | Air Resistance = 143 | [0 0 0] 144 | 145 | Flap Force = 146 | [1840.949 44933.51 -282227.4] 147 | 148 | Net Force = 149 | [72980.79 2.573734e-08 2.535526e-07] 150 | 151 | Resultant Thrust = 152 | [-72980.79 3670.16 0] 153 | 154 | ---THURST & POWER--- 155 | Thrust Magnitude 73073.02 N 156 | Effective Thrust 72980.79 N 157 | Eff. Power 953.859 kW 158 | Eff. Horsepower 1279.146 hp 159 | 160 | ---EOM MATRICES--- 161 | Mass matrix, [kg, kg*m/rad; kg*m, kg*m²/rad] = 162 | [[501800.8 67648.82] 163 | [67648.82 2.046024e+07]] 164 | 165 | Damping matrix, [kg/s, kg*m/(s*rad); kg*m/s, kg*m²/(s*rad)] = 166 | [[447299.8 -8182636] 167 | [3078703 2.909537e+07]] 168 | 169 | Restoring matrix, [N/m, N/rad; N, N*m/rad] = 170 | [[1325673 -2390482] 171 | [4940227 5.375431e+07]] 172 | 173 | ---PORPOISING--- 174 | [[Eigenvalue check result, Est. pitch settling time (s)], 175 | [Savitsky chart result, Critical trim angle (deg)]] = 176 | [[0 7.097941] 177 | [0 9.955598]] 178 | 179 | ---BEHAVIOR IN WAVES--- 180 | H_sig 1.402 m, significant wave heigth 181 | R_AW 38406.03 N, added resistance in waves 182 | Average impact acceleration [n_cg, n_bow] (g's) = 183 | [0.3082269 0.754686] 184 | ``` 185 | 186 | ## Dependencies 187 | 188 | * [NumPy](https://numpy.org/) 189 | * [SciPy](https://www.scipy.org/) 190 | * [ndmath](https://github.com/elcf/python-ndmath) 191 | 192 | ## Contributing 193 | Contributions and feedback are welcome and greatly appreciated. Feel free to open an issue first to discuss what you would like to change. 194 | 195 | ## License 196 | [MIT](https://choosealicense.com/licenses/mit/) 197 | 198 | ## Citing 199 | This package was presented as a conference paper at the SNAME FAST Conference 2021: 200 | * Castro-Feliciano, E. L., 2021, "OpenPlaning: Open-Source Framework for the Hydrodynamic Design of Planing Hulls," SNAME FAST '21 Conference Proceedings 201 | 202 | ## References 203 | * Castro-Feliciano, E. L., Sun, J., and Troesch, A. W., 2017, "First Step Toward the Codesign of Planing Craft and Active Control Systems," J. Offshore Mech. Arct. Eng., 139(1) 204 | * Faltinsen, O. M., 2005, "Planing Vessels," Hydrodynamics of High-Speed Marine Vehicles, Cambridge University Press, New York, p. 342 205 | * Fridsma, G., 1971, "A Systematic Study of the Rough-Water Performance of Planing Boats (Irregular Waves - Part II)," Tech. Rep. 1495, Stevens Institute of Technology 206 | * Hadler, J. B., 1966, "The Prediction of Power Performance on Planing Craft," SNAME Trans., 74, pp. 563–610 207 | * ITTC, 1978, "15th International Towing Tank Conference (Proceedings - Part 1)," Netherlands Ship Model Basin, Wageningen, pp. 273–277 208 | * Mosaad, M. A., 1986, "Marine Propeller Roughness Penalties," PhD Thesis, University of Newcastle, p. 193 209 | * Savitsky, D., 1964, "Hydrodynamic Design of Planing Hulls," Mar. Technol., 1(1), pp. 71–94 210 | * Savitsky, D., and Brown, P. W., 1976, "Procedures for Hydrodynamic Evaluation of Planing Hulls in Smooth and Rough Water," Mar. Technol., 13(4), pp. 381–400 211 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | import os 14 | import sys 15 | sys.path.insert(0, os.path.abspath('../openplaning/')) 16 | 17 | 18 | # -- Project information ----------------------------------------------------- 19 | 20 | project = 'OpenPlaning' 21 | copyright = '2024, Esteban L. Castro-Feliciano' 22 | author = 'Esteban L. Castro-Feliciano' 23 | 24 | # The full version, including alpha/beta/rc tags 25 | release = '0.4.6' 26 | 27 | 28 | # -- General configuration --------------------------------------------------- 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | import sphinx_rtd_theme 34 | extensions = ['sphinx.ext.autodoc', 'sphinx_rtd_theme', 'sphinx.ext.viewcode', 'sphinx.ext.napoleon', 'myst_parser'] 35 | 36 | # Mock external packages that are not installed in the docs build environment 37 | autodoc_mock_imports = ['numpy', 'scipy', 'ndmath'] 38 | 39 | # Add any paths that contain templates here, relative to this directory. 40 | templates_path = ['_templates'] 41 | 42 | # List of patterns, relative to source directory, that match files and 43 | # directories to ignore when looking for source files. 44 | # This pattern also affects html_static_path and html_extra_path. 45 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 46 | 47 | 48 | # -- Options for HTML output ------------------------------------------------- 49 | 50 | # The theme to use for HTML and HTML Help pages. See the documentation for 51 | # a list of builtin themes. 52 | # 53 | html_theme = 'sphinx_rtd_theme' 54 | 55 | # Add any paths that contain custom static files (such as style sheets) here, 56 | # relative to this directory. They are copied after the builtin static files, 57 | # so a file named "default.css" will overwrite the builtin "default.css". 58 | html_static_path = ['_static'] 59 | 60 | # -- Options for LaTeX output --------------------------------------------- 61 | 62 | # Grouping the document tree into LaTeX files. List of tuples 63 | # (source start file, target name, title, 64 | # author, documentclass [howto, manual, or own class]). 65 | # latex_documents = [ 66 | # ('openplaning', 'doc.tex', u'Documentation', 67 | # u'Esteban L. Castro', 'howto'), 68 | # ] 69 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. OpenPlaning documentation master file, created by 2 | sphinx-quickstart on Wed Jun 9 11:05:31 2021. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to OpenPlaning's documentation! 7 | ======================================= 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | :caption: Contents: 12 | 13 | readme.rst 14 | openplaning.rst 15 | 16 | 17 | Indices and tables 18 | ================== 19 | 20 | * :ref:`genindex` 21 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/modules.rst: -------------------------------------------------------------------------------- 1 | OpenPlaning 2 | =========== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | openplaning -------------------------------------------------------------------------------- /docs/openplaning.rst: -------------------------------------------------------------------------------- 1 | Documentation 2 | ============= 3 | 4 | See the source code at `GitHub `_. 5 | 6 | The ``PlaningBoat`` class 7 | ************************* 8 | .. autoclass:: openplaning.PlaningBoat 9 | :members: 10 | :undoc-members: 11 | :show-inheritance: 12 | -------------------------------------------------------------------------------- /docs/readme.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../README.md 2 | :parser: myst_parser.sphinx_ 3 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | myst_parser 2 | sphinx_rtd_theme 3 | -------------------------------------------------------------------------------- /openplaning/__init__.py: -------------------------------------------------------------------------------- 1 | from .openplaning import * -------------------------------------------------------------------------------- /openplaning/openplaning.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy import interpolate, signal 3 | from scipy.special import gamma 4 | import ndmath 5 | import warnings 6 | import pkg_resources 7 | 8 | class PlaningBoat(): 9 | """Prismatic planing craft 10 | 11 | Attributes: 12 | speed (float): Speed (m/s). It is an input to :class:`PlaningBoat`. 13 | weight (float): Weight (N). It is an input to :class:`PlaningBoat`. 14 | beam (float): Beam (m). It is an input to :class:`PlaningBoat`. 15 | lcg (float): Longitudinal center of gravity, measured from the stern (m). It is an input to :class:`PlaningBoat`. 16 | vcg (float): Vertical center of gravity, measured from the keel (m). It is an input to :class:`PlaningBoat`. 17 | r_g (float): Radius of gyration (m). It is an input to :class:`PlaningBoat`. 18 | beta (float): Deadrise (deg). It is an input to :class:`PlaningBoat`. 19 | epsilon (float): Thrust angle w.r.t. keel, CCW with body-fixed origin at 9 o'clock (deg). It is an input to :class:`PlaningBoat`. 20 | vT (float): Thrust vertical distance, measured from keel, and positive up (m). It is an input to :class:`PlaningBoat`. 21 | lT (float): Thrust horizontal distance, measured from stern, and positive forward (m). It is an input to :class:`PlaningBoat`. 22 | loa (float): Vessel LOA for seaway behavior estimates (m). Defaults to None. It is an input to :class:`PlaningBoat`. 23 | H_sig (float): Significant wave heigth in an irregular sea state (m). Defaults to None. It is an input to :class:`PlaningBoat`. 24 | ahr (float): Average hull roughness (m). Defaults to 150*10**-6. It is an input to :class:`PlaningBoat`. 25 | LD_change (float): Roughness induced change of hull lift to change of hull drag ratio (dimensionless). Defaults to None, but ITTC '78 approximates a value of -1.1 for propellers. It is an input to :class:`PlaningBoat`. 26 | Lf (float): Flap chord (m). Defaults to 0. It is an input to :class:`PlaningBoat`. 27 | sigma (float): Flap span-beam ratio (dimensionless). Defaults to 0. It is an input to :class:`PlaningBoat`. 28 | delta (float): Flap deflection (deg). Defaults to 0. It is an input to :class:`PlaningBoat`. 29 | l_air (float): Distance from stern to center of air pressure (m). Defaults to 0. It is an input to :class:`PlaningBoat`. 30 | h_air (float): Height from keel to top of square which bounds the air-drag-inducing area (m). Defaults to 0. It is an input to :class:`PlaningBoat`. 31 | b_air (float): Transverse width of square which bounds the air-drag-inducing area (m). Defaults to 0. It is an input to :class:`PlaningBoat`. 32 | C_shape (float): Area coefficient for air-drag-inducing area (dimensionless). C_shape = 1 means the air drag reference area is h_air*b_air. Defaults to 0. It is an input to :class:`PlaningBoat`. 33 | C_D (float): Air drag coefficient (dimensionless). Defaults to 0.7. It is an input to :class:`PlaningBoat`. 34 | rho (float): Water density (kg/m^3). Defaults to 1025.87. It is an input to :class:`PlaningBoat`. 35 | nu (float): Water kinematic viscosity (m^2/s). Defaults to 1.19*10**-6. It is an input to :class:`PlaningBoat`. 36 | rho_air (float): Air density (kg/m^3). Defaults to 1.225. It is an input to :class:`PlaningBoat`. 37 | g (float): Gravitational acceleration (m/s^2). Defaults to 9.8066. It is an input to :class:`PlaningBoat`. 38 | z_wl (float): Vertical distance of center of gravity to the calm water line (m). Defaults to 0. It is an input to :class:`PlaningBoat`, but modified when running :meth:`get_steady_trim`. 39 | tau (float): Trim angle (deg). Defaults to 5. It is an input to :class:`PlaningBoat`, but modified when running :meth:`get_steady_trim`. 40 | eta_3 (float): Additional heave (m). Initiates to 0. 41 | eta_5 (float): Additional trim (deg). Initiates to zero. 42 | wetted_lengths_type (int): 1 = Use Faltinsen 2005 wave rise approximation, 2 = Use Savitsky's '64 approach, 3 = Use Savitsky's '76 approach. Defaults to 1. It is an input to :class:`PlaningBoat`. 43 | z_max_type (int): 1 = Uses 3rd order polynomial fit, 2 = Uses cubic interpolation from table. This is only used if wetted_lenghts_type == 1. Defaults to 1. It is an input to :class:`PlaningBoat`. 44 | L_K (float): Keel wetted length (m). It is updated when running :meth:`get_geo_lengths`. 45 | L_C (float): Chine wetted length (m). It is updated when running :meth:`get_geo_lengths`. 46 | L_C2 (float): Side chine wetted length with reattached flow (m). It is updated when running :meth:`get_geo_lengths`. 47 | wetted_bottom_area (float): Bottom wetted surface area (m^2). It is updated when running :meth:`get_geo_lengths`. 48 | lambda_W (float): Mean wetted-length to beam ratio, (L_K+L_C)/(2*beam) (dimensionless). It is updated when running :meth:`get_geo_lengths`. 49 | x_s (float): Distance from keel/water-line intersection to start of wetted chine (m). It is updated when running :meth:`get_geo_lengths`. 50 | alpha (float): Angle between spray line and keel, projected to plan view (deg). It is updated when running :meth:`get_geo_lengths`. 51 | z_max (float): Maximum pressure coordinate coefficient, z_max/Ut (dimensionless). It is updated when running :meth:`get_geo_lengths`. 52 | T (float): Transom draft (m). It is updated when running :meth:`get_geo_lengths`. 53 | lcp (float): Longitudinal center of pressure, measured from the stern (m). It is updated when running :meth:`get_forces`. 54 | roughness_penalty_type (int): 1 = Use Mosaad's '86 regression, 2 = Use Townsin's '84 regression. Defaults to 1. It is an input to :class:`PlaningBoat`. 55 | C_Lbeta (float): Lift coefficient with deadrise. It is updated when running :meth:`get_forces`. 56 | deltaC_L (float): Change in hydrodynamic lift coefficient due to roughness, excluding lift change due to roughness. It is updated when running :meth:`get_forces`. 57 | hydrodynamic_force ((3,) ndarray): Hydrodynamic force (N, N, N*m). [F_x, F_z, M_cg] with x, y, rot directions in intertial coordinates. It is updated when running :meth:`get_forces`. 58 | bottom_fluid_speed (float): Mean bottom fluid speed (m/s). It is updated when running :meth:`get_forces`. 59 | C_f (float): Friction coefficient, smooth case. It is updated when running :meth:`get_forces`. 60 | deltaC_f (float): Change in friction coefficient due to roughness. It is updated when running :meth:`get_forces`. 61 | skin_friction ((3,) ndarray): Skin friction force (N, N, N*m). [F_x, F_z, M_cg]. It is updated when running :meth:`get_forces`. 62 | lift_change ((3,) ndarray): Lift change due to roughness (N, N, N*m). [F_x, F_z, M_cg]. It is updated when running :meth:`get_forces`. 63 | air_resistance ((3,) ndarray): Air resistance force (N, N, N*m). [F_x, F_z, M_cg]. It is updated when running :meth:`get_forces`. 64 | flap_force ((3,) ndarray): Flap resultant force (N, N, N*m). [F_x, F_z, M_cg]. It is updated when running :meth:`get_forces`. 65 | thrust_force ((3,) ndarray): Thrust resultant force (N, N, N*m). [F_x, F_z, M_cg]. It is updated when running :meth:`get_forces`. 66 | net_force ((3,) ndarray): Net force (N, N, N*m). [F_x, F_z, M_cg]. It is updated when running :meth:`get_forces`. 67 | mass_matrix ((2, 2) ndarray): Mass coefficients matrix. [[A_33 (kg), A_35 (kg*m/rad)], [A_53 (kg*m), A_55 (kg*m^2/rad)]]. It is updated when running :meth:`get_eom_matrices`. 68 | damping_matrix ((2, 2) ndarray): Damping coefficients matrix. [[B_33 (kg/s), B_35 (kg*m/(s*rad))], [B_53 (kg*m/s), B_55 (kg*m**2/(s*rad))]]. It is updated when running :meth:`get_eom_matrices`. 69 | restoring_matrix ((2, 2) ndarray): Restoring coefficients matrix. [[C_33 (N/m), C_35 (N/rad)], [C_53 (N), C_55 (N*m/rad)]]. It is updated when running :meth:`get_eom_matrices`. 70 | porpoising (list): [[eigenvalue result (bool), est. pitch settling time (s)], [Savitsky chart result (bool), critical trim angle (deg)]]. It is updated when running :meth:`check_porpoising`. 71 | seaway_drag_type (int): 1 = Use Savitsky's '76 approximation, 2 = Use Fridsma's '71 designs charts. Defaults to 1. It is an input to :class:`PlaningBoat`. 72 | avg_impact_acc ((2,) ndarray): Average impact acceleration at center of gravity and bow (g's). [n_cg, n_bow]. It is updated when running :meth:`get_seaway_behavior`. 73 | R_AW (float): Added resistance in waves (N). It is updated when running :meth:`get_seaway_behavior`. 74 | """ 75 | 76 | def __init__(self, speed, weight, beam, lcg, vcg, r_g, beta, epsilon, vT, lT, loa=None, H_sig=None, ahr=150e-6, LD_change=None, Lf=0, sigma=0, delta=0, l_air=0, h_air=0, b_air=0, C_shape=0, C_D=0.7, z_wl=0, tau=5, rho=1025.87, nu=1.19e-6, rho_air=1.225, g=9.8066, wetted_lengths_type=1, z_max_type=1, roughness_penalty_type=1, seaway_drag_type=1): 77 | """Initialize attributes for PlaningBoat 78 | 79 | Args: 80 | speed (float): Speed (m/s). 81 | weight (float): Weidght (N). 82 | beam (float): Beam (m). 83 | lcg (float): Longitudinal center of gravity, measured from the stern (m). 84 | vcg (float): Vertical center of gravity, measured from the keel (m). 85 | r_g (float): Radius of gyration (m). 86 | beta (float): Deadrise (deg). 87 | epsilon (float): Thrust angle w.r.t. keel, CCW with body-fixed origin at 9 o'clock (deg). 88 | vT (float): Thrust vertical distance, measured from keel, and positive up (m). 89 | lT (float): Thrust horizontal distance, measured from stern, and positive forward (m). 90 | loa (float, optional): Vessel LOA for seaway behavior estimates (m). Defaults to None. 91 | H_sig (float, optional): Significant wave heigth in an irregular sea state (m). Defaults to None. 92 | ahr (float, optional): Average hull roughness (m). Defaults to 150*10**-6. 93 | LD_change (float, optional): Roughness induced change of hull lift to change of hull drag ratio (dimensionless). Defaults to None (ITTC '78 estimates -1.1). 94 | Lf (float, optional): Flap chord (m). Defaults to 0. 95 | sigma (float, optional): Flap span-beam ratio (dimensionless). Defaults to 0. 96 | delta (float, optional): Flap deflection (deg). Defaults to 0. 97 | l_air (float, optional): Distance from stern to center of air pressure (m). Defaults to 0. 98 | h_air (float, optional): Height from keel to top of square which bounds the air-drag-inducing area (m). Defaults to 0. 99 | b_air (float, optional): Transverse width of square which bounds the air-drag-inducing area (m). Defaults to 0. 100 | C_shape (float, optional): Area coefficient for air-drag-inducing area (dimensionless). C_shape = 1 means the air drag reference area is h_air*b_air. Defaults to 0. 101 | C_D (float, optional): Air drag coefficient (dimensionless). Defaults to 0.7. 102 | z_wl (float, optional): Vertical distance of center of gravity to the calm water line (m). Defaults to 0. 103 | tau (float, optional): Trim angle (deg). Defaults to 5. 104 | rho (float, optional): Water density (kg/m^3). Defaults to 1025.87. 105 | nu (float, optional): Water kinematic viscosity (m^2/s). Defaults to 1.19*10**-6. 106 | rho_air (float, optional): Air density (kg/m^3). Defaults to 1.225. 107 | g (float, optional): Gravitational acceleration (m/s^2). Defaults to 9.8066. 108 | wetted_lengths_type (int, optional): 1 = Use Faltinsen 2005 wave rise approximation, 2 = Use Savitsky's '64 approach, 3 = Use Savitsky's '76 approach. Defaults to 1. 109 | z_max_type (int, optional): 1 = Uses 3rd order polynomial fit, 2 = Uses cubic interpolation from table. This is only used if wetted_lenghts_type == 1. Defaults to 1. 110 | roughness_penalty_type (int, optional): 1 = Use Mosaad's '86 regression, 2 = Use Townsin's '84 regression. Defaults to 1. 111 | seaway_drag_type (int, optional): 1 = Use Savitsky's '76 approximation, 2 = Use Fridsma's '71 designs charts. Defaults to 1. 112 | """ 113 | self.speed = speed 114 | self.weight = weight 115 | self.beam = beam 116 | self.lcg = lcg 117 | self.vcg = vcg 118 | self.r_g = r_g 119 | self.beta = beta 120 | self.epsilon = epsilon 121 | self.vT = vT 122 | self.lT = lT 123 | self.loa = loa 124 | self.H_sig = H_sig 125 | self.ahr = ahr 126 | self.LD_change = LD_change 127 | self.Lf = Lf 128 | self.sigma = sigma 129 | self.delta = delta 130 | self.l_air = l_air 131 | self.h_air = h_air 132 | self.b_air= b_air 133 | self.C_shape = C_shape 134 | self.z_wl = z_wl 135 | self.tau = tau 136 | self.eta_3 = 0 137 | self.eta_5 = 0 138 | self.rho = rho 139 | self.nu = nu 140 | self.rho_air = rho_air 141 | self.C_D = C_D 142 | self.g = g 143 | 144 | self.gravity_force = np.array([0, -self.weight, 0]) 145 | 146 | self.wetted_lengths_type = wetted_lengths_type 147 | self.z_max_type = z_max_type 148 | 149 | self.roughness_penalty_type = roughness_penalty_type 150 | 151 | self.seaway_drag_type = seaway_drag_type 152 | 153 | def print_description(self, sigFigs=7, runAllFunctions=True): 154 | """Returns a formatted description of the vessel. 155 | 156 | Args: 157 | sigFigs (int, optional): Number of significant figures to display. Defaults to 7. 158 | runAllFunctions (bool, optional): Runs all functions with default values before printing results. Defaults to True. 159 | """ 160 | if runAllFunctions: 161 | self.get_geo_lengths() 162 | self.get_forces(runGeoLengths=False) 163 | self.get_eom_matrices(runGeoLengths=False) 164 | self.get_seaway_behavior() 165 | self.check_porpoising() 166 | 167 | volume = self.weight/(self.g*self.rho) 168 | 169 | table = [ 170 | ['---VESSEL---'], 171 | ['Speed', self.speed, 'm/s'], 172 | ['V_k', self.speed*1.944, 'knot'], 173 | ['Fn (beam)', self.speed/np.sqrt(self.g*self.beam), ''], 174 | ['Fn (volume)', self.speed/np.sqrt(self.g*(volume)**(1/3)), ''], 175 | ['V_m', self.bottom_fluid_speed, 'm/s, mean bottom fluid speed'], 176 | ['Rn', self.bottom_fluid_speed * self.lambda_W * self.beam / self.nu, 'based on V_m and mean wetted-length'], 177 | [''], 178 | ['Weight', self.weight, 'N'], 179 | ['Mass', self.weight/self.g, 'kg'], 180 | ['Volume', volume, 'm\u00B3'], 181 | ['Beam', self.beam, 'm'], 182 | ['LCG', self.lcg, 'm from stern'], 183 | ['VCG', self.vcg, 'm from keel'], 184 | ['R_g', self.r_g, 'm'], 185 | ['Deadrise', self.beta, 'deg'], #'\N{greek small letter beta}' 186 | [''], 187 | ['LOA', self.loa, 'm'], 188 | ['AHR', self.ahr*10**6, '10\u207b\u2076m, average hull roughness'], 189 | [''], 190 | ['---ATTITUDE---'], 191 | ['z_wl', self.z_wl, 'm, vertical distance of center of gravity to the calm water line'], 192 | ['tau', self.tau, 'deg, trim angle'], 193 | ['\u03B7\u2083', self.eta_3, 'deg, additional heave'], 194 | ['\u03B7\u2085', self.eta_5, 'deg, additional trim'], 195 | [''], 196 | ['---PROPULSION---'], 197 | ['Thrust angle', self.epsilon, 'deg w.r.t. keel (CCW with body-fixed origin at 9 o\'clock)'], 198 | ['LCT', self.lT, 'm from stern, positive forward'], 199 | ['VCT', self.vT, 'm from keel, positive up'], 200 | [''], 201 | ['---FLAP---'], 202 | ['Chord', self.Lf, 'm'], 203 | ['Span/Beam', self.sigma, ''], 204 | ['Angle', self.delta, 'deg w.r.t. keel (CCW with body-fixed origin at 9 o\'clock)'], 205 | [''], 206 | ['---AIR DRAG---'], 207 | ['l_air', self.l_air, 'm, distance from stern to center of air pressure'], 208 | ['h_air', self.h_air, 'm, height from keel to top of square which bounds the air-drag-inducing shape'], 209 | ['b_air', self.b_air, 'm, transverse width of square which bounds the air-drag-inducing shape'], 210 | ['C_shape', self.C_shape, 'area coefficient for air-drag-inducing shape. C_shape = 1 means the air drag reference area is h_air*b_air'], 211 | ['C_D', self.C_D, 'air drag coefficient'], 212 | [''], 213 | ['---ENVIRONMENT---'], 214 | ['\u03C1', self.rho, 'kg/m\u00B3, water density'], 215 | ['\u03BD', self.nu, 'm\u00B2/s, water kinematic viscosity'], 216 | ['\u03C1_air', self.rho_air, 'kg/m\u00B3, air density'], 217 | ['g', self.g, 'm/s\u00B2, gravitational acceleration'], 218 | [''], 219 | ['---WETTED LENGTH OPTIONS---'], 220 | ['wetted_lengths_type', self.wetted_lengths_type, '(1 = Use Faltinsen 2005 wave rise approximation, 2 = Use Savitsky\'s \'64 approach, 3 = Use Savitsky\'s \'76 approach)'], 221 | ['z_max_type', self.z_max_type, '(1 = Uses 3rd order polynomial fit (faster, recommended), 2 = Use cubic interpolation)'], 222 | [''], 223 | ['---RUNNING GEOMETRY---'], 224 | ['L_K', self.L_K, 'm, keel wetted length'], 225 | ['L_C', self.L_C, 'm, chine wetted length'], 226 | ['L_C2', self.L_C2, 'm, side chine wetted length'], 227 | ['\u03BB', self.lambda_W, 'mean wetted-length to beam ratio (L_K+L_C)/(2*beam)'], 228 | ['x_s', self.x_s, 'm, distance from keel/water-line intersection to start of wetted chine'], 229 | ['z_max', self.z_max, 'maximum pressure coordinate coefficient (z_max/Ut)'], 230 | ['alpha', self.alpha, 'deg, spray line angle w.r.t. keel in plan view'], 231 | ['LCP', self.lcp, 'm, longitudinal center of pressure from stern'], 232 | ['T', self.T, 'm, draft of keel at transom'], 233 | ['wetted_bottom_area', self.wetted_bottom_area, 'm\u00B2, bottom wetted surface area'], 234 | [''], 235 | ['---ROUGHNESS DRAG PENALTY---'], 236 | ['roughness_penalty_type', self.roughness_penalty_type, '(1 = Use Mosaad\'s \'86 regression, 2 = Use Townsin\'s \'84 regression)'], 237 | ['\u0394C_f', self.deltaC_f*10**3, '10\u207b\u00b3 change in friction coefficient'], 238 | ['\u0394L/\u0394D', self.LD_change, 'roughness induced change of hull lift to change of hull drag ratio'], 239 | ['\u0394C_L', self.deltaC_L*10**3, '10\u207b\u00b3 change in lift coefficient'], 240 | [''], 241 | ['---FORCES [F_x (N, +aft), F_z (N, +up), M_cg (N*m, +pitch up)]---'], 242 | ['Hydrodynamic Force', self.hydrodynamic_force, ''], 243 | ['Skin Friction', self.skin_friction, ''], 244 | ['Roughness Lift Change', self.lift_change, ''], 245 | ['Air Resistance', self.air_resistance, ''], 246 | ['Flap Force', self.flap_force, ''], 247 | ['Net Force', self.net_force, ''], 248 | ['Resultant Thrust', self.thrust_force, ''], 249 | ['---THURST & POWER---'], 250 | ['Thrust Magnitude', np.sqrt(self.thrust_force[0]**2+self.thrust_force[1]**2), 'N'], 251 | ['Effective Thrust', -self.thrust_force[0], 'N'], 252 | ['Eff. Power', -self.thrust_force[0]*self.speed/1000, 'kW'], 253 | ['Eff. Horsepower', -self.thrust_force[0]*self.speed/1000/0.7457, 'hp'], 254 | [''], 255 | ['---EOM MATRICES---'], 256 | ['Mass matrix, [kg, kg*m/rad; kg*m, kg*m\u00B2/rad]', self.mass_matrix, ''], 257 | ['Damping matrix, [kg/s, kg*m/(s*rad); kg*m/s, kg*m\u00B2/(s*rad)]', self.damping_matrix, ''], 258 | ['Restoring matrix, [N/m, N/rad; N, N*m/rad]', self.restoring_matrix, ''], 259 | ['---PORPOISING---'], 260 | ['[[Eigenvalue check result, Est. pitch settling time (s)],\n [Savitsky chart result, Critical trim angle (deg)]]', np.array(self.porpoising), ''], 261 | ['---BEHAVIOR IN WAVES---'], 262 | ['H_sig', self.H_sig, 'm, significant wave heigth'], 263 | ['R_AW', self.R_AW, 'N, added resistance in waves'], 264 | ['Average impact acceleration [n_cg, n_bow] (g\'s)', self.avg_impact_acc, ''], 265 | ] 266 | 267 | cLens=[16,0,0] #Min spacing for columns 268 | for row in table: 269 | if len(row)==3: 270 | if row[1] is None: 271 | print('{desc:<{cL0}} {val:<{cL1}} {unit:<{cL2}}'.format(desc=row[0], val='None', unit=row[2], cL0=cLens[0], cL1=cLens[1], cL2=cLens[2])) 272 | elif isinstance(row[1], (list,np.ndarray)): 273 | print(row[0]+' =') 274 | with np.printoptions(formatter={'float': f'{{:.{sigFigs}g}}'.format}): 275 | print(row[1]) 276 | print(row[2]) 277 | else: 278 | print('{desc:<{cL0}} {val:<{cL1}.{sNum}g} {unit:<{cL2}}'.format(desc=row[0], val=row[1], unit=row[2], cL0=cLens[0], cL1=cLens[1], cL2=cLens[2], sNum=sigFigs)) 279 | else: 280 | print(row[0]) 281 | 282 | def get_geo_lengths(self): 283 | """This function outputs the geometric lengths. 284 | 285 | Adds/updates the following attributes: 286 | 287 | - :attr:`L_K` 288 | - :attr:`L_C` 289 | - :attr:`lambda_W` 290 | - :attr:`x_s` 291 | - :attr:`z_max` 292 | """ 293 | U = self.speed 294 | b = self.beam 295 | lcg = self.lcg 296 | vcg = self.vcg 297 | z_wl = self.z_wl 298 | tau = self.tau 299 | beta = self.beta 300 | eta_3 = self.eta_3 301 | eta_5 = self.eta_5 302 | pi = np.pi 303 | wetted_lengths_type = self.wetted_lengths_type 304 | z_max_type = self.z_max_type 305 | g = self.g 306 | 307 | #Keel wetted length, Eq. 9.50 of Faltinsen 2005, page 367 308 | L_K = lcg + vcg / np.tan(pi/180*(tau + eta_5)) - (z_wl + eta_3) / np.sin(pi/180*(tau + eta_5)) 309 | if L_K < 0: 310 | L_K = 0 311 | 312 | if wetted_lengths_type == 1: 313 | #z_max/Vt coefficient, Table 8.3 of Faltinsen 2005, page 303--------------- 314 | beta_table = [4, 7.5, 10, 15, 20, 25, 30, 40] 315 | z_max_table = [0.5695, 0.5623, 0.5556, 0.5361, 0.5087, 0.4709, 0.4243, 0.2866] 316 | 317 | #Extrapolation warning 318 | if beta < beta_table[0] or beta > beta_table[-1]: 319 | warnings.warn('Deadrise ({0:.3f}) outside the interpolation range of 4-40 deg (Table 8.3 of Faltinsen 2005). Extrapolated values might be inaccurate.'.format(beta), stacklevel=2) 320 | 321 | if z_max_type == 1: 322 | z_max = np.polyval([-2.100644618790201e-006, -6.815747611588763e-005, -1.130563334939335e-003, 5.754510457848798e-001], beta) 323 | elif z_max_type == 2: 324 | z_max_func = interpolate.interp1d(beta_table, z_max_table, kind='cubic', fill_value='extrapolate') #Interpolation of the table 325 | z_max = z_max_func(beta) 326 | #-------------------------------------------------------------------------- 327 | 328 | #Distance from keel/water-line intersection to start of wetted chine (Eq. 9.10 of Faltinsen) 329 | x_s = 0.5 * b * np.tan(pi/180*beta) / ((1 + z_max) * (pi/180)*(tau + eta_5)) 330 | alpha = np.arctan(b/(2*x_s))*180/pi #Angle between spray line and keel (projected to plan view) 331 | if x_s < 0: 332 | x_s = 0 333 | 334 | #Chine wetted length, Eq. 9.51 of Faltinsen 2005 335 | L_C = L_K - x_s 336 | if L_C < 0: 337 | L_C = 0 338 | x_s = L_K 339 | warnings.warn('Vessel operating without wetted chines (L_C = 0).', stacklevel=2) 340 | 341 | #Mean wetted length-to-beam ratio 342 | lambda_W = (L_K + L_C) / (2 * b) 343 | 344 | elif wetted_lengths_type == 2: 345 | #Eq. 3 of Savitsky '64 346 | x_s = b/pi*np.tan(pi/180*beta)/np.tan(pi/180*(tau + eta_5)) 347 | alpha = np.arctan(b/(2*x_s))*180/pi #Angle between spray line and keel (projected to plan view) 348 | 349 | #z_max/Vt coefficient (E. 9.10 of Faltinsen 2005 rearranged) 350 | z_max = 0.5 * b * np.tan(pi/180*beta) / (x_s * (pi/180)*(tau + eta_5)) - 1 351 | 352 | #Chine wetted length 353 | L_C = L_K - x_s 354 | if L_C < 0: 355 | L_C = 0 356 | x_s = L_K 357 | warnings.warn('Vessel operating without wetted chines (L_C = 0).', stacklevel=2) 358 | 359 | #Mean wetted length-to-beam ratio 360 | lambda_W = (L_K + L_C)/(2*b) 361 | 362 | elif wetted_lengths_type == 3: 363 | #Eq. 12 of Savitsky '76 364 | w = (0.57 + beta/1000)*(np.tan(pi/180*beta)/(2*np.tan(pi/180*(tau+eta_5)))-beta/167) 365 | 366 | lambda_K = L_K/b 367 | 368 | #Eq. 14 of Savitsky '76 369 | lambda_C = (lambda_K-w)-0.2*np.exp(-(lambda_K-w)/0.3) 370 | L_C = lambda_C*b 371 | 372 | x_s = L_K - L_C 373 | alpha = np.arctan(b/(2*x_s))*180/pi #Angle between spray line and keel (projected to plan view) 374 | 375 | #z_max/Vt coefficient (Eq. 9.10 of Faltinsen 2005 rearranged) 376 | z_max = 0.5 * b * np.tan(pi/180*beta) / (x_s * (pi/180)*(tau + eta_5)) - 1 377 | 378 | if lambda_C < 0: 379 | lambda_C = 0 380 | L_C = 0 381 | x_s = 0 382 | warnings.warn('Vessel operating without wetted chines (L_C = 0).', stacklevel=2) 383 | 384 | #Mean wetted length-to-beam ratio, Eq. 15 of Savitsky '76 385 | lambda_W = (lambda_K + lambda_C)/2+0.03 386 | 387 | if self.loa is not None: 388 | if L_K > self.loa: 389 | warnings.warn('The estimated wetted chine length ({0:.3f}) is larger than the vessel overall length ({1:.3f}).'.format(L_K, self.loa), stacklevel=2) 390 | 391 | #Chines-dry planing condition (Eq. 3 of Savitsky '76) 392 | Fn_B = U/np.sqrt(g*b) #Beam Froude number 393 | chines_dry = Fn_B**2 - (lambda_W - 0.16*np.tan(beta*pi/180)/np.tan(tau*pi/180))/(3*np.sin(tau*pi/180)) 394 | if chines_dry >= 0: 395 | L_C2 = 0 396 | else: 397 | L_C2 = L_C - 3*U**2*np.sin(tau*pi/180)/g #Side wetting length (Eq. 1 of Savitsky '76) 398 | 399 | #Transom draft 400 | T = L_K*np.sin((tau+eta_5)*pi/180) 401 | 402 | #Update values 403 | self.L_K = L_K 404 | self.L_C = L_C 405 | self.L_C2 = L_C2 406 | self.lambda_W = lambda_W 407 | self.x_s = x_s 408 | self.alpha = alpha 409 | self.z_max = z_max 410 | self.T = T 411 | 412 | def get_forces(self, runGeoLengths=True): 413 | """This function calls all the force functions to update the respective object attributes. 414 | 415 | Adds/updates the following attributes: 416 | 417 | - :attr:`C_Lbeta` 418 | - :attr:`lcp` 419 | - :attr:`wetted_bottom_area` 420 | - :attr:`bottom_fluid_speed` 421 | - :attr:`C_f` 422 | - :attr:`deltaC_f` 423 | - :attr:`deltaC_L` 424 | - :attr:`hydrodynamic_force` 425 | - :attr:`skin_friction` 426 | - :attr:`lift_change` 427 | - :attr:`air_resistance` 428 | - :attr:`flap_force` 429 | - :attr:`thrust_force` 430 | - :attr:`net_force` 431 | 432 | Args: 433 | runGeoLengths (boolean, optional): Calculate the wetted lengths before calculating the forces. Defaults to True. 434 | 435 | Methods: 436 | get_hydrodynamic_force(): This function follows Savitsky 1964 and Faltinsen 2005 in calculating the vessel's hydrodynamic forces and moment. 437 | get_skin_friction(): This function outputs the frictional force of the vessel using ITTC 1957 and the Townsin 1985 roughness allowance. 438 | get_lift_change(): This function estimates the lift change due to roughness wr.r.t. global coordinates. 439 | get_air_resistance(): This function estimates the air drag. It assumes a square shape projected area with a shape coefficient. 440 | get_flap_force(): This function outputs the flap forces w.r.t. global coordinates (Savitsky & Brown 1976). Horz: Positive Aft, Vert: Positive Up, Moment: Positive CCW. 441 | sum_forces(): This function gets the sum of forces and moments, and consequently the required net thrust. The coordinates are positive aft, positive up, and positive counterclockwise. 442 | """ 443 | if runGeoLengths: 444 | self.get_geo_lengths() #Calculated wetted lengths in get_forces() 445 | 446 | g = self.g 447 | rho_air = self.rho_air 448 | C_D = self.C_D 449 | rho = self.rho 450 | nu = self.nu 451 | AHR = self.ahr 452 | LD_change = self.LD_change 453 | W = self.weight 454 | epsilon = self.epsilon 455 | vT = self.vT 456 | lT = self.lT 457 | U = self.speed 458 | b = self.beam 459 | lcg = self.lcg 460 | vcg = self.vcg 461 | 462 | Lf = self.Lf 463 | sigma = self.sigma 464 | delta = self.delta 465 | beam = self.beam 466 | 467 | l_air = self.l_air 468 | h_air = self.h_air 469 | b_air = self.b_air 470 | C_shape = self.C_shape 471 | 472 | z_wl = self.z_wl 473 | tau = self.tau 474 | beta = self.beta 475 | eta_3 = self.eta_3 476 | eta_5 = self.eta_5 477 | 478 | L_K = self.L_K 479 | L_C = self.L_C 480 | lambda_W = self.lambda_W 481 | x_s = self.x_s 482 | alpha = self.alpha 483 | z_max = self.z_max 484 | 485 | roughness_penalty_type = self.roughness_penalty_type 486 | 487 | pi = np.pi 488 | 489 | def get_hydrodynamic_force(): 490 | """This function follows Savitsky 1964 and Faltinsen 2005 in calculating the vessel's hydrodynamic forces and moment. 491 | """ 492 | #Beam Froude number 493 | Fn_B = U/np.sqrt(g*b) 494 | 495 | #Warnings 496 | if Fn_B < 0.6 or Fn_B > 13: 497 | warnings.warn('Beam Froude number = {0:.3f}, outside of range of applicability (0.60 <= U/sqrt(g*b) <= 13.00) for planing lift equation. Results are extrapolations.'.format(Fn_B), stacklevel=2) 498 | if lambda_W > 4: 499 | warnings.warn('Mean wetted length-beam ratio = {0:.3f}, outside of range of applicability (lambda <= 4) for planing lift equation. Results are extrapolations.'.format(lambda_W), stacklevel=2) 500 | if tau < 2 or tau > 15: 501 | warnings.warn('Vessel trim = {0:.3f}, outside of range of applicability (2 deg <= tau <= 15 deg) for planing lift equation. Results are extrapolations.'.format(tau), stacklevel=2) 502 | 503 | #0-Deadrise lift coefficient 504 | C_L0 = (tau + eta_5)**1.1 * (0.012 * lambda_W**0.5 + 0.0055 * lambda_W**2.5 / Fn_B**2) 505 | 506 | #Lift coefficient with deadrise, C_Lbeta 507 | C_Lbeta = C_L0 - 0.0065 * beta * C_L0**0.6 508 | 509 | #Vertical force (lift) 510 | F_z = C_Lbeta * 0.5 * rho * U**2 * b**2 511 | 512 | #Horizontal force 513 | F_x = F_z*np.tan(pi/180*(tau + eta_5)) 514 | 515 | #Lift's Normal force w.r.t. keel 516 | F_N = F_z / np.cos(pi/180*(tau + eta_5)) 517 | 518 | #Longitudinal position of the center of pressure, l_p (Eq. 4.41, Doctors 1985) 519 | l_p = lambda_W * b * (0.75 - 1 / (5.21 * (Fn_B / lambda_W)**2 + 2.39)) #Limits for this is (0.60 < Fn_B < 13.0, lambda < 4.0) 520 | 521 | #Moment about CG (Axis consistent with Fig. 9.24 of Faltinsen (P. 366) 522 | M_cg = - F_N * (lcg - l_p) 523 | 524 | #Update values 525 | self.C_Lbeta = C_Lbeta 526 | self.lcp = l_p 527 | self.hydrodynamic_force = np.array([F_x, F_z, M_cg]) 528 | 529 | def get_skin_friction(): 530 | """This function outputs the frictional force of the vessel using ITTC 1957 and the Townsin 1985 roughness allowance. 531 | """ 532 | #Surface area of the non-wetted-chine region 533 | S1 = x_s**2 * np.tan(alpha*pi/180) / np.cos(pi/180*beta) 534 | # S1 = x_s * b / (2 * np.cos(pi/180*beta)) #This no longer works because x_s is set to L_K when x_s estimate is > L_K 535 | # if L_K < x_s: 536 | # S1 = S1 * (L_K / x_s)**2 537 | 538 | #Surface area of the wetted-chine region 539 | S2 = b * L_C / np.cos(pi/180*beta) 540 | 541 | #Total surface area 542 | S = S1 + S2 543 | if S == 0: 544 | F_x = 0 545 | F_z = 0 546 | M_cg = 0 547 | else: 548 | #Beam Froude number 549 | Fn_B = U/np.sqrt(g*b) 550 | 551 | #Warnings 552 | if Fn_B < 1.0 or Fn_B > 13: 553 | warnings.warn('Beam Froude number = {0:.3f}, outside of range of applicability (1.0 <= U/sqrt(g*b) <= 13.00) for average bottom velocity estimate. Results are extrapolations.'.format(Fn_B), stacklevel=2) 554 | 555 | #Mean bottom fluid velocity, Savitsky 1964 - derived to include deadrise effect 556 | V_m = U * np.sqrt(1 - (0.012 * tau**1.1 * np.sqrt(lambda_W) - 0.0065 * beta * (0.012 * np.sqrt(lambda_W) * tau**1.1)**0.6) / (lambda_W * np.cos(tau * pi/180))) 557 | 558 | #Reynolds number (with bottom fluid velocity) 559 | Rn = V_m * lambda_W * b / nu 560 | 561 | #'Friction coefficient' ITTC 1957 562 | C_f = 0.075/(np.log10(Rn) - 2)**2 563 | 564 | #Additional 'friction coefficient' due to skin friction 565 | if AHR > 0: 566 | if roughness_penalty_type == 1: #Mosaad 1986 roughness allowance 567 | deltaC_f = (6*Rn**0.093*((AHR/(lambda_W*b))**(1/3) - 5.8*Rn**(-1/3)))/10**3 568 | elif roughness_penalty_type == 2: #Townsin 1984 roughness allowance 569 | deltaC_f = (44*((AHR/(lambda_W*b))**(1/3) - 10*Rn**(-1/3)) + 0.125)/10**3 570 | else: 571 | deltaC_f = 0 572 | 573 | #Frictional force 574 | R_f = 0.5 * rho * (C_f + deltaC_f) * S * U**2 575 | 576 | #Geometric vertical distance from keel 577 | l_f = (b / 4 * np.tan(pi/180*beta) * S2 + b / 6 * np.tan(pi/180*beta) * S1) / (S1 + S2) 578 | 579 | #Horizontal force 580 | F_x = R_f * np.cos(pi/180*(tau + eta_5)) 581 | 582 | #Vertical force 583 | F_z = - R_f * np.sin(pi/180*(tau + eta_5)) 584 | 585 | #Moment about CG (Axis consistent with Fig. 9.24 of Faltinsen (P. 366)) 586 | M_cg = R_f * (l_f - vcg) 587 | 588 | #Update values 589 | self.wetted_bottom_area = S 590 | self.bottom_fluid_speed = V_m 591 | self.C_f = C_f 592 | self.deltaC_f = deltaC_f 593 | self.skin_friction = np.array([F_x, F_z, M_cg]) 594 | 595 | def get_lift_change(): 596 | """This function estimates the lift change due to roughness wr.r.t. global coordinates. 597 | """ 598 | if LD_change is None: 599 | self.lift_change = np.array([0, 0, 0]) 600 | self.deltaC_L = 0 601 | return 602 | 603 | S = self.wetted_bottom_area 604 | if S == 0: 605 | deltaR_f = 0 606 | else: 607 | #Frictional force due to roughness only 608 | deltaR_f = 0.5 * rho * self.deltaC_f * S * U**2 609 | 610 | #Change of hydrodynamic normal force based on ITTC '78 report on propeller tests (P. 274) 611 | deltaF_N = deltaR_f * (LD_change*np.cos(pi/180*(tau + eta_5)) + np.sin(pi/180*(tau + eta_5))) / (LD_change*np.sin(pi/180*(tau + eta_5)) - np.cos(pi/180*(tau + eta_5))) 612 | 613 | #Horizontal force 614 | F_x = - deltaF_N * np.sin(pi/180*(tau + eta_5)) 615 | 616 | #Vertical force (lift) 617 | F_z = - deltaF_N * np.cos(pi/180*(tau + eta_5)) 618 | 619 | #Moment about CG (Axis consistent with Fig. 9.24 of Faltinsen (P. 366) 620 | M_cg = deltaF_N * (lcg - self.lcp) 621 | 622 | #Change in hydrodynamic lift coefficient due to roughness (Note: Does not include lift change due to roughness) 623 | deltaC_L = F_z/(0.5 * rho * U**2 * b**2) 624 | 625 | #Update values 626 | self.deltaC_L = deltaC_L 627 | self.lift_change = np.array([F_x, F_z, M_cg]) 628 | 629 | def get_air_resistance(): 630 | """This function estimates the air drag. It assumes a square shape projected area with a shape coefficient. 631 | """ 632 | if C_shape == 0 or b_air == 0: 633 | self.air_resistance = np.array([0, 0, 0]) 634 | return 635 | 636 | #Vertical distance from running calm water line to keel at l_air 637 | a_dist = np.sin(pi/180*(tau + eta_5))*(l_air-L_K) 638 | 639 | #Vertical distance from keel at l_air to horizontal line level with h_air 640 | b_dist = np.cos(pi/180*(tau + eta_5))*h_air 641 | 642 | #Vertical distance from CG to center of square (moment arm, positive is CG above) 643 | momArm = z_wl - (a_dist + b_dist)/2 644 | 645 | #Square projected area 646 | Area = (a_dist+b_dist)*b_air*C_shape 647 | if Area < 0: 648 | Area = 0 649 | 650 | #Horizontal force (Positive aft) 651 | F_x = 0.5*rho_air*C_D*Area*U**2 652 | 653 | #Vertical force (Positive up) 654 | F_z = 0 655 | 656 | #Moment (positve CCW) 657 | M_cg = -F_x*momArm 658 | 659 | #Update values 660 | self.air_resistance = np.array([F_x, F_z, M_cg]) 661 | 662 | def get_flap_force(): 663 | """This function outputs the flap forces w.r.t. global coordinates (Savitsky & Brown 1976). Horz: Positive Aft, Vert: Positive Up, Moment: Positive CCW. 664 | """ 665 | if Lf == 0: 666 | self.flap_force = np.array([0, 0, 0]) 667 | return 668 | 669 | #Warnings 670 | if Lf > 0.10*(L_K + L_C)/2 or Lf < 0: 671 | warnings.warn('Flap chord = {0:.3f} outside of bounds (0-10% of mean wetted length) for flap forces estimates with Savitsky & Brown 1976'.format(Lf), stacklevel=2) 672 | if delta < 0 or delta > 15: 673 | warnings.warn('Flap deflection angle = {0:.3f} out of bounds (0-15 deg) for flap forces estimates with Savitsky & Brown 1976'.format(delta), stacklevel=2) 674 | Fn_B = U/np.sqrt(g*b) 675 | if Fn_B < 2 or Fn_B > 7: 676 | warnings.warn('Beam-based Froude number Fn_B = {0:.3f} out of bounds (2-7) for flap forces estimates with Savitsky & Brown 1976'.format(Fn_B), stacklevel=2) 677 | 678 | F_z = 0.046*(Lf*3.28084)*delta*sigma*(b*3.28084)*(rho/515.379)/2*(U*3.28084)**2*4.44822 679 | 680 | F_x = 0.0052*F_z*(tau+eta_5+delta) 681 | 682 | l_flap = 0.6*b+Lf*(1-sigma) 683 | 684 | M_cg = -F_z*(lcg-l_flap) 685 | 686 | #Update values 687 | self.flap_force = np.array([F_x, F_z, M_cg]) 688 | 689 | def sum_forces(): 690 | """This function gets the sum of forces and moments, and consequently the required net thrust. The coordinates are positive aft, positive up, and positive counterclockwise. 691 | """ 692 | #Call all force functions------- 693 | get_hydrodynamic_force() 694 | get_skin_friction() 695 | get_lift_change() 696 | get_air_resistance() 697 | get_flap_force() 698 | #------------------------------- 699 | 700 | forcesMatrix = np.column_stack((self.gravity_force, self.hydrodynamic_force, self.skin_friction, self.lift_change, self.air_resistance, self.flap_force)) #Forces and moments 701 | F_sum = np.sum(forcesMatrix, axis=1) #F[0] is x-dir, F[1] is z-dir, and F[2] is moment 702 | 703 | #Required thrust and resultant forces 704 | thrust = F_sum[0]/np.cos(pi/180*(epsilon+tau+eta_5)); #Magnitude 705 | thrust_z = thrust*np.sin(pi/180*(epsilon+tau+eta_5)); #Vertical 706 | thrust_cg = thrust*np.cos(pi/180*epsilon)*(vcg - vT) - thrust*np.sin(pi/180*epsilon)*(lcg - lT); #Moment about cg 707 | 708 | #Update resultant thurst values 709 | self.thrust_force = np.array([-F_sum[0], thrust_z, thrust_cg]) 710 | 711 | #Include resultant thrust forces in sum 712 | F_sum[1] = F_sum[1]+thrust_z 713 | F_sum[2] = F_sum[2]+thrust_cg 714 | 715 | #Update values 716 | self.net_force = F_sum 717 | 718 | #Call functions 719 | sum_forces() 720 | 721 | def get_steady_trim(self, x0=[0, 3], tauLims=[0.5, 35], tolF=10**-6, maxiter=50): 722 | """This function finds and sets the equilibrium point when the vessel is steadily running in calm water. 723 | 724 | Updates the following attributes: 725 | 726 | - :attr:`z_wl` 727 | - :attr:`tau` 728 | 729 | Args: 730 | x0 (list of float): Initial guess for equilibirum point [z_wl (m), tau (deg)]. Defaults to [0, 3]. 731 | tauLims (list of float): Limits for equilibrium trim search. Defaults to [0.5, 35]. 732 | tolF (float): Tolerance for convergence to zero. Defaults to 10**-6. 733 | maxiter (float): Maximum iterations. Defaults to 50. 734 | """ 735 | def _boatForces(x): 736 | self.z_wl = x[0]/10 #the division is for normalization of the variables 737 | self.tau = x[1] 738 | self.get_forces() 739 | return self.net_force[1:3] 740 | 741 | def _boatForcesPrime(x): 742 | return ndmath.complexGrad(_boatForces, x) 743 | 744 | def _L_K(x): 745 | # self.z_wl = x[0]/10 746 | # self.tau = x[1] 747 | # self.get_geo_lengths() #No need to call, because ndmath's nDimNewton allways calls the obj function before calling this "constraint" 748 | return [-self.L_K] 749 | 750 | xlims = np.array([[-np.inf, np.inf], tauLims]) 751 | warnings.filterwarnings("ignore", category=UserWarning) 752 | [self.z_wl, self.tau] = ndmath.nDimNewton(_boatForces, x0, _boatForcesPrime, tolF, maxiter, xlims, hehcon=_L_K)/[10, 1] 753 | warnings.filterwarnings("default", category=UserWarning) 754 | 755 | def get_eom_matrices(self, runGeoLengths=True): 756 | """This function returns the mass, damping, and stiffness matrices following Faltinsen 2005. 757 | 758 | Adds/updates the following parameters: 759 | 760 | - :attr:`mass_matrix` 761 | - :attr:`damping_matrix` 762 | - :attr:`restoring_matrix` 763 | 764 | Args: 765 | runGeoLengths (boolean, optional): Calculate the wetted lengths before calculating the EOM matrices. Defaults to True. 766 | 767 | Methods: 768 | get_mass_matrix(): This function returns the added mass coefficients following Sec. 9.4.1 of Faltinsen 2005, including weight and moment of inertia. 769 | get_damping_matrix(): This function returns the damping coefficients following Sec. 9.4.1 of Faltinsen 2005. 770 | get_restoring_matrix(diffType=1, step=10**-6.6): This function returns the restoring coefficients following the approach in Sec. 9.4.1 of Faltinsen 2005. 771 | """ 772 | if runGeoLengths: 773 | self.get_geo_lengths() #Calculated wetted lengths in get_eom_matrices() 774 | 775 | W = self.weight 776 | U = self.speed 777 | rho = self.rho 778 | b = self.beam 779 | lcg = self.lcg 780 | tau = self.tau 781 | beta = self.beta 782 | g = self.g 783 | r_g = self.r_g 784 | 785 | eta_5 = self.eta_5 786 | 787 | L_K = self.L_K 788 | L_C = self.L_C 789 | lambda_W = self.lambda_W 790 | x_s = self.x_s 791 | z_max = self.z_max 792 | 793 | pi = np.pi 794 | 795 | def get_mass_matrix(): 796 | """This function returns the added mass coefficients following Sec. 9.4.1 of Faltinsen 2005, including weight and moment of inertia 797 | """ 798 | 799 | #Distance of CG from keel-WL intersection 800 | x_G = L_K - lcg 801 | 802 | #K constant (Eq. 9.63 of Faltinsen 2005) 803 | K = (pi / np.sin(pi/180*beta) * gamma(1.5 - beta/180) / (gamma(1 - beta/180)**2 * gamma(0.5 + beta/180)) - 1) / np.tan(pi/180*beta) 804 | 805 | kappa = (1 + z_max) * (pi/180)*(tau + eta_5) #User defined constant 806 | 807 | #Based on Faltinsen's 808 | A1_33 = rho * kappa**2 * K * x_s**3 / 3 809 | A1_35 = A1_33 * (x_G - x_s * 3/4) 810 | A1_53 = A1_35 811 | A1_55 = A1_33 * (x_G**2 - 3/2 * x_G * x_s + 3/5 * x_s**2) 812 | 813 | #Contribution from wet-chine region 814 | if L_C > 0: 815 | C_1 = 2 * np.tan(pi/180*beta)**2 / pi * K 816 | 817 | A2_33 = (rho * b**3) * C_1 * pi / 8 * L_C / b 818 | A2_35 = (rho * b**4) * (- C_1 * pi / 16 * ((L_K / b)**2 - (x_s / b)**2) + x_G / b * A2_33 / (rho * b**3)) 819 | A2_53 = A2_35 820 | A2_55 = (rho * b**5) * (C_1 * pi / 24 * ((L_K / b)**3 - (x_s / b)**3) - C_1 / 8 * pi * (x_G / b) * ((L_K / b)**2 - (x_s / b)**2) + (x_G / b)**2 * A2_33 / (rho * b**3)) 821 | else: 822 | A2_33 = 0 823 | A2_35 = 0 824 | A2_53 = 0 825 | A2_55 = 0 826 | 827 | #Total added mass & update values 828 | A_33 = A1_33 + A2_33 + W/g # kg, A_33 829 | A_35 = A1_35 + A2_35 # kg*m/rad, A_35 830 | A_53 = A1_53 + A2_53 # kg*m, A_53 831 | A_55 = A1_55 + A2_55 + W/g*r_g**2 # kg*m^2/rad, A_55 832 | self.mass_matrix = np.array([[A_33, A_35], [A_53, A_55]]) 833 | 834 | def get_damping_matrix(): 835 | """This function returns the damping coefficients following Sec. 9.4.1 of Faltinsen 2005 836 | """ 837 | #Heave-heave added mass (need to substract W/g since it was added) 838 | A_33 = self.mass_matrix[0,0] - W/g 839 | 840 | if L_C > 0: 841 | d = 0.5 * b * np.tan(pi/180*beta) 842 | else: 843 | d = (1 + z_max) * (pi/180)*(tau + eta_5) * L_K 844 | 845 | #K constant (Eq. 9.63 of Faltinsen 2005, P. 369) 846 | K = (pi / np.sin(pi/180*beta) * gamma(1.5 - beta/180) / (gamma(1 - beta/180)**2 * gamma(0.5 + beta/180)) - 1) / np.tan(pi/180*beta) 847 | 848 | #2D Added mass coefficient in heave 849 | a_33 = rho * d**2 * K 850 | 851 | #Infinite Fn lift coefficient 852 | C_L0 = (tau + eta_5)**1.1 * 0.012 * lambda_W**0.5 853 | 854 | #Derivative w.r.t. tau (rad) of inf. Fn C_L0 855 | dC_L0 = (180 / pi)**1.1 * 0.0132 * (pi/180*(tau + eta_5))**0.1 * lambda_W**0.5 856 | 857 | #Derivative w.r.t. tau (rad) of inf. Fn C_Lbeta 858 | dC_Lbeta = dC_L0 * (1 - 0.0039 * beta * C_L0**-0.4) 859 | 860 | #Damping coefficients & update values 861 | B_33 = rho / 2 * U * b**2 * dC_Lbeta # kg/s, B_33, Savitsky based 862 | B_35 = - U * (A_33 + lcg * a_33) # kg*m/(s*rad), B_35, Infinite frequency based 863 | B_53 = B_33 * (0.75 * lambda_W * b - lcg) # kg*m/s, B_53, Savitsky based 864 | B_55 = U * lcg**2 * a_33 # kg*m**2/(s*rad), B_55, Infinite frequency based 865 | self.damping_matrix = np.array([[B_33, B_35], [B_53, B_55]]) 866 | 867 | def get_restoring_matrix(diffType=1, step=10**-6.6): 868 | """This function returns the restoring coefficients following the approach in Sec. 9.4.1 of Faltinsen 2005 869 | 870 | Args: 871 | diffType (int, optional): 1 (recommended) = Complex step method, 2 = Foward step difference. Defaults to 1. 872 | step (float, optional): Step size if using diffType == 2. Defaults to 10**-6. 873 | """ 874 | def _func(eta): 875 | self.eta_3 = eta[0] 876 | self.eta_5 = eta[1] 877 | self.get_forces() #This needs to run get_geo_lengths() to work 878 | return self.net_force[1:3] 879 | 880 | temp_eta_3 = self.eta_3 881 | temp_eta_5 = self.eta_5 882 | 883 | warnings.filterwarnings("ignore", category=UserWarning) 884 | if diffType == 1: 885 | C_full = -ndmath.complexGrad(_func, [temp_eta_3, temp_eta_5]) 886 | elif diffType == 2: 887 | C_full = -ndmath.finiteGrad(_func, [temp_eta_3, temp_eta_5], step) 888 | 889 | #Reset values 890 | self.eta_3 = temp_eta_3 891 | self.eta_5 = temp_eta_5 892 | self.get_forces() 893 | warnings.filterwarnings("default", category=UserWarning) 894 | 895 | #Conversion deg to rad (degree in denominator) 896 | C_full[0,1] = C_full[0,1] / (pi/180) # N/rad, C_35 897 | C_full[1,1] = C_full[1,1] / (pi/180) # N*m/rad, C_55 898 | 899 | #Update values 900 | self.restoring_matrix = C_full 901 | 902 | #Call functions 903 | get_mass_matrix() 904 | get_damping_matrix() 905 | get_restoring_matrix() 906 | 907 | def check_porpoising(self, stepEstimateType=1): 908 | """This function checks for porpoising. 909 | 910 | Adds/updates the following parameters: 911 | 912 | - :attr:`porpoising` (list): 913 | 914 | Args: 915 | stepEstimateType (int, optional): Pitch step response settling time estimate type, 1 = -3/np.real(eigVals[0])], 2 = Time-domain simulation estimate. Defaults to 1. 916 | """ 917 | #Eigenvalue analysis 918 | try: 919 | self.mass_matrix 920 | except AttributeError: 921 | warnings.warn('No Equation Of Motion (EOM) matrices found. Running get_eom_matrices().', stacklevel=2) 922 | self.get_eom_matrices() 923 | 924 | M = self.mass_matrix 925 | C = self.damping_matrix 926 | K = self.restoring_matrix 927 | 928 | nDim = len(M) 929 | A_ss = np.concatenate((np.concatenate((np.zeros((nDim,nDim)), np.identity(nDim)), axis=1), np.concatenate((-np.linalg.solve(M,K), -np.linalg.solve(M,C)), axis=1))) #State space reprecentation 930 | 931 | eigVals = np.linalg.eigvals(A_ss) 932 | 933 | eig_porpoise = any(eigVal >= 0 for eigVal in eigVals) 934 | 935 | if stepEstimateType == 1: 936 | settling_time = -3/np.real(eigVals[0]) 937 | elif stepEstimateType == 2: 938 | B_ss = np.array([[1],[0],[0],[0]]) #Pitch only 939 | C_ss = np.array([[1,0,0,0]]) #Pitch only 940 | D_ss = np.array([[0]]) 941 | 942 | system = (A_ss,B_ss,C_ss,D_ss) 943 | t, y = signal.step(system) 944 | settling_time = (t[next(len(y)-i for i in range(2,len(y)-1) if abs(y[-i]/y[-1])>1.02)]-t[0]) 945 | 946 | #Savitsky '64 chart method 947 | C_L = self.weight/(1/2*self.rho*self.speed**2*self.beam**2) 948 | x = np.sqrt(C_L/2) 949 | 950 | #Warnings 951 | if x > 0.3 or x < 0.13: 952 | warnings.warn('Lift Coefficient = {0:.3f} outside of bounds (0.0338-0.18) for porpoising estimates with Savitsky 1964. Results are extrapolations.'.format(C_L), stacklevel=2) 953 | if self.beta > 20: 954 | warnings.warn('Deadrise = {0:.3f} outside of bounds (0-20 deg) for porpoising estimates with Savitsky 1964. Results are extrapolations.'.format(self.beta), stacklevel=2) 955 | 956 | tau_crit_0 = -376.37*x**3 + 329.74*x**2 - 38.485*x + 1.3415 957 | tau_crit_10 = -356.05*x**3 + 314.36*x**2 - 41.674*x + 3.5786 958 | tau_crit_20 = -254.51*x**3 + 239.65*x**2 - 23.936*x + 3.0195 959 | 960 | tau_crit_func = interpolate.interp1d([0, 10, 20], [tau_crit_0, tau_crit_10, tau_crit_20], kind='quadratic', fill_value='extrapolate') 961 | tau_crit = tau_crit_func(self.beta) 962 | 963 | if self.tau > tau_crit: 964 | chart_porpoise = True 965 | else: 966 | chart_porpoise = False 967 | 968 | #Update values 969 | self.porpoising = [[eig_porpoise, settling_time], [chart_porpoise, float(tau_crit)]] 970 | 971 | def get_seaway_behavior(self): 972 | """This function calculates the seaway behavior as stated in Savitsky & Brown '76. 973 | 974 | Adds/updates the following parameters: 975 | 976 | - :attr:`avg_impact_acc` 977 | - :attr:`R_AW` 978 | """ 979 | if self.H_sig is None: 980 | self.H_sig = self.beam*0.5 #Arbitrary wave height if no user-defined wave height 981 | warnings.warn('Significant wave height has not been specified. Using beam*0.5 = {0:.3f} m.'.format(self.H_sig), stacklevel=2) 982 | if self.loa is None: 983 | self.loa = self.beam*3 984 | warnings.warn('Vessel overall length has not been specified. Using beam*3 = {0:.3f} m.'.format(self.loa), stacklevel=2) 985 | H_sig = self.H_sig 986 | 987 | W = self.weight 988 | beta = self.beta 989 | tau = self.tau 990 | 991 | pi = np.pi 992 | 993 | Delta_LT = W/9964 #Displacement in long tons 994 | Delta = Delta_LT*2240 #Displacement in lbf 995 | L = self.loa*3.281 #Length in ft 996 | b = self.beam*3.281 #Beam in ft 997 | Vk = self.speed*1.944 #Speed in knots 998 | Vk_L = Vk/np.sqrt(L) #Vk/sqrt(L) 999 | H_sig = H_sig*3.281 #Significant wave height in ft 1000 | 1001 | w = self.rho*self.g/(4.448*35.315) #Specific weight in lbf/ft^3 1002 | 1003 | C_Delta = Delta/(w*b**3) #Static beam-loading coefficient 1004 | 1005 | if self.seaway_drag_type == 1: #Savitsky '76 1006 | #Check that variables are inside range of applicability (P. 395 of Savitsky & Brown '76) 1007 | P1 = Delta_LT/(0.01*L)**3 1008 | P2 = L/b 1009 | P5 = H_sig/b 1010 | P6 = Vk_L 1011 | if P1 < 100 or P1 > 250: 1012 | warnings.warn('Vessel displacement coefficient = {0:.3f}, outside of range of applicability (100 <= Delta_LT/(0.01*L)^3 <= 250, with units LT/ft^3). Results are extrapolations.'.format(P1), stacklevel=2) 1013 | if P2 < 3 or P2 > 5: 1014 | warnings.warn('Vessel overall length/beam = {0:.3f}, outside of range of applicability (3 <= L/b <= 5). Results are extrapolations.'.format(P2), stacklevel=2) 1015 | if tau < 3 or tau > 7: 1016 | warnings.warn('Vessel trim = {0:.3f}, outside of range of applicability (3 deg <= tau <= 7 deg). Results are extrapolations.'.format(tau), stacklevel=2) 1017 | if beta < 10 or beta > 30: 1018 | warnings.warn('Vessel deadrise = {0:.3f}, outside of range of applicability (10 deg <= beta <= 30 deg). Results are extrapolations.'.format(beta), stacklevel=2) 1019 | if P5 < 0.2 or P5 > 0.7: 1020 | warnings.warn('Significant wave height / beam = {0:.3f}, outside of range of applicability (0.2 <= H_sig/b <= 0.7). Results are extrapolations.'.format(P5), stacklevel=2) 1021 | if P6 < 2 or P6 > 6: 1022 | warnings.warn('Speed coefficient = {0:.3f}, outside of range of applicability (2 <= Vk/sqrt(L) <= 6, with units knots/ft^0.5). Results are extrapolations.'.format(P6), stacklevel=2) 1023 | 1024 | R_AW_2 = (w*b**3)*66*10**-6*(H_sig/b+0.5)*(L/b)**3/C_Delta+0.0043*(tau-4) #Added resistance at Vk/sqrt(L) = 2 1025 | R_AW_4 = (Delta)*(0.3*H_sig/b)/(1+2*H_sig/b)*(1.76-tau/6-2*np.tan(beta*pi/180)**3) #Vk/sqrt(L) = 4 1026 | R_AW_6 = (w*b**3)*(0.158*H_sig/b)/(1+(H_sig/b)*(0.12*beta-21*C_Delta*(5.6-L/b)+7.5*(6-L/b))) #Vk/sqrt(L) = 6 1027 | R_AWs = np.array([R_AW_2, R_AW_4, R_AW_6]) 1028 | 1029 | R_AWs_interp = interpolate.interp1d([2,4,6], R_AWs, kind='quadratic', fill_value='extrapolate') 1030 | R_AW = R_AWs_interp([Vk_L])[0] 1031 | 1032 | elif self.seaway_drag_type == 2: #Fridsma '71 design charts 1033 | #Check that variables are inside range of applicability (P. R-1495 of Fridsma '71) 1034 | if C_Delta < 0.3 or C_Delta > 0.9: 1035 | warnings.warn('C_Delta = {0:.3f}, outside of range of applicability (0.3 <= C_Delta <= 0.9). Results are extrapolations'.format(C_Delta), stacklevel=2) 1036 | if L/b < 3 or L/b > 6: 1037 | warnings.warn('L/b = {0:.3f}, outside of range of applicability (3 <= L/b <= 6). Results are extrapolations'.format(L/b), stacklevel=2) 1038 | if C_Delta/(L/b) < 0.06 or C_Delta/(L/b) > 0.18: 1039 | warnings.warn('C_Delta/(L/b) = {0:.3f}, outside of range of applicability (0.06 <= C_Delta/(L/b) <= 0.18). Results are extrapolations'.format(C_Delta/(L/b)), stacklevel=2) 1040 | if tau < 3 or tau > 7: 1041 | warnings.warn('tau = {0:.3f}, outside of range of applicability (3 <= tau <= 7). Results are extrapolations'.format(tau), stacklevel=2) 1042 | if beta < 10 or beta > 30: 1043 | warnings.warn('beta = {0:.3f}, outside of range of applicability (10 <= beta <= 30). Results are extrapolations'.format(beta), stacklevel=2) 1044 | if H_sig/b > 0.8: 1045 | warnings.warn('H_sig/b = {0:.3f}, outside of range of applicability (H_sig/b <= 0.8). Results are extrapolations'.format(H_sig/b), stacklevel=2) 1046 | if Vk_L > 6: 1047 | warnings.warn('Vk_L = {0:.3f}, outside of range of applicability (Vk_L <= 6). Results are extrapolations'.format(Vk_L), stacklevel=2) 1048 | 1049 | #Get data tables (required for when package is distributed) 1050 | Raw2_tab = pkg_resources.resource_filename(__name__, 'tables\Raw_0.2.csv') 1051 | Raw4_tab = pkg_resources.resource_filename(__name__, 'tables\Raw_0.4.csv') 1052 | Raw6_tab = pkg_resources.resource_filename(__name__, 'tables\Raw_0.6.csv') 1053 | 1054 | V2_tab = pkg_resources.resource_filename(__name__, 'tables\V_0.2.csv') 1055 | V4_tab = pkg_resources.resource_filename(__name__, 'tables\V_0.4.csv') 1056 | 1057 | RawV2_tab = pkg_resources.resource_filename(__name__, 'tables\Raw_V_0.2.csv') 1058 | RawV4_tab = pkg_resources.resource_filename(__name__, 'tables\Raw_V_0.4.csv') 1059 | RawV6_tab = pkg_resources.resource_filename(__name__, 'tables\Raw_V_0.6.csv') 1060 | 1061 | #Read values from extracted chart points 1062 | arr_Raw2 = np.genfromtxt(Raw2_tab, delimiter=',', skip_header=1) 1063 | arr_Raw4 = np.genfromtxt(Raw4_tab, delimiter=',', skip_header=1) 1064 | arr_Raw6 = np.genfromtxt(Raw6_tab, delimiter=',', skip_header=1) 1065 | 1066 | arr_V2 = np.genfromtxt(V2_tab, delimiter=',', skip_header=1) 1067 | arr_V4 = np.genfromtxt(V4_tab, delimiter=',', skip_header=1) 1068 | 1069 | arr_Raw_V2 = np.genfromtxt(RawV2_tab, delimiter=',', skip_header=1) 1070 | arr_Raw_V4 = np.genfromtxt(RawV4_tab, delimiter=',', skip_header=1) 1071 | arr_Raw_V6 = np.genfromtxt(RawV6_tab, delimiter=',', skip_header=1) 1072 | 1073 | #Create interpolation functions 1074 | interp1Type = 'linear' 1075 | interp2Type = 'linear' 1076 | 1077 | Raw2m_interp = interpolate.interp2d(arr_Raw2[:, 1], arr_Raw2[:, 0], arr_Raw2[:, 2], kind=interp2Type) 1078 | Raw4m_interp = interpolate.interp2d(arr_Raw4[:, 1], arr_Raw4[:, 0], arr_Raw4[:, 2], kind=interp2Type) 1079 | Raw6m_interp = interpolate.interp2d(arr_Raw6[:, 1], arr_Raw6[:, 0], arr_Raw6[:, 2], kind=interp2Type) 1080 | 1081 | V2m_interp = interpolate.interp2d(arr_V2[:, 1], arr_V2[:, 0], arr_V2[:, 2], kind=interp2Type) 1082 | V4m_interp = interpolate.interp2d(arr_V4[:, 1], arr_V4[:, 0], arr_V4[:, 2], kind=interp2Type) 1083 | V6m_interp = V4m_interp 1084 | 1085 | RawRaw2m_interp = interpolate.interp1d(arr_Raw_V2[:, 0], arr_Raw_V2[:, 1], kind=interp1Type, fill_value='extrapolate') 1086 | RawRaw4m_interp = interpolate.interp1d(arr_Raw_V4[:, 0], arr_Raw_V4[:, 1], kind=interp1Type, fill_value='extrapolate') 1087 | RawRaw6m_interp = interpolate.interp1d(arr_Raw_V6[:, 0], arr_Raw_V6[:, 1], kind=interp1Type, fill_value='extrapolate') 1088 | 1089 | #Get values following procedure shown in Fridsma 1971 paper 1090 | VLm = [V2m_interp(beta, tau)[0], V4m_interp(beta, tau)[0], V6m_interp(beta, tau)[0]] 1091 | Rwbm = [Raw2m_interp(beta, tau)[0], Raw4m_interp(beta, tau)[0], Raw6m_interp(beta, tau)[0]] 1092 | VVm = Vk_L/VLm 1093 | RRm = [RawRaw2m_interp(VVm[0]), RawRaw4m_interp(VVm[1]), RawRaw6m_interp(VVm[2])] 1094 | Rwb = np.multiply(RRm, Rwbm) 1095 | 1096 | E1 = lambda H_sig: 1 + ((L/b)**2/25 - 1)/(1 + 0.895*(H_sig/b - 0.6)) #V/sqrt(L) = 2 1097 | E2 = lambda H_sig: 1 + 10*H_sig/b*(C_Delta/(L/b) - 0.12) #V/sqrt(L) = 4 1098 | E3 = lambda H_sig: 1 + 2*H_sig/b*(0.9*(C_Delta-0.6)-0.7*(C_Delta-0.6)**2) #V/sqrt(L) = 6 1099 | E_interp = lambda H_sig: interpolate.interp1d([2, 4, 6], [E1(H_sig), E2(H_sig), E3(H_sig)], kind=interp1Type, fill_value='extrapolate') 1100 | E = [E_interp(0.2*b)(Vk_L), E_interp(0.4*b)(Vk_L), E_interp(0.6*b)(Vk_L)] 1101 | 1102 | Rwb_final = np.multiply(Rwb,E) 1103 | Rwb_final_interp = interpolate.interp1d([0.2, 0.4, 0.6], Rwb_final, kind=interp1Type, fill_value='extrapolate') 1104 | 1105 | R_AW = Rwb_final_interp(H_sig/b)*w*b**3 1106 | 1107 | warnings.warn('Average impact acceleration based on the Fridsma charts is currently not implemented. Using Savitsky & Brown approximation.', stacklevel=2) 1108 | 1109 | n_cg = 0.0104*(H_sig/b+0.084)*tau/4*(5/3-beta/30)*(Vk_L)**2*L/b/C_Delta #g, at CG 1110 | n_bow = n_cg*(1+3.8*(L/b-2.25)/(Vk_L)) #g, at bow 1111 | avg_impact_acc = np.array([n_cg, n_bow]) 1112 | 1113 | #Update values 1114 | self.avg_impact_acc = avg_impact_acc 1115 | self.R_AW = R_AW*4.448 #lbf to N conversion 1116 | -------------------------------------------------------------------------------- /openplaning/tables/Raw_0.2.csv: -------------------------------------------------------------------------------- 1 | tau,beta,Raw 2 | 3,10,0.033918674 3 | 4,10,0.03030747 4 | 5,10,0.026577497 5 | 6,10,0.023087653 6 | 7,10,0.01959892 7 | 7,15,0.02029489 8 | 6,15,0.022693603 9 | 5,15,0.025211455 10 | 4,15,0.028093018 11 | 3,15,0.03049099 12 | 7,20,0.02014393 13 | 6,20,0.021813742 14 | 5,20,0.023603804 15 | 4,20,0.025150405 16 | 3,20,0.026940837 17 | 7,25,0.019143819 18 | 6,25,0.020206645 19 | 5,25,0.021085581 20 | 4,25,0.022330632 21 | 3,25,0.023088763 22 | 7,30,0.016807637 23 | 6,30,0.017202427 24 | 5,30,0.017718763 25 | 4,30,0.018111333 26 | 3,30,0.018626929 27 | -------------------------------------------------------------------------------- /openplaning/tables/Raw_0.4.csv: -------------------------------------------------------------------------------- 1 | tau,beta,Raw 2 | 3,10,0.048091281 3 | 4,10,0.045086893 4 | 5,10,0.042082752 5 | 6,10,0.039136797 6 | 7,10,0.036133149 7 | 3,15,0.047382837 8 | 4,15,0.044559181 9 | 5,15,0.041676351 10 | 6,15,0.038913844 11 | 7,15,0.035789626 12 | 3,20,0.045510083 13 | 4,20,0.042934976 14 | 5,20,0.040478464 15 | 6,20,0.03790064 16 | 7,20,0.035202987 17 | 3,25,0.043036206 18 | 4,25,0.039789442 19 | 5,25,0.03678456 20 | 6,25,0.033657873 21 | 7,25,0.03047053 22 | 3,30,0.038854588 23 | 4,30,0.033840662 24 | 5,30,0.028887393 25 | 6,30,0.023995766 26 | 7,30,0.019102412 27 | -------------------------------------------------------------------------------- /openplaning/tables/Raw_0.6.csv: -------------------------------------------------------------------------------- 1 | tau,beta,Raw 2 | 3,10,0.058522841 3 | 4,10,0.054070122 4 | 5,10,0.049619863 5 | 6,10,0.044986528 6 | 7,10,0.040716392 7 | 3,15,0.057085956 8 | 4,15,0.05305861 9 | 5,15,0.048789704 10 | 6,15,0.044823055 11 | 7,15,0.040614109 12 | 3,20,0.054493854 13 | 4,20,0.051014998 14 | 5,20,0.047355034 15 | 6,20,0.043812773 16 | 7,20,0.040332687 17 | 3,25,0.05135966 18 | 4,25,0.047697235 19 | 5,25,0.044036533 20 | 6,25,0.040494273 21 | 7,25,0.036711684 22 | 3,30,0.046219749 23 | 4,30,0.041768752 24 | 5,30,0.037318739 25 | 6,30,0.03298889 26 | 7,30,0.028658549 27 | -------------------------------------------------------------------------------- /openplaning/tables/Raw_V_0.2.csv: -------------------------------------------------------------------------------- 1 | V,Raw 2 | 0.025365873,0.018990668 3 | 0.068805147,0.049279575 4 | 0.114248716,0.080161715 5 | 0.15738141,0.109880521 6 | 0.196991159,0.140123691 7 | 0.235410541,0.172374312 8 | 0.273838961,0.203593124 9 | 0.3087475,0.23498646 10 | 0.338974881,0.265240781 11 | 0.368022261,0.296318917 12 | 0.396794626,0.327896327 13 | 0.421431535,0.357830382 14 | 0.44461318,0.389115791 15 | 0.467572408,0.421449813 16 | 0.488092804,0.453611669 17 | 0.506842934,0.485011075 18 | 0.526742254,0.519104108 19 | 0.545201106,0.554003885 20 | 0.562799387,0.586732473 21 | 0.575858937,0.61843266 22 | 0.591275157,0.649596262 23 | 0.606222855,0.683674147 24 | 0.624681198,0.718632011 25 | 0.641990716,0.750854399 26 | 0.667659184,0.790357451 27 | 0.701050895,0.83668506 28 | 0.728930469,0.867194916 29 | 0.761485546,0.899489568 30 | 0.798749107,0.929803491 31 | 0.844217769,0.960618375 32 | 0.90026179,0.989115117 33 | 0.948031819,0.997797296 34 | 0.998393667,1.00274723 35 | 1.003425663,0.999403701 36 | 1.068620773,0.983151083 37 | 1.111127559,0.950434945 38 | 1.149525438,0.916795327 39 | 1.18616359,0.884617381 40 | 1.223956452,0.854502902 41 | 1.269971585,0.82292884 42 | 1.325357252,0.792694948 43 | 1.38434715,0.76336787 44 | 1.443011206,0.739106704 45 | 1.503120482,0.715876868 46 | 1.564296835,0.694003748 47 | 1.625449815,0.674798981 48 | 1.686584184,0.657719072 49 | 1.747715335,0.641006413 50 | 1.808836551,0.625428138 51 | 1.869951944,0.610514569 52 | 1.931056587,0.596828329 53 | 1.992148781,0.584563378 54 | 2.05323564,0.572907489 55 | 2.114326295,0.560818175 56 | 2.175404545,0.550145164 57 | 2.236471787,0.540728855 58 | 2.297536305,0.531623571 59 | 2.358599632,0.522654233 60 | 2.419661977,0.513797093 61 | 2.480723775,0.505002241 62 | 2.541783538,0.496439809 63 | 2.60283389,0.488951817 64 | 2.663880852,0.481850777 65 | 2.72492323,0.475273193 66 | 2.785964867,0.468780092 67 | 2.84700538,0.462415433 68 | 2.90804703,0.455920852 69 | 2.972005667,0.449736922 70 | -------------------------------------------------------------------------------- /openplaning/tables/Raw_V_0.4.csv: -------------------------------------------------------------------------------- 1 | V,Raw 2 | 0.022184177,0.013329629 3 | 0.070016112,0.042123074 4 | 0.127463546,0.074266048 5 | 0.173988583,0.101783854 6 | 0.219210786,0.133252721 7 | 0.2612876,0.163718999 8 | 0.30206881,0.194113267 9 | 0.339334416,0.224193865 10 | 0.37308211,0.254224224 11 | 0.405639688,0.286233234 12 | 0.433520621,0.316587973 13 | 0.459054933,0.347062991 14 | 0.483392172,0.38031092 15 | 0.506575102,0.41144974 16 | 0.528567361,0.444630595 17 | 0.54938819,0.477656853 18 | 0.569034923,0.510833047 19 | 0.587513208,0.543514094 20 | 0.602504154,0.572654548 21 | 0.617698873,0.605309447 22 | 0.639420954,0.64255716 23 | 0.655576839,0.672593208 24 | 0.671729247,0.703026209 25 | 0.689054633,0.733437082 26 | 0.70870115,0.766637834 27 | 0.725137987,0.800169932 28 | 0.75035715,0.831053088 29 | 0.774712013,0.862289031 30 | 0.801240318,0.892577011 31 | 0.832983914,0.923349315 32 | 0.868764037,0.954250092 33 | 0.917625675,0.982356835 34 | 0.971722597,0.998135686 35 | 1.003425663,0.999403701 36 | 1.068620773,0.983151083 37 | 1.111127559,0.950434945 38 | 1.149525438,0.916795327 39 | 1.18616359,0.884617381 40 | 1.223956452,0.854502902 41 | 1.269971585,0.82292884 42 | 1.325357252,0.792694948 43 | 1.38434715,0.76336787 44 | 1.443011206,0.739106704 45 | 1.503120482,0.715876868 46 | 1.564296835,0.694003748 47 | 1.625449815,0.674798981 48 | 1.686584184,0.657719072 49 | 1.747715335,0.641006413 50 | 1.808836551,0.625428138 51 | 1.869951944,0.610514569 52 | 1.931056587,0.596828329 53 | 1.992148781,0.584563378 54 | 2.05323564,0.572907489 55 | 2.114326295,0.560818175 56 | 2.175404545,0.550145164 57 | 2.236471787,0.540728855 58 | 2.297536305,0.531623571 59 | 2.358599632,0.522654233 60 | 2.419661977,0.513797093 61 | 2.480723775,0.505002241 62 | 2.541783538,0.496439809 63 | 2.60283389,0.488951817 64 | 2.663880852,0.481850777 65 | 2.72492323,0.475273193 66 | 2.785964867,0.468780092 67 | 2.84700538,0.462415433 68 | 2.90804703,0.455920852 69 | 2.972005667,0.449736922 70 | -------------------------------------------------------------------------------- /openplaning/tables/Raw_V_0.6.csv: -------------------------------------------------------------------------------- 1 | V,Raw 2 | 0.031546704,0.015583803 3 | 0.092870429,0.042433394 4 | 0.148322003,0.073020138 5 | 0.2068858,0.103943645 6 | 0.256612531,0.133696833 7 | 0.301155719,0.164465032 8 | 0.343116808,0.19404919 9 | 0.381557709,0.223843066 10 | 0.418666391,0.25510257 11 | 0.455642372,0.290354288 12 | 0.481405418,0.320218815 13 | 0.508241844,0.355056231 14 | 0.532587486,0.387344823 15 | 0.554619709,0.41596317 16 | 0.575442579,0.448756476 17 | 0.595099967,0.480716098 18 | 0.611947444,0.512131061 19 | 0.631409146,0.545514865 20 | 0.655064862,0.588091429 21 | 0.668101854,0.616454214 22 | 0.683972113,0.644227312 23 | 0.701581033,0.675741289 24 | 0.717438112,0.706417843 25 | 0.735933896,0.73710117 26 | 0.755399447,0.766617975 27 | 0.771479343,0.799697304 28 | 0.794939024,0.829216643 29 | 0.819297168,0.860077992 30 | 0.844432926,0.891423607 31 | 0.870342928,0.92363831 32 | 0.90173769,0.954509351 33 | 0.940802145,0.984256789 34 | 0.998673405,1.000786847 35 | 1.003425663,0.999403701 36 | 1.068620773,0.983151083 37 | 1.111127559,0.950434945 38 | 1.149525438,0.916795327 39 | 1.18616359,0.884617381 40 | 1.223956452,0.854502902 41 | 1.269971585,0.82292884 42 | 1.325357252,0.792694948 43 | 1.38434715,0.76336787 44 | 1.443011206,0.739106704 45 | 1.503120482,0.715876868 46 | 1.564296835,0.694003748 47 | 1.625449815,0.674798981 48 | 1.686584184,0.657719072 49 | 1.747715335,0.641006413 50 | 1.808836551,0.625428138 51 | 1.869951944,0.610514569 52 | 1.931056587,0.596828329 53 | 1.992148781,0.584563378 54 | 2.05323564,0.572907489 55 | 2.114326295,0.560818175 56 | 2.175404545,0.550145164 57 | 2.236471787,0.540728855 58 | 2.297536305,0.531623571 59 | 2.358599632,0.522654233 60 | 2.419661977,0.513797093 61 | 2.480723775,0.505002241 62 | 2.541783538,0.496439809 63 | 2.60283389,0.488951817 64 | 2.663880852,0.481850777 65 | 2.72492323,0.475273193 66 | 2.785964867,0.468780092 67 | 2.84700538,0.462415433 68 | 2.90804703,0.455920852 69 | 2.972005667,0.449736922 70 | -------------------------------------------------------------------------------- /openplaning/tables/V_0.2.csv: -------------------------------------------------------------------------------- 1 | tau,beta,V 2 | 3,10,6.127617643 3 | 4,10,5.170781672 4 | 5,10,4.195756945 5 | 6,10,3.269223218 6 | 7,10,2.306349024 7 | 3,15,5.13431155 8 | 4,15,4.505558499 9 | 5,15,3.894901592 10 | 6,15,3.235846296 11 | 7,15,2.649323756 12 | 3,20,4.402167833 13 | 4,20,3.986049113 14 | 5,20,3.57600566 15 | 6,20,3.184095397 16 | 7,20,2.743583 17 | 3,25,4.077233679 18 | 4,25,3.630664538 19 | 5,25,3.190189185 20 | 6,25,2.725505377 21 | 7,25,2.297106469 22 | 3,30,4.365827366 23 | 4,30,3.512159794 24 | 5,30,2.610038267 25 | 6,30,1.750350995 26 | 7,30,0.866418223 27 | -------------------------------------------------------------------------------- /openplaning/tables/V_0.4.csv: -------------------------------------------------------------------------------- 1 | tau,beta,V 2 | 3,10,4.943336207 3 | 4,10,4.345241538 4 | 5,10,3.819602076 5 | 6,10,3.282038877 6 | 7,10,2.726170685 7 | 3,15,4.756610479 8 | 4,15,4.243560839 9 | 5,15,3.742582956 10 | 6,15,3.24769029 11 | 7,15,2.758685483 12 | 3,20,4.697822342 13 | 4,20,4.184649353 14 | 5,20,3.659379938 15 | 6,20,3.164413263 16 | 7,20,2.645130387 17 | 3,25,4.791337336 18 | 4,25,4.192798611 19 | 5,25,3.582212799 20 | 6,25,2.977761544 21 | 7,25,2.373211611 22 | 3,30,5.115671217 23 | 4,30,4.298212673 24 | 5,30,3.492998573 25 | 6,30,2.669504151 26 | 7,30,1.858106154 27 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r", encoding="utf-8") as fh: 4 | long_description = fh.read() 5 | 6 | setuptools.setup( 7 | name="openplaning", 8 | version="0.4.6", 9 | author="Esteban L. Castro-Feliciano", 10 | author_email="ecastro@crown-hydro.com", 11 | description="Hydrodynamic evaluation of planing hulls based on the Savitsky empirical methods.", 12 | long_description=long_description, 13 | long_description_content_type="text/markdown", 14 | url="https://github.com/elcf/python-openplaning", 15 | project_urls={ 16 | "Bug Tracker": "https://github.com/elcf/python-openplaning/issues", 17 | }, 18 | classifiers=[ 19 | "Programming Language :: Python :: 3", 20 | "License :: OSI Approved :: MIT License", 21 | "Operating System :: OS Independent", 22 | ], 23 | packages = ['openplaning'], 24 | python_requires=">=3.6", 25 | install_requires=[ 26 | 'numpy', 27 | 'scipy', 28 | 'ndmath', 29 | ], 30 | package_data={'openplaning':['tables/*.csv']}, 31 | ) 32 | --------------------------------------------------------------------------------