Repository: ImperialCollegeLondon/sharpy (language: python)

Issue:
Changed Github runner from Conda to Mamba
### Changed Github runner from Conda to Mamba
The recent change to the new Conda environment involved swapping out minimal environment used by the Github runner to the regular environment. This causes the Ubuntu runner to fail due to running out of its 7GB of RAM, and so new SHARPy versions could not be verified.

To fix this, Conda has been swapped out with Mamba, which is a faster and less memory-intensive drop-in replacement. This should not have any effect on running SHARPy.

### Fixed Typos in Linear_Goland_Flutter.ipynb
Small errors in example notebook have been fixed, should not have any effect on the results.


Discussion / hints:

Fix patch:
diff --git a/Dockerfile b/Dockerfile
index ec6701f39..c0cdd9d30 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -51,5 +51,4 @@ RUN cd sharpy_dir && \
     pip install . && \
     rm -rf build
     
-ENTRYPOINT ["/bin/bash", "--init-file", "/root/bashrc"]
-
+ENTRYPOINT ["/bin/bash", "--init-file", "/root/bashrc"]
\ No newline at end of file
diff --git a/docs/source/content/installation.md b/docs/source/content/installation.md
index 72912462c..0d47ed47c 100644
--- a/docs/source/content/installation.md
+++ b/docs/source/content/installation.md
@@ -1,7 +1,7 @@
-# SHARPy v2.0 Installation Guide
-__Last revision 9 October 2023__
+# SHARPy v2.2 Installation Guide
+__Last revision 26 February 2024__
 
-The following step by step tutorial will guide you through the installation process of SHARPy. This is the updated process valid from v2.0.
+The following step by step tutorial will guide you through the installation process of SHARPy. This is the updated process valid from v2.2.
 
 ## Requirements
 
@@ -9,8 +9,8 @@ __Operating System Requirements__
 
 SHARPy is being developed and tested on the following operating systems:
 * CentOS 7 and CentOS 8
-* Ubuntu 18.04 LTS
-* Debian 10
+* Ubuntu 22.04 LTS
+* Debian 12
 * MacOS Mojave and Catalina (Intel)
 * MacOS Sonoma (Apple Silicon M2)
 
@@ -18,8 +18,15 @@ Windows users can also run it by first installing the Windows Subsystem for Linu
 
 __Required Distributions__
 
-* Anaconda Python 3.10
-* GCC 6.0 or higher (recommended). C++ and Fortran.
+* Python 3.10 or higher
+* CMake
+* GCC 6.0 or higher, G++, GFortran (all included in Anaconda) 
+* Eigen3, BLAS, MKL/LAPACK (all included in Anaconda)
+
+If the prerequisite packages are not installed and you are not using Anaconda, they can be installed as following on Linux (with a Homebrew equivelent available for Mac installs):
+```bash
+sudo apt install -y cmake g++ gfortran libblas-dev liblapack-dev libeigen3-dev
+```
 
 __Recommended Software__
 
@@ -45,20 +52,47 @@ once you initialise SHARPy you will also automatically clone the relevant versio
 
 ### Set up the folder structure
 
-1. Clone `sharpy` in your desired location, if you agree with the license in `license.txt`.
+Clone `sharpy` in your desired location, if you agree with the license in `license.txt`.
+```bash
+git clone --recursive http://github.com/ImperialCollegeLondon/sharpy
+```
+The `--recursive` flag will also initialise and update the submodules SHARPy depends on,
+[xbeam](http://github.com/imperialcollegelondon/xbeam) and [UVLM](http://github.com/imperialcollegelondon/UVLM).
+
+
+### Quick install (Standalone)
+
+SHARPy can be installed as a standalone package, without the use of a package manager. If you wish to install using the Anaconda package manager, please use the following tutorial [HERE](#setting-up-the-python-environment-anaconda), or make a custom installation with a develop build or modified compilation settings [HERE](#custom-installation). The quick install is geared towards getting the release build of SHARPy running as quickly and simply as possible. 
+
+1. Check that your Python version is 3.10 or higher. Other versions may be incompatible with the required modules.
+    ```bash
+    python --version
+    ```
+    
+2. Move into the cloned repository:
+    ```bash
+    cd sharpy
+    ```
+
+3. Install SHARPy. This will install any required Python packages as well as building the xbeam and UVLM libraries, and may take a  few  minutes.
     ```bash
-    git clone --recursive http://github.com/ImperialCollegeLondon/sharpy
+    pip install --user .
     ```
-    The `--recursive` flag will also initialise and update the submodules SHARPy depends on,
-    [xbeam](http://github.com/imperialcollegelondon/xbeam) and [UVLM](http://github.com/imperialcollegelondon/UVLM).
+   The ```--user``` flag is included for systems without root access (often Linux) as the install will fail otherwise. This flag can be removed when a global install is required, and your machine allows it (works on Mac).
+   
+    There are options for what to install if required. For instance, to install the basic package with documentation, just do ```bash pip install --user .[docs]```. For the whole lot, ```bash pip install --user .[all]```.
 
-2. We will now set up the SHARPy environment that will install other required distributions.
+4. You can check the version of SHARPy you are running with:
+    ```bash
+    sharpy --version
+    ```
 
-### Setting up the Python Environment
+__You are ready to run SHARPy__. Continue reading the [Running SHARPy](#running-sharpy) section.
+
+### Setting up the Python Environment (Anaconda)
 
-SHARPy uses the Anaconda package manager to provide the necessary Python packages.
-These are specified in an Anaconda environment that shall be activated prior to compiling the xbeam and UVLM libraries
-or running any SHARPy cases.
+SHARPy can use the Anaconda package manager to provide the necessary Python packages.
+These are specified in an Anaconda environment that shall be activated prior to compiling the xbeam and UVLM libraries or running any SHARPy cases.
 
 1. If you still do not have it in your system, install the [Anaconda](https://conda.io/docs/) Python 3 distribution.
 
@@ -66,7 +100,7 @@ or running any SHARPy cases.
     ```bash
     python --version
     ```
-3. If python 3.10 is needed, use:
+3. If a specific python version is required, for example 3.10, use:
 
     ```bash
     conda install python=3.10
@@ -75,23 +109,19 @@ or running any SHARPy cases.
 4. Create the conda environment that SHARPy will use:
 
     ```bash
-    cd sharpy/utils
     conda env create -f environment.yml
-    cd ../..
     ```
-    This should take approximately 15 minutes to complete (Tested on Ubuntu 22.04.1). 
+    This should take approximately 5 minutes to complete (Tested on Ubuntu 22.04.1). For installation on Apple Silicon, use ```environment_arm64.yml```; this requires GCC and GFortran to be installed prior.
 
 5. Activate the `sharpy` conda environment:
     ```bash
     conda activate sharpy
     ```
-    you need to do this before you compile the `xbeam` and `uvlm` libraries, as
-    some dependencies are included in the conda environment. You should now see ```(sharpy)``` on your command line. 
+    You should now see ```(sharpy)``` on your command line. 
 
 
-### Quick install
-The quick install is geared towards getting the release build of SHARPy running as quickly and simply as possible. If
-you would like to install a develop build or modify the compilation settings of the libraries skip to the next section.
+### Quick install (Anaconda)
+The quick install is geared towards getting the release build of SHARPy running as quickly and simply as possible.
 1. Move into the cloned repository:
     ```bash
     cd sharpy
@@ -102,48 +132,30 @@ you would like to install a develop build or modify the compilation settings of
     (sharpy) [usr@host] $
     ```
 
-    If this is not the case, activate the environment otherwise xbeam and UVLM will not compile.
+3. Install SHARPy. This will install any required Python packages as well as building the xbeam and UVLM libraries, and may take a few minutes.
     ```bash
-    conda activate sharpy
-    ```
-
-3. Create a directory `build` that will be used during CMake's building process and `cd` into it.
-   Ensure it is located in the main ./sharpy folder otherwise the following steps won't work:
-    ```bash
-    mkdir build
-    cd build
-    ```
-
-4. Prepare UVLM and xbeam for compilation using `gfortran` and `g++` in their release builds running. If you'd like to
-   change compilers see the Custom Installation.
-    ```bash
-    cmake ..
-    ```
-
-5. Compile the libraries.
-    ```bash
-    make install -j 4
-    ```
-    where the number after the `-j` flag will specify how many cores to use during installation.
-    This should take approximately 5 minutes (Tested on Ubuntu 22.04.1).
-
-7. Finally, leave the build directory and install SHARPy:
-    ```bash
-    cd ..
-    pip install .
+    pip install --user .
     ```
+   The ```--user``` flag is included for systems without root access (often Linux) as the install will fail otherwise. This flag can be removed when a global install is required, and your machine allows it (works on Mac).
+   
+   There are options for what to install if required. For instance, to install the basic package with documentation, just do ```bash pip install --user .[docs]```. For the whole lot, ```bash pip install --user .[all]```.
 
-8. You can check the version of SHARPy you are running with:
+4. You can check the version of SHARPy you are running with:
     ```bash
     sharpy --version
     ```
 
+If running SHARPy from Anaconda, you need to load the conda environment. Therefore, __before you run any SHARPy case or test__, activate the SHARPy conda environment: 
+```bash
+conda activate sharpy
+```
+
 __You are ready to run SHARPy__. Continue reading the [Running SHARPy](#running-sharpy) section.
 
 ### Custom installation
 
 These steps will show you how to compile the xbeam and UVLM libraries such that you can modify the compilation settings
-to your taste.
+to your taste. This is compatible with both standalone and Anaconda installations.
 
 1. If you want to use SHARPy's latest release, skip this step. If you would like to use the latest development work,
    you will need to checkout the `develop` branch. For more info on how we structure our development and what branches
@@ -154,14 +166,11 @@ to your taste.
     ```
     This command will check out the `develop` branch and set it to track the remote origin. It will also set the submodules (xbeam and UVLM) to the right commit.
 
-2. Create the conda environment that SHARPy will use:
+2. If using Anaconda, create the conda environment that SHARPy will use and activate the environment:
     ```bash
-    cd sharpy/utils
     conda env create -f environment.yml
-    cd ../..
     ```
 
-3. Activate the `sharpy` conda environment:
     ```bash
     conda activate sharpy
     ```
@@ -276,18 +285,8 @@ python -m unittest
 
 **Enjoy!**
 
-
 ## Running SHARPy
 
-In order to run SHARPy, you need to load the conda environment. Therefore, __before you run any SHARPy case__:
-
-1. Activate the SHARPy conda environment
-    ```bash
-    conda activate sharpy
-    ```
-
-You are now ready to run SHARPy cases from the terminal.
-
 ### Automated tests
 
 SHARPy uses unittests to verify the integrity of the code.
@@ -341,12 +340,12 @@ SHARPy cases are therefore usually ran in the following way:
 
 2. Run it to produce the `.h5` files and the `.sharpy` files.
     ```bash
-    (sharpy_env) python generate_case.py
+    python generate_case.py
     ```
 
 3. Run SHARPy (ensure the environment is activated).
     ```bash
-    (sharpy_env) sharpy case.sharpy
+    sharpy case.sharpy
     ```
 
 #### Output
@@ -381,7 +380,6 @@ is stored in [HDF5](https://support.hdfgroup.org/HDF5/) format, which is compres
 
     The `sharpy` call is:
     ```bash
-    # Make sure that the sharpy_env conda environment is active
     sharpy <path to solver file>
     ```
 
@@ -409,7 +407,6 @@ is stored in [HDF5](https://support.hdfgroup.org/HDF5/) format, which is compres
 
     You are now ready to run the case again:
     ```bash
-    # Make sure that the sharpy_env conda environment is active
     sharpy <path to solver file>
     ```
 
diff --git a/environment.yml b/environment.yml
new file mode 100644
index 000000000..97bcf0db6
--- /dev/null
+++ b/environment.yml
@@ -0,0 +1,14 @@
+name: sharpy
+channels:
+  - conda-forge
+  - defaults
+dependencies:
+  - eigen
+  - libopenblas
+  - libblas
+  - libcblas
+  - liblapack
+  - libgfortran
+  - libgcc
+  - libgfortran-ng
+  - python=3.10
diff --git a/environment_arm64.yml b/environment_arm64.yml
new file mode 100644
index 000000000..2b5246d0c
--- /dev/null
+++ b/environment_arm64.yml
@@ -0,0 +1,12 @@
+name: sharpy
+channels:
+  - conda-forge
+  - defaults
+dependencies:
+  - eigen
+  - libopenblas
+  - libblas
+  - libcblas
+  - liblapack
+  - libgfortran
+  - python=3.10
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 000000000..3f6f8a088
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,6 @@
+[build-system]
+requires = [
+    "setuptools",
+    #"scikit-build>=0.13",
+    "cmake>=3.14.3"
+    ]
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 06207ffc3..2dd39ab92 100644
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,98 @@
-from setuptools import setup, find_packages
+from setuptools import setup, find_packages, Extension, Command
+#from skbuild import setup
+from setuptools.command.build_ext import build_ext
+import subprocess
+
 import re
 import os
 
+class CMakeBuildExt(build_ext):
+    """Custom command to build Submodules packages during installation."""
+
+    # def copy_extensions_to_source(self):
+    #     "Override the method to prevent copying package files"
+    #     pass
+    
+    def finalize_options(self):
+        super().finalize_options()
+        # Process and use os.environ['CUSTOM_CONFIG_SETTINGS'] as needed
+        self.pip_nobuild = os.environ.get('PIP_NOBUILD')
+
+    def run(self):
+
+        package_dir = os.path.dirname(os.path.abspath(__file__))
+        build_dir = package_dir + "/build"
+        cmake_args = []
+        if self.pip_nobuild=="yes":
+            pass
+        else:
+            if not os.path.isdir(build_dir):
+                os.makedirs(build_dir)
+            subprocess.check_call(
+                ["cmake", ".."] + cmake_args, cwd=build_dir
+            )
+            subprocess.check_call(
+                ["make", "install", "-j4"], cwd=build_dir
+            )
+
+        super().run()
+
+def run():
+
+    pip_nobuild = os.environ.get('PIP_NOBUILD')
+    package_dir = os.path.dirname(os.path.abspath(__file__))
+    build_dir = package_dir + "/build"
+    cmake_args = []
+    if pip_nobuild=="yes":
+        pass
+    else:
+        if not os.path.isdir(build_dir):
+            os.makedirs(build_dir)
+        subprocess.check_call(
+            ["cmake", ".."] + cmake_args, cwd=build_dir
+        )
+        subprocess.check_call(
+            ["make", "install", "-j4"], cwd=build_dir
+        )
+
+class BuildCommand(Command):
+    """Custom command to build Submodules packages without installation."""
+
+    description = 'Build Submodules in lib packages'
+    user_options = [
+        ('cmake-args=', None, 'Additional CMake arguments'),
+    ]
+
+    def initialize_options(self):
+        self.cmake_args = None
+
+    def finalize_options(self):
+        pass
+
+    def run(self):
+        # Run the CMake build step with additional cmake_args
+        package_dir = os.path.dirname(os.path.abspath(__file__))
+        build_dir = package_dir + "/build"
+        if not os.path.isdir(build_dir):
+            os.makedirs(build_dir)
+        if self.cmake_args is not None:
+            subprocess.check_call(
+                ["cmake", f"{self.cmake_args}", ".."], cwd=build_dir
+            )
+        else:
+            subprocess.check_call(
+                ["cmake", ".."], cwd=build_dir
+            )
+            
+        subprocess.check_call(
+            ["make", "install", "-j4"], cwd=build_dir
+        )
+
+ext_modules = [
+    Extension('lib', []),
+    # Add more Extension instances for additional extension modules
+]
+
 this_directory = os.path.abspath(os.path.dirname(__file__))
 __version__ = re.findall(
     r"""__version__ = ["']+([0-9\.]*)["']+""",
@@ -10,7 +101,7 @@
 
 with open(os.path.join(this_directory, "README.md"), encoding="utf-8") as f:
     long_description = f.read()
-
+run()
 setup(
     name="sharpy",
     version=__version__,
@@ -25,13 +116,51 @@
     author_email="",
     url="https://github.com/ImperialCollegeLondon/sharpy",
     license="BSD 3-Clause License",
+    #ext_modules=ext_modules,
+    cmdclass={#"build_ext": CMakeBuildExt,
+              "build_subm": BuildCommand},
     packages=find_packages(
         where='./',
         include=['sharpy*'],
         exclude=['tests']
         ),
+    # data_files=[
+    #     ("./lib/UVLM/lib", ["libuvlm.so"]),
+    #     ("./lib/xbeam/lib", ["libxbeam.so"])
+    #     ],
+    python_requires=">=3.8",
     install_requires=[
+        "numpy",
+        "configobj",
+        "h5py",
+        "scipy",
+        "sympy",
+        "matplotlib",
+        "colorama",
+        "dill",
+        "jupyterlab",
+        "mayavi @ git+https://github.com/enthought/mayavi.git", #Used for TVTK. Bug in pip install, hence git clone
+        "pandas",
+        "control",
+        "openpyxl>=3.0.10",
+        "lxml>=4.4.1",
+        "PySocks",
+        "PyYAML"
     ],
+    extras_require={
+        "docs": [
+            "sphinx",
+            "recommonmark>=0.6.0",
+            "sphinx_rtd_theme>=0.4.3",
+            "nbsphinx>=0.4.3"
+                 ],
+        "all": [
+            "sphinx",
+            "recommonmark>=0.6.0",
+            "sphinx_rtd_theme>=0.4.3",
+            "nbsphinx>=0.4.3"
+                 ],
+    },
     classifiers=[
         "Operating System :: Linux, Mac OS",
         "Programming Language :: Python, C++",
diff --git a/sharpy/generators/floatingforces.py b/sharpy/generators/floatingforces.py
index c296d97f7..832833321 100644
--- a/sharpy/generators/floatingforces.py
+++ b/sharpy/generators/floatingforces.py
@@ -2,7 +2,7 @@
 import h5py as h5
 import ctypes as ct
 import os
-from scipy import fft, ifft
+from scipy.fft import fft, ifft
 from scipy.interpolate import interp1d
 from control import forced_response, TransferFunction
 


Test patch:
diff --git a/.github/workflows/sharpy_tests.yaml b/.github/workflows/sharpy_tests.yaml
index 5b8b1d546..543f39f48 100644
--- a/.github/workflows/sharpy_tests.yaml
+++ b/.github/workflows/sharpy_tests.yaml
@@ -31,26 +31,11 @@ jobs:
         with:
           version: 10
           platform: x64
-        #Swapped from Conda to Mamba due to Github runner memory constraint
-      - name: Setup Mamba
-        uses: mamba-org/setup-micromamba@v1
       - name: Pre-Install dependencies
         run: |
           gfortran --version
-          wget --no-check-certificate https://github.com/conda-forge/miniforge/releases/latest/download/Mambaforge-Linux-x86_64.sh -O mamba.sh
-          bash mamba.sh -b -p $HOME/mamba
-          export PATH="$HOME/mamba/bin:$PATH"
-          hash -r
-          mamba config --set always_yes yes --set changeps1 no
-          mamba update --name base mamba
-          mamba list --name base mamba
-          mamba init bash
-          hash -r
           export QT_QPA_PLATFORM='offscreen'
           sudo apt install libeigen3-dev
-          mamba env create -f utils/environment.yml
-          mamba init bash
-          source activate sharpy
           git submodule init
           git submodule update
           git fetch --tags -f