Skip to content

Commit

Permalink
fix: Removing cython dependency (#168)
Browse files Browse the repository at this point in the history
* fix: adding json exports

* fix: moving and renaming conversion functions

also adding some type hints and a generic numeric type

* tests: adding tests for conversions

need to add some better tests not just 0 and 1s

* fix: remove process_map2loop

not required, can be done using processor

* fix: adding synthetic horizontal layers model

* fix: adding corners property to bounding box

* fix: substituting pli for p1

same interpolator but this implementation uses numpy instead of cython

* tests: test that horizontal layers are working

* fix: making structured tetra compatible with p1 interp

* fix: making p1 interpolator work

* fix: cast input to np array

* fix: adding type enums

* fix: adding sparse matrix neighbour calculator

this means no more cython code

* fix: adding some type hints

* refactor: ➖ removing model builder

there is no real advantage to using a builder design pattern for loopstructural

* fix: ✨ adding ability to calculate normalised value of a feature

this just makes it easier to scale scalar field between 0 and 1

* fix: adding map for interpolator to support type

* fix: enabling support builder with interpolator factory

* fix: updating imports for maths functions

* fix: adding support creator from bbox

* fix: when reset clear the constraints dictionary

* fix: reset when setting up interpolator

* fix: removing old pli interpolator

* fix: fixing tetra indexing and masks

* fix: removing unused imports

* fix: speeding up element getter

* fix: adding onGeometryChange function for supports

* fix: reset change number of constraints

* fix: adding gradient orthogonal to p1interpolator

* fix: changing instrusion example to rescale points

* fix: setting model tolerance using new bounding box

* fix: updating fold to use p1 interpolator

* fix: renaming variables

* fix: adding name and vector args to min edge jumps

* fix: removing old imports

* fix: fixing orthogonal constraints for p1

* fix: moving fault setup into fault builder frm model

* fix: flake8 error

* fix: add bb origin to surfaces

* fix: fold constraints added in setup interpolator

this allows for interpolator to be reset
and doesn't remove the fold constraints when resetting

* fix: temp fix for issue with np types

* Replace reqs with correct theme

Theme being loaded by requirements.txt was wrong (an old one?) So have updated with the pydata_sphinx_theme so now builds correctly

* Adding PyPI custom link to navigation

* Replacing square icon with (nicer) round icon

* Adding navbar options and metadata

Image is a placeholder for now, but this is an (onvs opinionated) view on a nicer format. Adds a limit to the number of nav items, adds a logo, changes the title to look cleaner (and not say "documentation" at the end), adds metadata

* Fixing svg img

* Updating the contributors docs

* removing accidental tab

* docs: fixing missing automodule

* fix: adding length to bbox

* fix: removing dof and name from interpolator json

* docs: adding explicit imports for submodules

Without explicit import of submodules and using __ALL__ the majority of the code isn't imported so sphinx doesn't document it.

* docs: adding citations to documentation

* fix: removing reference to dtm creator and using get interpolator correctly

* fix: face table resize when geometry reset

* fix: elements property for base structured support

* fix: adding imports to api

* fix: changing from interpolator type string to enum

* fix: removing cython

* fix: only axial foliation is constrained by fold frame

* Update release-please.yml

* Update release-please.yml

* Update release-please.yml

* ci: updating to use pip wheel .

* Update release-please.yml

* fix: disable progress bar for model update

* ci: remove numpy version for conda

* fix: changing to interpolator factory

required changing how fold interpolator is setup. Rather than
initialising with a fold, the fold is added to the interpolator
added map between stringinterpolator names and the interpolatortypes enum

* fix: identify points that fall on face of shared tetras. Automatically chooses the second one.

* fix: rename surface vtk property to vtk from pyvista

* fix: add docstring and cast isinside to an array

* docs: adding docs to surface datatype

* fix: removing cython code

* fix: removing model api for time being

* build: adding conda builds to git ignore

* typo

* ci: using conda convert to create osx

* ci: removing cython

* ci: typo in yml

* ci: again

* ci: again

---------

Co-authored-by: Sam <samuel.joseph.roberts@hotmail.co.uk>
  • Loading branch information
lachlangrose and samjroberts authored Jan 31, 2024
1 parent c3a7221 commit 21e5572
Show file tree
Hide file tree
Showing 65 changed files with 1,758 additions and 2,010 deletions.
125 changes: 52 additions & 73 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,30 @@ on:
name: release-please
jobs:
continuous-integration:
name: Continuous integration ${{ matrix.os }} python ${{ matrix.python-version }}
name: Continuous integration ${{ matrix.os }} python ${{ matrix.python-version }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: ["ubuntu-latest", "windows-latest"] #"macos-latest",
python-version: ["3.8","3.9","3.10"]
os: ${{ fromJSON(vars.BUILD_OS)}}
python-version: ${{ fromJSON(vars.PYTHON_VERSIONS)}}
steps:
- uses: actions/checkout@v2
- uses: conda-incubator/setup-miniconda@v2
with:
python-version: ${{ matrix.python }}
python-version: ${{ matrix.python }}
- name: Installing dependencies
shell: bash -l {0}
run: |
conda install -c conda-forge cython numpy scipy scikit-image scikit-learn pyamg flake8 pytest networkx osqp matplotlib -y
- name: Checking formatting of code
shell: bash -l {0}
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 LoopStructural --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 LoopStructural --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Building and install
# stop the build if there are Python syntax errors or undefined names
flake8 LoopStructural --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 LoopStructural --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Building and install
shell: bash -l {0}
run: |
python setup.py install build_ext --inplace
Expand Down Expand Up @@ -78,109 +78,88 @@ jobs:
with:
branch: gh-pages # The branch the action should deploy to.
folder: docs/build/html # The folder the action should deploy.

conda-deploy:
name: Uploading to Loop3d for python ${{ matrix.os }})
needs: ["documentation-test", "continuous-integration"]
runs-on: ${{ matrix.os }}
runs-on: ${{matrix.os}}
strategy:
fail-fast: false
matrix:
os: ["ubuntu-latest", "windows-latest"]
python-version: ["3.10","3.9","3.8"]
os: ["ubuntu-latest"]
python-version: ${{ fromJSON(vars.PYTHON_VERSIONS)}}
steps:
- uses: conda-incubator/setup-miniconda@v2
with:
auto-update-conda: true
python-version: ${{ matrix.python-version }}

- uses: actions/checkout@v2
- name: update submodules
# shell: bash -l {0}
# shell: bash -l {0}
run: |
git submodule update --init --recursive
- name: Add msbuild to PATH
if: matrix.os == 'windows-latest'
uses: microsoft/setup-msbuild@v1.0.2
- name: Conda build'
git submodule update --init --recursive
- name: Conda build
env:
ANACONDA_API_TOKEN: ${{ secrets.ANACONDA_TOKEN }}
shell: bash -l {0}
run: |
conda install -c conda-forge conda-build scikit-build numpy cython anaconda-client -y
conda build -c anaconda -c conda-forge -c loop3d --output-folder conda conda --numpy 1.21
conda install anaconda-client -y
conda install -c conda-forge conda-build scikit-build numpy cython anaconda-client -y
conda build -c anaconda -c conda-forge -c loop3d --output-folder conda conda
conda convert -p all conda/linux-64/*.tar.bz2 -f -o conda
conda install anaconda-client -y
- name: upload artifacts
uses: actions/upload-artifact@v3
with:
name: conda
path: conda


make_sdist:
needs: ["documentation-test", "continuous-integration"]
name: Make SDist
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Build SDist
run: |
pip install numpy cython
python setup.py sdist
- uses: actions/checkout@v3

- uses: actions/upload-artifact@v3
with:
name: dist
path: dist/*.tar.gz

build_wheels:
needs: ["documentation-test", "continuous-integration"]
name: Build wheels
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v2
- name: Build SDist
run: |
pip install numpy
python setup.py sdist
pip wheel -w wheelhouse .
- name: Build wheels
uses: pypa/cibuildwheel@v2.12.0
env:
CIBW_ARCHS_MACOS: x86_64 universal2
CIBW_BUILD: "cp36-* cp37-* cp38-* cp39-* cp310*"
CIBW_BEFORE_BUILD: "pip install numpy cython" #make sure numpy is the same version as required by LS
- uses: actions/upload-artifact@v3
with:
name: dist
path: ./wheelhouse/*.whl

path: dist/*.tar.gz
- uses: actions/upload-artifact@v3
with:
name: dist
path: wheelhouse/*.whl

upload_to_pypi:
needs: ["release-please","build_wheels","make_sdist","conda-deploy"]
needs: ["release-please", "make_sdist", "conda-deploy"]
runs-on: ubuntu-latest
if: ${{ needs.release-please.outputs.release_created }}
steps:
- uses: actions/download-artifact@v3
with:
- uses: actions/download-artifact@v3
with:
name: dist
path: dist
- uses: actions/download-artifact@v3
with:
- uses: actions/download-artifact@v3
with:
name: conda
path: conda
- uses: pypa/gh-action-pypi-publish@v1.6.4
with:
skip_existing: true
verbose: true
user: ${{ secrets.PYPI_USERNAME }}
password: ${{ secrets.PYPI_PASSWORD }}
- uses: conda-incubator/setup-miniconda@v2
- name: upload all files to conda-forge
shell: bash -l {0}
env:
ANACONDA_API_TOKEN: ${{ secrets.ANACONDA_TOKEN }}
run: |
conda install -c anaconda anaconda-client -y
anaconda upload --label main conda/*/*.tar.bz2
- uses: pypa/gh-action-pypi-publish@v1.6.4
with:
skip_existing: true
verbose: true
user: ${{ secrets.PYPI_USERNAME }}
password: ${{ secrets.PYPI_PASSWORD }}
- uses: conda-incubator/setup-miniconda@v2
- name: upload all files to conda-forge
shell: bash -l {0}
env:
ANACONDA_API_TOKEN: ${{ secrets.ANACONDA_TOKEN }}
run: |
conda install -c anaconda anaconda-client -y
anaconda upload --label main conda/*/*.tar.bz2
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,7 @@ examples/*.png
dev/scalar_field
dev/unconf
docs/source/sg_execution_times.rst
conda/index.html
conda/channeldata.json
conda/noarch
conda/win-64
4 changes: 2 additions & 2 deletions LoopStructural/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
LoopStructural API
=======================
LoopStructural
==============
"""

Expand Down
2 changes: 2 additions & 0 deletions LoopStructural/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from ._interpolate import LoopInterpolator
from ._surface import LoopIsosurfacer
3 changes: 2 additions & 1 deletion LoopStructural/api/_interpolate.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from LoopStructural.interpolators import (
GeologicalInterpolator,
InterpolatorFactory,
InterpolatorType,
)
from LoopStructural.utils import BoundingBox
from LoopStructural.utils import getLogger
Expand All @@ -18,7 +19,7 @@ def __init__(
self,
bounding_box: BoundingBox,
dimensions: int = 3,
type: str = "FDI",
type=InterpolatorType.FINITE_DIFFERENCE,
nelements: int = 1000,
):
"""Scikitlearn like interface for LoopStructural interpolators
Expand Down
73 changes: 66 additions & 7 deletions LoopStructural/api/_surface.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import Optional, Union
from typing import Optional, Union, Callable
import numpy as np
import numpy.typing as npt
from LoopStructural.utils import getLogger

logger = getLogger(__name__)
Expand All @@ -13,23 +14,81 @@
from LoopStructural.utils import BoundingBox
from LoopStructural.datatypes import Surface

surface_list = dict[str, tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]]
surface_list = dict[
str, tuple[npt.ArrayLike, npt.ArrayLike, npt.ArrayLike, npt.ArrayLike]
]


class LoopIsosurfacer:
def __init__(self, bounding_box: BoundingBox, interpolator: GeologicalInterpolator):
def __init__(
self,
bounding_box: BoundingBox,
interpolator: Optional[GeologicalInterpolator] = None,
callable: Optional[Callable[[npt.ArrayLike], npt.ArrayLike]] = None,
):
"""Extract isosurfaces from a geological interpolator or a callable function.
Parameters
----------
bounding_box : BoundingBox
_description_
interpolator : Optional[GeologicalInterpolator], optional
interpolator object, by default None
callable : Optional[Callable[[npt.ArrayLike], npt.ArrayLike]], optional
callable object, by default None
Raises
------
ValueError
_description_
ValueError
_description_
ValueError
_description_
"""
self.bounding_box = bounding_box
self.interpolator = interpolator
self.callable = callable
if interpolator is None and callable is None:
raise ValueError("Must specify either interpolator or callable")
if interpolator is not None and self.callable is not None:
raise ValueError("Must specify either interpolator or callable")

if interpolator is not None:
self.callable = interpolator.evaluate_value
if self.callable is None:
raise ValueError("Must specify either interpolator or callable")

def fit(self, values: Union[list, int, float]) -> surface_list:
"""Extract isosurfaces from the interpolator
Parameters
----------
values : Union[list, int, float]
Either a list of values to extract isosurfaces for, or a single value
to extract a single isosurface for, or an integer to extract that many
isosurfaces evenly spaced between the minimum and maximum values of the
interpolator.
Returns
-------
surface_list
a dictionary containing the extracted isosurfaces
"""
if not callable(self.callable):
raise ValueError("No interpolator of callable function set")
surfaces = {}
all_values = self.interpolator.evaluate_value(self.bounding_box.regular_grid())
all_values = self.callable(self.bounding_box.regular_grid())
if isinstance(values, list):
isovalues = values
elif isinstance(values, float):
isovalues = [values]
elif isinstance(values, int):
isovalues = np.linspace(np.min(all_values), np.max(all_values), values)
isovalues = np.linspace(
np.min(all_values) + np.finfo(float).eps,
np.max(all_values) + np.finfo(float).eps,
values,
)
for isovalue in isovalues:
verts, faces, normals, values = marching_cubes(
all_values.reshape(self.bounding_box.nsteps, order="C"),
Expand All @@ -38,7 +97,7 @@ def fit(self, values: Union[list, int, float]) -> surface_list:
)
values = np.zeros(verts.shape[0]) + isovalue
surfaces[f"surface_{isovalue}"] = Surface(
vertices=verts,
vertices=verts + self.bounding_box.origin,
triangles=faces,
normals=normals,
name=f"surface_{isovalue}",
Expand Down
6 changes: 0 additions & 6 deletions LoopStructural/api/model.py

This file was deleted.

1 change: 1 addition & 0 deletions LoopStructural/datasets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@
from ._base import load_tabular_intrusion
from ._base import load_geological_map_data
from ._base import load_fault_trace
from ._base import load_horizontal
37 changes: 37 additions & 0 deletions LoopStructural/datasets/_base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,46 @@
from os.path import dirname, join
from pathlib import Path
from typing import Tuple
import numpy as np
import pandas as pd


def load_horizontal() -> Tuple[pd.DataFrame, np.ndarray]:
"""Synthetic model for horizontal layers
Returns
-------
Tuple[pd.DataFrame, np.ndarray]
dataframe with feature_name 'strati', bounding box array
"""
bb = np.array([[0, 0, 0], [10, 10, 10]])
xy = np.mgrid[0:10, 0:10].reshape(2, -1).T
data = pd.DataFrame(
np.vstack(
[
np.hstack(
[
xy,
np.zeros(xy.shape[0])[:, None] + 2,
np.zeros(xy.shape[0])[:, None] + 2,
]
),
np.hstack(
[
xy,
np.zeros(xy.shape[0])[:, None] + 3,
np.zeros(xy.shape[0])[:, None] + 3,
]
),
]
),
columns=["X", "Y", "Z", "val"],
)

data["feature_name"] = "strati"
return data, bb


def load_claudius():
"""Model dataset sampled from 3D seismic data
Expand Down
Loading

0 comments on commit 21e5572

Please sign in to comment.