diff --git a/Procfile b/Procfile
index c99e5a1..6adb339 100644
--- a/Procfile
+++ b/Procfile
@@ -1 +1 @@
-web: gunicorn src.api:app --log-file=-
\ No newline at end of file
+web: gunicorn --chdir src python_vpp.api:app --log-file=-
\ No newline at end of file
diff --git a/README.md b/README.md
index 592b128..4722d5b 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,39 @@
Please see the demo hosted at [https://yacht-vpp.streamlit.app/](https://yacht-vpp.streamlit.app/) and go for a test sail!
+## Installation
+
+Python-VPP is on PyPI and needs Python 3.11 or newer:
+
+```bash
+pip install python-vpp
+```
+
+Then build a yacht and run the VPP:
+
+```python
+import numpy as np
+from python_vpp import VPP, Yacht, Keel, Rudder, Main, Jib, Kite
+
+yacht = Yacht(
+ Name="YD41", Lwl=11.90, Vol=6.05, Bwl=3.18, Tc=0.4, WSA=28.20, Tmax=2.30,
+ Amax=1.051, Mass=6500, Ff=1.5, Fa=1.5, Boa=4.2, Loa=12.5,
+ App=[Keel(Cu=1.00, Cl=0.78, Span=1.90), Rudder(Cu=0.48, Cl=0.22, Span=1.15)],
+ Sails=[
+ Main("MN1", P=16.60, E=5.60, Roach=0.1, BAD=1.0),
+ Jib("J1", I=16.20, J=5.10, LPG=5.40, HBI=1.8),
+ Kite("A2", area=150.0, vce=9.55),
+ ],
+)
+vpp = VPP(Yacht=yacht)
+vpp.set_analysis(tws_range=np.arange(4.0, 22.0, 2.0), twa_range=np.linspace(30.0, 180.0, 31))
+vpp.run()
+vpp.polar(n=3, save=True) # Polars.pdf
+vpp.write_csv("polar.csv") # best boat speed, TWA rows x TWS columns
+```
+
+The righting arm curve is passed with `Yacht(..., GZ={"Heel": [...], "GZ": [...]})`. Without it, `righting_moment.json` is read from the working directory, falling back to the YD-41 curve shipped with the package. The ORC sail and resistance coefficients are part of the package.
+
## Using the code
The project is managed with [uv](https://docs.astral.sh/uv/). Install it once (see the [uv docs](https://docs.astral.sh/uv/getting-started/installation/) for other methods):
@@ -20,7 +53,7 @@ cd Python-VPP
uv sync
```
-The main files that are used are `runVPP.py` and `righting_moment.json`. These have to be filled with the data of your boat. By default they are using the YD-41 (from Principle of Yacht Design).
+From a clone of the repository, the main files that are used are `runVPP.py` and `righting_moment.json`. These have to be filled with the data of your boat. By default they are using the YD-41 (from Principle of Yacht Design). The code lives in `src/python_vpp/`, with the model coefficient data in `src/python_vpp/dat/`; `uv sync` installs it in editable mode.
The VPP is run from the root of the repository with
@@ -37,7 +70,7 @@ uv sync --extra demo --extra api
uv run streamlit run demos/Home.py
```
-The run writes the results to `results.json` and saves the polars (`Polars.pdf`) and the sail chart (`SailChart.pdf`) as PDF, so the curves stay sharp when zoomed. For the YD-41 the polars look like this (`Figure.png` is a PNG copy of `Polars.pdf` for this page)
+The run writes the results to `results.json`, the best boat speed to `polar.csv` (TWA rows, TWS columns, `;` separated, for routing software such as OpenCPN; `tws_range`/`twa_range` interpolate it onto another grid, e.g. the 5° / 2 kn steps OpenCPN expects) and saves the polars (`Polars.pdf`) and the sail chart (`SailChart.pdf`) as PDF, so the curves stay sharp when zoomed. For the YD-41 the polars look like this (`Figure.png` is a PNG copy of `Polars.pdf` for this page)
@@ -111,6 +144,10 @@ You can run tests with
uv run pytest -vv
```
+### Release
+
+Bump `version` in `pyproject.toml`, then create a GitHub release with a matching tag (e.g. `v1.0.0`). The `python-publish.yml` workflow builds the package with `uv build` and uploads it to PyPI through trusted publishing. Check the build locally first with `uv build`.
+
You can run a benchmark against the YD-41 results from WinVPP by running the `benchmark.py` script (`-g` saves the polars as one `Figure__.pdf` per wind speed plus a `Figure__stats.pdf` error summary, `-s` saves the results).
```bash
diff --git a/benchmark/benchmark.py b/benchmark/benchmark.py
index a44fcbe..9c5b07d 100644
--- a/benchmark/benchmark.py
+++ b/benchmark/benchmark.py
@@ -6,11 +6,9 @@
import matplotlib.pyplot as plt
from datetime import datetime
-sys.path.append(os.path.realpath("."))
-
-from src.YachtMod import Yacht, Keel, Rudder
-from src.SailMod import Main, Jib, Kite
-from src.VPPMod import VPP, KNOTS_TO_MPS
+from python_vpp.YachtMod import Yacht, Keel, Rudder
+from python_vpp.SailMod import Main, Jib, Kite
+from python_vpp.VPPMod import VPP, KNOTS_TO_MPS
tws = np.array([3, 4, 5, 6, 7, 8, 10]) # TWS in m/s
data_file = "dat/YD-41/YD41.dat"
diff --git "a/demos/pages/1_VPP_\342\233\265.py" "b/demos/pages/1_VPP_\342\233\265.py"
index b7629e3..27b18a0 100644
--- "a/demos/pages/1_VPP_\342\233\265.py"
+++ "b/demos/pages/1_VPP_\342\233\265.py"
@@ -9,9 +9,10 @@
import streamlit as st
from utils import footer, header
-sys.path.append(os.path.realpath("."))
-from src.api import app
-from src.UtilsMod import KNOTS_TO_MPS, _get_cross, _get_vmg, _polar, cols, stl
+# the demo may run without the package installed (e.g. Streamlit Cloud)
+sys.path.append(os.path.realpath("src"))
+from python_vpp.api import app
+from python_vpp.UtilsMod import KNOTS_TO_MPS, _get_cross, _get_vmg, _polar, cols, stl
st.set_page_config(page_title="VPP", page_icon="⛵")
diff --git a/pyproject.toml b/pyproject.toml
index 76bee68..09bc5fa 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[project]
name = "python-vpp"
version = "1.0.0"
-description = "OOP Velocity Prediction Program"
+description = "3-DOF sailing yacht Velocity Prediction Program based on the ORC models"
readme = "README.md"
license = "MIT"
requires-python = ">=3.11"
@@ -10,8 +10,11 @@ authors = [
{ name = "Otto Villani" },
{ name = "Thomas Dickson" },
]
+keywords = ["VPP", "velocity prediction", "sailing", "yacht", "ORC", "naval architecture"]
classifiers = [
"Programming Language :: Python :: 3",
+ "Intended Audience :: Science/Research",
+ "Topic :: Scientific/Engineering",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]
@@ -21,6 +24,11 @@ dependencies = [
"scipy>=1.12",
]
+[project.urls]
+Homepage = "https://github.com/marinlauber/Python-VPP"
+Documentation = "https://marinlauber.github.io/Python-VPP/"
+Demo = "https://yacht-vpp.streamlit.app/"
+
[project.optional-dependencies]
api = ["flask", "gunicorn"]
demo = ["streamlit>=1.37"]
@@ -35,7 +43,10 @@ requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
-packages = ["src"]
+packages = ["src/python_vpp"]
+
+[tool.hatch.build.targets.sdist]
+include = ["src/python_vpp", "tests", "README.md", "LICENSE"]
[tool.pytest.ini_options]
testpaths = ["tests"]
diff --git a/runVPP.py b/runVPP.py
index c806690..aeaec14 100755
--- a/runVPP.py
+++ b/runVPP.py
@@ -6,9 +6,9 @@
logging.basicConfig(level=logging.INFO)
-from src.SailMod import Jib, Kite, Main
-from src.VPPMod import VPP
-from src.YachtMod import Keel, Rudder, Yacht
+from python_vpp.SailMod import Jib, Kite, Main
+from python_vpp.VPPMod import VPP
+from python_vpp.YachtMod import Keel, Rudder, Yacht
YD41 = Yacht(
Name="YD41",
diff --git a/scripts/Resistance.py b/scripts/Resistance.py
index 034b72c..6ed005d 100644
--- a/scripts/Resistance.py
+++ b/scripts/Resistance.py
@@ -8,8 +8,8 @@
__email__ = "M.Lauber@soton.ac.uk"
import numpy as np
-from src.HydroMod import HydroMod
-from src.YachtMod import Yacht, Keel, Rudder
+from python_vpp.HydroMod import HydroMod
+from python_vpp.YachtMod import Yacht, Keel, Rudder
if __name__ == "__main__":
diff --git a/sphinx/conf.py b/sphinx/conf.py
index c7d2707..6b03392 100644
--- a/sphinx/conf.py
+++ b/sphinx/conf.py
@@ -13,7 +13,7 @@
import os
import sys
-sys.path.insert(0, os.path.abspath(".."))
+sys.path.insert(0, os.path.abspath("../src"))
# -- Project information -----------------------------------------------------
diff --git a/sphinx/index.rst b/sphinx/index.rst
index 39b1d34..7b748d7 100644
--- a/sphinx/index.rst
+++ b/sphinx/index.rst
@@ -1,7 +1,7 @@
Welcome to Python-VPP's documentation!
======================================
-.. automodule:: src.VPPMod
+.. automodule:: python_vpp.VPPMod
:members:
.. toctree::
diff --git a/src/__init__.py b/src/__init__.py
deleted file mode 100644
index 8c43447..0000000
--- a/src/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# empty __init__.py file
diff --git a/src/AeroMod.py b/src/python_vpp/AeroMod.py
similarity index 99%
rename from src/AeroMod.py
rename to src/python_vpp/AeroMod.py
index 5127f18..611dde2 100644
--- a/src/AeroMod.py
+++ b/src/python_vpp/AeroMod.py
@@ -6,7 +6,7 @@
import matplotlib.pyplot as plt
import numpy as np
-from src.UtilsMod import build_interp_func
+from .UtilsMod import build_interp_func
Z_REF = 10.0 # m, height the true wind speed is given at (ORC 7.1)
diff --git a/src/HydroMod.py b/src/python_vpp/HydroMod.py
similarity index 99%
rename from src/HydroMod.py
rename to src/python_vpp/HydroMod.py
index a91962c..3476fb6 100644
--- a/src/HydroMod.py
+++ b/src/python_vpp/HydroMod.py
@@ -7,6 +7,8 @@
import numpy as np
from scipy.interpolate import RegularGridInterpolator
+from .UtilsMod import DATA_DIR
+
class HydroMod(object):
def __init__(self, Yacht, rho=1025.0, mu=0.00119, g=9.81):
@@ -61,7 +63,7 @@ def __load_data(self):
"""
Loads ORC resistance surfaces from file and build interpolation function
"""
- surf = np.genfromtxt("dat/ORCi_Drag_Surfaces.csv", delimiter=",", skip_header=1)
+ surf = np.genfromtxt(DATA_DIR / "ORCi_Drag_Surfaces.csv", delimiter=",", skip_header=1)
surf = surf.reshape((24, 43, 42))
# x is Fn := [0.,0.7], y is btr := [2.5,9], z is lvr := [3,9]
# we add zero Fn resistance (0.)
diff --git a/src/SailMod.py b/src/python_vpp/SailMod.py
similarity index 97%
rename from src/SailMod.py
rename to src/python_vpp/SailMod.py
index 1612750..bbc0398 100644
--- a/src/SailMod.py
+++ b/src/python_vpp/SailMod.py
@@ -5,6 +5,8 @@
import numpy as np
from scipy import interpolate
+from .UtilsMod import DATA_DIR
+
SAIL_TYPES = {
"main": "main",
@@ -68,12 +70,11 @@ def _build_interp_func(self, fname, data_source="orc"):
"""Build cubic B-spline interpolation functions for CL and CD.
Falls back to linear (k=1) if fewer than 4 data points are available.
- Tries ``dat/{data_source}/{fname}.dat`` first, then ``dat/{fname}.dat``.
+ Tries ``DATA_DIR/{data_source}/{fname}.dat`` first, then ``DATA_DIR/{fname}.dat``.
"""
- import os
- path = os.path.join("dat", data_source, fname + ".dat")
- if not os.path.exists(path):
- path = os.path.join("dat", fname + ".dat")
+ path = DATA_DIR / data_source / (fname + ".dat")
+ if not path.exists():
+ path = DATA_DIR / (fname + ".dat")
a = np.genfromtxt(path, delimiter=",", skip_header=1)
self.kp = a[0, 0]
# Filter NaN values (trailing commas in CSV create NaNs)
diff --git a/src/UtilsMod.py b/src/python_vpp/UtilsMod.py
similarity index 98%
rename from src/UtilsMod.py
rename to src/python_vpp/UtilsMod.py
index 08f2719..5dcafeb 100644
--- a/src/UtilsMod.py
+++ b/src/python_vpp/UtilsMod.py
@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
import json
+from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
@@ -10,6 +11,8 @@
from scipy.interpolate import RegularGridInterpolator
KNOTS_TO_MPS = 0.5144
+# model coefficient data shipped with the package
+DATA_DIR = Path(__file__).parent / "dat"
stl = [
(0, ()),
(0, (1.1, 1.1)),
@@ -90,7 +93,7 @@ def build_interp_func(fname, i=1, kind="linear"):
"""
build interpolation function and returns it in a list
"""
- a = np.genfromtxt("dat/" + fname + ".dat", delimiter=",", skip_header=1)
+ a = np.genfromtxt(DATA_DIR / (fname + ".dat"), delimiter=",", skip_header=1)
# Filter out NaN values for make_interp_spline compatibility
mask = ~(np.isnan(a[0, :]) | np.isnan(a[i, :]))
x = a[0, mask]
diff --git a/src/VPPMod.py b/src/python_vpp/VPPMod.py
similarity index 98%
rename from src/VPPMod.py
rename to src/python_vpp/VPPMod.py
index 511000c..3f41c02 100644
--- a/src/VPPMod.py
+++ b/src/python_vpp/VPPMod.py
@@ -7,10 +7,10 @@
import numpy as np
from scipy.optimize import minimize
-from src.AeroMod import AeroMod
-from src.HydroMod import HydroMod
-from src.UtilsMod import KNOTS_TO_MPS, csv_write, json_write, polar_plot, sail_chart
-from src.YachtMod import Yacht as YachtClass
+from .AeroMod import AeroMod
+from .HydroMod import HydroMod
+from .UtilsMod import KNOTS_TO_MPS, csv_write, json_write, polar_plot, sail_chart
+from .YachtMod import Yacht as YachtClass
logger = logging.getLogger(__name__)
diff --git a/src/YachtMod.py b/src/python_vpp/YachtMod.py
similarity index 96%
rename from src/YachtMod.py
rename to src/python_vpp/YachtMod.py
index 0628263..108159e 100644
--- a/src/YachtMod.py
+++ b/src/python_vpp/YachtMod.py
@@ -1,10 +1,12 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
+import os
+
import numpy as np
from scipy import interpolate
-from src.UtilsMod import build_interp_func, json_read, json_write
+from .UtilsMod import DATA_DIR, build_interp_func, json_read, json_write
def _zero_cr(fn):
@@ -248,8 +250,9 @@ def __init__(self, Name, Lwl, Vol, Bwl, Tc, WSA, Tmax,
Sail inventory. Default is [].
GZ : dict, optional
Righting arm curve as ``{"Heel": [...], "GZ": [...]}``.
- Heel in degrees, GZ in metres. If *None*, loads from
- ``righting_moment.json`` (backward compatible).
+ Heel in degrees, GZ in metres. If *None*, loads
+ ``righting_moment.json`` from the working directory, or the
+ packaged YD-41 curve if there is none.
crew_weight : float, optional
Total crew weight (kg). If *None*, uses empirical formula
``25.8 * Lwl ** 1.4262``.
@@ -316,7 +319,10 @@ def _build_rm_interp(self):
if self._gz_data is not None:
a = self._gz_data
else:
- a = json_read('righting_moment')
+ fname = 'righting_moment'
+ if not os.path.exists(fname + '.json'):
+ fname = str(DATA_DIR / fname)
+ a = json_read(fname)
return interpolate.interp1d(np.array(a["Heel"]), np.array(a["GZ"]),
kind="linear", fill_value="extrapolate")
diff --git a/src/python_vpp/__init__.py b/src/python_vpp/__init__.py
new file mode 100644
index 0000000..31239dd
--- /dev/null
+++ b/src/python_vpp/__init__.py
@@ -0,0 +1,14 @@
+"""Python-VPP: a 3-DOF velocity prediction program based on the ORC models."""
+
+from importlib.metadata import PackageNotFoundError, version
+
+from .SailMod import Jib, Kite, Main
+from .VPPMod import VPP
+from .YachtMod import Bulb, Keel, Rudder, ShortKeel, Yacht
+
+try:
+ __version__ = version("python-vpp")
+except PackageNotFoundError: # running from a source checkout
+ __version__ = "unknown"
+
+__all__ = ["VPP", "Yacht", "Keel", "ShortKeel", "Rudder", "Bulb", "Main", "Jib", "Kite"]
diff --git a/src/api.py b/src/python_vpp/api.py
similarity index 95%
rename from src/api.py
rename to src/python_vpp/api.py
index 006ec15..fe1d7fa 100644
--- a/src/api.py
+++ b/src/python_vpp/api.py
@@ -3,17 +3,14 @@
"""
import logging
-import os
-import sys
from typing import Any, Dict
import numpy as np
from flask import Flask, jsonify, request
-sys.path.append(os.path.realpath("."))
-from src.SailMod import Jib, Kite, Main
-from src.VPPMod import VPP
-from src.YachtMod import Keel, Rudder, ShortKeel, Yacht
+from .SailMod import Jib, Kite, Main
+from .VPPMod import VPP
+from .YachtMod import Keel, Rudder, ShortKeel, Yacht
app = Flask(__name__)
diff --git a/dat/ORCi_Drag_Surfaces.csv b/src/python_vpp/dat/ORCi_Drag_Surfaces.csv
similarity index 100%
rename from dat/ORCi_Drag_Surfaces.csv
rename to src/python_vpp/dat/ORCi_Drag_Surfaces.csv
diff --git a/dat/fcdmult.dat b/src/python_vpp/dat/fcdmult.dat
similarity index 100%
rename from dat/fcdmult.dat
rename to src/python_vpp/dat/fcdmult.dat
diff --git a/dat/jib.dat b/src/python_vpp/dat/jib.dat
similarity index 100%
rename from dat/jib.dat
rename to src/python_vpp/dat/jib.dat
diff --git a/dat/kheff.dat b/src/python_vpp/dat/kheff.dat
similarity index 100%
rename from dat/kheff.dat
rename to src/python_vpp/dat/kheff.dat
diff --git a/dat/kite.dat b/src/python_vpp/dat/kite.dat
similarity index 100%
rename from dat/kite.dat
rename to src/python_vpp/dat/kite.dat
diff --git a/dat/main.dat b/src/python_vpp/dat/main.dat
similarity index 100%
rename from dat/main.dat
rename to src/python_vpp/dat/main.dat
diff --git a/dat/orc/asym_cl_kite.dat b/src/python_vpp/dat/orc/asym_cl_kite.dat
similarity index 100%
rename from dat/orc/asym_cl_kite.dat
rename to src/python_vpp/dat/orc/asym_cl_kite.dat
diff --git a/dat/orc/asym_pole_kite.dat b/src/python_vpp/dat/orc/asym_pole_kite.dat
similarity index 100%
rename from dat/orc/asym_pole_kite.dat
rename to src/python_vpp/dat/orc/asym_pole_kite.dat
diff --git a/dat/orc/jib.dat b/src/python_vpp/dat/orc/jib.dat
similarity index 100%
rename from dat/orc/jib.dat
rename to src/python_vpp/dat/orc/jib.dat
diff --git a/dat/orc/jib_low.dat b/src/python_vpp/dat/orc/jib_low.dat
similarity index 100%
rename from dat/orc/jib_low.dat
rename to src/python_vpp/dat/orc/jib_low.dat
diff --git a/dat/orc/kite.dat b/src/python_vpp/dat/orc/kite.dat
similarity index 100%
rename from dat/orc/kite.dat
rename to src/python_vpp/dat/orc/kite.dat
diff --git a/dat/orc/main.dat b/src/python_vpp/dat/orc/main.dat
similarity index 100%
rename from dat/orc/main.dat
rename to src/python_vpp/dat/orc/main.dat
diff --git a/dat/orc/main_low.dat b/src/python_vpp/dat/orc/main_low.dat
similarity index 100%
rename from dat/orc/main_low.dat
rename to src/python_vpp/dat/orc/main_low.dat
diff --git a/dat/orc/sym_kite.dat b/src/python_vpp/dat/orc/sym_kite.dat
similarity index 100%
rename from dat/orc/sym_kite.dat
rename to src/python_vpp/dat/orc/sym_kite.dat
diff --git a/src/python_vpp/dat/righting_moment.json b/src/python_vpp/dat/righting_moment.json
new file mode 100644
index 0000000..d331dff
--- /dev/null
+++ b/src/python_vpp/dat/righting_moment.json
@@ -0,0 +1,4 @@
+{
+ "Heel": [0.00,10.0,20.0,30.0,40.0],
+ "GZ": [0.00,0.400,0.730,0.960,1.100]
+}
\ No newline at end of file
diff --git a/dat/rrk.dat b/src/python_vpp/dat/rrk.dat
similarity index 100%
rename from dat/rrk.dat
rename to src/python_vpp/dat/rrk.dat
diff --git a/dat/st.dat b/src/python_vpp/dat/st.dat
similarity index 100%
rename from dat/st.dat
rename to src/python_vpp/dat/st.dat
diff --git a/dat/ut.dat b/src/python_vpp/dat/ut.dat
similarity index 100%
rename from dat/ut.dat
rename to src/python_vpp/dat/ut.dat
diff --git a/dat/wr.dat b/src/python_vpp/dat/wr.dat
similarity index 100%
rename from dat/wr.dat
rename to src/python_vpp/dat/wr.dat
diff --git a/tests/test_aero.py b/tests/test_aero.py
index 7e4718c..cf218fd 100644
--- a/tests/test_aero.py
+++ b/tests/test_aero.py
@@ -8,7 +8,7 @@ class TestWindTriangleCache:
"""Verify that wind_triangle uses lru_cache."""
def test_wind_triangle_is_cached(self):
- from src.AeroMod import wind_triangle
+ from python_vpp.AeroMod import wind_triangle
# lru_cache-decorated functions have cache_info()
assert hasattr(wind_triangle, "cache_info"), "wind_triangle should be lru_cache-decorated"
@@ -20,7 +20,7 @@ def test_wind_triangle_is_cached(self):
assert info.misses == 1
def test_cache_returns_same_result(self):
- from src.AeroMod import wind_triangle
+ from python_vpp.AeroMod import wind_triangle
wind_triangle.cache_clear()
r1 = wind_triangle(10.0, 45.0, 6.0)
@@ -34,7 +34,7 @@ class TestSailCoefficientCalls:
def test_get_coeffs_calls_cl_once_per_sail(self):
"""Each sail's cl(awa) should be called at most once per _get_coeffs()."""
from unittest.mock import MagicMock, patch
- from src.AeroMod import AeroMod
+ from python_vpp.AeroMod import AeroMod
# Create a mock yacht with minimal structure
mock_sail = MagicMock()
@@ -109,7 +109,7 @@ class TestWindTriangle:
)
def test_wind_triangle_analytical(self, tws, twa, vb, expected_aws, expected_awa):
"""AWA and AWS must match law-of-cosines closed-form solution."""
- from src.AeroMod import wind_triangle
+ from python_vpp.AeroMod import wind_triangle
awa, aws = wind_triangle(tws, twa, vb)
assert aws == pytest.approx(expected_aws, abs=1e-10)
@@ -117,7 +117,7 @@ def test_wind_triangle_analytical(self, tws, twa, vb, expected_aws, expected_awa
def test_no_fsolve_import_used(self):
"""The analytical path must not import or call fsolve."""
- from src.AeroMod import wind_triangle
+ from python_vpp.AeroMod import wind_triangle
import inspect
source = inspect.getsource(wind_triangle)
@@ -128,7 +128,7 @@ class TestHeeledWindTriangle:
"""ORC 7.1: heel and the log wind gradient in the wind triangle."""
def test_gradient_reference_height(self):
- from src.AeroMod import Z_REF, wind_gradient
+ from python_vpp.AeroMod import Z_REF, wind_gradient
assert wind_gradient(Z_REF) == pytest.approx(1.0)
assert wind_gradient(5.0) < 1.0 < wind_gradient(20.0)
@@ -136,7 +136,7 @@ def test_gradient_reference_height(self):
assert wind_gradient(5.0) == pytest.approx(np.log(1000.0) / np.log(2000.0))
def test_heel_reduces_cross_wind(self):
- from src.AeroMod import wind_triangle
+ from python_vpp.AeroMod import wind_triangle
awa0, aws0 = wind_triangle(10.0, 45.0, 3.0)
awa, aws = wind_triangle(10.0, 45.0, 3.0, 30.0)
diff --git a/tests/test_api.py b/tests/test_api.py
index 87d2fc7..7a4fdcd 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -2,7 +2,7 @@
import numpy as np
-from src.api import app
+from python_vpp.api import app
HEADERS = {"content-type": "application/json", "Accept-Charset": "UTF-8"}
diff --git a/tests/test_hydro.py b/tests/test_hydro.py
index 2de97b2..4bce1ce 100644
--- a/tests/test_hydro.py
+++ b/tests/test_hydro.py
@@ -2,9 +2,9 @@
import numpy as np
-from src.HydroMod import HydroMod
-from src.SailMod import Jib, Kite, Main
-from src.YachtMod import Keel, Rudder, Yacht
+from python_vpp.HydroMod import HydroMod
+from python_vpp.SailMod import Jib, Kite, Main
+from python_vpp.YachtMod import Keel, Rudder, Yacht
def _make_yacht(**kwargs):
diff --git a/tests/test_resistance.py b/tests/test_resistance.py
index f395ff4..8dadc3a 100644
--- a/tests/test_resistance.py
+++ b/tests/test_resistance.py
@@ -1,7 +1,7 @@
import numpy.testing as np_testing
-from src.HydroMod import HydroMod
-from src.YachtMod import Keel, Rudder, Yacht
+from python_vpp.HydroMod import HydroMod
+from python_vpp.YachtMod import Keel, Rudder, Yacht
def generate_YD41():
@@ -49,7 +49,7 @@ def test_build_interp_func_no_deprecation():
"""Verify build_interp_func doesn't use deprecated interp1d."""
import warnings
- from src.UtilsMod import build_interp_func
+ from python_vpp.UtilsMod import build_interp_func
with warnings.catch_warnings():
warnings.simplefilter("error", DeprecationWarning)
func = build_interp_func("main", i=1)
diff --git a/tests/test_utils.py b/tests/test_utils.py
index c2a7849..826763a 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -2,10 +2,10 @@
import numpy as np
-from src.SailMod import Jib, Kite, Main
-from src.UtilsMod import KNOTS_TO_MPS, _get_cross, csv_write
-from src.VPPMod import VPP
-from src.YachtMod import Keel, Rudder, Yacht
+from python_vpp.SailMod import Jib, Kite, Main
+from python_vpp.UtilsMod import KNOTS_TO_MPS, _get_cross, csv_write
+from python_vpp.VPPMod import VPP
+from python_vpp.YachtMod import Keel, Rudder, Yacht
def return_YD41_particulars():
diff --git a/tests/test_vpp.py b/tests/test_vpp.py
index 8e85037..bc81fe2 100644
--- a/tests/test_vpp.py
+++ b/tests/test_vpp.py
@@ -3,8 +3,8 @@
import numpy as np
import pytest
-from src.SailMod import Jib, Kite, Main
-from src.VPPMod import VPP
+from python_vpp.SailMod import Jib, Kite, Main
+from python_vpp.VPPMod import VPP
from tests.test_utils import return_YD41_particulars
@@ -162,8 +162,8 @@ def test_speed_increases_with_tws_when_depowered():
def test_stored_points_are_in_equilibrium():
"""Every stored point satisfies force and moment equilibrium."""
- from src.UtilsMod import KNOTS_TO_MPS
- from src.VPPMod import RESID_TOL
+ from python_vpp.UtilsMod import KNOTS_TO_MPS
+ from python_vpp.VPPMod import RESID_TOL
vpp = _upwind_vpp(np.array([6.0, 20.0]), np.linspace(30.0, 120.0, 4))
vpp.aero.sails[1] = vpp.yacht.sails[1]
@@ -191,8 +191,8 @@ def test_kite_heel_capped():
def test_infeasible_kite_left_unsolved():
"""A kite that cannot be held at the heel limit is stored as 0, not a fake point."""
- from src.UtilsMod import KNOTS_TO_MPS
- from src.VPPMod import RESID_TOL
+ from python_vpp.UtilsMod import KNOTS_TO_MPS
+ from python_vpp.VPPMod import RESID_TOL
yacht = return_YD41_particulars()
yacht.sails = [Main("MN1", P=16.60, E=5.60, Roach=0.1, BAD=1.0),
@@ -297,7 +297,7 @@ def test_parallel_method_accepted():
def test_sym_kite_vpp_runs():
"""VPP runs with symmetric spinnaker coefficients."""
- from src.YachtMod import Keel, Rudder, Yacht
+ from python_vpp.YachtMod import Keel, Rudder, Yacht
yacht = Yacht(
Name="YD41", Lwl=11.90, Vol=6.05, Bwl=3.18, Tc=0.4, WSA=28.20,
Tmax=2.30, Amax=1.051, Mass=6500, Ff=1.5, Fa=1.5, Boa=4.2, Loa=12.5,
diff --git a/tests/test_yacht.py b/tests/test_yacht.py
index 7f1a1b9..cc6e61d 100644
--- a/tests/test_yacht.py
+++ b/tests/test_yacht.py
@@ -1,7 +1,7 @@
import numpy as np
import pytest
-from src.YachtMod import Yacht, Keel, Rudder, ShortKeel
-from src.SailMod import Main, Jib
+from python_vpp.YachtMod import Yacht, Keel, Rudder, ShortKeel
+from python_vpp.SailMod import Main, Jib
def _minimal_yacht(**overrides):