diff --git a/geoprob_pipe/calculations/systems/moria/initial_input.py b/geoprob_pipe/calculations/systems/moria/initial_input.py index 877b9178..46e69f93 100644 --- a/geoprob_pipe/calculations/systems/moria/initial_input.py +++ b/geoprob_pipe/calculations/systems/moria/initial_input.py @@ -276,7 +276,7 @@ "remark": "", "unit": "kN/m³", "distribution_type": DistributionType.deterministic, - "mean": 16.5, + "mean": 26.0, "source": "excel", }, { diff --git a/geoprob_pipe/calculations/systems/wbi/initial_input.py b/geoprob_pipe/calculations/systems/wbi/initial_input.py index d3285ac1..9065d42a 100644 --- a/geoprob_pipe/calculations/systems/wbi/initial_input.py +++ b/geoprob_pipe/calculations/systems/wbi/initial_input.py @@ -239,7 +239,7 @@ "remark": "", "unit": "kN/m³", "distribution_type": DistributionType.deterministic, - "mean": 16.5, + "mean": 26.0, "source": "excel", }, { diff --git a/geoprob_pipe/changelog.py b/geoprob_pipe/changelog.py index e3fa28a7..e4ae840e 100644 --- a/geoprob_pipe/changelog.py +++ b/geoprob_pipe/changelog.py @@ -1,5 +1,9 @@ CHANGELOG = { + "2.2.10": + "Bug fix en added validation. Traject probability of failure kon net boven 1.0 uitkomen door de toegepaste " + "rekentechniek. Daarnaast om het gebruik van de applicatie te verbeteren extra validatie toegepast, zoals dat " + "de vakindeling gezamenlijk één lijn moet zijn (zonder onderbrekingen).", "2.2.3": "Bug fix. Validation issue.", "2.2.0": diff --git a/geoprob_pipe/cmd_app/parameter_input/input_parameter_tables.py b/geoprob_pipe/cmd_app/parameter_input/input_parameter_tables.py index 15062cfa..6bf15201 100644 --- a/geoprob_pipe/cmd_app/parameter_input/input_parameter_tables.py +++ b/geoprob_pipe/cmd_app/parameter_input/input_parameter_tables.py @@ -37,10 +37,6 @@ def _validate_df_parameter_invoer(df: DataFrame, app_settings: ApplicationSettin label = "Parameter invoer" - # obj = DataFrameQueryValidation(df=df, failure_queries=FAILURE_QUERIES) - # result: bool = obj.validate(export_dir=export_dir, label_humanized="Parameter invoer") - # TODO: Remove FailureQueries-code - # Set up validator validator = ValidationParameterInvoer(df=df) geohydrologisch_model = app_settings.geohydrologisch_model diff --git a/geoprob_pipe/cmd_app/spatial_layers/hrd/hrd.py b/geoprob_pipe/cmd_app/spatial_layers/hrd/hrd.py index 234d01b7..f72a3cf8 100644 --- a/geoprob_pipe/cmd_app/spatial_layers/hrd/hrd.py +++ b/geoprob_pipe/cmd_app/spatial_layers/hrd/hrd.py @@ -29,7 +29,7 @@ def _hrd_data_requested(app_settings: ApplicationSettings) -> bool: choices_list = ["Ja", "Nee", "Applicatie afsluiten"] choice = inquirer.select( message=f"Wil je HRD-data importeren? Dit is optioneel. " - f"Je kunt ook handmatig overschrijdingsfrequentielijnen toevoegen.", + f"Je kunt op een later moment handmatig overschrijdingsfrequentielijnen toevoegen aan de invoer-Excel.", choices=choices_list, default=choices_list[0]).execute() # Close app? diff --git a/geoprob_pipe/cmd_app/spatial_layers/polderpeil.py b/geoprob_pipe/cmd_app/spatial_layers/polderpeil.py index ad73a421..3a2cfbe6 100644 --- a/geoprob_pipe/cmd_app/spatial_layers/polderpeil.py +++ b/geoprob_pipe/cmd_app/spatial_layers/polderpeil.py @@ -5,6 +5,7 @@ from shapely import Polygon, MultiPolygon from geopandas import GeoDataFrame, read_file import fiona +from geoprob_pipe.input_data.df_validation.df_polderpeilen import ValidationPolderpeilen from geoprob_pipe.utils.validation_messages import BColors import warnings if TYPE_CHECKING: @@ -58,10 +59,10 @@ def request_polderpeil_filepath(app_settings: ApplicationSettings): raise NotImplementedError( f"Applicatie vroegtijdig afgesloten: Een {filepath.split(sep='.')[-1]}-bestand is niet geïmplementeerd.") - # Confirm all are points - all_geometries_are_points = gdf.geometry.apply( + # Confirm all are polygons + all_geometries_are_polygons = gdf.geometry.apply( lambda geom: isinstance(geom, Polygon) or isinstance(geom, MultiPolygon)).all() - if not all_geometries_are_points: + if not all_geometries_are_polygons: print(BColors.WARNING, f"Het geïmporteerde bestand bestaat niet (volledig) uit vlakken/polygonen, maar ook uit " f"andere typen geometrie. Enkel vlakken/polygonen zijn toegestaan.", BColors.ENDC) request_polderpeil_filepath(app_settings=app_settings) @@ -143,7 +144,27 @@ def specify_column_with_polderpeil_niveau(app_settings: ApplicationSettings, gdf continue column_name_is_valid = True + # Rename columns gdf_to_add = gdf[["geometry", column_name]] gdf_to_add = gdf_to_add.rename(columns={column_name: "polderpeil"}) - gdf_to_add.to_file(app_settings.geopackage_filepath, layer="polderpeil", driver="GPKG") - print(BColors.OKBLUE, f"✅ Polderpeilen toegevoegd.", BColors.ENDC) + + # Validate input + validator = ValidationPolderpeilen(df=gdf_to_add) + validator.run() + if validator.df_failures is not None and validator.df_failures.__len__() > 0: + export_dir: str = os.path.join( + app_settings.workspace_dir, + "exports", + app_settings.datetime_stamp, + "parameter_input_process") + os.makedirs(export_dir, exist_ok=True) + export_path: str = os.path.join(export_dir, "validation_messages_polderpeilen.xlsx") + validator.df_failures.to_excel(export_path) + print(BColors.WARNING, f"Het valideren van de polderpeil-data resulteerde in validatieberichten. Zorg dat " + f"deze eerst opgelost zijn. De validatieberichten zijn hiernaartoe geëxporteerd: " + f"{export_path}.", BColors.ENDC) + request_polderpeil_filepath(app_settings=app_settings) + else: + # Add to geopackage + gdf_to_add.to_file(app_settings.geopackage_filepath, layer="polderpeil", driver="GPKG") + print(BColors.OKBLUE, f"✅ Polderpeilen toegevoegd.", BColors.ENDC) diff --git a/geoprob_pipe/cmd_app/spatial_layers/vakindeling.py b/geoprob_pipe/cmd_app/spatial_layers/vakindeling.py index eb40641c..e50b5172 100644 --- a/geoprob_pipe/cmd_app/spatial_layers/vakindeling.py +++ b/geoprob_pipe/cmd_app/spatial_layers/vakindeling.py @@ -3,7 +3,8 @@ from InquirerPy import inquirer import warnings from geoprob_pipe.cmd_app.utils.spatial import load_dijktraject_linestring -from geoprob_pipe.utils.gdf import convert_mls_geom_column_to_ls +from geoprob_pipe.utils.gdf import ( + convert_mls_geom_column_to_ls, validate_geometry_types, validate_vakindeling_merges_to_single_linestring) import os from pathlib import Path from shapely import LineString, MultiLineString @@ -144,10 +145,29 @@ def request_vakindeling_filepath(app_settings: ApplicationSettings): def validate_vakindeling(app_settings: ApplicationSettings, gdf: GeoDataFrame): """ Validates the vakindeling shape, with some conversions if they can be applied safely. """ + + # Validate geometry types + allowed_types = {"LineString", "MultiLineString"} + valid, invalid_types = validate_geometry_types(gdf=gdf, allowed_types=allowed_types) + assert valid, (f"De opgegeven vakindeling heeft geometrie types die niet toegestaan zijn. De volgende types zijn " + f"toegestaan: {allowed_types}. De volgende niet toegestane types werden ook gevonden: " + f"{invalid_types}. " + f"Zorg er voor dat je alleen line strings importeert.") + + # Convergeer multi line strings naar single line strings gdf = convert_mls_geom_column_to_ls(gdf=gdf) + + # Controleer of elk vak een geometrie heeft assert gdf.geometry.apply(lambda geom: isinstance(geom, LineString)).all(), \ ("De opgegeven vakindeling heeft niet voor elk vak een geometry. De " "applicatie sluit nu af.") + + # Check if vakindeling does not have gaps + valid, merged_type = validate_vakindeling_merges_to_single_linestring(gdf=gdf) + assert valid, (f"De vakindeling is niet valide. Ter controle is een poging gedaan of de vakindeling " + f"samenvoegbaar is tot één lijn. Dit blijkt niet het geval. Zitten er gaten tussen de vakken?") + + # Continue questioner specify_column_with_vaknaam(app_settings, gdf=gdf) @@ -286,6 +306,12 @@ def align_vak_shp_to_dijktraject( gdf_new_vakindeling: GeoDataFrame = gdf_new_vakindeling[ ["id", "naam", "m_start", "m_end", "geometry"]] + # Validate correct geometry types + valid, invalid_types = validate_geometry_types(gdf=gdf_new_vakindeling, allowed_types={"LineString"}) + assert valid, (f"Most probably a bug. Invalid geometry types resulted from projecting the imported vakindeling " + f"to the dijktraject-geometry. The invalid geometry types are {invalid_types}. Only LineStrings " + f"were expected. Re-examine your input, or contact a developer with this error.") + # Add to geopackage gdf_new_vakindeling.to_file( Path(app_settings.geopackage_filepath), diff --git a/geoprob_pipe/input_data/df_validation/df_polderpeilen.py b/geoprob_pipe/input_data/df_validation/df_polderpeilen.py new file mode 100644 index 00000000..462e90ad --- /dev/null +++ b/geoprob_pipe/input_data/df_validation/df_polderpeilen.py @@ -0,0 +1,27 @@ +from geoprob_pipe.utils.df_validation import ( + DataFrameValidation, ColumnValidation, ValidationRequirement, requirements) +from pandas import DataFrame + + +POLDERPEIL = ColumnValidation(column_name="polderpeil", requirements=[ + ValidationRequirement( + requirement=requirements.is_not_null, + failure_msg=f"De waarde van het polderpeil moet ingevuld zijn met een numerieke waarde. Eén of meerdere " + f"rijen heeft geen waarde."), + ValidationRequirement( + requirement=requirements.is_numeric, + failure_msg=f"De waarde van het polderpeil moet een numerieke waarde hebben. Eén of meerdere rijen voldoet " + f"hier niet aan. ", + ), + requirements.IsInRange(left=-99, right=999), +]) + +class ValidationPolderpeilen(DataFrameValidation): + + def __init__(self, df: DataFrame): + super().__init__( + df=df, + label="Polderpeilen", + required_columns=[], + columns_validations=[POLDERPEIL] + ) diff --git a/geoprob_pipe/input_data/validation/__init__.py b/geoprob_pipe/input_data/validation/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/geoprob_pipe/input_data/validation/dataframes/__init__.py b/geoprob_pipe/input_data/validation/dataframes/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/geoprob_pipe/input_data/validation/dataframes/df_parameter_invoer.py b/geoprob_pipe/input_data/validation/dataframes/df_parameter_invoer.py deleted file mode 100644 index 4f2363ab..00000000 --- a/geoprob_pipe/input_data/validation/dataframes/df_parameter_invoer.py +++ /dev/null @@ -1,40 +0,0 @@ -from typing import List -from geoprob_pipe.input_data.validation.dataframes.validation_objects import FailureQuery - - -FAILURE_QUERIES: List[FailureQuery] = [ - - # Kolom 'scope' - FailureQuery( - query="scope in ['traject', 'vak', 'uittredepunt']", - msg="De scope moet 'traject', 'vak' of 'uittredepunt' zijn."), - - # Kolom 'scope_referentie' - FailureQuery( - query="scope in ['vak', 'uittredepunt'] and scope_referentie.isnull()", - msg="De scope referentie moet worden gespecificeerd wanneer de scope 'vak' of 'uittredepunt' is."), - - # Kolom 'mean' - FailureQuery( - query="distribution_type in ['normal', 'log_normal'] and mean.isnull()", - msg="Geen gemiddelde waarde gespecificeerd voor deze rij. Dit is vereist bij distributie types 'normal' en " - "'log_normal'."), - FailureQuery( - query="parameter in ['d70', 'd70_m'] " - "and distribution_type in ['deterministic', 'log_normal', 'normal'] " - "and mean >= 0.001", - msg="Parameter d70/d70_m moet een waarde hebben die kleiner is dan 0.001. Deze geef je op in de eenheid " - "meters. Wellicht is de eenheid van de opgegeven waarde verkeerd?"), - - # Kolom 'variation' en 'deviation' - FailureQuery( - query="distribution_type in ['normal', 'log_normal'] and variation.isnull() and deviation.isnull()", - msg="Geen variatie coefficient of standaard deviatie gespecificeerd voor deze rij. Dit is vereist bij " - "distributie types 'normal' en 'log_normal'."), - - # Kolom 'fragility_values_ref' - FailureQuery( - query="distribution_type == 'cdf_curve' and fragility_values_ref.isnull()", - msg="Geen referentie naar de fragility values gespecificeerd voor deze rij. Dit is vereist bij distributie " - "type 'cdf_curve'."), -] diff --git a/geoprob_pipe/input_data/validation/dataframes/df_scenario_invoer_som_kans.py b/geoprob_pipe/input_data/validation/dataframes/df_scenario_invoer_som_kans.py deleted file mode 100644 index b6d01088..00000000 --- a/geoprob_pipe/input_data/validation/dataframes/df_scenario_invoer_som_kans.py +++ /dev/null @@ -1,19 +0,0 @@ -from typing import List -from geoprob_pipe.input_data.validation.dataframes.validation_objects import FailureQuery -from pandas import DataFrame - - -def _df_scenario_group_sum_kans(df: DataFrame) -> DataFrame: - df_grouped: DataFrame = df.groupby(["vak_id"], as_index=False)["kans"].sum() - df_grouped = df_grouped.rename(columns={"kans": "som_kans"}) - df_grouped["som_kans"] = df_grouped["som_kans"].round(3) - return df_grouped - - -FAILURE_QUERIES: List[FailureQuery] = [ - FailureQuery( - query="som_kans != 1.0", - msg="In de tabel 'Scenario invoer' moet de som van de kansen gelijk zijn aan 1.0 (100%). " - "Dit is niet het geval voor dit vak." - ), -] diff --git a/geoprob_pipe/input_data/validation/dataframes/validation_objects.py b/geoprob_pipe/input_data/validation/dataframes/validation_objects.py deleted file mode 100644 index c3afacd1..00000000 --- a/geoprob_pipe/input_data/validation/dataframes/validation_objects.py +++ /dev/null @@ -1,51 +0,0 @@ -from typing import List -from pandas import DataFrame -import os -from dataclasses import dataclass -from geoprob_pipe.utils.validation_messages import BColors - - -@dataclass -class FailureQuery: - query: str - msg: str - - -class DataFrameQueryValidation: - - def __init__(self, df: DataFrame, failure_queries: List[FailureQuery]): - self.df = df - self.failure_queries = failure_queries - - def validate(self, export_dir: str, label_humanized: str) -> bool: - for failure_query in self.failure_queries: - - # Assess - df_failure_rows: DataFrame = self.df.query(failure_query.query) - df_failure_rows = df_failure_rows.copy(deep=True) - - # Continue if no failures - if df_failure_rows.__len__() == 0: - continue - - # Prepare dataframe for export - current_columns = df_failure_rows.columns - new_column_order = ["validation_msg", "tabel"] - new_column_order.extend(current_columns) - df_failure_rows['validation_msg'] = failure_query.msg - df_failure_rows['tabel'] = label_humanized - df_failure_rows = df_failure_rows[new_column_order] - - # Report back - os.makedirs(export_dir, exist_ok=True) - export_path = os.path.join(export_dir, "df_failure_rows.xlsx") - if os.path.exists(export_path): - os.remove(export_path) - df_failure_rows.to_excel(export_path, index=False) - print(f"{BColors.WARNING}Validatie is (voortijdig) beëindigd omdat er {df_failure_rows.__len__()} " - f"validatie issues voor de '{label_humanized}'-tabel zijn gevonden. De gedetailleerde lijst is " - f"geëxporteerd naar onderstaande locatie. Los deze issues s.v.p. eerst op. \n" - f"{export_path}{BColors.ENDC}") - return False - - return True diff --git a/geoprob_pipe/results/assemblage/functions.py b/geoprob_pipe/results/assemblage/functions.py index c3db7dd3..2ba7f72c 100644 --- a/geoprob_pipe/results/assemblage/functions.py +++ b/geoprob_pipe/results/assemblage/functions.py @@ -17,7 +17,7 @@ def combine_series(list_pf: list[float]) -> Tuple[float, float]: doorsneden. :param list_pf: Lijst met faalkansen van de elementen. - :return bovengrens en ondergrens + :return: bovengrens en ondergrens """ # If empty @@ -28,7 +28,7 @@ def combine_series(list_pf: list[float]) -> Tuple[float, float]: ondergrens = max(list_pf) # We have to use Decimal for bovengrens - getcontext().prec = 30 + getcontext().prec = 100 # Because with small numbers (e-18 and smaller) it turns out that 1 - e-18 is rounded to one. Therefore, we have to # use Decimal with a lowered precision (we use up to e-30). We now first convert the necessary values to Decimal: one = Decimal(1) @@ -111,22 +111,18 @@ def window_collect(window_size: float, point_list: list[UittredepuntElement], bins_window = np.arange( m_van, m_tot, window_size ).tolist() - bins_window.append(m_tot) + if m_tot not in bins_window: + bins_window.append(m_tot) # add end of final window bin_cat: pd.Categorical = cast(pd.Categorical, pd.cut( - list_m_value, - bins=bins_window, - right=False, - include_lowest=True - )) + list_m_value, bins=bins_window, right=False, include_lowest=True, duplicates="drop")) df_vak = df_vak.assign(bin=bin_cat) df_bin = (df_vak.groupby("bin", observed=False)["pf"].max() .reindex(bin_cat.categories).fillna(0)) sum_pf, max_pf = combine_series(df_bin.to_list()) window_elements: List[WindowElement] = [] - bins_window.append(m_tot) # add end of final window - for i in range(len(bins_window)-2): + for i in range(len(bins_window)-1): window_elements.append(WindowElement( m_van=bins_window[i], m_tot=bins_window[i+1], @@ -164,7 +160,7 @@ def scaled_collect( windows. """ from geoprob_pipe.results.assemblage.objects import WindowElement - if point_list.__len__() == 0: # Leeg element + if len(point_list) == 0: # Leeg element return 0.0, 0.0, [] fcn_list = [p.flow_chart_number for p in point_list] if min(fcn_list) == 11: @@ -214,7 +210,7 @@ def scaled_collect( length = seg_end - seg_start a = sel.a N_vak = bepaal_N_vak(length, a, dL) - pf = cast(float, sel.pf) * N_vak + pf = min(1.0, cast(float, sel.pf) * N_vak) # Limiet als pf zeer hoog is. pfs.append(pf) window_elements.append( WindowElement( diff --git a/geoprob_pipe/results/assemblage/objects.py b/geoprob_pipe/results/assemblage/objects.py index cb8f1ad5..b26b5c12 100644 --- a/geoprob_pipe/results/assemblage/objects.py +++ b/geoprob_pipe/results/assemblage/objects.py @@ -25,13 +25,13 @@ def __post_init__(self): if self.pf is not None: if not (0.0 <= self.pf <= 1.0): - raise ValueError("pof moet tussen 0.0 en 1.0 liggen.") + raise ValueError(f"pof moet tussen 0.0 en 1.0 liggen. Huidige waarde is {self.pf}") - if self.beta is not None: - if not math.isfinite(self.beta): - raise ValueError("beta moet een eindige waarde zijn.") - if (-38.0 <= self.beta <= 38.0) is False: - raise ValueError("beta moet tussen -38.0 en 38.0 liggen.") + # if self.beta is not None: + # if not math.isfinite(self.beta): + # raise ValueError("beta moet een eindige waarde zijn.") + # if (-38.0 <= self.beta <= 38.0) is False: + # raise ValueError("beta moet tussen -38.0 en 38.0 liggen.") if self.pf is None and self.beta is not None: self.pf = float(stats.norm.cdf(-1.0 * self.beta)) @@ -56,13 +56,13 @@ def __post_init__(self): if self.pf is not None: if not (0.0 <= self.pf <= 1.0): - raise ValueError("pof moet tussen 0.0 en 1.0 liggen.") + raise ValueError(f"pof moet tussen 0.0 en 1.0 liggen. Huidige waarde is {self.pf}") - if self.beta is not None: - if not math.isfinite(self.beta): - raise ValueError("beta moet een eindige waarde zijn.") - if (-38.0 <= self.beta <= 38.0) is False: - raise ValueError("beta moet tussen -38.0 en 38.0 liggen.") + # if self.beta is not None: + # if not math.isfinite(self.beta): + # raise ValueError("beta moet een eindige waarde zijn.") + # if (-38.0 <= self.beta <= 38.0) is False: + # raise ValueError("beta moet tussen -38.0 en 38.0 liggen.") if self.pf is None and self.beta is not None: self.pf = float(stats.norm.cdf(-1.0 * self.beta)) @@ -121,7 +121,8 @@ def pf_max_dsn(self) -> Tuple[KansElement, KansElement]: pf_dsn = max(list_pf) # Bereken vak kans - pf_vak = self.N_vak * pf_dsn + # pf_vak = self.N_vak * pf_dsn + pf_vak = min(1.0, self.N_vak * pf_dsn) # Limit naar 1 bij zeer hoge pof. return KansElement(pf=pf_dsn), KansElement(pf=pf_vak) @@ -131,7 +132,7 @@ def conv_max_dsn(self) -> bool: kansen geconvergeerd zijn, dan True, anders False. Want een niet geconvergeerde kans had de maximale faalkans kunnen zijn. """ list_conv = [cast(bool, dsn.converged) for dsn in self.dsn_list] - if False in list_conv: + if all(list_conv): return False return True diff --git a/geoprob_pipe/results/construct_dataframes.py b/geoprob_pipe/results/construct_dataframes.py index 63a0f42d..d0172423 100644 --- a/geoprob_pipe/results/construct_dataframes.py +++ b/geoprob_pipe/results/construct_dataframes.py @@ -1,18 +1,23 @@ from __future__ import annotations -from geoprob_pipe.utils.statistics import convert_failure_probability_to_beta +import operator +from decimal import Decimal, getcontext +from functools import reduce +import numpy as np import pandas as pd -from geoprob_pipe.results.assemblage.objects import ( - UittredepuntElement, VakElement, TrajectElement) from pandas import DataFrame, concat -import numpy as np +from geoprob_pipe.results.assemblage.objects import TrajectElement, UittredepuntElement, VakElement +from geoprob_pipe.utils.statistics import convert_failure_probability_to_beta from typing import TYPE_CHECKING, List if TYPE_CHECKING: from geoprob_pipe.results import Results from geoprob_pipe import GeoProbPipe - from geoprob_pipe.calculations.systems.base_objects.system_calculation import \ - SystemCalculation + from geoprob_pipe.calculations.systems.base_objects.system_calculation import SystemCalculation from probabilistic_library import DesignPoint from geoprob_pipe.calculations.systems.build_and_run import CalcResult + from geoprob_pipe import GeoProbPipe + from geoprob_pipe.calculations.systems.base_objects.system_calculation import SystemCalculation + from geoprob_pipe.calculations.systems.build_and_run import CalcResult + from geoprob_pipe.results import Results def collect_df_beta_limit_state(calculation: SystemCalculation) -> DataFrame: @@ -196,12 +201,39 @@ def calculate_df_beta_per_uittredepunt(geoprob_pipe: GeoProbPipe, results: Resul df_beta_scenarios_final = results.df_beta_scenarios_final.copy(deep=True) - df = df_beta_scenarios_final.assign( - failure_probability=df_beta_scenarios_final.apply( - lambda row: row['failure_probability'] * geoprob_pipe.input_data.scenarios.scenario_kans( - vak_id=row['vak_id'], scenario_naam=row['ondergrondscenario_id'] - ), axis=1)).groupby('uittredepunt_id', as_index=False)[ - 'failure_probability'].sum() + # The sum() leads to failure probabilities of larger than 1. Due to very small + # values decimal is used. + getcontext().prec = 100 + one = Decimal(1) + df = ( + df_beta_scenarios_final.assign( + failure_probability=lambda row: ( + row["failure_probability"] + * row.apply( + lambda row2: ( + geoprob_pipe.input_data.scenarios.scenario_kans( + vak_id=row2["vak_id"], + scenario_naam=row2["ondergrondscenario_id"], + ) + ), + axis=1, + ) + ) + ) + .groupby("uittredepunt_id", as_index=False) + .agg( + failure_probability=( + "failure_probability", + lambda x: float( + one + - reduce( + operator.mul, (one - Decimal(str(v)) for v in x), one + ) + ), + ) + ) + ) + df["beta"] = df["failure_probability"].apply(lambda failure_prob: convert_failure_probability_to_beta(failure_prob)) # Determine when uittredepunt is converged (when all scenarios are converged) diff --git a/geoprob_pipe/utils/gdf.py b/geoprob_pipe/utils/gdf.py index 93467e2f..0cad7a26 100644 --- a/geoprob_pipe/utils/gdf.py +++ b/geoprob_pipe/utils/gdf.py @@ -1,5 +1,30 @@ from geopandas import GeoDataFrame from shapely import LineString, MultiLineString +from typing import List, Tuple +from shapely import unary_union, line_merge + + +def validate_geometry_types(gdf: GeoDataFrame, allowed_types=None) -> Tuple[bool, List[str]]: + """ Controleert of alle geometrieën van een valide type zijn. + + :param gdf: + :param allowed_types: + :return: + 1st item: Boolean if valid + 2nd item: Invalid geometry types + """ + + # Mutable arguments + if allowed_types is None: + allowed_types = {"LineString", "MultiLineString"} + + # Validate + geom_types = gdf.geometry.geom_type # of gdf.geom_type + mask_invalid = ~geom_types.isin(allowed_types) + invalid_gdf = gdf.loc[mask_invalid] + is_valid = not mask_invalid.any() + + return is_valid, list(invalid_gdf.geometry.geom_type.unique()) def convert_mls_geom_column_to_ls(gdf: GeoDataFrame) -> GeoDataFrame: @@ -24,3 +49,9 @@ def unwrap_ls_in_mls(geom): gdf["geometry"] = gdf["geometry"].apply(unwrap_ls_in_mls) return gdf + + +def validate_vakindeling_merges_to_single_linestring(gdf: GeoDataFrame) -> Tuple[bool, type]: + """ Controleert of alle LineStrings aaneengesloten zijn. """ + merged = line_merge(unary_union(gdf.geometry)) + return isinstance(merged, LineString), type(merged) diff --git a/pyproject.toml b/pyproject.toml index b7521ec3..a6af3c9b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.poetry] name = "geoprob_pipe" -version = "2.2.7" +version = "2.2.10" description = "Execute probabilistic piping calculations using the Deltares probabilic library." keywords = [ "levee",