Skip to content
Open
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
11 changes: 8 additions & 3 deletions loopstructural/gui/modelling/steps/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,18 @@ def check_data(data_manager, model_manager=None) -> StepCheck:
def check_stratigraphy(data_manager, model_manager=None) -> StepCheck:
"""Step 2: the column and the results that come from it."""
problems, todo = [], []
unit_names = data_manager.get_stratigraphic_unit_names()
if not unit_names:
# The step is optional: a model of faults only has no column
todo.append(
"Add units to the stratigraphic column. This step is optional if you model "
"only faults."
)
return StepCheck(tuple(problems), tuple(todo))
if data_manager.get_layer_role(layer_roles.GEOLOGY) is None:
todo.append("Select the geology layer.")
if data_manager.get_layer_role(layer_roles.STRUCTURE) is None:
todo.append("Select the structure layer.")
unit_names = data_manager.get_stratigraphic_unit_names()
if not unit_names:
todo.append("Add units to the stratigraphic column.")
problems.extend(_out_of_date_messages(data_manager))

contact_units = set(data_manager.get_unique_basal_units())
Expand Down
21 changes: 21 additions & 0 deletions loopstructural/gui/modelling/steps/export_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,30 @@ def set_model_solved(self, solved):
)
for tab_index in range(self.tabs.count()):
self.tabs.widget(tab_index).setEnabled(self._model_solved and self._task is None)
# A model of faults only has no units: no stratigraphic surfaces, no block model
has_units = not solved or self._has_stratigraphic_features()
if has_units and not self.surface_strat.isEnabled():
self.surface_strat.setChecked(True)
self.surface_strat.setEnabled(has_units)
if not has_units:
self.surface_strat.setChecked(False)
self.tabs.setTabEnabled(1, has_units)
if not has_units:
self.hint.setText("The model has faults only, so the block model is not offered.")
if self._model_solved:
self._set_plane_defaults()

def _has_stratigraphic_features(self):
"""Return False if every feature of the model is a fault."""
manager = self.model_manager
if manager is None:
return True
faults = set(getattr(manager, 'faults', {}) or {}) | set(
getattr(manager, 'parametric_faults', {}) or {}
)
names = [f.name for f in manager.features() if not f.name.startswith('__')]
return any(name not in faults for name in names)

def _plane_values(self):
return tuple(w.value() for w in self.section_origin) + (self.section_size.value(),)

Expand Down
6 changes: 5 additions & 1 deletion loopstructural/main/derived_refresh.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,16 @@ def names_to_refresh(data_manager) -> List[str]:
because the layer is an input of the user.
- Thickness: only when it is out of date. A thickness that was never
calculated can be typed, so the build does not calculate it.

With no units in the column (a model of faults only), nothing comes from
the column, so the list is empty.
"""
derived = data_manager.derived
names = []
if not data_manager.get_stratigraphic_unit_names():
return names
if (
data_manager.layer_roles.contacts_source == layer_roles.CONTACTS_FROM_GEOLOGY
and data_manager.get_stratigraphic_unit_names()
and data_manager.get_layer_role(layer_roles.GEOLOGY) is not None
and data_manager.get_layer_role(layer_roles.GEOLOGY_UNIT_FIELD)
and derived.status(derived_data.BASAL_CONTACTS) != derived_data.CURRENT
Expand Down
33 changes: 21 additions & 12 deletions loopstructural/main/model_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,14 @@ def update_foliation_features(self):
and the basal contacts and structural orientations data.
This method will automatically add unconformities based on the stratigraphic column.
"""
if self.stratigraphic_column is not None:
# also for an empty column, so the model does not keep the units
# of the last build
self.model.stratigraphic_column = self.stratigraphic_column
# A model with faults only has no column, or a column with no units
if self.stratigraphic_column is None or not self.stratigraphic_column.get_groups():
self._emit('foliation_features_updated')
return
stratigraphic_column = {}
# Take each unit's training value straight from `get_isovalues()`
# (LoopStructural core), which is what labels the extracted
Expand Down Expand Up @@ -1719,18 +1727,19 @@ def model_state(self) -> str:

@property
def valid(self):
valid = True
if len(self.groups) == 0:
valid = False
if len(self.stratigraphy) == 0:
valid = False
if len(self.faults) > 0:
for _fault_name, fault_data in self.faults.items():
if 'data' in fault_data and not fault_data['data'].empty:
valid = True
else:
valid = False
return valid
"""True if the model has stratigraphy or faults, and each fault trace has data.

A model with faults only (traces or parametric faults) has no groups
and no stratigraphy.
"""
if any(
'data' not in fault_data or fault_data['data'].empty
for fault_data in self.faults.values()
):
return False
has_stratigraphy = len(self.groups) > 0 and len(self.stratigraphy) > 0
has_faults = len(self.faults) > 0 or len(self.parametric_faults) > 0
return has_stratigraphy or has_faults

def update_model(
self,
Expand Down
14 changes: 14 additions & 0 deletions tests/qgis/test_parametric_fault.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,20 @@ def test_the_fault_is_built_with_constraints(self, manager):
manager.update_model(notify_observers=False)
assert 'F1' in _names(manager)

def test_a_model_of_faults_only_with_an_empty_column(self, manager):
column = StratigraphicColumn()
column.clear(basement=False)
manager.stratigraphic_column = column
manager.add_parametric_fault(_spec())
manager.update_model(notify_observers=False)
manager.update_all_features(notify_observers=False)
assert 'F1' in _names(manager)
# no stratigraphic feature
assert all(name.startswith(('F1', '__')) for name in _names(manager))
assert manager.model.stratigraphic_column is column
assert manager.valid
assert manager.model_state == 'solved'

def test_the_fault_surface_is_where_the_fault_is(self, manager):
manager.add_parametric_fault(_spec())
manager.update_model(notify_observers=False)
Expand Down
7 changes: 7 additions & 0 deletions tests/unit/test_build_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,13 @@ def test_no_column_calculates_no_contacts(self, dm):
dm.units = []
assert names_to_refresh(dm) == []

def test_no_column_calculates_no_thickness_even_if_out_of_date(self, dm):
dm.derived.record(derived_data.BASAL_CONTACTS)
dm.derived.record(derived_data.THICKNESS)
dm.inputs = {'unit_order': []}
dm.units = []
assert names_to_refresh(dm) == []

def test_no_geology_layer_calculates_no_contacts(self, dm):
dm.layer_roles.values[layer_roles.GEOLOGY] = None
assert names_to_refresh(dm) == []
Expand Down
9 changes: 8 additions & 1 deletion tests/unit/test_step_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,16 @@ def map_layers(self, dm, request):
def test_an_empty_column_is_not_started(self, dm):
check = checks.check_stratigraphy(dm)
assert check.status == Status.NOT_STARTED
assert 'Add units to the stratigraphic column.' in check.todo
assert any(t.startswith('Add units to the stratigraphic column.') for t in check.todo)
assert 'optional' in check.todo[0]

def test_an_empty_column_does_not_ask_for_layers_or_contacts(self, dm):
todo = checks.check_stratigraphy(dm).todo
assert len(todo) == 1
assert not checks.check_stratigraphy(dm).problems

def test_missing_map_layers_are_a_todo(self, dm):
dm.units = ['a']
todo = checks.check_stratigraphy(dm).todo
assert 'Select the geology layer.' in todo
assert 'Select the structure layer.' in todo
Expand Down
Loading