From d12953ed22125455a358a24d71f225db7a8c687e Mon Sep 17 00:00:00 2001 From: Robin Wimmers Date: Wed, 29 May 2024 17:26:00 +0200 Subject: [PATCH 1/3] feat: initial setup SBR-B --- src/pyvibracore/results/nuisance.py | 294 ++++++++++++++++++++++++++++ 1 file changed, 294 insertions(+) create mode 100644 src/pyvibracore/results/nuisance.py diff --git a/src/pyvibracore/results/nuisance.py b/src/pyvibracore/results/nuisance.py new file mode 100644 index 0000000..7555dc1 --- /dev/null +++ b/src/pyvibracore/results/nuisance.py @@ -0,0 +1,294 @@ +from __future__ import annotations + +from typing import Any, List, Tuple + +import geopandas as gpd +import matplotlib.patches as patches +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd +from numpy.typing import NDArray +from scipy import interpolate +from scipy.interpolate import interpolate +from shapely.geometry import LineString, Point, Polygon + +from pyvibracore.results.plot_utils import _north_arrow, _scalebar + +# CUR 166-1997 Tabel 5.20 Factor Cfc voor flooren +CFC_FACTOR_FLOORS = { + "driving": { + "concrete": {"Cfc": 1.4, "Vfc": 0.17}, + "wood": {"Cfc": 1.4, "Vfc": 0.17}, + }, + "vibrate": { + "concrete": {"Cfc": 1.7, "Vfc": 0.27}, + "wood": {"Cfc": 2.5, "Vfc": 0.44}, + }, +} + +TARGET_VALUE = { + "<= 1 day": [0.8, 6, 0.4], + "2 days": [0.72, 6, 0.38], + "3 days": [0.64, 6, 0.34], + "4 days": [0.56, 6, 0.36], + "5 days": [0.48, 6, 0.32], + ">= 6 days; <26 days": [0.4, 6, 0.3], + ">= 26 days; <78 days": [0.3, 6, 0.2], +} + + +def _nuisance_prediction( + A1: List[float], + A2: List[float], + A3: List[float], + vibrationVelocity_eff: List[float], + vibrationVelocity_per: List[float], + distance: List[float], +) -> NDArray: + """ + Based on the 'Handleiding meten en rekenen industrielawaai' 2004 methode l. + More information: https://open.overheid.nl/Details/ronl-15eb5528-d835-4f6a-b3a1-fc0851b334f9/1 + + Parameters + ---------- + A1: + target value [-] + A2: + target value [-] + A3: + target value [-] + vibrationVelocity_eff: + vibration velocity [mm/s] + vibrationVelocity_per: list + vibration velocity [mm/s] + distance: + distance with respect ot building [m] + + Returns + ------- + space: NDArray + """ + + df = pd.DataFrame( + { + "vibrationVelocity_per": vibrationVelocity_per, + "vibrationVelocity_eff": vibrationVelocity_eff, + "distance": distance, + } + ).drop_duplicates(subset=["vibrationVelocity_per", "vibrationVelocity_eff"]) + + # interpolate and predict + f_eff = interpolate.interp1d( + df["vibrationVelocity_eff"], + df["distance"], + kind="cubic", + assume_sorted=False, + fill_value="extrapolate", + ) + A1_d = f_eff(A1) + A2_d = f_eff(A2) + + # interpolate and predict + f_per = interpolate.interp1d( + df["vibrationVelocity_per"], + df["distance"], + kind="cubic", + assume_sorted=False, + fill_value="extrapolate", + ) + A3_d = f_per(A3) + + return np.min([np.max([A2_d, A3_d], axis=0), A1_d], axis=0) + + +def df_nuisance( + response_dict: dict, + cfc: float, + u_eff: float, + period: float, +) -> pd.DataFrame: + arr = np.array(response_dict["data"]["vibrationVelocity"]) + distances = _nuisance_prediction( + *zip(*TARGET_VALUE.values()), + vibrationVelocity_per=arr * cfc * u_eff * np.sqrt(period / 12), + vibrationVelocity_eff=arr * cfc * u_eff, + distance=response_dict["data"]["distance"], + ) + + return pd.DataFrame( + { + "labels": TARGET_VALUE.keys(), + "distance": distances, + } + ) + + +def map_nuisance( + buildings: gpd.GeoDataFrame, + source_location: Point | LineString | Polygon, + building_name: str, + response_dict: dict, + cfc: float, + u_eff: float, + period: float, + title: str = "Legend:", + figsize: Tuple[float, float] = (10.0, 12.0), + settings: dict | None = None, + **kwargs: Any, +) -> plt.Figure: + """ + Create map of the input building settings. + + Parameters + ---------- + buildings: + GeoDataFrame of the input buildings + response_dict: + response of the single prepal or cur166 endpoint. + source_location: + location of the vibration source + building_name: + name of the building + u_eff: + Vibration transfer to part of a building (u_eff) CUR 166-1997 page 514 [-] + cfc: + Vibration transfer to part of a building (Cfc) CUR 166-1997 table 5.20 or 5.21 [-] + period: + Operating period of the building code [hours] + title: + Legend title + figsize: + Size of the activate figure, as the `plt.figure()` argument. + settings: + Plot settings used in plot: default settings are: + + .. code-block:: python + + { + "source_location": {"label": "Trillingsbron", "color": "blue"}, + "levels": [ + { + "label": ">80 db [0 dagen]", + "level": 80, + "color": "darkred", + }, + { + "label": ">75 db [5 dagen]", + "level": 75, + "color": "red", + }, + { + "label": ">70 db [15 dagen]", + "level": 70, + "color": "orange", + }, + { + "label": ">65 db [30 dagen]", + "level": 65, + "color": "darkgreen", + }, + { + "label": ">60 db [50 dagen]", + "level": 60, + "color": "green", + }, + ], + } + **kwargs: + All additional keyword arguments are passed to the `pyplot.subplots()` call. + + Returns + ------- + Figure + """ + if settings is None: + settings = { + "source_location": {"label": "Trillingsbron", "color": "blue"}, + "levels": [ + { + "label": "<= 1 day", + "color": "darkred", + }, + { + "label": ">= 6 days; <26 days", + "color": "orange", + }, + { + "label": ">= 26 days; <78 days", + "color": "green", + }, + ], + } + + kwargs_subplot = { + "figsize": figsize, + "tight_layout": True, + } + + kwargs_subplot.update(kwargs) + + fig, axes = plt.subplots(**kwargs_subplot) + + gpd.GeoSeries(source_location).plot( + ax=axes, color=settings["source_location"]["color"], alpha=1, zorder=1, aspect=1 + ) + + building = buildings.get(buildings["name"] == building_name) + if building.empty: + raise ValueError(f"No buildings with name {building_name}.") + + buildings.where(buildings["name"] == building_name).plot( + ax=axes, zorder=2, color="gray", aspect=1 + ) + buildings.where(buildings["name"] != building_name).plot( + ax=axes, zorder=2, color="lightgray", aspect=1 + ) + + # plot contour + levels = [TARGET_VALUE[values["label"]] for values in settings["levels"]] + arr = np.array(response_dict["data"]["vibrationVelocity"]) + distances = _nuisance_prediction( + *zip(*levels), + vibrationVelocity_per=arr * cfc * u_eff * np.sqrt(period / 12), + vibrationVelocity_eff=arr * cfc * u_eff, + distance=response_dict["data"]["distance"], + ) + colors = [values["color"] for values in settings["levels"]] + for distance, color in zip(distances, colors): + gpd.GeoSeries(building.buffer(distance).exterior).plot( + ax=axes, zorder=3, color=color, aspect=1 + ) + + # plot name + for idx, row in buildings.iterrows(): + x = row.geometry.centroid.xy[0][0] + y = row.geometry.centroid.xy[1][0] + + axes.annotate( + idx, + xy=(x, y), + horizontalalignment="center", + ) + + # add legend + axes.legend( + title=title, + title_fontsize=18, + fontsize=15, + loc="lower right", + handles=[ + patches.Patch( + facecolor=value["color"], + label=value["label"], + alpha=0.9, + linewidth=2, + edgecolor="black", + ) + for value in settings["levels"] + ], + ) + + _north_arrow(axes) + _scalebar(axes) + + return fig From cec5b6e17b3b05ce9601419e4f2d7c685dc43eb6 Mon Sep 17 00:00:00 2001 From: Robin Wimmers Date: Thu, 30 May 2024 08:38:17 +0200 Subject: [PATCH 2/3] docs: use more descriptive attribute names --- src/pyvibracore/results/nuisance.py | 114 ++++++++++++++++------------ 1 file changed, 66 insertions(+), 48 deletions(-) diff --git a/src/pyvibracore/results/nuisance.py b/src/pyvibracore/results/nuisance.py index 7555dc1..2b17aad 100644 --- a/src/pyvibracore/results/nuisance.py +++ b/src/pyvibracore/results/nuisance.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, List, Tuple +from typing import Any, List, Sequence, Tuple import geopandas as gpd import matplotlib.patches as patches @@ -8,13 +8,12 @@ import numpy as np import pandas as pd from numpy.typing import NDArray -from scipy import interpolate from scipy.interpolate import interpolate from shapely.geometry import LineString, Point, Polygon from pyvibracore.results.plot_utils import _north_arrow, _scalebar -# CUR 166-1997 Tabel 5.20 Factor Cfc voor flooren +# CUR 166-1997 Tabel 5.20 Factor Cfc CFC_FACTOR_FLOORS = { "driving": { "concrete": {"Cfc": 1.4, "Vfc": 0.17}, @@ -26,6 +25,7 @@ }, } +# SBR-B Trillingen meet en beoordelingsrichtlijnen hinder voor personen in gebouwen [2002] art. 10.5.4 Table 4 TARGET_VALUE = { "<= 1 day": [0.8, 6, 0.4], "2 days": [0.72, 6, 0.38], @@ -38,31 +38,30 @@ def _nuisance_prediction( - A1: List[float], - A2: List[float], - A3: List[float], - vibrationVelocity_eff: List[float], - vibrationVelocity_per: List[float], - distance: List[float], + target_value_one: List[float] | NDArray | Sequence, + target_value_two: List[float] | NDArray | Sequence, + target_value_three: List[float] | NDArray | Sequence, + vibration_velocity_eff: List[float] | NDArray, + vibration_velocity_per: List[float] | NDArray, + distance: List[float] | NDArray, ) -> NDArray: """ - Based on the 'Handleiding meten en rekenen industrielawaai' 2004 methode l. - More information: https://open.overheid.nl/Details/ronl-15eb5528-d835-4f6a-b3a1-fc0851b334f9/1 + Based on the 'SBR-B Trillingen meet en beoordelingsrichtlijnen hinder voor personen in gebouwen [2002]'. Parameters ---------- - A1: - target value [-] - A2: - target value [-] - A3: - target value [-] - vibrationVelocity_eff: + target_value_one: + target value SBR-B [2002] art. 10.5.1 [-] + target_value_two: + target value SBR-B [2002] art. 10.5.1 [-] + target_value_three: + target value SBR-B [2002] art. 10.5.1 [-] + vibration_velocity_eff: vibration velocity [mm/s] - vibrationVelocity_per: list + vibration_velocity_per: list vibration velocity [mm/s] distance: - distance with respect ot building [m] + distance with respect to building [m] Returns ------- @@ -71,8 +70,8 @@ def _nuisance_prediction( df = pd.DataFrame( { - "vibrationVelocity_per": vibrationVelocity_per, - "vibrationVelocity_eff": vibrationVelocity_eff, + "vibrationVelocity_per": vibration_velocity_per, + "vibrationVelocity_eff": vibration_velocity_eff, "distance": distance, } ).drop_duplicates(subset=["vibrationVelocity_per", "vibrationVelocity_eff"]) @@ -85,8 +84,8 @@ def _nuisance_prediction( assume_sorted=False, fill_value="extrapolate", ) - A1_d = f_eff(A1) - A2_d = f_eff(A2) + target_value_one_spaces = f_eff(target_value_one) + target_value_two_spaces = f_eff(target_value_two) # interpolate and predict f_per = interpolate.interp1d( @@ -96,9 +95,15 @@ def _nuisance_prediction( assume_sorted=False, fill_value="extrapolate", ) - A3_d = f_per(A3) + target_value_three_spaces = f_per(target_value_three) - return np.min([np.max([A2_d, A3_d], axis=0), A1_d], axis=0) + return np.min( + [ + np.max([target_value_two_spaces, target_value_three_spaces], axis=0), + target_value_one_spaces, + ], + axis=0, + ) def df_nuisance( @@ -107,11 +112,33 @@ def df_nuisance( u_eff: float, period: float, ) -> pd.DataFrame: + """ + Get a DataFrame that holds the distance of the different durations + + Parameters + ---------- + response_dict: + response of the single prepal or cur166 endpoint. + u_eff: + Vibration transfer to part of a building (u_eff) CUR 166-1997 page 514 [-] + cfc: + Vibration transfer to part of a building (Cfc) CUR 166-1997 table 5.20 or 5.21 [-] + period: + Operating period of the building code [hours] + + Returns + ------- + dataframe + """ arr = np.array(response_dict["data"]["vibrationVelocity"]) + a_one, a_two, a_three = [*zip(*TARGET_VALUE.values())] + distances = _nuisance_prediction( - *zip(*TARGET_VALUE.values()), - vibrationVelocity_per=arr * cfc * u_eff * np.sqrt(period / 12), - vibrationVelocity_eff=arr * cfc * u_eff, + target_value_one=a_one, + target_value_two=a_two, + target_value_three=a_three, + vibration_velocity_per=arr * cfc * u_eff * np.sqrt(period / 12), + vibration_velocity_eff=arr * cfc * u_eff, distance=response_dict["data"]["distance"], ) @@ -168,28 +195,15 @@ def map_nuisance( "source_location": {"label": "Trillingsbron", "color": "blue"}, "levels": [ { - "label": ">80 db [0 dagen]", - "level": 80, + "label": "<= 1 day", "color": "darkred", }, { - "label": ">75 db [5 dagen]", - "level": 75, - "color": "red", - }, - { - "label": ">70 db [15 dagen]", - "level": 70, + "label": ">= 6 days; <26 days", "color": "orange", }, { - "label": ">65 db [30 dagen]", - "level": 65, - "color": "darkgreen", - }, - { - "label": ">60 db [50 dagen]", - "level": 60, + "label": ">= 26 days; <78 days", "color": "green", }, ], @@ -247,10 +261,14 @@ def map_nuisance( # plot contour levels = [TARGET_VALUE[values["label"]] for values in settings["levels"]] arr = np.array(response_dict["data"]["vibrationVelocity"]) + a_one, a_two, a_three = [*zip(*levels)] + distances = _nuisance_prediction( - *zip(*levels), - vibrationVelocity_per=arr * cfc * u_eff * np.sqrt(period / 12), - vibrationVelocity_eff=arr * cfc * u_eff, + target_value_one=a_one, + target_value_two=a_two, + target_value_three=a_three, + vibration_velocity_per=arr * cfc * u_eff * np.sqrt(period / 12), + vibration_velocity_eff=arr * cfc * u_eff, distance=response_dict["data"]["distance"], ) colors = [values["color"] for values in settings["levels"]] From b81939407e7cbdc320219163c69018c3b67cebc2 Mon Sep 17 00:00:00 2001 From: Robin Wimmers Date: Thu, 6 Jun 2024 14:23:44 +0200 Subject: [PATCH 3/3] feat: add SBR-B to notebook --- .../{NBxxxxx_SBRA.ipynb => NBxxxxx_SBR.ipynb} | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) rename notebooks/{NBxxxxx_SBRA.ipynb => NBxxxxx_SBR.ipynb} (93%) diff --git a/notebooks/NBxxxxx_SBRA.ipynb b/notebooks/NBxxxxx_SBR.ipynb similarity index 93% rename from notebooks/NBxxxxx_SBRA.ipynb rename to notebooks/NBxxxxx_SBR.ipynb index fe90d61..f0a7cb1 100644 --- a/notebooks/NBxxxxx_SBRA.ipynb +++ b/notebooks/NBxxxxx_SBR.ipynb @@ -56,6 +56,7 @@ " map_payload,\n", " plot_reduction,\n", ")\n", + "from pyvibracore.results.nuisance import map_nuisance, df_nuisance, CFC_FACTOR_FLOORS\n", "from pyvibracore.results.sound_result import (\n", " get_normative_building as get_normative_building_sound,\n", ")\n", @@ -339,7 +340,7 @@ "\"\"\"\n", "# ** power\n", "# source power [dB]\n", - "power = 140\n", + "power = 100\n", "\n", "# ** k2\n", "# Correction term [dB]\n", @@ -599,6 +600,49 @@ " plot_reduction(result, sensitive=True);" ] }, + { + "cell_type": "markdown", + "id": "c0be8b21-6caf-4273-b7e0-8da89ae82b65", + "metadata": {}, + "source": [ + "# SBR-B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6029c996-666a-403b-bf3a-25f7c5867888", + "metadata": {}, + "outputs": [], + "source": [ + "name = get_normative_building_sound(buildings, location)\n", + "\n", + "if name:\n", + " single_payload = create_single_payload(multi_vibration_payload, name=name)\n", + " result = client.call_endpoint(\n", + " \"VibraCore\", f\"/{methode.lower()}/validation/single\", schema=single_payload\n", + " )\n", + " cfc = CFC_FACTOR_FLOORS[installation_type][\n", + " buildings.loc[buildings[\"name\"] == name][\"material\"].item()\n", + " ][\"Cfc\"]\n", + " fig = map_nuisance(\n", + " buildings,\n", + " source_location=location,\n", + " building_name=name,\n", + " response_dict=result,\n", + " cfc=cfc,\n", + " u_eff=0.54,\n", + " period=period,\n", + " )\n", + "\n", + " # add basemap\n", + " ctx.add_basemap(\n", + " fig.axes[0], crs=\"EPSG:28992\", source=ctx.providers.Esri.WorldTopoMap\n", + " )\n", + "\n", + " print(df_nuisance(response_dict=result, cfc=cfc, u_eff=0.54, period=period))" + ] + }, { "cell_type": "markdown", "id": "526b706d-95c2-420c-9f45-2e46c19480b0",