Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wedge load noise #97

Merged
merged 3 commits into from
Sep 15, 2023
Merged
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
2 changes: 1 addition & 1 deletion src/progpy/loading/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
# National Aeronautics and Space Administration. All Rights Reserved.

from progpy.loading.moving_average import MovingAverage
from progpy.loading.gaussian_wrapper import GaussianNoiseLoadWrapper
from progpy.loading.gaussian_wrapper import GaussianNoiseLoadWrapper, GaussianNoiseWrapper
from progpy.loading.piecewise import Piecewise
25 changes: 19 additions & 6 deletions src/progpy/loading/gaussian_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import numpy as np


class GaussianNoiseLoadWrapper():
class GaussianNoiseWrapper():
"""
.. versionadded:: 1.5.0

Expand All @@ -22,6 +22,10 @@ class GaussianNoiseLoadWrapper():
-------------------
seed: {int, SeedSequence, BitGenerator, Generator}, optional
The seed for random number generator. This can be set to make results repeatable.
std_slope: float, optional
The increase in standard deviation per second of time. Std is the standard deviation at t<=t0.
t0: float, optional
teubert marked this conversation as resolved.
Show resolved Hide resolved
Initial time (at which standard deviation is std). After t0, the standard deviation increases at a rate of std_slope.

Example
-------
Expand All @@ -30,23 +34,32 @@ class GaussianNoiseLoadWrapper():
>>> future_load = GaussianNoiseLoadWrapper(future_load, STANDARD_DEV)
>>> m.simulate_to_threshold(future_load)
"""
def __init__(self, fcn: Callable, std: float, seed: int = None):
def __init__(self, fcn: Callable, std: float, seed: int = None, std_slope: float = 0, t0: float = 0):
self.fcn = fcn
self.std = std
self.std_slope = std_slope
self.t0 = t0
self.gen = np.random.default_rng(seed)

def __call__(self, t: float, x=None):
"""
Return the load with noise added

Args:
t (float): Time (s)
x (StateContainer, optional): Current state. Defaults to None.
Arguments
------------
t: float
Time (s)
x: StateContainer, optional
Current state. Defaults to None.

Returns:
InputContainer: The load with noise added
"""
input = self.fcn(t, x)
std = self.std + self.std_slope*(t-self.t0) if t > self.t0 else self.std
for key, value in input.items():
input[key] = self.gen.normal(value, self.std)
input[key] = self.gen.normal(value, std)
return input

# Old name kept for backwards compatability
GaussianNoiseLoadWrapper = GaussianNoiseWrapper
teubert marked this conversation as resolved.
Show resolved Hide resolved
47 changes: 42 additions & 5 deletions tests/test_loading.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Copyright © 2021 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved.

import numpy as np
import unittest

from progpy.loading import Piecewise, GaussianNoiseLoadWrapper
from progpy.loading import Piecewise, GaussianNoiseWrapper


class Testloading(unittest.TestCase):
Expand Down Expand Up @@ -30,21 +31,57 @@ def loading(t, x=None):
return {'a': 10}

# Default: two values should be different (because of randomness)
loading_with_noise = GaussianNoiseLoadWrapper(loading, 10)
loading_with_noise = GaussianNoiseWrapper(loading, 10)
load1 = loading_with_noise(10)

loading_with_noise = GaussianNoiseLoadWrapper(loading, 10)
loading_with_noise = GaussianNoiseWrapper(loading, 10)
load2 = loading_with_noise(10)

self.assertNotEqual(load1['a'], load2['a'])

# Setting seed, two values should be the same now
loading_with_noise = GaussianNoiseLoadWrapper(loading, 10, seed=550)
loading_with_noise = GaussianNoiseWrapper(loading, 10, seed=550)
load1 = loading_with_noise(10)

loading_with_noise = GaussianNoiseLoadWrapper(loading, 10, seed=550)
loading_with_noise = GaussianNoiseWrapper(loading, 10, seed=550)
load2 = loading_with_noise(10)
self.assertEqual(load1['a'], load2['a'])

def test_wedge_gaussian_load(self):
def loading(t, x=None):
return {'a': 10}

loading_with_noise = GaussianNoiseWrapper(loading, 0, std_slope=10)

# Standard deviation at t=0 should be 0.
self.assertEqual(
loading_with_noise(0),
loading_with_noise(0))

# At any greater time, the standard deviation is not 0
self.assertNotEqual(
loading_with_noise(10),
loading_with_noise(10))

# Check that standard deviation increases with time.
values = [loading_with_noise(10)['a'] for _ in range(20)]
std = np.std(values)

values = [loading_with_noise(1e4)['a'] for _ in range(20)]
std2 = np.std(values)
self.assertGreater(std2, std)

# Check t0 functionality
loading_with_noise.t0 = 10
self.assertEqual( # Case t<t0
loading_with_noise(5),
loading_with_noise(5))
self.assertEqual( # Case t==t0
loading_with_noise(10),
loading_with_noise(10))
self.assertNotEqual( # Case t > t0
loading_with_noise(11),
loading_with_noise(11))

# This allows the module to be executed directly
def main():
Expand Down
Loading