From 8c60e9a2cf2ca97fec50099f2ebfb329e15f3a83 Mon Sep 17 00:00:00 2001 From: Christopher Teubert Date: Fri, 12 Jul 2024 15:22:17 -0700 Subject: [PATCH 1/3] update key --- examples/01_Simulation.ipynb | 36 +++++++-------- examples/04_New Models.ipynb | 18 ++++---- examples/full_lstm_model.py | 14 +++--- examples/lstm_model.py | 10 ++--- examples/new_model.py | 10 ++--- examples/noise.py | 2 +- examples/sensitivity.py | 4 +- examples/sim_battery_eol.py | 2 +- examples/vectorized.py | 2 +- src/progpy/data_models/data_model.py | 2 +- src/progpy/data_models/dmd.py | 4 +- src/progpy/data_models/lstm_model.py | 4 +- .../models/aircraft_model/small_rotorcraft.py | 4 +- src/progpy/predictors/monte_carlo.py | 4 +- src/progpy/prognostics_model.py | 45 +++++++++++-------- tests/test_base_models.py | 27 ++++++----- tests/test_data_model.py | 2 +- tests/test_predictors.py | 2 +- tests/test_sim_result.py | 4 +- tests/test_state_estimators.py | 2 +- tests/test_surrogates.py | 36 +++++++-------- tutorial.ipynb | 14 +++--- 22 files changed, 130 insertions(+), 118 deletions(-) diff --git a/examples/01_Simulation.ipynb b/examples/01_Simulation.ipynb index 436d224..da00d85 100644 --- a/examples/01_Simulation.ipynb +++ b/examples/01_Simulation.ipynb @@ -237,7 +237,7 @@ "source": [ "By default, `simulate_to_threshold` simulates until the first event occurs. In this case, that's `falling` (i.e., when the object begins falling). For this model `falling` will always occur before `impact`, but for many models you won't have such a strict ordering of events. \n", "\n", - "For users interested in when a specific event is reached, you can indicate which event(s) you'd like to simulate to using the `threshold_keys` argument. For example," + "For users interested in when a specific event is reached, you can indicate which event(s) you'd like to simulate to using the `events` argument. For example," ] }, { @@ -246,7 +246,7 @@ "metadata": {}, "outputs": [], "source": [ - "results = m.simulate_to_threshold(save_freq=0.5, threshold_keys='impact')\n", + "results = m.simulate_to_threshold(save_freq=0.5, events='impact')\n", "fig = results.outputs.plot(ylabel='Position (m)')\n", "fig = results.event_states.plot()" ] @@ -255,7 +255,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now the model simulated past the `falling` event untill the `impact` event occured. `threshold_keys` accepts a single event, or a list of events, so for models with many events you can specify a list of events where any will stop simulation." + "Now the model simulated past the `falling` event untill the `impact` event occured. `events` accepts a single event, or a list of events, so for models with many events you can specify a list of events where any will stop simulation." ] }, { @@ -273,7 +273,7 @@ "metadata": {}, "outputs": [], "source": [ - "results = m.simulate_to_threshold(save_freq=0.5, threshold_keys='impact', horizon=7)\n", + "results = m.simulate_to_threshold(save_freq=0.5, events='impact', horizon=7)\n", "fig = results.outputs.plot(ylabel='Position (m)')\n", "fig = results.event_states.plot()" ] @@ -291,7 +291,7 @@ "metadata": {}, "outputs": [], "source": [ - "results = m.simulate_to_threshold(save_freq=0.5, threshold_keys='impact', horizon=10)\n", + "results = m.simulate_to_threshold(save_freq=0.5, events='impact', horizon=10)\n", "fig = results.outputs.plot(ylabel='Position (m)')\n", "fig = results.event_states.plot()" ] @@ -318,7 +318,7 @@ "metadata": {}, "outputs": [], "source": [ - "results = m.simulate_to_threshold(save_freq=0.5, threshold_keys='impact', print=True, progress=True)" + "results = m.simulate_to_threshold(save_freq=0.5, events='impact', print=True, progress=True)" ] }, { @@ -970,7 +970,7 @@ "outputs": [], "source": [ "results = m.simulate_to_threshold(\n", - " threshold_keys='impact',\n", + " events='impact',\n", " dt=2.5,\n", " save_freq=2.5)\n", "fig = results.outputs.plot(ylabel='Position (m)')" @@ -992,7 +992,7 @@ "outputs": [], "source": [ "results = m.simulate_to_threshold(\n", - " threshold_keys='impact',\n", + " events='impact',\n", " dt=0.25,\n", " save_freq=0.25)\n", "fig = results.outputs.plot(ylabel='Position (m)')" @@ -1016,7 +1016,7 @@ "outputs": [], "source": [ "results = m.simulate_to_threshold(\n", - " threshold_keys='impact',\n", + " events='impact',\n", " dt=0.05,\n", " save_freq=0.05)\n", "fig = results.outputs.plot(ylabel='Position (m)')" @@ -1052,7 +1052,7 @@ "outputs": [], "source": [ "results = m.simulate_to_threshold(\n", - " threshold_keys='impact',\n", + " events='impact',\n", " dt=1,\n", " save_freq=1.5)\n", "print('Times saved: ', results.times)" @@ -1074,7 +1074,7 @@ "outputs": [], "source": [ "results = m.simulate_to_threshold(\n", - " threshold_keys='impact',\n", + " events='impact',\n", " dt=('auto', 1),\n", " save_freq=1.5)\n", "print('Times saved: ', results.times)" @@ -1118,7 +1118,7 @@ " return 0.25\n", " return 1\n", "\n", - "results=m.simulate_to_threshold(dt=next_time, save_freq= 0.25, threshold_keys='impact')\n", + "results=m.simulate_to_threshold(dt=next_time, save_freq= 0.25, events='impact')\n", "\n", "print(results.times)" ] @@ -1197,15 +1197,15 @@ "metadata": {}, "outputs": [], "source": [ - "results1 = m.simulate_to_threshold(threshold_keys='impact', dt=0.1, save_freq=0.1)\n", + "results1 = m.simulate_to_threshold(events='impact', dt=0.1, save_freq=0.1)\n", "fig = results1.outputs.plot(title='default')\n", "\n", "m['throwing_speed'] = 10\n", - "results2 = m.simulate_to_threshold(threshold_keys='impact', dt=0.1, save_freq=0.1)\n", + "results2 = m.simulate_to_threshold(events='impact', dt=0.1, save_freq=0.1)\n", "fig = results2.outputs.plot(title='slow')\n", "\n", "m['throwing_speed'] = 80\n", - "results3 = m.simulate_to_threshold(threshold_keys='impact', dt=0.1, save_freq=0.1)\n", + "results3 = m.simulate_to_threshold(events='impact', dt=0.1, save_freq=0.1)\n", "fig = results3.outputs.plot(title='fast')" ] }, @@ -1223,11 +1223,11 @@ "outputs": [], "source": [ "m_e = ThrownObject(g=-9.81) # Earth gravity\n", - "results_earth = m_e.simulate_to_threshold(threshold_keys='impact', dt=0.1, save_freq=0.1)\n", + "results_earth = m_e.simulate_to_threshold(events='impact', dt=0.1, save_freq=0.1)\n", "fig = results_earth.outputs.plot(title='Earth')\n", "\n", "m_j = ThrownObject(g=-24.79) # Jupiter gravity\n", - "results_jupiter = m_j.simulate_to_threshold(threshold_keys='impact', dt=0.1, save_freq=0.1)\n", + "results_jupiter = m_j.simulate_to_threshold(events='impact', dt=0.1, save_freq=0.1)\n", "fig = results_jupiter.outputs.plot(title='Jupiter')" ] }, @@ -1293,7 +1293,7 @@ "orig_nbformat": 4, "vscode": { "interpreter": { - "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49" + "hash": "1a1af0ee75eeea9e2e1ee996c87e7a2b11a0bebd85af04bb136d915cefc0abce" } } }, diff --git a/examples/04_New Models.ipynb b/examples/04_New Models.ipynb index 0bb2d2f..e3f2dc7 100644 --- a/examples/04_New Models.ipynb +++ b/examples/04_New Models.ipynb @@ -256,7 +256,7 @@ "outputs": [], "source": [ "m = ThrownObject()\n", - "save = m.simulate_to_threshold(print=True, save_freq=1, threshold_keys='impact', dt=0.1)" + "save = m.simulate_to_threshold(print=True, save_freq=1, events='impact', dt=0.1)" ] }, { @@ -547,7 +547,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We'll start by simulating to impact. We'll specify the `threshold_keys` to specifically indicate we are interested in impact. For more information on simulation, see 1. Simulation. " + "We'll start by simulating to impact. We'll specify the `events` to specifically indicate we are interested in impact. For more information on simulation, see 1. Simulation. " ] }, { @@ -558,7 +558,7 @@ "source": [ "# Simulate to impact\n", "event = 'impact'\n", - "simulated_results = m_st.simulate_to_threshold(threshold_keys=[event], dt=0.005, save_freq=1, print = True)\n", + "simulated_results = m_st.simulate_to_threshold(events=event, dt=0.005, save_freq=1, print = True)\n", "\n", "# Print result: \n", "print('The object hit the ground in {} seconds'.format(round(simulated_results.times[-1],2)))" @@ -938,7 +938,7 @@ "# Simulate to threshold \n", "m_matrix.simulate_to_threshold(\n", " print = True, \n", - " threshold_keys = 'impact', \n", + " events = 'impact', \n", " dt = 0.1, \n", " save_freq = 1)\n", "toc_matrix = time.perf_counter()" @@ -963,7 +963,7 @@ "tic_st = time.perf_counter()\n", "m_st.simulate_to_threshold(\n", " print = True, \n", - " threshold_keys = 'impact', \n", + " events = 'impact', \n", " dt = 0.1, \n", " save_freq = 1)\n", "toc_st = time.perf_counter()\n", @@ -1030,7 +1030,7 @@ "outputs": [], "source": [ "event = 'impact'\n", - "simulated_results = m_limits.simulate_to_threshold(threshold_keys=[event], dt=0.005, save_freq=1)\n", + "simulated_results = m_limits.simulate_to_threshold(events=event, dt=0.005, save_freq=1)\n", "\n", "print('Example: No State Limits')\n", "for i, state in enumerate(simulated_results.states):\n", @@ -1086,7 +1086,7 @@ "outputs": [], "source": [ "event = 'impact'\n", - "simulated_results = m_limits.simulate_to_threshold(threshold_keys=[event], dt=0.005, save_freq=1)\n", + "simulated_results = m_limits.simulate_to_threshold(events=event, dt=0.005, save_freq=1)\n", "\n", "print('Example: With State Limits')\n", "for i, state in enumerate(simulated_results.states):\n", @@ -1117,7 +1117,7 @@ "x0 = m_limits.initialize(u = {}, z = {})\n", "x0['x'] = -1 # Initial position value set to an unrealistic value of -1\n", "\n", - "simulated_results = m_limits.simulate_to_threshold(threshold_keys=[event], dt=0.005, save_freq=1, x = x0)\n", + "simulated_results = m_limits.simulate_to_threshold(events=event, dt=0.005, save_freq=1, x = x0)\n", "\n", "# Print states\n", "print('Example 2: With -1 as initial x value')\n", @@ -1373,7 +1373,7 @@ "metadata": {}, "outputs": [], "source": [ - "simulated_results = m.simulate_to_threshold(future_loading, threshold_keys=['EOD'], print = True)\n", + "simulated_results = m.simulate_to_threshold(future_loading, events='EOD', print = True)\n", "\n", "simulated_results.event_states.plot()\n", "plt.show()" diff --git a/examples/full_lstm_model.py b/examples/full_lstm_model.py index dfa6829..b8f36b4 100644 --- a/examples/full_lstm_model.py +++ b/examples/full_lstm_model.py @@ -30,11 +30,11 @@ def future_loading(t, x=None): # Step 1: Generate additional data # We will use data generated above, but we also want data at additional timesteps print('Generating data...') - data = m.simulate_to_threshold(future_loading, threshold_keys='impact', save_freq=TIMESTEP, dt=TIMESTEP) - data_half = m.simulate_to_threshold(future_loading, threshold_keys='impact', save_freq=TIMESTEP/2, dt=TIMESTEP/2) - data_quarter = m.simulate_to_threshold(future_loading, threshold_keys='impact', save_freq=TIMESTEP/4, dt=TIMESTEP/4) - data_twice = m.simulate_to_threshold(future_loading, threshold_keys='impact', save_freq=TIMESTEP*2, dt=TIMESTEP*2) - data_four = m.simulate_to_threshold(future_loading, threshold_keys='impact', save_freq=TIMESTEP*4, dt=TIMESTEP*4) + data = m.simulate_to_threshold(future_loading, events='impact', save_freq=TIMESTEP, dt=TIMESTEP) + data_half = m.simulate_to_threshold(future_loading, events='impact', save_freq=TIMESTEP/2, dt=TIMESTEP/2) + data_quarter = m.simulate_to_threshold(future_loading, events='impact', save_freq=TIMESTEP/4, dt=TIMESTEP/4) + data_twice = m.simulate_to_threshold(future_loading, events='impact', save_freq=TIMESTEP*2, dt=TIMESTEP*2) + data_four = m.simulate_to_threshold(future_loading, events='impact', save_freq=TIMESTEP*4, dt=TIMESTEP*4) # Step 2: Data Prep # We need to add the timestep as a input @@ -109,8 +109,8 @@ def future_loading3(t, x = None): # Use new dt, not used in training # Using a dt not used in training will demonstrate the model's # ability to handle different timesteps not part of training set - data = m.simulate_to_threshold(future_loading, threshold_keys='impact', dt=TIMESTEP*3, save_freq=TIMESTEP*3) - results3 = m2.simulate_to_threshold(future_loading3, threshold_keys='impact', dt=TIMESTEP*3, save_freq=TIMESTEP*3) + data = m.simulate_to_threshold(future_loading, events='impact', dt=TIMESTEP*3, save_freq=TIMESTEP*3) + results3 = m2.simulate_to_threshold(future_loading3, events='impact', dt=TIMESTEP*3, save_freq=TIMESTEP*3) # Step 6: Compare Results print('Comparing results...') diff --git a/examples/lstm_model.py b/examples/lstm_model.py index e012368..7968caf 100644 --- a/examples/lstm_model.py +++ b/examples/lstm_model.py @@ -36,7 +36,7 @@ def run_example(): def future_loading(t, x=None): return m.InputContainer({}) # No input for thrown object - data = m.simulate_to_threshold(future_loading, threshold_keys='impact', save_freq=TIMESTEP, dt=TIMESTEP) + data = m.simulate_to_threshold(future_loading, events='impact', save_freq=TIMESTEP, dt=TIMESTEP) # Step 2: Generate model # We'll use the LSTMStateTransitionModel class to generate a model @@ -91,10 +91,10 @@ def future_loading2(t, x=None): # We will use data generated above, but we also want data at additional timesteps print('\n------------------------------------------\nExample 2...') print('Generating additional data...') - data_half = m.simulate_to_threshold(future_loading, threshold_keys='impact', save_freq=TIMESTEP/2, dt=TIMESTEP/2) - data_quarter = m.simulate_to_threshold(future_loading, threshold_keys='impact', save_freq=TIMESTEP/4, dt=TIMESTEP/4) - data_twice = m.simulate_to_threshold(future_loading, threshold_keys='impact', save_freq=TIMESTEP*2, dt=TIMESTEP*2) - data_four = m.simulate_to_threshold(future_loading, threshold_keys='impact', save_freq=TIMESTEP*4, dt=TIMESTEP*4) + data_half = m.simulate_to_threshold(future_loading, events='impact', save_freq=TIMESTEP/2, dt=TIMESTEP/2) + data_quarter = m.simulate_to_threshold(future_loading, events='impact', save_freq=TIMESTEP/4, dt=TIMESTEP/4) + data_twice = m.simulate_to_threshold(future_loading, events='impact', save_freq=TIMESTEP*2, dt=TIMESTEP*2) + data_four = m.simulate_to_threshold(future_loading, events='impact', save_freq=TIMESTEP*4, dt=TIMESTEP*4) # Step 2: Data Prep # We need to add the timestep as a input diff --git a/examples/new_model.py b/examples/new_model.py index afd9053..46fbb3f 100644 --- a/examples/new_model.py +++ b/examples/new_model.py @@ -75,7 +75,7 @@ def future_load(t, x=None): # Step 3: Simulate to impact event = 'impact' - simulated_results = m.simulate_to_threshold(future_load, threshold_keys=[event], dt=0.005, save_freq=1, print = True) + simulated_results = m.simulate_to_threshold(future_load, events=event, dt=0.005, save_freq=1, print = True) # Print flight time print('The object hit the ground in {} seconds'.format(round(simulated_results.times[-1],2))) @@ -86,16 +86,16 @@ def future_load(t, x=None): # The first way to change the configuration is to pass in your desired config into construction of the model m = ThrownObject(g = grav_moon) - simulated_moon_results = m.simulate_to_threshold(future_load, threshold_keys=[event], dt=0.005, save_freq=1) + simulated_moon_results = m.simulate_to_threshold(future_load, events=event, dt=0.005, save_freq=1) grav_mars = -3.711 # You can also update the parameters after it's constructed m.parameters['g'] = grav_mars - simulated_mars_results = m.simulate_to_threshold(future_load, threshold_keys=[event], dt=0.005, save_freq=1) + simulated_mars_results = m.simulate_to_threshold(future_load, events=event, dt=0.005, save_freq=1) grav_venus = -8.87 m.parameters['g'] = grav_venus - simulated_venus_results = m.simulate_to_threshold(future_load, threshold_keys=[event], dt=0.005, save_freq=1) + simulated_venus_results = m.simulate_to_threshold(future_load, events=event, dt=0.005, save_freq=1) print('Time to hit the ground: ') print('\tvenus: {}s'.format(round(simulated_venus_results.times[-1],2))) @@ -103,7 +103,7 @@ def future_load(t, x=None): print('\tmars: {}s'.format(round(simulated_mars_results.times[-1],2))) print('\tmoon: {}s'.format(round(simulated_moon_results.times[-1],2))) - # We can also simulate until any event is met by neglecting the threshold_keys argument + # We can also simulate until any event is met by neglecting the events argument simulated_results = m.simulate_to_threshold(future_load, dt=0.005, save_freq=1) threshs_met = m.threshold_met(simulated_results.states[-1]) for (key, met) in threshs_met.items(): diff --git a/examples/noise.py b/examples/noise.py index ddc3b9a..2b06be0 100644 --- a/examples/noise.py +++ b/examples/noise.py @@ -17,7 +17,7 @@ def future_load(t=None, x=None): # Define configuration for simulation config = { - 'threshold_keys': 'impact', # Simulate until the thrown object has impacted the ground + 'events': 'impact', # Simulate until the thrown object has impacted the ground 'dt': 0.005, # Time step (s) 'save_freq': 0.5, # Frequency at which results are saved (s) } diff --git a/examples/sensitivity.py b/examples/sensitivity.py index 375f098..0e39ae4 100644 --- a/examples/sensitivity.py +++ b/examples/sensitivity.py @@ -22,7 +22,7 @@ def run_example(): eods = np.empty(len(thrower_height_range)) for (i, thrower_height) in zip(range(len(thrower_height_range)), thrower_height_range): m.parameters['thrower_height'] = thrower_height - simulated_results = m.simulate_to_threshold(threshold_keys=[event], dt =1e-3, save_freq =10) + simulated_results = m.simulate_to_threshold(events=event, dt =1e-3, save_freq =10) eods[i] = simulated_results.times[-1] # Step 4: Analysis @@ -36,7 +36,7 @@ def run_example(): eods = np.empty(len(throw_speed_range)) for (i, throw_speed) in zip(range(len(throw_speed_range)), throw_speed_range): m.parameters['throwing_speed'] = throw_speed - simulated_results = m.simulate_to_threshold(threshold_keys=[event], options={'dt':1e-3, 'save_freq':10}) + simulated_results = m.simulate_to_threshold(events=event, options={'dt':1e-3, 'save_freq':10}) eods[i] = simulated_results.times[-1] print('\nFor a reasonable range of throwing speeds, impact time is between {} and {}'.format(round(eods[0],3), round(eods[-1],3))) diff --git a/examples/sim_battery_eol.py b/examples/sim_battery_eol.py index d1fcaef..ad4de0f 100644 --- a/examples/sim_battery_eol.py +++ b/examples/sim_battery_eol.py @@ -37,7 +37,7 @@ def future_loading(t, x=None): options = { 'save_freq': 1000, # Frequency at which results are saved 'dt': 2, # Timestep - 'threshold_keys': ['InsufficientCapacity'], # Simulate to InsufficientCapacity + 'events': 'InsufficientCapacity', # Simulate to InsufficientCapacity 'print': True } simulated_results = batt.simulate_to_threshold(future_loading, **options) diff --git a/examples/vectorized.py b/examples/vectorized.py index 5ff54cd..57a178d 100644 --- a/examples/vectorized.py +++ b/examples/vectorized.py @@ -23,7 +23,7 @@ def future_load(t, x=None): # Step 3: Simulate to threshold # Here we are simulating till impact using the first state defined above - (times, inputs, states, outputs, event_states) = m.simulate_to_threshold(future_load, x = first_state, threshold_keys=['impact'], print = True, dt=0.1, save_freq=2) + (times, inputs, states, outputs, event_states) = m.simulate_to_threshold(future_load, x = first_state, events='impact', print = True, dt=0.1, save_freq=2) # Now lets do the same thing but only stop when all hit the ground def thresholds_met_eqn(thresholds_met): diff --git a/src/progpy/data_models/data_model.py b/src/progpy/data_models/data_model.py index 831f18d..410781a 100644 --- a/src/progpy/data_models/data_model.py +++ b/src/progpy/data_models/data_model.py @@ -120,7 +120,7 @@ def from_model(cls, m: PrognosticsModel, load_functions: list, **kwargs) -> "Dat # Only one function load_functions = [load_functions] - sim_cfg_params = ['dt', 't0', 'integration_method', 'save_freq', 'save_pts', 'horizon', 'first_output', 'threshold_keys', 'x', 'thresholds_met_eqn'] + sim_cfg_params = ['dt', 't0', 'integration_method', 'save_freq', 'save_pts', 'horizon', 'first_output', 'events', 'x', 'thresholds_met_eqn'] # Check format of cfg item and split into cfg for each sim if scalar for cfg in sim_cfg_params: diff --git a/src/progpy/data_models/dmd.py b/src/progpy/data_models/dmd.py index 5653ce0..730ae97 100644 --- a/src/progpy/data_models/dmd.py +++ b/src/progpy/data_models/dmd.py @@ -416,14 +416,14 @@ def next_state(self, x, u, _): return x - def simulate_to_threshold(self, future_loading_eqn, first_output=None, threshold_keys=None, **kwargs): + def simulate_to_threshold(self, future_loading_eqn, first_output=None, events=None, **kwargs): # Save keyword arguments same as DMD training for approximation kwargs_sim = kwargs.copy() kwargs_sim['save_freq'] = self.dt kwargs_sim['dt'] = self.dt # Simulate to threshold at DMD time step - results = super().simulate_to_threshold(future_loading_eqn, first_output, threshold_keys, **kwargs_sim) + results = super().simulate_to_threshold(future_loading_eqn, first_output, events, **kwargs_sim) # Interpolate results to be at user-desired time step if 'dt' in kwargs: diff --git a/src/progpy/data_models/lstm_model.py b/src/progpy/data_models/lstm_model.py index 6231e1f..4e27de4 100644 --- a/src/progpy/data_models/lstm_model.py +++ b/src/progpy/data_models/lstm_model.py @@ -615,7 +615,7 @@ def from_data(cls, inputs, outputs, event_states=None, t_met=None, **kwargs): return cls(output_model, state_model, event_state_model, t_met_model, history = history, **params) - def simulate_to_threshold(self, future_loading_eqn, first_output=None, threshold_keys=None, **kwargs): + def simulate_to_threshold(self, future_loading_eqn, first_output=None, events=None, **kwargs): t = kwargs.get('t0', 0) dt = kwargs.get('dt', 0.1) x = kwargs.get('x', self.initialize(future_loading_eqn(t), first_output)) @@ -665,7 +665,7 @@ def next_time(t, x): if kwargs['horizon'] < t: raise ValueError('Prediction horizon does not allow enough steps to fully initialize model') kwargs['horizon'] = kwargs['horizon'] - t - return super().simulate_to_threshold(future_loading_eqn, first_output, threshold_keys, **kwargs) + return super().simulate_to_threshold(future_loading_eqn, first_output, events, **kwargs) def plot_history(self, metrics=None): """ diff --git a/src/progpy/models/aircraft_model/small_rotorcraft.py b/src/progpy/models/aircraft_model/small_rotorcraft.py index b2f5390..068ed99 100644 --- a/src/progpy/models/aircraft_model/small_rotorcraft.py +++ b/src/progpy/models/aircraft_model/small_rotorcraft.py @@ -251,7 +251,7 @@ def threshold_met(self, x) -> dict: # Progress through the reference trajectory is saved in the state 'mission_complete' return {'TrajectoryComplete': x['mission_complete'] >= 1} - def simulate_to_threshold(self, future_loading_eqn, first_output=None, threshold_keys=None, **kwargs): + def simulate_to_threshold(self, future_loading_eqn, first_output=None, events=None, **kwargs): # Check for appropriately defined dt - must be same as vehicle model if 'dt' in kwargs and kwargs['dt'] != self.parameters['dt']: kwargs['dt'] = self.parameters['dt'] @@ -260,7 +260,7 @@ def simulate_to_threshold(self, future_loading_eqn, first_output=None, threshold kwargs['dt'] = self.parameters['dt'] # Simulate to threshold - sim_res = super().simulate_to_threshold(future_loading_eqn, first_output, threshold_keys, **kwargs) + sim_res = super().simulate_to_threshold(future_loading_eqn, first_output, events, **kwargs) return sim_res diff --git a/src/progpy/predictors/monte_carlo.py b/src/progpy/predictors/monte_carlo.py index 35172aa..9eed779 100644 --- a/src/progpy/predictors/monte_carlo.py +++ b/src/progpy/predictors/monte_carlo.py @@ -133,7 +133,7 @@ def predict(self, state: UncertainData, future_loading_eqn: Callable=None, **kwa (times, inputs, states, outputs, event_states) = simulate_to_threshold( future_loading_eqn, first_output, - threshold_keys=[], + events=[], **params ) else: @@ -154,7 +154,7 @@ def predict(self, state: UncertainData, future_loading_eqn: Callable=None, **kwa (t, u, xi, z, es) = simulate_to_threshold( future_loading_eqn, first_output, - threshold_keys=events_remaining, + events=events_remaining, **params ) diff --git a/src/progpy/prognostics_model.py b/src/progpy/prognostics_model.py index 2febd02..f4b27e5 100644 --- a/src/progpy/prognostics_model.py +++ b/src/progpy/prognostics_model.py @@ -656,14 +656,14 @@ def state_at_event(self, x, future_loading_eqn = lambda t,x=None: {}, **kwargs): } params.update(kwargs) - threshold_keys = self.events.copy() + events_remaining = self.events.copy() t = 0 state_at_event = {} - while len(threshold_keys) > 0: + while len(events_remaining) > 0: result = self.simulate_to_threshold(x = x, t0 = t, **params) for key, value in result.event_states[-1].items(): if value <= 0 and key not in state_at_event: - threshold_keys.remove(key) + events_remaining.remove(key) state_at_event[key] = result.states[-1] x = result.states[-1] t = result.times[-1] @@ -698,14 +698,14 @@ def time_of_event(self, x, future_loading_eqn = lambda t,x=None: {}, **kwargs) - } params.update(kwargs) - threshold_keys = self.events.copy() + events_remaining = self.events.copy() t = 0 time_of_event = {} - while len(threshold_keys) > 0: + while len(events_remaining) > 0: result = self.simulate_to_threshold(x = x, t0 = t, **params) for key, value in result.event_states[-1].items(): if value <= 0 and key not in time_of_event: - threshold_keys.remove(key) + events_remaining.remove(key) time_of_event[key] = result.times[-1] x = result.states[-1] t = result.times[-1] @@ -772,7 +772,7 @@ def simulate_to(self, time : float, future_loading_eqn: abc.Callable = lambda t, return self.simulate_to_threshold(future_loading_eqn, first_output, **kwargs) - def simulate_to_threshold(self, future_loading_eqn: abc.Callable = None, first_output = None, threshold_keys: list = None, **kwargs) -> namedtuple: + def simulate_to_threshold(self, future_loading_eqn: abc.Callable = None, first_output = None, events: list = None, **kwargs) -> namedtuple: """ Simulate prognostics model until any or specified threshold(s) have been met @@ -802,7 +802,7 @@ def simulate_to_threshold(self, future_loading_eqn: abc.Callable = None, first_o maximum time that the model will be simulated forward (s), e.g., horizon = 1000 \n first_output : OutputContainer, optional First measured output, needed to initialize state for some classes. Can be omitted for classes that don't use this - threshold_keys: abc.Sequence[str] or str, optional + events: abc.Sequence[str] or str, optional Keys for events that will trigger the end of simulation. If blank, simulation will occur if any event will be met () x : StateContainer, optional @@ -860,13 +860,20 @@ def simulate_to_threshold(self, future_loading_eqn: abc.Callable = None, first_o future_loading_eqn = lambda t,x=None: self.InputContainer({}) elif not (callable(future_loading_eqn)): raise ValueError("'future_loading_eqn' must be callable f(t)") + + if 'threshold_keys' in kwargs: + warn('Please use the keyword argument `events` instead of `threshold_keys`') + if events is None: + events = kwargs['threshold_keys'] + else: + warn('Both `events` and `threshold_keys` were set. `events` will be used.') - if isinstance(threshold_keys, str): + if isinstance(events, str): # A single threshold key - threshold_keys = [threshold_keys] + events = [events] - if threshold_keys and not all([key in self.events for key in threshold_keys]): - raise ValueError("threshold_keys must be event names") + if (events is not None) and not all([key in self.events for key in events]): + raise ValueError("`events` must be event names") # Configure config = { # Defaults @@ -930,20 +937,20 @@ def simulate_to_threshold(self, future_loading_eqn: abc.Callable = None, first_o # Threshold Met Equations def check_thresholds(thresholds_met): - t_met = [thresholds_met[key] for key in threshold_keys] + t_met = [thresholds_met[key] for key in events] if len(t_met) > 0 and not np.isscalar(list(t_met)[0]): return np.any(t_met) return any(t_met) if 'thresholds_met_eqn' in config: check_thresholds = config['thresholds_met_eqn'] - threshold_keys = [] - elif threshold_keys is None: - # Note: Setting threshold_keys to be all events if it is None - threshold_keys = self.events - elif len(threshold_keys) == 0: + events = [] + elif events is None: + # Note: Setting events to be all events if it is None + events = self.events + elif len(events) == 0: check_thresholds = lambda _: False - if len(threshold_keys) == 0 and config.get('thresholds_met_eqn', None) is None and 'horizon' not in kwargs: + if len(events) == 0 and config.get('thresholds_met_eqn', None) is None and 'horizon' not in kwargs: raise ValueError("Running simulate to threshold for a model with no events requires a horizon to be set. Otherwise simulation would never end.") # Initialization of save arrays diff --git a/tests/test_base_models.py b/tests/test_base_models.py index 7dfa3da..75c344d 100644 --- a/tests/test_base_models.py +++ b/tests/test_base_models.py @@ -601,28 +601,33 @@ def load(t, x=None): self.assertAlmostEqual(states[0]['t'], -1.0, 5) # Any event, manual - (times, inputs, states, outputs, event_states) = m.simulate_to_threshold(load, {'o1': 0.8}, dt=0.5, save_freq=1.0, threshold_keys=['e1', 'e2']) + (times, inputs, states, outputs, event_states) = m.simulate_to_threshold(load, {'o1': 0.8}, dt=0.5, save_freq=1.0, events=['e1', 'e2']) self.assertAlmostEqual(times[-1], 5.0, 5) # Only event 2 - (times, inputs, states, outputs, event_states) = m.simulate_to_threshold(load, {'o1': 0.8}, dt=0.5, save_freq=1.0, threshold_keys=['e2']) + (times, inputs, states, outputs, event_states) = m.simulate_to_threshold(load, {'o1': 0.8}, dt=0.5, save_freq=1.0, events=['e2']) + self.assertAlmostEqual(times[-1], 15.0, 5) + + # Only event 2 - threshold keys + with self.assertWarns(Warning): + (times, inputs, states, outputs, event_states) = m.simulate_to_threshold(load, {'o1': 0.8}, dt=0.5, save_freq=1.0, threshold_keys=['e2']) self.assertAlmostEqual(times[-1], 15.0, 5) # Threshold before event - (times, inputs, states, outputs, event_states) = m.simulate_to_threshold(load, {'o1': 0.8}, dt=0.5, save_freq=1.0, horizon=5.0, threshold_keys=['e2']) + (times, inputs, states, outputs, event_states) = m.simulate_to_threshold(load, {'o1': 0.8}, dt=0.5, save_freq=1.0, horizon=5.0, events='e2') self.assertAlmostEqual(times[-1], 5.0, 5) # Threshold after event - (times, inputs, states, outputs, event_states) = m.simulate_to_threshold(load, {'o1': 0.8}, dt=0.5, save_freq=1.0, horizon=20.0, threshold_keys=['e2']) + (times, inputs, states, outputs, event_states) = m.simulate_to_threshold(load, {'o1': 0.8}, dt=0.5, save_freq=1.0, horizon=20.0, events=['e2']) self.assertAlmostEqual(times[-1], 15.0, 5) # No thresholds - (times, inputs, states, outputs, event_states) = m.simulate_to_threshold(load, {'o1': 0.8}, dt=0.5, save_freq=1.0, horizon=20.0, threshold_keys=[]) + (times, inputs, states, outputs, event_states) = m.simulate_to_threshold(load, {'o1': 0.8}, dt=0.5, save_freq=1.0, horizon=20.0, events=[]) self.assertAlmostEqual(times[-1], 20.0, 5) # No thresholds and no horizon with self.assertRaises(ValueError): - (times, inputs, states, outputs, event_states) = m.simulate_to_threshold(load, {'o1': 0.8}, dt=0.5, save_freq=1.0, threshold_keys=[]) + (times, inputs, states, outputs, event_states) = m.simulate_to_threshold(load, {'o1': 0.8}, dt=0.5, save_freq=1.0, events=[]) # No events and no horizon m_noevents = OneInputNoOutputNoEventLM() @@ -633,7 +638,7 @@ def load(t, x=None): def thresh_met(thresholds): return all(thresholds.values()) config = {'dt': 0.5, 'save_freq': 1.0, 'horizon': 20.0, 'thresholds_met_eqn': thresh_met} - (times, inputs, states, outputs, event_states) = m.simulate_to_threshold(load, {'o1': 0.8}, **config, threshold_keys=['e1', 'e2']) + (times, inputs, states, outputs, event_states) = m.simulate_to_threshold(load, {'o1': 0.8}, **config, events=['e1', 'e2']) self.assertAlmostEqual(times[-1], 15.0, 5) # With no events and no horizon, but a threshold met eqn @@ -645,7 +650,7 @@ def thresh_met(thresholds): self.assertListEqual(times, [0, 0.5]) # Only one step with self.assertRaises(ValueError): - result = m.simulate_to_threshold(load, {'o1': 0.8}, threshold_keys=['e1', 'e2', 'e3'], dt=0.5, save_freq=1.0) + result = m.simulate_to_threshold(load, {'o1': 0.8}, events=['e1', 'e2', 'e3'], dt=0.5, save_freq=1.0) def test_sim_past_thresh(self): m = MockProgModel(process_noise=0.0) @@ -1087,14 +1092,14 @@ def future_load(t, x=None): # Create no drag model ('cd' = 0) m_nd.parameters['cd'] = 0 - simulated_results_nd = m_nd.simulate_to_threshold(future_load, threshold_keys=[event], dt=0.005, save_freq=1) + simulated_results_nd = m_nd.simulate_to_threshold(future_load, events=[event], dt=0.005, save_freq=1) # Create default drag model ('cd' = 0.007) m_df = ThrownObject(process_noise_dist='none') - simulated_results_df = m_df.simulate_to_threshold(future_load, threshold_keys=[event], dt=0.005, save_freq=1) + simulated_results_df = m_df.simulate_to_threshold(future_load, events=[event], dt=0.005, save_freq=1) # Create high drag model ('cd' = 1.0) m_hi = ThrownObject(process_noise_dist='none') m_hi.parameters['cd'] = 1 - simulated_results_hi = m_hi.simulate_to_threshold(future_load, threshold_keys=[event], dt=0.005, save_freq=1) + simulated_results_hi = m_hi.simulate_to_threshold(future_load, events=[event], dt=0.005, save_freq=1) # Test no drag simulated results different from default self.assertNotEqual(simulated_results_nd.times, simulated_results_df.times) diff --git a/tests/test_data_model.py b/tests/test_data_model.py index e3c1810..dc091a4 100644 --- a/tests/test_data_model.py +++ b/tests/test_data_model.py @@ -36,7 +36,7 @@ def _test_simple_case( def future_loading(t, x=None): return m.InputContainer({}) # No input for thrown object - data = m.simulate_to_threshold(future_loading, threshold_keys='impact', save_freq=TIMESTEP, dt=TIMESTEP) + data = m.simulate_to_threshold(future_loading, events='impact', save_freq=TIMESTEP, dt=TIMESTEP) if WITH_STATES: kwargs['states'] = [data.states, data.states] diff --git a/tests/test_predictors.py b/tests/test_predictors.py index 879f965..73a355e 100644 --- a/tests/test_predictors.py +++ b/tests/test_predictors.py @@ -55,7 +55,7 @@ def setUpClass(cls): cls._m = ThrownObject(process_noise=0, measurement_noise=0) def future_loading(t, x=None): return cls._m.InputContainer({}) - cls._s = cls._m.generate_surrogate([future_loading], state_keys=['v'], dt=0.1, save_freq=0.1, threshold_keys='impact') + cls._s = cls._m.generate_surrogate([future_loading], state_keys=['v'], dt=0.1, save_freq=0.1, events='impact') def test_pred_template(self): from predictor_template import TemplatePredictor diff --git a/tests/test_sim_result.py b/tests/test_sim_result.py index 2d5a401..ff20696 100644 --- a/tests/test_sim_result.py +++ b/tests/test_sim_result.py @@ -402,7 +402,7 @@ def future_loading(t, x=None): return {'i': i} m = MyBatt() - (times, inputs, states, outputs, event_states) = m.simulate_to_threshold(future_loading, threshold_keys=['EOD'], + (times, inputs, states, outputs, event_states) = m.simulate_to_threshold(future_loading, events='EOD', print=False) plot_test = event_states.plot() # Plot doesn't raise error @@ -442,7 +442,7 @@ def future_loading(t, x=None): return {'i': i} m = MyBatt() - named_results = m.simulate_to_threshold(future_loading, threshold_keys=['EOD'], print=False) + named_results = m.simulate_to_threshold(future_loading, events=['EOD'], print=False) times = named_results.times inputs = named_results.inputs states = named_results.states diff --git a/tests/test_state_estimators.py b/tests/test_state_estimators.py index d57129b..99bbf6e 100644 --- a/tests/test_state_estimators.py +++ b/tests/test_state_estimators.py @@ -545,7 +545,7 @@ def future_loading(t, x=None): config = { 'dt': 0.01, 'save_freq': 0.01, - 'threshold_keys': 'zero' + 'events': 'zero' } simulation_result = m.simulate_to_threshold(future_loading, **config) diff --git a/tests/test_surrogates.py b/tests/test_surrogates.py index 31678e2..15bf811 100644 --- a/tests/test_surrogates.py +++ b/tests/test_surrogates.py @@ -70,7 +70,7 @@ def test_surrogate_basic_thrown_object(self): def load_eqn(t=None, x=None): return m.InputContainer({}) - surrogate = m.generate_surrogate([load_eqn], dt=0.1, save_freq=0.25, threshold_keys='impact', training_noise=0) + surrogate = m.generate_surrogate([load_eqn], dt=0.1, save_freq=0.25, events='impact', training_noise=0) self.assertEqual(surrogate.dt, 0.25) self.assertListEqual(surrogate.states, [stateTest for stateTest in m.states if (stateTest not in m.outputs and stateTest not in m.events)] + m.outputs + m.events) @@ -79,7 +79,7 @@ def load_eqn(t=None, x=None): self.assertListEqual(surrogate.events, m.events) options = { - 'threshold_keys': 'impact', + 'events': 'impact', 'save_freq': 0.25, 'dt': 0.25 } @@ -177,7 +177,7 @@ def load_eqn(t=None, x=None): return m.InputContainer({}) # Perfect subset - surrogate = m.generate_surrogate([load_eqn], dt=0.1, save_freq=0.25, threshold_keys='impact', state_keys=['x', 'v'], training_noise=0) + surrogate = m.generate_surrogate([load_eqn], dt=0.1, save_freq=0.25, events='impact', state_keys=['x', 'v'], training_noise=0) surrogate.parameters['process_noise'] = 0 surrogate.parameters['measurement_noise'] = 0 self.assertEqual(surrogate.dt, 0.25) @@ -188,7 +188,7 @@ def load_eqn(t=None, x=None): self.assertListEqual(surrogate.events, m.events) options = { - 'threshold_keys': 'impact', + 'events': 'impact', 'save_freq': 0.25, 'dt': 0.25 } @@ -206,20 +206,20 @@ def load_eqn(t=None, x=None): self.assertEqual(surrogate_results.states[i]['impact'], surrogate_results.event_states[i]['impact']) # State subset - surrogate = m.generate_surrogate([load_eqn], dt=0.1, save_freq=0.25, threshold_keys='impact', state_keys=['v'], training_noise=0) + surrogate = m.generate_surrogate([load_eqn], dt=0.1, save_freq=0.25, events='impact', state_keys=['v'], training_noise=0) self.assertListEqual(surrogate.states, ['v'] + m.outputs + m.events + m.inputs) self.assertListEqual(surrogate.inputs, m.inputs) self.assertListEqual(surrogate.outputs, m.outputs) self.assertListEqual(surrogate.events, m.events) - surrogate = m.generate_surrogate([load_eqn], dt=0.1, save_freq=0.25, threshold_keys='impact', state_keys='v', training_noise=0) + surrogate = m.generate_surrogate([load_eqn], dt=0.1, save_freq=0.25, events='impact', state_keys='v', training_noise=0) self.assertListEqual(surrogate.states, ['v'] + m.outputs + m.events + m.inputs) self.assertListEqual(surrogate.inputs, m.inputs) self.assertListEqual(surrogate.outputs, m.outputs) self.assertListEqual(surrogate.events, m.events) options = { - 'threshold_keys': 'impact', + 'events': 'impact', 'save_freq': 0.25, 'dt': 0.25 } @@ -237,15 +237,15 @@ def load_eqn(t=None, x=None): self.assertEqual(surrogate_results.states[i]['impact'], surrogate_results.event_states[i]['impact']) # Events subset - surrogate = m.generate_surrogate([load_eqn], dt=0.1, save_freq=0.25, threshold_keys='impact', event_keys=['impact'], training_noise=0) - surrogate = m.generate_surrogate([load_eqn], dt=0.1, save_freq=0.25, threshold_keys='impact', event_keys='impact', training_noise=0) + surrogate = m.generate_surrogate([load_eqn], dt=0.1, save_freq=0.25, events='impact', event_keys=['impact'], training_noise=0) + surrogate = m.generate_surrogate([load_eqn], dt=0.1, save_freq=0.25, events='impact', event_keys='impact', training_noise=0) self.assertListEqual(surrogate.states, [stateTest for stateTest in m.states if (stateTest not in m.inputs and stateTest not in m.outputs and stateTest not in m.events)] + m.outputs + ['impact'] + m.inputs) self.assertListEqual(surrogate.inputs, m.inputs) self.assertListEqual(surrogate.outputs, m.outputs) self.assertListEqual(surrogate.events, ['impact']) options = { - 'threshold_keys': 'impact', + 'events': 'impact', 'save_freq': 0.25, 'dt': 0.25 } @@ -261,14 +261,14 @@ def load_eqn(t=None, x=None): self.assertEqual(surrogate_results.states[i]['impact'], surrogate_results.event_states[i]['impact']) # Outputs - Empty - surrogate = m.generate_surrogate([load_eqn], dt=0.1, save_freq=0.25, threshold_keys='impact', output_keys=[], training_noise=0) + surrogate = m.generate_surrogate([load_eqn], dt=0.1, save_freq=0.25, events='impact', output_keys=[], training_noise=0) self.assertListEqual(surrogate.states, m.states + m.events + m.inputs) self.assertListEqual(surrogate.inputs, m.inputs) self.assertListEqual(surrogate.outputs, []) self.assertListEqual(surrogate.events, m.events) options = { - 'threshold_keys': 'impact', + 'events': 'impact', 'save_freq': 0.25, 'dt': 0.25 } @@ -289,8 +289,8 @@ def test_surrogate_thrown_object_with_noise(self): def load_eqn(t=None, x=None): return m.InputContainer({}) - surrogate = m.generate_surrogate([load_eqn], dt=0.1, save_freq=0.25, threshold_keys='impact', training_noise=0) - surrogate_noise = m.generate_surrogate([load_eqn], dt=0.1, save_freq=0.25, threshold_keys='impact', training_noise=0.01) + surrogate = m.generate_surrogate([load_eqn], dt=0.1, save_freq=0.25, events='impact', training_noise=0) + surrogate_noise = m.generate_surrogate([load_eqn], dt=0.1, save_freq=0.25, events='impact', training_noise=0.01) self.assertEqual(surrogate.dt, 0.25) self.assertListEqual(surrogate.states, surrogate_noise.states) @@ -299,7 +299,7 @@ def load_eqn(t=None, x=None): self.assertListEqual(surrogate.events, surrogate_noise.events) options = { - 'threshold_keys': 'impact', + 'events': 'impact', 'save_freq': 0.25, 'dt': 0.25 } @@ -510,14 +510,14 @@ def load_eqn(t=None, x=None): # treat warnings as exceptions with self.assertWarns(UserWarning): - surrogate = m.generate_surrogate([load_eqn], dt = 0.1, save_freq = 0.25, threshold_keys = 'impact', state_keys = ['v'], training_noise = 0) + surrogate = m.generate_surrogate([load_eqn], dt = 0.1, save_freq = 0.25, events = 'impact', state_keys = ['v'], training_noise = 0) warnings.filterwarnings("error") warnings.filterwarnings("ignore", category=DeprecationWarning) # This is needed to check that warning doesn't occur try: # Set sufficiently large failure tolerance - surrogate = m.generate_surrogate([load_eqn], dt = 0.1, save_freq = 0.25, threshold_keys = 'impact', state_keys = ['v'], stability_tol=1e99) + surrogate = m.generate_surrogate([load_eqn], dt = 0.1, save_freq = 0.25, events = 'impact', state_keys = ['v'], stability_tol=1e99) except Warning as w: if w is not DeprecationWarning: # Ignore deprecation warnings self.fail('Warning raised') @@ -531,7 +531,7 @@ def test_surrogate_use_error_cases(self): def load_eqn(t=None, x=None): return m.InputContainer({}) - surrogate = m.generate_surrogate([load_eqn], dt=0.1, save_freq=0.25, threshold_keys='impact', training_noise=0) + surrogate = m.generate_surrogate([load_eqn], dt=0.1, save_freq=0.25, events='impact', training_noise=0) # Reset warnings seen so warning will occur from progpy import exceptions diff --git a/tutorial.ipynb b/tutorial.ipynb index d7b5b05..8150b05 100644 --- a/tutorial.ipynb +++ b/tutorial.ipynb @@ -444,7 +444,7 @@ "\n", "# Define configuration for simulation\n", "config = {\n", - " 'threshold_keys': 'impact', # Simulate until the thrown object has impacted the ground\n", + " 'events': 'impact', # Simulate until the thrown object has impacted the ground\n", " 'dt': 0.005, # Time step (s)\n", " 'save_freq': 0.5, # Frequency at which results are saved (s)\n", "}\n", @@ -581,7 +581,7 @@ "m.parameters['measurement_noise'] = False\n", "\n", "# Simulate to a threshold\n", - "(times, _, states, outputs, _) = m.simulate_to_threshold(future_load, dt=config['dt'], threshold_keys='impact', save_pts=[1, 5, 6, 7])\n", + "(times, _, states, outputs, _) = m.simulate_to_threshold(future_load, dt=config['dt'], events='impact', save_pts=[1, 5, 6, 7])\n", "\n", "# Print Results\n", "print('states:')\n", @@ -609,7 +609,7 @@ "outputs": [], "source": [ "# Simulate to a threshold\n", - "(times, _, states, outputs, _) = m.simulate_to_threshold(future_load, dt=config['dt'], threshold_keys='impact', save_pts=[1, 2, 3, 4, 5, 5.5, 6, 6.25, 6.5, 6.75, 7, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.8])\n", + "(times, _, states, outputs, _) = m.simulate_to_threshold(future_load, dt=config['dt'], events='impact', save_pts=[1, 2, 3, 4, 5, 5.5, 6, 6.25, 6.5, 6.75, 7, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.8])\n", "\n", "# Print Results\n", "print('states:')\n", @@ -659,7 +659,7 @@ "outputs": [], "source": [ "# Simulate to a threshold\n", - "(times, _, states, outputs, _) = m.simulate_to_threshold(future_load, dt=0.2, threshold_keys='impact', save_pts=[1, 2.5, 3, 4.5, 6, 7.5])\n", + "(times, _, states, outputs, _) = m.simulate_to_threshold(future_load, dt=0.2, events='impact', save_pts=[1, 2.5, 3, 4.5, 6, 7.5])\n", "\n", "# Print Results\n", "print('states:')\n", @@ -687,7 +687,7 @@ "outputs": [], "source": [ "# Simulate to a threshold\n", - "(times, _, states, outputs, _) = m.simulate_to_threshold(future_load, dt='auto', threshold_keys='impact', save_pts=[1, 2.5, 3, 4.5, 6, 7.5])\n", + "(times, _, states, outputs, _) = m.simulate_to_threshold(future_load, dt='auto', events='impact', save_pts=[1, 2.5, 3, 4.5, 6, 7.5])\n", "\n", "# Print Results\n", "print('states:')\n", @@ -715,7 +715,7 @@ "outputs": [], "source": [ "# Simulate to a threshold\n", - "(times, _, states, outputs, _) = m.simulate_to_threshold(future_load, dt=('auto', 0.2), threshold_keys='impact', save_pts=[1, 2.5, 3, 4.5, 6, 7.5])\n", + "(times, _, states, outputs, _) = m.simulate_to_threshold(future_load, dt=('auto', 0.2), events='impact', save_pts=[1, 2.5, 3, 4.5, 6, 7.5])\n", "\n", "# Print Results\n", "print('states:')\n", @@ -934,7 +934,7 @@ " return m.InputContainer({}) # No loading\n", "event = 'impact' # Simulate until impact\n", "\n", - "(times, inputs, states, outputs, event_states) = m.simulate_to_threshold(future_load, threshold_keys=[event], dt=0.005, save_freq=1)\n", + "(times, inputs, states, outputs, event_states) = m.simulate_to_threshold(future_load, events=[event], dt=0.005, save_freq=1)\n", "\n", "# Plot results\n", "event_states.plot(ylabel= ['falling', 'impact'], compact= False)\n", From 1ace59ab59a4b2c260dacf67bf4450aff6c01561 Mon Sep 17 00:00:00 2001 From: Christopher Teubert Date: Fri, 12 Jul 2024 16:16:07 -0700 Subject: [PATCH 2/3] fix events conflict --- src/progpy/predictors/monte_carlo.py | 12 ++++++++---- src/progpy/predictors/predictor.py | 3 --- src/progpy/predictors/unscented_transform.py | 15 +++++++++------ tests/test_horizon.py | 2 +- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/progpy/predictors/monte_carlo.py b/src/progpy/predictors/monte_carlo.py index 9eed779..f2f8a95 100644 --- a/src/progpy/predictors/monte_carlo.py +++ b/src/progpy/predictors/monte_carlo.py @@ -31,7 +31,7 @@ class MonteCarlo(Predictor): 'n_samples': None } - def predict(self, state: UncertainData, future_loading_eqn: Callable=None, **kwargs) -> PredictionResults: + def predict(self, state: UncertainData, future_loading_eqn: Callable=None, events=None, **kwargs) -> PredictionResults: """ Perform a single prediction @@ -91,7 +91,11 @@ def predict(self, state: UncertainData, future_loading_eqn: Callable=None, **kwa elif isinstance(state, UnweightedSamples) and params['n_samples'] is None: params['n_samples'] = len(state) # number of samples is from provided state - if len(params['events']) == 0 and 'horizon' not in params: + if events is None: + # Predict to all events + # change to list because of limits of jsonify + events = list(self.model.events) + if len(events) == 0 and 'horizon' not in params: raise ValueError("If specifying no event (i.e., simulate to time), must specify horizon") # Sample from state if n_samples specified or state is not UnweightedSamples (Case 2) @@ -129,7 +133,7 @@ def predict(self, state: UncertainData, future_loading_eqn: Callable=None, **kwa if 'save_freq' in params and not isinstance(params['save_freq'], tuple): params['save_freq'] = (params['t0'], params['save_freq']) - if len(params['events']) == 0: # Predict to time + if len(events) == 0: # Predict to time (times, inputs, states, outputs, event_states) = simulate_to_threshold( future_loading_eqn, first_output, @@ -137,7 +141,7 @@ def predict(self, state: UncertainData, future_loading_eqn: Callable=None, **kwa **params ) else: - events_remaining = params['events'].copy() + events_remaining = events.copy() times = [] inputs = SimResult(_copy=False) diff --git a/src/progpy/predictors/predictor.py b/src/progpy/predictors/predictor.py index 6e433c5..0a52fd5 100644 --- a/src/progpy/predictors/predictor.py +++ b/src/progpy/predictors/predictor.py @@ -39,9 +39,6 @@ def __init__(self, model, **kwargs): self.model = model self.parameters = deepcopy(self.default_parameters) - # Events to predict to - must be a list - # This is because of limitations with jsonify for sets - self.parameters['events'] = list(self.model.events.copy()) self.parameters.update(kwargs) @abstractmethod diff --git a/src/progpy/predictors/unscented_transform.py b/src/progpy/predictors/unscented_transform.py index a714ee9..e8e3012 100644 --- a/src/progpy/predictors/unscented_transform.py +++ b/src/progpy/predictors/unscented_transform.py @@ -123,7 +123,7 @@ def state_transition(x, dt): self.filter = kalman.UnscentedKalmanFilter(num_states, num_measurements, self.parameters['dt'], measure, state_transition, self.sigma_points) self.filter.Q = self.parameters['Q'] - def predict(self, state, future_loading_eqn: Callable = None, **kwargs) -> PredictionResults: + def predict(self, state, future_loading_eqn: Callable = None, events=None, **kwargs) -> PredictionResults: """ Perform a single prediction @@ -175,11 +175,14 @@ def predict(self, state, future_loading_eqn: Callable = None, **kwargs) -> Predi params = deepcopy(self.parameters) # copy parameters params.update(kwargs) # update for specific run - if len(params['events']) == 0 and 'horizon' not in params: + if events is None: + # Predict to all events + # change to list because of limits of jsonify + events = list(self.model.events) + if len(events) == 0 and 'horizon' not in params: raise ValueError("If specifying no event (i.e., simulate to time), must specify horizon") # Optimizations - events_to_predict = params['events'] dt = params['dt'] model = self.model filt = self.filter @@ -196,8 +199,8 @@ def predict(self, state, future_loading_eqn: Callable = None, **kwargs) -> Predi # Setup first states t = params['t0'] save_pt_index = 0 - ToE = {key: [float('nan') for i in range(n_points)] for key in events_to_predict} # Keep track of final ToE values - last_state = {key: [None for i in range(n_points)] for key in events_to_predict} # Keep track of final state values + ToE = {key: [float('nan') for i in range(n_points)] for key in events} # Keep track of final ToE values + last_state = {key: [None for i in range(n_points)] for key in events} # Keep track of final state values times = [] inputs = [] @@ -239,7 +242,7 @@ def update_all(): t_met = threshold_met(x) # Check Thresholds - for key in events_to_predict: + for key in events: if t_met[key]: if isnan(ToE[key][i]): # First time event has been reached diff --git a/tests/test_horizon.py b/tests/test_horizon.py index c9c226f..ccf3473 100644 --- a/tests/test_horizon.py +++ b/tests/test_horizon.py @@ -36,7 +36,7 @@ def future_loading(t, x=None): # With this horizon, all samples will reach 'falling', but only some will reach 'impact' PREDICTION_HORIZON = 2.127 STEP_SIZE = 0.001 - mc_results = mc.predict(initial_state, future_loading, dt=STEP_SIZE, horizon = PREDICTION_HORIZON) + mc_results = mc.predict(initial_state, future_loading, dt=STEP_SIZE, horizon=PREDICTION_HORIZON) # 'falling' happens before the horizon is met falling_res = [mc_results.time_of_event[iter]['falling'] for iter in range(mc.parameters['n_samples']) if mc_results.time_of_event[iter]['falling'] is not None] From a71717520a50341b9bc94aaaa10bded7c38e63d6 Mon Sep 17 00:00:00 2001 From: Christopher Teubert Date: Mon, 15 Jul 2024 12:55:00 -0700 Subject: [PATCH 3/3] Update examples/01_Simulation.ipynb Co-authored-by: Katy Jarvis Griffith <55932920+kjjarvis@users.noreply.github.com> --- examples/01_Simulation.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/01_Simulation.ipynb b/examples/01_Simulation.ipynb index da00d85..dbbc75f 100644 --- a/examples/01_Simulation.ipynb +++ b/examples/01_Simulation.ipynb @@ -255,7 +255,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now the model simulated past the `falling` event untill the `impact` event occured. `events` accepts a single event, or a list of events, so for models with many events you can specify a list of events where any will stop simulation." + "Now the model simulated past the `falling` event until the `impact` event occurred. `events` accepts a single event, or a list of events, so for models with many events you can specify a list of events where any will stop simulation." ] }, {