From 2b1bbfef08b92e2503901072f8eecacbdf9acad4 Mon Sep 17 00:00:00 2001 From: Johannes Mueller Date: Tue, 17 Dec 2024 11:05:18 +0100 Subject: [PATCH 1/2] Add broadcaster example of bridges of Prague Signed-off-by: Johannes Mueller --- docs/broadcaster.rst | 130 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 2 deletions(-) diff --git a/docs/broadcaster.rst b/docs/broadcaster.rst index 39906ab4..7406d2d6 100644 --- a/docs/broadcaster.rst +++ b/docs/broadcaster.rst @@ -147,6 +147,132 @@ example the the calls to the :class:`pylife.Broadcaster` are done inside You do need to deal with the :class:`pylife.Broadcaster` when you implement new calculation methods. Let's go through an example. -.. todo:: +Lets assume we have a collective of seasonal flood events on the river Vitava +in Prague. This is an oversimplified damage model, which assumes that we +multiply the water level of a flood event with a sensitivity value of a bridge +to calculate the damage that the flood events causes to the bridge. - **Sorry**, this is still to be written. +.. jupyter-execute:: + + from pylife import Broadcaster + + flood_events = pd.Series( + [10., 13., 9., 5.], + name="water_level", + index=pd.Index( + ["spring", "summer", "autumn", "winter"], + name="flood_event" + ) + ) + flood_events + +Lets assume some sensitivity value for the bridges of Prague. + +.. jupyter-execute:: + + sensitivities = pd.Series([2.3, 0.7, 2.7, 6.4, 3.9, 0.8], + name="sensitivity", + index=pd.Index( + [ + "Palackého most", + "Jiraskův most", + "Most Legií", + "Karlův most", + "Mánesův most", + "Centrův most" + ], + name="bridge" + ) + ) + sensitivities + + +Now we want to multiply the water levels with the sensitivity value in order +to get a damage value: + +.. jupyter-execute:: + + damage = flood_events * sensitivities + damage + +As we can see, this multiplication failed, as the indices of our two series do +not match. First we need to broadcast the two indices to a mapped hierarchical +index. + +.. jupyter-execute:: + + sens_mapped, flood_mapped = Broadcaster(flood_events).broadcast(sensitivities) + +Now we have a mapped flood values + +.. jupyter-execute:: + + flood_mapped + +and the mapped sensitivity values + +.. jupyter-execute:: + + sens_mapped + +These mapped series we can multiply. + +.. jupyter-execute:: + + damage = flood_mapped * sens_mapped + damage + +Now we can see for every bridge for every flood event the expected damage to +every bridge. We can now reduce this map to get the total damage of every +bridge during all flood events: + +.. jupyter-execute:: + + damage.groupby("bridge").sum() + + +Now let's assume that we have for each bridge some kind of protection measure +that reduces the damage. + +.. jupyter-execute:: + + protection = pd.Series( + [10.0, 15.0, 12.0, 25.0, 13.0, 17.0], + name="dwell_time", + index=pd.Index( + [ + "Palackého most", + "Jiraskův most", + "Most Legií", + "Karlův most", + "Mánesův most", + "Centrův most" + ], + name="bridge" + ) + ) + protection + +We also need divide the damage value by the protection value. Therefore we need +to broadcast the protection values to the damage values + +.. jupyter-execute:: + + protection_mapped, _ = Broadcaster(damage).broadcast(protection) + protection_mapped + +As you can see, the broadcaster recognized the common index name "bridge" and +did not spread it again. + +Now we can easily multiply the mapped protection values to the damage. + +.. jupyter-execute:: + + damage_with_protection = damage / protection + + +And we can again easily calculate the damage for a certain bridge + +.. jupyter-execute:: + + damage_with_protection.groupby("bridge").sum() From f778dc91d230519f4c7034521bbea3d588d6a3ff Mon Sep 17 00:00:00 2001 From: Johannes Mueller Date: Tue, 17 Dec 2024 13:19:43 +0100 Subject: [PATCH 2/2] Fix docs link to load collective Signed-off-by: Johannes Mueller --- docs/reference.rst | 3 +-- docs/stress/collective.rst | 6 ++++++ docs/stress/load_collective.rst | 6 ------ docs/stress/load_histogram.rst | 6 ------ docs/tutorials.rst | 10 +++++----- src/pylife/stress/collective/__init__.py | 17 +++++++++++++++++ 6 files changed, 29 insertions(+), 19 deletions(-) create mode 100644 docs/stress/collective.rst delete mode 100644 docs/stress/load_collective.rst delete mode 100644 docs/stress/load_histogram.rst diff --git a/docs/reference.rst b/docs/reference.rst index f020832f..549ced5d 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -16,8 +16,7 @@ Stress stress/index stress/equistress stress/rainflow - stress/load_collective - stress/load_histogram + stress/collective stress/stresssignal stress/timesignal stress/frequencysignal diff --git a/docs/stress/collective.rst b/docs/stress/collective.rst new file mode 100644 index 00000000..2de9a317 --- /dev/null +++ b/docs/stress/collective.rst @@ -0,0 +1,6 @@ +The module for load collectives +############################### + +.. automodule:: pylife.stress.collective + :undoc-members: + :members: diff --git a/docs/stress/load_collective.rst b/docs/stress/load_collective.rst deleted file mode 100644 index 2fc4f18a..00000000 --- a/docs/stress/load_collective.rst +++ /dev/null @@ -1,6 +0,0 @@ -The ``LoadCollective`` class -############################ - -.. autoclass:: pylife.stress.LoadCollective - :undoc-members: - :members: diff --git a/docs/stress/load_histogram.rst b/docs/stress/load_histogram.rst deleted file mode 100644 index 9a64275c..00000000 --- a/docs/stress/load_histogram.rst +++ /dev/null @@ -1,6 +0,0 @@ -The ``LoadHistogram`` class -########################### - -.. autoclass:: pylife.stress.LoadHistogram - :undoc-members: - :members: diff --git a/docs/tutorials.rst b/docs/tutorials.rst index b3a61fd4..44a49760 100644 --- a/docs/tutorials.rst +++ b/docs/tutorials.rst @@ -14,8 +14,8 @@ computer, you can use `MyBinder .. toctree:: :maxdepth: 1 - tutorials/woehler_curve.nblink - tutorials/load_collective.nblink - tutorials/stress-strength.rst - demos/fkm_nonlinear.nblink - demos/fkm_nonlinear_full.nblink + tutorials/woehler_curve + tutorials/load_collective + tutorials/stress-strength + demos/fkm_nonlinear + demos/fkm_nonlinear_full diff --git a/src/pylife/stress/collective/__init__.py b/src/pylife/stress/collective/__init__.py index 99c3ea7b..116bdaaa 100644 --- a/src/pylife/stress/collective/__init__.py +++ b/src/pylife/stress/collective/__init__.py @@ -14,6 +14,23 @@ # See the License for the specific language governing permissions and # limitations under the License. +""" +There are two ways to deal with a load collective. + +* :class:`~pylife.stress.LoadCollective` lets you keep every load hysteresis in + and then calculate the amplitude, meanstress and damage for each and every + hyteresis indivudually. + +* :class:`~pylife.stress.LoadHistogram` keeps the load information in a binned + histogram. That means that not each and every hystresis is stored + individually but there are bin classes for the load levels the hysteresis is + originating from and one for the levels the hysteresis is open. + +This :doc:`tutorial ` shows the difference and how +to use the two. + +""" + __author__ = "Johannes Mueller" __maintainer__ = __author__