Skip to content

Commit

Permalink
WIP all major features working and tests refactored for new API; some…
Browse files Browse the repository at this point in the history
… oustanding typing issues + detox etc; composite to_archive attention
  • Loading branch information
chrisbc committed Oct 28, 2024
1 parent 2616870 commit f1c4e3c
Show file tree
Hide file tree
Showing 41 changed files with 683 additions and 682 deletions.
2 changes: 1 addition & 1 deletion solvis/dochelper/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .extension import DynamicDocstrings

__all__ = ["DynamicDocstrings"]
__all__ = ["DynamicDocstrings"]
3 changes: 2 additions & 1 deletion solvis/dochelper/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
import griffe

from typing import Union, Optional

logger = griffe.get_logger(__name__)

logger.setLevel('DEBUG')

class DynamicDocstrings(griffe.Extension):

class DynamicDocstrings(griffe.Extension):
def __init__(self, object_paths: Optional[list[str]] = None) -> None:
self.object_paths = object_paths

Expand Down
2 changes: 1 addition & 1 deletion solvis/fault_system_solution_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
def build_rupture_groups(
solution: InversionSolutionProtocol, rupture_ids: Optional[List[int]] = None, min_overlap: float = 0.8
) -> Iterator[Dict]:
dfrs = solution.rupture_sections
dfrs = solution.model.rupture_sections
rupture_ids = rupture_ids or dfrs['rupture'].unique().tolist()
print(f"there are {len(rupture_ids)} unique ruptures")
count = 0
Expand Down
29 changes: 15 additions & 14 deletions solvis/filter/parent_fault_id_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

import shapely.geometry

from solvis.inversion_solution.typing import InversionSolutionProtocol
from solvis.inversion_solution.typing import InversionSolutionModelProtocol


class ParentFaultMapping(NamedTuple):
Expand All @@ -34,7 +34,7 @@ class ParentFaultMapping(NamedTuple):


def parent_fault_name_id_mapping(
solution: InversionSolutionProtocol, parent_fault_ids: Iterable[int]
model: InversionSolutionModelProtocol, parent_fault_ids: Iterable[int]
) -> Iterator[ParentFaultMapping]:
"""For each unique parent_fault_id yield a ParentFaultMapping object.
Expand All @@ -44,15 +44,15 @@ def parent_fault_name_id_mapping(
Yields:
A mapping object.
"""
df0 = solution.fault_sections
df0 = model.fault_sections
df1 = df0[df0['ParentID'].isin(list(parent_fault_ids))][['ParentID', 'ParentName']]
unique_ids = list(df1.ParentID.unique())
unique_names = list(df1.ParentName.unique())
for idx, parent_id in enumerate(unique_ids):
yield ParentFaultMapping(parent_id, unique_names[idx])


def valid_parent_fault_names(solution, validate_names: Iterable[str]) -> Set[str]:
def valid_parent_fault_names(model, validate_names: Iterable[str]) -> Set[str]:
"""Check that parent_fault_names are valid for the given solution.
Args:
Expand All @@ -64,9 +64,9 @@ def valid_parent_fault_names(solution, validate_names: Iterable[str]) -> Set[str
Raises:
ValueError: If any member of `validate_names` argument is not valid.
"""
unknown = set(validate_names).difference(set(solution.parent_fault_names))
unknown = set(validate_names).difference(set(model.parent_fault_names))
if unknown:
raise ValueError(f"The solution {solution} does not contain the parent_fault_names: {unknown}.")
raise ValueError(f"The solution model {model} does not contain the parent_fault_names: {unknown}.")
return set(validate_names)


Expand All @@ -85,8 +85,9 @@ class FilterParentFaultIds:
```
"""

def __init__(self, solution: InversionSolutionProtocol):
self._solution = solution
def __init__(self, model: InversionSolutionModelProtocol):
# self._solution = solution
self._model = model

def for_named_faults(self, named_fault_names: Iterable[str]):
raise NotImplementedError()
Expand All @@ -99,7 +100,7 @@ def all(self) -> Set[int]:
Returns:
the parent_fault_ids.
"""
result = set(self._solution.fault_sections['ParentID'].tolist())
result = set(self._model.fault_sections['ParentID'].tolist())
return result

def for_parent_fault_names(self, parent_fault_names: Iterable[str]) -> Set[int]:
Expand All @@ -114,8 +115,8 @@ def for_parent_fault_names(self, parent_fault_names: Iterable[str]) -> Set[int]:
Raises:
ValueError: If any `parent_fault_names` argument is not valid.
"""
df0 = self._solution.fault_sections
ids = df0[df0['ParentName'].isin(list(valid_parent_fault_names(self._solution, parent_fault_names)))][
df0 = self._model.fault_sections
ids = df0[df0['ParentName'].isin(list(valid_parent_fault_names(self._model, parent_fault_names)))][
'ParentID'
].tolist()
return set([int(id) for id in ids])
Expand All @@ -129,7 +130,7 @@ def for_subsection_ids(self, fault_section_ids: Iterable[int]) -> Set[int]:
Returns:
The fault_ids matching the filter.
"""
df0 = self._solution.fault_sections
df0 = self._model.fault_sections
ids = df0[df0['FaultID'].isin(list(fault_section_ids))]['ParentID'].unique().tolist()
return set([int(id) for id in ids])

Expand All @@ -145,7 +146,7 @@ def for_rupture_ids(self, rupture_ids: Iterable[int]) -> Set[int]:
Returns:
The parent_fault_ids matching the filter.
"""
# df0 = self._solution.rupture_sections
df0 = self._solution.fault_sections_with_rupture_rates
# df0 = self._model.rupture_sections
df0 = self._model.fault_sections_with_rupture_rates
ids = df0[df0['Rupture Index'].isin(list(rupture_ids))].ParentID.unique().tolist()
return set([int(id) for id in ids])
65 changes: 33 additions & 32 deletions solvis/filter/rupture_id_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@
>>> ham50 = solvis.circle_polygon(50000, -37.78, 175.28) # 50km radius around Hamilton
<POLYGON ((175.849 -37.779, 175.847 -37.823, 175.839 -37.866, 175.825 -37.90...>
>>> solution = solvis.InversionSolution.from_archive(filename)
>>> rupture_ids = FilterRuptureIds(solution)\\
>>> rupture_ids = FilterRuptureIds(model)\\
.for_magnitude(min_mag=5.75, max_mag=6.25)\\
.for_polygon(ham50)
>>> # ruptures on any of faults A, B, with magnitude and rupture rate limits
>>> rupture_ids = FilterRuptureIds(solution)\\
>>> rupture_ids = FilterRuptureIds(model)\\
>>> .for_parent_fault_names(["Alpine Jacksons to Kaniere", "Vernon"])\\
>>> .for_magnitude(7.0, 8.0)\\
>>> .for_rupture_rate(1e-6, 1e-2)
>>> # ruptures on fault A that do not involve fault B:
>>> rupture_ids = FilterRuptureIds(solution)\\
>>> rupture_ids = FilterRuptureIds(model)\\
>>> .for_parent_fault_names(["Alpine Jacksons to Kaniere"])\\
>>> .for_parent_fault_names(["Vernon], join_prior='difference')
```
Expand All @@ -32,7 +32,7 @@
import shapely.geometry

import solvis.inversion_solution
from solvis.inversion_solution.typing import InversionSolutionProtocol, SetOperationEnum
from solvis.inversion_solution.typing import InversionSolutionModelProtocol, SetOperationEnum

from .chainable_set_base import ChainableSetBase
from .parent_fault_id_filter import FilterParentFaultIds
Expand All @@ -44,16 +44,16 @@ class FilterRuptureIds(ChainableSetBase):
A helper class to filter solution ruptures, returning the qualifying rupture_ids.
"""

def __init__(self, solution: InversionSolutionProtocol, drop_zero_rates: bool = True):
def __init__(self, model: InversionSolutionModelProtocol, drop_zero_rates: bool = True):
"""
Args:
solution: The solution instance to act on.
drop_zero_rates: Exclude ruptures with rupture_rate == 0 (default=True)
"""
self._solution = solution
self._model = model
self._drop_zero_rates = drop_zero_rates
self._filter_subsection_ids = FilterSubsectionIds(solution)
self._filter_parent_fault_ids = FilterParentFaultIds(solution)
self._filter_subsection_ids = FilterSubsectionIds(model)
self._filter_parent_fault_ids = FilterParentFaultIds(model)

def all(self) -> ChainableSetBase:
"""Convenience method returning ids for all solution ruptures.
Expand All @@ -63,8 +63,8 @@ def all(self) -> ChainableSetBase:
Returns:
A chainable set of all the rupture_ids.
"""
result = set(self._solution.ruptures['Rupture Index'].to_list())
return self.new_chainable_set(result, self._solution)
result = set(self._model.ruptures['Rupture Index'].to_list())
return self.new_chainable_set(result, self._model)

def for_named_faults(self, named_fault_names: Set[str]) -> ChainableSetBase:
"""Find ruptures that occur on any of the given named_fault names.
Expand Down Expand Up @@ -118,23 +118,23 @@ def for_parent_fault_ids(
A chainable set of rupture_ids matching the filter.
"""
subsection_ids = self._filter_subsection_ids.for_parent_fault_ids(parent_fault_ids)
df0 = self._solution.rupture_sections
df0 = self._model.rupture_sections

# TODO: this is needed because the rupture rate concept differs between IS and FSS classes
rate_column = (
"rate_weighted_mean"
if isinstance(self._solution, solvis.inversion_solution.FaultSystemSolution)
if isinstance(self._model, solvis.inversion_solution.FaultSystemSolutionModel)
else "Annual Rate"
)
if self._drop_zero_rates:
df0 = df0.join(self._solution.rupture_rates.set_index("Rupture Index"), on='rupture', how='inner')[
df0 = df0.join(self._model.rupture_rates.set_index("Rupture Index"), on='rupture', how='inner')[
[rate_column, "rupture", "section"]
]
df0 = df0[df0[rate_column] > 0]

ids = df0[df0['section'].isin(list(subsection_ids))]['rupture'].tolist()
result = set([int(id) for id in ids])
return self.new_chainable_set(result, self._solution, self._drop_zero_rates, join_prior=join_prior)
return self.new_chainable_set(result, self._model, self._drop_zero_rates, join_prior=join_prior)

def for_subsection_ids(
self,
Expand All @@ -150,18 +150,18 @@ def for_subsection_ids(
Returns:
A chainable set of rupture_ids matching the filter.
"""
df0 = self._solution.rupture_sections
df0 = self._model.rupture_sections
ids = df0[df0.section.isin(list(fault_section_ids))].rupture.tolist()
result = set([int(id) for id in ids])
return self.new_chainable_set(result, self._solution, self._drop_zero_rates, join_prior=join_prior)
return self.new_chainable_set(result, self._model, self._drop_zero_rates, join_prior=join_prior)

def _ruptures_with_and_without_rupture_rates(self):
"""Helper method
# TODO this dataframe could be cached?? And used by above??
"""
df_rr = self._solution.rupture_rates.drop(columns=["Rupture Index", "fault_system"])
df_rr = self._model.rupture_rates.drop(columns=["Rupture Index", "fault_system"])
df_rr.index = df_rr.index.droplevel(0) # so we're indexed by "Rupture Index" without "fault_system"
return self._solution.ruptures.join(df_rr, on=self._solution.ruptures["Rupture Index"], rsuffix='_r')
return self._model.ruptures.join(df_rr, on=self._model.ruptures["Rupture Index"], rsuffix='_r')

def for_rupture_rate(
self,
Expand All @@ -182,21 +182,22 @@ def for_rupture_rate(
"""
index = "Rupture Index"
if self._drop_zero_rates:
df0 = self._solution.ruptures_with_rupture_rates
df0 = self._model.ruptures_with_rupture_rates
else:
df0 = self._ruptures_with_and_without_rupture_rates()

rate_column = (
"rate_weighted_mean"
if isinstance(self._solution, solvis.inversion_solution.FaultSystemSolution)
else "Annual Rate"
)
# rate_column = (
# "rate_weighted_mean"
# if isinstance(self._model, solvis.inversion_solution.FaultSystemSolution)
# else "Annual Rate"
# )
rate_column = self._model.rate_column_name()

# rate col is different for InversionSolution
df0 = df0 if not max_rate else df0[df0[rate_column] <= max_rate]
df0 = df0 if not min_rate else df0[df0[rate_column] > min_rate]
result = set(df0[index].tolist())
return self.new_chainable_set(result, self._solution, self._drop_zero_rates, join_prior=join_prior)
return self.new_chainable_set(result, self._model, self._drop_zero_rates, join_prior=join_prior)

def for_magnitude(
self,
Expand All @@ -216,14 +217,14 @@ def for_magnitude(
"""
index = "Rupture Index"
if self._drop_zero_rates:
df0 = self._solution.ruptures_with_rupture_rates
df0 = self._model.ruptures_with_rupture_rates
else:
df0 = self._ruptures_with_and_without_rupture_rates()

df0 = df0 if not max_mag else df0[df0.Magnitude <= max_mag]
df0 = df0 if not min_mag else df0[df0.Magnitude > min_mag]
result = set(df0[index].tolist())
return self.new_chainable_set(result, self._solution, self._drop_zero_rates, join_prior=join_prior)
return self.new_chainable_set(result, self._model, self._drop_zero_rates, join_prior=join_prior)

def for_polygons(
self,
Expand Down Expand Up @@ -264,7 +265,7 @@ def for_polygons(
rupture_ids = set.difference(*rupture_id_sets)
else:
raise ValueError("Only INTERSECTION, UNION & DIFFERENCE operations are supported for `join_type`")
return self.new_chainable_set(rupture_ids, self._solution, self._drop_zero_rates, join_prior=join_prior)
return self.new_chainable_set(rupture_ids, self._model, self._drop_zero_rates, join_prior=join_prior)

def for_polygon(
self,
Expand All @@ -280,16 +281,16 @@ def for_polygon(
Returns:
A chainable set of rupture_ids matching the filter arguments.
"""
df0 = gpd.GeoDataFrame(self._solution.fault_sections)
df0 = gpd.GeoDataFrame(self._model.fault_sections)
df0 = df0[df0['geometry'].intersects(polygon)]

if self._drop_zero_rates:
index = "Rupture Index"
df1 = self._solution.rs_with_rupture_rates
df1 = self._model.rs_with_rupture_rates
else:
index = "rupture"
df1 = self._solution.rupture_sections
df1 = self._model.rupture_sections

df2 = df1.join(df0, 'section', how='inner')
result = set(df2[index].unique())
return self.new_chainable_set(result, self._solution, self._drop_zero_rates, join_prior=join_prior)
return self.new_chainable_set(result, self._model, self._drop_zero_rates, join_prior=join_prior)
20 changes: 10 additions & 10 deletions solvis/filter/subsection_id_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"""
from typing import Iterable, Union

from solvis.inversion_solution.typing import InversionSolutionProtocol, SetOperationEnum
from solvis.inversion_solution.typing import InversionSolutionModelProtocol, SetOperationEnum

from .chainable_set_base import ChainableSetBase
from .parent_fault_id_filter import FilterParentFaultIds
Expand All @@ -33,9 +33,9 @@ class FilterSubsectionIds(ChainableSetBase):
set operands like `union`, `intersection`, `difference` etc).
"""

def __init__(self, solution: InversionSolutionProtocol):
self._solution = solution
self._filter_parent_fault_ids = FilterParentFaultIds(solution)
def __init__(self, model: InversionSolutionModelProtocol):
self._model = model
self._filter_parent_fault_ids = FilterParentFaultIds(model)

def for_named_faults(self, named_fault_names: Iterable[str]) -> ChainableSetBase:
raise NotImplementedError()
Expand All @@ -48,8 +48,8 @@ def all(self) -> ChainableSetBase:
Returns:
A chainable set of all the subsection_ids.
"""
result = set(self._solution.fault_sections.index.to_list())
return self.new_chainable_set(result, self._solution)
result = set(self._model.fault_sections.index.to_list())
return self.new_chainable_set(result, self._model)

def for_parent_fault_names(
self, parent_fault_names: Iterable[str], join_prior: Union[SetOperationEnum, str] = 'intersection'
Expand Down Expand Up @@ -79,10 +79,10 @@ def for_parent_fault_ids(
Returns:
The fault_subsection_ids matching the filter.
"""
df0 = self._solution.fault_sections
df0 = self._model.fault_sections
ids = df0[df0['ParentID'].isin(list(parent_fault_ids))]['FaultID'].tolist()
result = set([int(id) for id in ids])
return self.new_chainable_set(result, self._solution, join_prior=join_prior)
return self.new_chainable_set(result, self._model, join_prior=join_prior)

def for_rupture_ids(
self, rupture_ids: Iterable[int], join_prior: Union[SetOperationEnum, str] = 'intersection'
Expand All @@ -95,10 +95,10 @@ def for_rupture_ids(
Returns:
The fault_subsection_ids matching the filter.
"""
df0 = self._solution.rupture_sections
df0 = self._model.rupture_sections
ids = df0[df0.rupture.isin(list(rupture_ids))].section.tolist()
result = set([int(id) for id in ids])
return self.new_chainable_set(result, self._solution, join_prior=join_prior)
return self.new_chainable_set(result, self._model, join_prior=join_prior)

def for_polygon(self, polygon, contained=True) -> ChainableSetBase:
raise NotImplementedError()
1 change: 1 addition & 0 deletions solvis/inversion_solution/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

from .composite_solution import CompositeSolution
from .fault_system_solution import FaultSystemSolution
from .fault_system_solution_model import FaultSystemSolutionModel
from .inversion_solution import InversionSolution

"""
Expand Down
Loading

0 comments on commit f1c4e3c

Please sign in to comment.