--- jupytext: text_representation: extension: .md format_name: myst format_version: 0.13 jupytext_version: 1.17.2 kernelspec: display_name: Python 3 (ipykernel) language: python name: python3 --- (changing-time-res-example)= # Changing time resolution It is usually not possible to change the time resolution (start time, end time, time unit, and time step length) of the optimization after it has been specified in SHOP. However, this is now possible with the online SHOP functionality. This example will show how the time resolution can be changed between iterations in SHOP, and how SHOP handles the resampling of time series data. The `SHOP_ONLINE_OPTIMIZATION` license in the "Online Optimization Capabilities" license group is required to change the time resolution after it has been set the first time in SHOP. The following [YAML](yaml-standard) model will be used as a base for the rest of the example: - [](model.yaml) ```{code-cell} ipython3 from pyshop import ShopSession import pandas as pd import plotly.graph_objects as go pd.options.plotting.backend = "plotly" ``` The original time resolution has a horizon of three days with hourly time resolution: ```{code-cell} ipython3 shop = ShopSession() shop.load_yaml("model.yaml") time_res_orig = shop.get_time_resolution() start_orig = time_res_orig["starttime"] end_orig = time_res_orig["endtime"] unit_orig = time_res_orig["timeunit"] step_size_orig = time_res_orig["timeresolution"] print(f"Start time: {start_orig}") print(f"End time: {end_orig}") print(f"Time unit: {unit_orig}") print(f"Step sizes: {step_size_orig.values}") ``` First, a single iteration with this hourly time resolution is performed, and the [reservoir volume](reservoir:storage) for Reservoir2 is retrieved: ```{code-cell} ipython3 shop.start_sim([],3) rsv = shop.model.reservoir["Reservoir2"] storage_orig = rsv.storage.get() ``` Now, the start and end times of the optimization are shifted 24 hours forward in time, and the step size is changed to be 15 minutes. Note that it is required to set the command [activate online_optimization /on](activate_online_optimization) before changing the time resolution in an existing model: ```{code-cell} ipython3 start_new = start_orig + pd.Timedelta(hours = 24) end_new = end_orig + pd.Timedelta(hours = 24) shop.activate_online_optimization("on", []) shop.set_time_resolution(start_new, end_new, "minute", pd.Series([15], index=[start_new])) storage_resampled = rsv.storage.get() ``` After resetting the time resolution, all existing time series have automatically been resampled. The original and resampled reservoir trajectories are plotted in the figure below: ```{code-cell} ipython3 fig = go.Figure() fig.add_trace(go.Scatter(x=[start_new, start_new], y=[min(storage_orig)*0.9, max(storage_orig)*1.1], name="new start time", line=dict(dash='dash'))) fig.add_trace(go.Scatter(x=[end_orig, end_orig], y=[min(storage_orig)*0.9, max(storage_orig)*1.1], name="original end time", line=dict(dash='dash'))) fig.add_trace(go.Scatter(x=storage_orig.index, y=storage_orig.values, name="Original volume")) fig.add_trace(go.Scatter(x=storage_resampled.index, y=storage_resampled.values, name="Resampled volume", line=dict(dash='dash'))) fig.update_layout(xaxis_title="Time", yaxis_title="Volume [Mm3]",title="Reservoir volume before and after changing time resolution") fig.show() ``` Note that SHOP keeps the data before the new start time of the optimization as historical values for the time series. These values are not resampled to 15 minute intervals like the other values inside the new optimization horizon. Since SHOP does not know what the reservoir volume (or any other time series) are supposed to be in the interval between the original and new end times, the last value of the time series is forward filled into the unknown time steps. It is important that the user sets all input time series again to have proper data for the whole period, otherwise the price, inflow, etc. will be constant. Similarly, the time series will be constant for all new time steps inside the old hourly time steps, and so input data with a 15 minute resolution (such as the spot price) should also be set again. To simplify this example, we will reset the time resolution again so that the new and original end times are identical: ```{code-cell} ipython3 end_new = end_orig shop.set_time_resolution(start_new, end_new, "minute", pd.Series([15], index=[start_new])) ``` Now we run a new iteration with the updated time resolution. We also switch to incremental iterations to lock in the hourly unit commitment status calculated with the faster hourly model. This means that the units are on or off for whole clock hours instead of potentially switching on or off every 15 minutes. This also helps with calculation time since running the full iterations with hourly time resolution is faster than the more granular 15 minute time resolution: ```{code-cell} ipython3 shop.set_code("incremental", []) shop.start_sim([],1) ``` The reservoir storage results compared to the original hourly run is shown below: ```{code-cell} ipython3 storage_final = rsv.storage.get() fig = go.Figure() fig.add_trace(go.Scatter(x=[start_new, start_new], y=[min(storage_orig)*0.9, max(storage_orig)*1.1], name="New start time", line=dict(dash='dash'))) fig.add_trace(go.Scatter(x=storage_orig.index, y=storage_orig.values, name="Hourly run")) fig.add_trace(go.Scatter(x=storage_final.index, y=storage_final.values, name="15 min run", line=dict(dash='dash'))) fig.update_layout(xaxis_title="Time", yaxis_title="Volume [Mm3]",title="Reservoir volume in the hourly and 15 min model") fig.show() ``` We also see that the unit commitment status of the generator Plant1_G1 is constant for every clock hour since the commitment was decided based on the hourly model. Also note the hourly historical time series that is returned between the old and new start times: ```{code-cell} ipython3 gen = shop.model.generator["Plant1_G1"] committed = gen.committed_out.get() fig = go.Figure() fig.add_trace(go.Scatter(x=[start_new, start_new], y=[-0.1, 1.1], name="New start time", line=dict(dash='dash'))) fig.add_trace(go.Scatter(x=committed.index, y=committed.values, name="Committed status", mode="lines+markers")) fig.update_layout(xaxis_title="Time", yaxis_title="On/off status",title=f"Commitment status of generator {gen.get_name()}") fig.show() ``` SHOP does not save the entire raw input time series that was set by the user, so changing the step size of the time resolution back and forth will result in a loss of precision. This is due to resampling an already resampled time series, but is mostly a problem when changing to a coarser time step size. The resampling also does not consider whether the data should be averaged, linearly interpolated, etc., the value at the start of the new time step is simply the value of the old time series at that point in time.