Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@
caption: Usage
maxdepth: 1
---
Installation <usage/installation>
usage/installation
usage/3d-modeling
usage/interface
usage/tutorials
usage/faq
```

```{toctree}
Expand Down
Binary file added docs/static/bounding_box_widget.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/static/dem_widget.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/static/fault_layers.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/static/fault_topology_hamersley.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/static/model-setup.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1,332 changes: 1,332 additions & 0 deletions docs/static/src/fault_topology_hamersley.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/static/stratigraphic_column_01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/static/stratigraphic_column_02.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/static/stratigraphic_column_03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/static/stratigraphic_column_04.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/static/stratigraphic_layer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions docs/usage/3d-modeling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# 3D Modelling Background

### Background on 3D Modeling

A **3D model** is a mathematical representation of a three-dimensional object. In the context of geology, 3D models are used to visualize and analyze the subsurface structure of the Earth. These models are constructed by integrating various types of geological data, such as borehole logs, geological maps, geophysical surveys, and structural measurements. The goal is to create a coherent representation of the subsurface that can be used for exploration, resource management, and scientific research.



#### Implicit Modeling

Implicit modeling is a modern approach to constructing 3D geological models. Unlike traditional methods that rely on explicit surfaces and manual digitization, implicit modeling uses mathematical functions to represent geological features. These functions are defined over the entire model space and allow for the automatic generation of surfaces, such as stratigraphic boundaries and faults.

Key advantages of implicit modeling include:
- **Efficiency**: Models can be constructed quickly, even with large datasets.
- **Flexibility**: Implicit methods can handle complex geometries and data uncertainties.
- **Automation**: The process is less reliant on manual interpretation, reducing subjectivity.

Implicit modeling has become widely used, enabling geoscientists to create detailed and accurate representations of the Earth's subsurface.

## LoopStructural
LoopStructural is an open-source Python library designed for implicit geological modeling. It provides tools for creating 3D geological models based on various types of input data, including borehole data, surface data, and structural measurements. LoopStructural provides both the implicit modelling algorithms and parameterisation of geological objects.

### Stratigraphic Modelling
In LoopStructural stratigraphic surfaces can be modelled using implicit functions. The function is approximated to fit observations of the surface for example the location of contacts, the orientation of the surface at the location of a contact. Combined with a stratigraphic column which defines the order of the contacts and any unconformable relationships between them, LoopStructural can interpolate a function which approximates the geometry of the surface.

### Fault modelling
Faults are modelled in LoopStructural by building three implicit functions defining the fault surface, fault slip vector and the fault extent. Combined with a parametric representation of the fault displacement within these coordinates a kinematic model.



Empty file added docs/usage/faq.md
Empty file.
34 changes: 34 additions & 0 deletions docs/usage/install/development.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## Development version

If you want to install the development version of LoopStructural plugin, you can clone the repository and install the dependencies using `pip`.
1. Clone the repository:
```bash
git clone https://github.com/Loop3d/plugin_loopstructural.git
```
2. Navigate to the cloned directory:
```bash
cd plugin_loopstructural
```
3. Install the required dependencies:
```bash
pip install -r loopstructural/requirements.txt
```
4. To add the plugin to QGIS make a symbolic link between the plugin location and the QGIS plugin folder:

**linux**
```bash
ln -s $(pwd)/loopstructural ~/.local/share/QGIS/QGIS3/profiles/default/python/plugins/LoopStructural
```
**macOS**

```bash
ln -s $(pwd)/loopstructural ~/Library/Application\ Support/QGIS/QGIS3/profiles/default/python/plugins/LoopStructural
```

**Windows**

```bash
mklink /D "%APPDATA%\QGIS\QGIS3\profiles\default\python\plugins\LoopStructural" "%cd%\loopstructural"
```
5. Restart QGIS to load the plugin.
6. Install plugin autoreload
43 changes: 43 additions & 0 deletions docs/usage/install/linux.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Linux

## Installation Instructions

### Step 1: Install QGIS
1. Open a terminal on your Linux system.
2. Add the QGIS repository to your system:
```bash
sudo add-apt-repository ppa:ubuntugis/ubuntugis-unstable
sudo add-apt-repository ppa:qgis/qgis-stable
```
3. Update your package list:
```bash
sudo apt update
```
4. Install QGIS:
```bash
sudo apt install qgis python3-qgis
```



### Step 2: Install Dependencies Using pip
1. Ensure you have `pip` installed. If not, install it using:
```bash
sudo apt install python3-pip
```
2. Use `pip` to install the required dependencies for the plugin. Navigate to the directory containing the `requirements` files and run:
```bash
pip install -e requirements.txt
```

### Step 3: Install the Plugin via QGIS Plugin Manager
1. Open QGIS on your system.
2. Navigate to the **Plugins** menu and select **Manage and Install Plugins**.
3. In the Plugin Manager, search for the plugin by name (e.g., `LoopStructural`).
4. Click **Install Plugin** to download and install it.

### Step 4: Verify Installation
1. Restart QGIS if necessary.
2. Confirm that the plugin is available under the **Plugins** menu.

You are now ready to use the plugin on your Linux system!
36 changes: 36 additions & 0 deletions docs/usage/install/macosx.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# MacOS

## Installation Instructions Using Homebrew or MacPorts

### Step 1: Install QGIS
1. Open a terminal on your Mac.
2. Install Homebrew if you haven't already:
```bash
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```
3. Use Homebrew to install QGIS:
```bash
brew install qgis
```

### Step 2: Install Dependencies Using pip
1. Ensure you have `pip` installed. If not, install it using:
```bash
sudo easy_install pip
```
2. Use `pip` to install the required dependencies for the plugin. Navigate to the directory containing the `requirements` files and run:
```bash
pip install -r requirements.txt
```

### Step 3: Install the Plugin via QGIS Plugin Manager
1. Open QGIS on your system.
2. Navigate to the **Plugins** menu and select **Manage and Install Plugins**.
3. In the Plugin Manager, search for the plugin by name (e.g., `LoopStructural`).
4. Click **Install Plugin** to download and install it.

### Step 4: Verify Installation
1. Restart QGIS if necessary.
2. Confirm that the plugin is available under the **Plugins** menu.

You are now ready to use the plugin on your MacOS system!
Empty file added docs/usage/install/qpip.md
Empty file.
33 changes: 33 additions & 0 deletions docs/usage/install/windows.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Installation Instructions for Windows

## Step 1: Install QGIS
1. Visit the [QGIS official website](https://qgis.org/en/site/forusers/download.html).
2. Download the latest QGIS installer for Windows.
3. Run the installer and follow the on-screen instructions to complete the installation.

## Step 2: Open the OSGeo4W Shell
1. After installing QGIS, locate the **OSGeo4W Shell**. You can find it in the Start Menu under the QGIS folder.
2. Open the **OSGeo4W Shell**. This shell provides a command-line environment for managing QGIS and its dependencies.

## Step 3: Install Dependencies Using `pip`
1. In the **OSGeo4W Shell**, ensure that Python is available by typing:
```bash
python --version
```
This should display the Python version bundled with QGIS.
2. Use `pip` to install the required dependencies. Run the following command:
```bash
pip install LoopStructural pyvista pyvistaqt meshio geoh5py
```

## Step 4: Install the Plugin Using the QGIS Plugin Manager
1. Open QGIS.
2. Navigate to the **Plugins** menu and select **Manage and Install Plugins**.
3. In the Plugin Manager, search for the plugin by name (e.g., "LoopStructural").
4. Click **Install** to download and install the plugin.
5. Once installed, the plugin will be available in the QGIS interface.

## Step 5: Verify Installation
1. Restart QGIS to ensure all changes take effect.
2. Check the **Plugins** menu or toolbar for the installed plugin.
3. Open the plugin and verify that it is functioning as expected.
22 changes: 15 additions & 7 deletions docs/usage/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,24 @@

This plugin is published on the official QGIS plugins repository: <https://plugins.qgis.org/plugins/loopstructural/>.

## Beta versions released
LoopStructural plugin requires the installation of `LoopStructural, loopsolver, pyvista, pyvistaqt and pyqtgraph`. Optionally meshio and geoh5py can also be installed for exporting surfaces/models into different formats.

Enable experimental extensions in the QGIS plugins manager settings panel.
To install these dependencies you can follow the instructions below for your operating system.

## Earlier development version
### Using QPIP
You can also use the experimental QGIS plugin QPIP which is developed by OPENGIS.ch <https://plugins.qgis.org/plugins/qpip/> that manages the Python dependencies for your QGIS environment and keeps the dependencies up to date.

If you define yourself as early adopter or a tester and can't wait for the release, the plugin is automatically packaged for each commit to main, so you can use this address as repository URL in your QGIS extensions manager settings:

```url
https://github.com/Loop3d/loopstructural-qgis//plugins.xml
----

```{toctree}
:caption: Installation
:maxdepth: 1

install/windows
install/linux
install/macosx

```

Be careful, this version can be unstable.

51 changes: 51 additions & 0 deletions docs/usage/interface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Plugin interface

## Selecting Layers
The LoopStructural plugin interfaces with QGIS to define the model input data and parameters.

### Bounding box
The bounding box defines the spatial extent of the model and can be either specified manually or automatically by calculating the extent from a selected layer or the current view. Not that the bounding box currently has to be axis aligned, meaning that the bounding box is defined by the minimum and maximum x, y and z coordinates.


![Bounding Box](../static/bounding_box_widget.png)
### Elevation data
The elevation data is used to define the height of the input data. If a digital elevation model (DEM) is available, the height for all data points will be extracted from the layer. Otherwise a constant elevation can be used.

If the points being modelled contain a Z coordinate, this can be used instead of the DEM or constant elevation and is selected on a per layer basis.

![DEM](../static/dem_widget.png)
### Fault layers
The faults trace layer is usually a line layer that contains the trace of the fault. The fault trace is used to define the location of the fault in the model. Optional attributes can be used to further constrain the model:
- **fault name** the name of the fault to be used in the model, if this is left blank the feature ID will be used instead.
- **Dip** the dip of the fault, if this is left blank the fault will be assumed to be vertical.
- **Displacement** - The maximum displacement magnitude of the fault. If this is not specified, a default value will be used.
- **Pitch** - defines the pitch of the fault slip vector in the fault surface. If this is left blank a vertical slip vector is assumed and projected onto the fault surface.

![Fault Layer](../static/fault_layers.png)
### Stratigraphy
Two layers can be used to constrain the stratigraphy of the model:
1. Basal contacts - this layer defines the basal contacts of the stratigraphy. The layer should contain a line layer with the contact traces. The attributes can be used to define the name of the contact.
2. Structural data - this layer defines the structural data that is used to constrain the model. The layer should contain a point layer with the structural data. The attributes can be used to define the orientation of the data, such as dip and dip direction.

![Stratigraphic Layer](../static/stratigraphic_layer.png)

## Stratigraphic Column
The stratigraphic column defines the order of the contacts and any unconformable relationships between them. The column is defined by a list of units - these units are ordered from oldest at the bottom to youngest at the top. Unconformities can be inserted between units to define an unconformable relationship. The thicknesses define the true thickness of each unit and are used to parameterise the interpolation. The unit names should match the names of the contacts in the basal contacts layer. Units without basal contacts can be included in the stratigraphic column but will not be constrained by any data.

The stratigraphic column can be initialised from the basal contacts layer by clicking the "Initialise from Layer" button. This will create a column with the contacts in the order they are found in the layer. The column can then be edited to add unconformities or change the order of the units. To change the order of units simply drag the units in the list. To add an unconformity, click the "Add Unconformity" button and drag the unconformity the location in the column.


![Stratigraphic Column](../static/stratigraphic_column_04.png)


## Fault topology relationships

The fault-fault relationship table defines the interaction between faults in the model. This is used to define abutting relationships and where one fault is faulted by another fault. The fault table is updated whenever the faults layer or fault name field is changed. For each fault the columns indicate whether the fault is abutting to another fault. By clicking the cell the relationship can be toggled between no relationship (white background), abutting (red) and faulted (green).

![Fault Topology](../static/fault_topology_hamersley.png)

## Model parameters
Once the layers have been selected, stratigraphic column defined and the fault topology relationships set, the LoopStructural model can be initialised.

Initialise model will create a LoopStructural model with all of the geological features in the model. For each feature in the model the number of interpolation elements (degrees of freedom), the weighting of the regularisation, contact points and orientation weight can be changed.
![Model Parameters](../static/model-setup.png)
Empty file added docs/usage/tutorials.md
Empty file.
109 changes: 109 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import os
import pickle
import importlib
import pytest

from qgis.core import QgsApplication, QgsProcessingContext, QgsProcessingFeedback


@pytest.fixture(scope="session", autouse=True)
def qgis_app():
"""Start a headless QGIS application for tests.

Requires QGIS_PREFIX_PATH to be set in the environment (pointing to the QGIS install).
"""
prefix = os.environ.get("QGIS_PREFIX_PATH")
if not prefix:
raise RuntimeError(
"QGIS_PREFIX_PATH environment variable must be set to your QGIS install path for tests to run."
)

app = QgsApplication([], False)
app.setPrefixPath(prefix, True)
app.initQgis()

yield app

app.exitQgis()


@pytest.fixture
def qgis_context():
"""Return a fresh processing context."""
return QgsProcessingContext()


@pytest.fixture
def feedback():
"""Return a simple QgsProcessingFeedback instance for algorithms."""
return QgsProcessingFeedback()


@pytest.fixture
def ensure_loopstructural(monkeypatch):
"""If the real LoopStructural classes are not available, inject minimal fakes into
the algorithm module so tests can run without the external dependency.

The algorithm module imports FaultTopology and FaultRelationshipType at import-time
and uses those module-level names; this fixture patches the algorithm module
attributes when they are missing.
"""
mod_name = "loopstructural.processing.algorithms.modelling.add_fault_topology"
mod = importlib.import_module(mod_name)

if getattr(mod, "FaultTopology", None) is not None and getattr(mod, "FaultRelationshipType", None) is not None:
# real dependency present; nothing to do
return

class _FakeFaultTopology:
def __init__(self, strat_col=None):
self.strat_col = strat_col
self.faults = set()
# store relationships in a dict for simple inspection
self._rels = {}

def add_fault(self, name):
self.faults.add(name)

def update_fault_relationship(self, a, b, rel):
self._rels[(a, b)] = rel

def __repr__(self):
return f"FakeFaultTopology(faults={sorted(self.faults)})"

class _FakeFaultRelationshipType:
ABUTTING = 1

monkeypatch.setattr(mod, "FaultTopology", _FakeFaultTopology, raising=False)
monkeypatch.setattr(mod, "FaultRelationshipType", _FakeFaultRelationshipType, raising=False)

return


@pytest.fixture
def simple_model_pickle(tmp_path):
"""Create and return a path to a simple pickled model object suitable for tests.

The returned object has a `features` attribute with simple feature-like objects
exposing a `name` attribute so the algorithm can find faults.
"""
class DummyFeature:
def __init__(self, name):
self.name = name

def __repr__(self):
return f"DummyFeature({self.name})"

class DummyModel:
def __init__(self, names=("fault1", "fault2")):
self.features = [DummyFeature(n) for n in names]

def __repr__(self):
return f"DummyModel(features={self.features})"

model = DummyModel()
path = tmp_path / "model.pkl"
with open(path, "wb") as fh:
pickle.dump(model, fh)

return str(path)
Loading